Stability and compatibility
sigmx is meant to be a dependency you can leave alone for years. This page says exactly what that means.
What is stable
Section titled “What is stable”These surfaces follow semantic versioning: they only change in a major release, with a migration note.
| surface | examples |
|---|---|
| Attribute syntax | data-<plugin>:<key>__<modifier>.<arg>="expression", the configurable prefix, data-ignore |
| The 50 built-in plugins | their names, keys, values, modifiers and behaviour as documented in the reference |
| Expression semantics | $name paths, $ for the root, @action() calls, el and evt in scope, statements versus a returned value |
| The store | dotted paths, merge-patch semantics, null removes, arrays are reactive in place, computeds are read-only |
| The wire protocol | Sigmx-Request and Accept headers, signals in the sigmx query parameter or JSON body, event-stream blocks (event: / data: key value), patch-elements and patch-signals fields, the Sigmx-Selector, Sigmx-Mode and Sigmx-Only-If-Missing response headers |
| Browser events | sigmx-fetch, sigmx-server-event, sigmx-signal-patch, sigmx-prop-change and their detail shapes |
| Package exports | sigmx, sigmx/plugins, sigmx/presets/*, sigmx/standalone, sigmx/server, sigmx/csp, sigmx/precompile, sigmx/scan, sigmx/vite and the types they export |
| The plugin API | attribute(), action(), handler() and the Ctx / ActionCtx / Runtime objects they receive |
Anything else is internal: module paths under dist/ other than the exports above, properties prefixed with _, the compact plugin constructors in src/plugins/def.ts, class internals of Signal and Computed, and the exact bytes of the bundle.
How changes are made
Section titled “How changes are made”- Additions (a new plugin, modifier, option or event field) are minor releases. Existing markup keeps working.
- Behaviour fixes that change observable results are called out in the CHANGELOG under “Changed”, even when the old behaviour was a bug.
- Removals happen only in a major release and are first deprecated for at least one minor release, during which the old form still works and logs nothing in production.
- Bundle size is a feature, not a promise: every release records it in the CHANGELOG (11.5 KB brotli for the shipped
everythingbuild with all 50 plugins at the time of writing), and a change that costs bytes is weighed against what it buys, correctness first.
How the promise is tested
Section titled “How the promise is tested”npm testruns the unit suite, one happy-dom test file per plugin, and the feature audit: 148 behavioural probes written against the public API, each flagged as expected to pass or expected to be absent. A feature disappearing by accident fails the build.npm run auditruns the same probes against any earlier build (mainby default, or--ref v0.1.1) and prints a before/after matrix, so a release can state exactly which behaviours changed.npm run test:browserexercises the shipped standalone in Chromium, including morphing, streams and the boost navigation, andnpm run test:sdkscovers the Astro and Hono packages.- Every reference page’s live demos are loaded from the built documentation site in a real browser as part of the docs check.
Runtime requirements
Section titled “Runtime requirements”The client needs a browser with ES2022, Proxy, MutationObserver and fetch with ReadableStream bodies: current evergreen versions of Chrome, Edge, Firefox and Safari. Optional APIs degrade quietly: without the Web Animations API data-transition switches display instantly, without moveBefore moved elements are re-inserted, without localStorage data-persist keeps state in memory. The server helpers need any runtime with the Fetch API (Node 20+, Deno, Bun, Cloudflare Workers).
Rules, settled
Section titled “Rules, settled”These were the open design questions before 1.0; each is now a rule with a mechanism behind it.
- One expression pipeline. Runtime and precompiler run the same
transform()and compile the same strict-mode bodies, so an expression means one thing everywhere. Typos throw; a misspelt signal reads asundefined. - Keys are paths. A dot in an object key means nesting:
merge({ 'a.b': 1 })createsa→b. _is the browser-only prefix. Requests leave_-prefixed paths out, and recasing keeps the underscore.- Objects are namespaces, arrays are leaves.
$listis reactive in place;$useris a proxy over paths. - Keys are kebab-case in markup. HTML lowercases attribute names; the build lints
data-bind:firstNameand the runtime camelCasesfirst-name. - Literal values are declared. A directive whose value is text sets
literal: trueand gains__dynamicfor free; everything else is an expression. - Plugin arguments are append-only. Precompiled tables are keyed by source text alone and stay valid across upgrades.
Failure policy
Section titled “Failure policy”Expressions and plugins fail one attribute at a time: an error in one data-text is reported through onError (the console by default) with the plugin name and element, and everything else on the page keeps running. Requests announce failures as sigmx-fetch events with type: 'error' and never throw into the page. Server patches that cannot be applied, such as a prepend or append without a selector, are reported once and are not retried. Corrupted storage, blocked clipboard access and missing optional APIs are handled where they occur.