A zero-build, fine-grained reactivity library leveraging the TC39 Signals proposal.
Iota delivers the microscopic DOM mutation performance of compiled frameworks (like Solid or Svelte) entirely at runtime. It bypasses the Virtual DOM by surgically targeting individual Text and Attr nodes, utilizing Explicit Resource Management (DisposableStack) for deterministic memory cleanup.
- Zero-Build: Runs natively in the browser. No compilers, no bundlers required.
- Micro-Updates: Mutates specific
TextandAttrnodes. Re-renders are physically impossible. - Deterministic Cleanup: Built-in memory management via
Symbol.disposeandDisposableStackprevents zombie listeners. - Security by Default: Bypasses
innerHTMLduring reactive updates. By assigning directly toNode.textContentandAttr.value, markup injection is neutralized by the platform. - Web Component Native: Designed to cleanly hydrate Shadow DOMs and standard elements alike.
npm install @aegisjsproject/iota<script type="importmap">
"imports": {
"@shgysk8zer0/signals": "https://unpkg.com/@shgysk8zer0/signals@0.0.3/signals.js,
"@aegisjsproject/iota": "https://unpkg.com/@aegisjsproject/iota@1.0.1/iota.js"
}
</script>Iota uses string placeholders (HTML comments and data-attributes) to position signals in your markup, which are then hydrated via $observe().
import { $text, $attr, $observe } from '@aegisjsproject/iota';
import { html } from '@aegisjsproject/core/parsers/html.js';
import { onInput, observeEvents } from '@aegisjsproject/callback-registry';
import { sanitizer as sanitizerConfig } from '@aegisjsproject/sanitizer/config/base.js';
// Manage lifecycle natively
const stack = new DisposableStack();
// Initialize signals and bind them to the stack
const $name = stack.use($text('World'));
const $value = stack.use($attr('value', () => $name.get()));
document.body.append(html`
<h1>Hello, ${$name}!</h1>
<form id="container">
<input
type="text"
${$value}
${onInput}="${$name.handleEvent}"
/>
<button type="button" command="--dispose" commandfor="root">Dispose</button>
</form>
`);
// Hydrate the DOM (replaces placeholders with live Text/Attr nodes)
$observe();
observeEvents();
// Tie disposal to a DOM event (e.g., Invoker Commands or unmount lifecycle)
document.addEventListener('command', ({ command }) => {
if (command === '--dispose') stack.dispose();
});$text(value | computeFn): Creates aTextStateorTextComputedsignal. Returns an HTML comment placeholder<!--ref-->when cast to a string.$attr(name, value | computeFn): Creates anAttrStateorAttrComputedsignal. Returns a data-attribute placeholderdata-attr-signal="ref"when cast to a string.$signal(value): Creates a baseDisposableState. Includes a.handleEvent(e)method that automatically updates the signal value from form inputs.$computed(fn): Creates a baseDisposableComputedsignal.
$observe(target = document.body, { stack, signal, base } = {}): Walks the target DOM node, locates signal placeholders, and replaces them with live, reactiveTextandAttrnodes.observeTextSignalRefs(...): Hydrates only text nodes.observeAttrSignalRefs(...): Hydrates only attribute nodes.
$watch(signal, callback): Manually subscribe to a signal.$unwatch(signal): Stop tracking a specific signal.unwatchSignalCallback(signal, callback): Remove a specific callback from a signal.
RegistryKey: An extendedStringrepresenting the signal's internal ID. Implements[Symbol.dispose]to automatically unwatch and unregister the associated signal when the stack clears.registerSignal(ref, signal)/unregisterSignal(ref): Manages the global Map of active signals used during hydration.