v2.4.0: feat(intent-element): un-privatize + publish @hyperfixi/intent-element
[2.4.0] - 2026-05-20
Two parallel arcs landed since v2.3.1: upstream _hyperscript 0.9.90 parity (9 phases — commands, comparators, collection ops, event modifiers, plugin system, 3 new public plugin packages, i18n) and htmx v4 compatibility (reactive hx-live, SSE/WebSocket streaming, per-element localized attribute names, the size-busting hyperfixi-hx-v4.js bundle).
Added
Core language (upstream _hyperscript 0.9.90 compat)
- 9 new commands:
focus,blur,empty,open,close,select,clear,reset,breakpoint— covers DOM focus management, form state, window control, and debugger integration. - 4 comparator expressions:
X starts with Y,X ends with Y,X is between A and B, and the postfix... ignoring casemodifier for case-insensitive string comparison.betweenis inclusive and auto-orders bounds. - 5 collection infix operators:
collection where <pred>,collection sorted by <key>,collection mapped to <expr>,string split by <sep>,array joined by <sep>.where/sorted by/mapped tobinditper element for inline predicates/keys. - Event modifiers:
on first <event>alias for.once, plus syntheticon resizefor HTMLElements backed byResizeObserver(browser standardresizeis window-only).
Plugin system
HyperfixiPluginpublic API:installPlugin(plugin)on Runtime,ParserExtensionRegistrysingleton with snapshot/restore for test isolation. Five plugin seams:registerFeature,registerNodeEvaluator,registerGlobalWriteHook,registerGlobalReadHook, andHyperfixiPluginContext.runtime.$nameglobals canonicalized to bare-key storage acrosssetVariableValueand both identifier evaluators.
New plugin packages
@hyperfixi/speech: Voice I/O via Web Speech API —speak "text"(with optionalrate/pitch/voice/volume),ask "question",answer with "value". Idempotent installation; no-ops cleanly when the Web Speech API orwindow.prompt()is unavailable.@hyperfixi/reactivity: Reactive signals —live { ... }blocks,bindexpressions (now also accepts explicit property via'sor.syntax),when X changes,^namereactive reads. Microtask-flushed scheduler with cycle guard and auto-stop on element disconnect.@hyperfixi/components: Custom Element registration from<template component="tag">/<script type="text/hyperscript-template" component="tag">. v2 shipped this release — reactive^varrendering (via@hyperfixi/reactivity),#if/#fordirectives, slot substitution,${expr}interpolation. Init scripts (_=on the<template>) run on each stamp.
Internationalization (i18n)
- Phase 1 commands translated across all 23 COMPLETE_LANGUAGES (7 new schemas in
@lokascript/semantic:empty,open,close,select,clear,reset,breakpoint). - Phase 2 comparators (
starts with,ends with,between,ignoring case) translated across all 23 languages (withstarts with/ends with/betweenbackfills in bn, th, vi). - Phase 3 collection ops (
sorted by,mapped to,split by,joined by) translated across all 23 languages (wherepredated this release). - New
packages/i18n/src/schema-alignment.test.ts— guards drift: everyCommandSchemamust have anen.tsentry, Phase 1/2/3 operators must exist across all complete dictionaries.
htmx v4 integration
hx-livereactive expressions: When@hyperfixi/reactivityis installed, the htmx-compat layer recognizes htmx v4'shx-liveattribute and translates it to alive ... endblock. The expression body is hyperscript (not JavaScript like upstream htmx v4), so it gets fine-grained dependency tracking + inherits multilingual support. If reactivity isn't installed, the element is skipped with a clear console error.sse-connect/sse-swap: Long-livedEventSourceper element. Bounded exponential reconnect backoff (1s → 2s → 4s … capped at 30s, 5 retries). Auto-close on DOM removal viaMutationObserver. Lifecycle events:htmx:sseOpen,htmx:sseMessage,htmx:sseError,htmx:sseClose. Multiple named events per connection. Swap targets resolveclosest/find/next/previous.ws-connect/ws-send: Bidirectional WebSocket per element. JSON envelope routing ({ target, swap?, data }) drives surgical updates through the existinghx-target/hx-swapmachinery; raw messages dispatch ashtmx:wsMessage. Outbound sends queue while connecting, flush onhtmx:wsOpen. Same reconnect backoff as SSE.- Localized htmx attribute names (Phases 8–10): Per-element
lang=resolution. Authors can writehx-obtener/hx-objetivo/sse-conectar(Spanish),hx-取得/hx-ターゲット(Japanese),hx-احصل/hx-هدف(Arabic), etc. — the orchestrator translates them to canonical English before they hit the existing processor. 8 priority vocab modules ship with the bundle (en, es, fr, ja, zh, ar, ko, de). Missing-vocab languages log a one-time warning per language and fall back to English.eventNameOf/selectorFor/nameOfhooks are namespaced (hx,sse,ws) sohx-on:*localizes correctly. - htmx event lifecycle: New CustomEvents
htmx:configuring(cancelable, mutatee.detail.config),htmx:beforeRequest(cancelable),htmx:afterSettle,htmx:error, plus the SSE/WS lifecycle events listed above.
Bundles & build tooling
hyperfixi-hx-v4.js(~257 KB gz): Full runtime +@hyperfixi/reactivity(auto-installed) + htmx-compat + SSE/WS support, all in a single script tag. The "batteries-included" choice for projects using htmx v4 reactive/streaming features. The slimhyperfixi-hx.js(~13 KB gz) does NOT ship reactivity/SSE/WS — choose hx-v4 when you reach forhx-live/sse-connect/ws-connect, hx otherwise.- Vite plugin: htmx v4 surface detection — scans HTML for
hx-live/sse-*/ws-*attributes and auto-routes tohyperfixi-hx-v4.js; otherwise emits a minimal handcrafted bundle as before. getDefaultRuntime()exposed for forcing lazy runtime construction (useful for bundles that defer wiring until first DOM event).
Fixed
- Runtime:
it/its/resultidentifier cache no longer returns stale values across loop iterations — required for Phase 3 collection operators that rebinditper element (16 ms TTL cache had masked all Phase 3 tests before fix). - Runtime: method-call
thisbinding preserved on member callees —items mapped to it.toUpperCase()no longer throws "called on null or undefined". Uses.apply(thisArg, args)for member expressions. - Parser:
is notbinary operator now routes tonotEquals.evaluate— pre-existing gap where tokens parsed but runtime dispatch threwUnknown binary operator: is not. - Runtime:
execute()now mutates the caller'sExecutionContextin place when injecting theExpressionRegistry, rather than shallow-cloning. Commands writing tocontext.result/context.it(e.g. the newanswercommand) now propagate those writes back to the caller — matching the long-standingcontext.locals.set(...)/context.globals.set(...)mutation pattern in the same function.ExecutionContext.registryis now mutable in the type signature (the runtime is the only legitimate writer; thereadonlymodifier was a defensive holdover from an earlier consolidation arc). - Runtime:
or/andshort-circuit now returns the operand value (truthy/falsy), not a coerced boolean — matches upstream_hyperscriptsemantics. - Runtime:
put/set/toggleaccept plain property names as write targets (not justthe X.Ychains). - Runtime:
evalHyperScript()defaults to shared globals when none provided. - Runtime: scoped
notifyLocalRead()now fires when reading scoped locals (was missing — broke reactivebindto local vars). - Math: binary-arithmetic operands accept DOM elements (coerced through the consolidated "is this convertible" path).
- Parser:
set the X.Y to Zdotted property chains (e.g.set the event.detail.result to ...) now use the full expression parser rather than a fixed 2-token lookahead — handles arbitrary-depth chains andofexpressions uniformly. - Parser:
pick first 3 of arrand other keyword-led command syntaxes (commands whose argument grammar starts with a keyword) now route through dedicated parsers and preserve trailing args. Added toCOMPOUND_COMMANDSwith a dedicatedparseXxxCommandper case. - Browser bundle:
ExpressionRegistrythreaded through the command-execution path (processCommand→adapter.execute→parseInput). Without this, commands liketell/send/togglewhoseparseInput()evaluates AST nodes failed in the example gallery with "Expression X not in ExecutionContext.registry". - htmx-compat:
hx-on:*registers real event listeners directly from the processor (the translator never sees them, so it can stay declarative-only). Refresh observer wired correctly; localizedhx-on:*prefix resolved via the namespaced i18n hooks. - i18n: Reactive blocks (
live,when X changes) route aroundparseStatementto fixwhen/unless+ SOV-languageliveblock parsing. Suppressed spurious "then" injection inside live blocks. - patterns-reference: HTML patterns marked non-translatable (they're DOM markup, not source code); cleaned orphan languages from the database seed.
- Semantic: registry singleton shared across tsup entries (multi-entry split was forking the registry, breaking cross-entry pattern lookups).
Changed (internal)
- Evaluator consolidation: Retired the
BaseExpressionEvaluatorclass hierarchy and 4 helper modules (~2,700 LOC removed) in favor of the single canonical evaluator atparser/runtime.ts:evaluateASTwith theExpressionRegistryonExecutionContext. Both consolidation arcs (α + β) closed. - Domain DSLs: Extracted a shared internal config package (workspace-only) consumed by all 8 domain DSLs.