@hyperfixi/reactivity 3.1.1 → 3.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -7,6 +7,189 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [3.3.0] - 2026-10-02
11
+
12
+ The first publication of `@hyperfixi/engine`, the hyperscript engine meant to replace the one
13
+ in `@hyperfixi/core`, and the example gallery moved onto it.
14
+
15
+ ### Added
16
+
17
+ - **`@hyperfixi/engine` is published.** A hyperscript engine written against upstream
18
+ `_hyperscript`'s source, with upstream's own test suite as the acceptance oracle (1,401 of
19
+ 1,467 tests; the 66 known failures are upstream's internal API, sockets and workers). It
20
+ ships `dist/hyperfixi-hs.js`, a script-tag bundle of hyperscript and nothing else, 34.1 KB
21
+ gzipped, which 36 of the repository's example pages now load instead of `hyperfixi.js`. It
22
+ keeps two forms upstream lacks, `new X(...)` and `toggle <element>`; the other hyperfixi-only
23
+ forms are not in it. It is typed (`tsc --strict`, no `any`), built from modules, and exposes
24
+ upstream's public API shape plus one hook, `addSourceTransform`, which
25
+ `@lokascript/hyperscript-adapter` uses to run the 24 languages on it.
26
+
27
+ ### Changed
28
+
29
+ - **Examples and docs are written in upstream `_hyperscript`'s spelling** wherever upstream has
30
+ one: `put Y into X` for the `swap` strategies, `debounced at 300ms`, `matches`, `set X's @a`,
31
+ `increment #count's textContent` (core counted in a bare `#count`; upstream does not),
32
+ `on mutation of childList from #x`, `my offsetLeft` in place of `measure x`. The multilingual
33
+ reader still accepts the old forms; the renderer writes upstream's. Corpus rows follow.
34
+ - **`@lokascript/semantic` renders `repeat for x in xs index i`** (was `with index`) and
35
+ `tell X show` (was `tell X to show`), upstream's spellings.
36
+ - **The examples' bundle loader** takes a per-page default (`data-default="hs"`), and `?bundle=hs`
37
+ switches any page to the engine bundle.
38
+
39
+ ### Fixed
40
+
41
+ - **`@hyperfixi/core`**: `toggle *display of X` parsed and then threw.
42
+ - **Hybrid bundles (`hyperfixi-hx.js`)**: `increment` / `decrement` of a possessive wrote only
43
+ style properties; `increment #count's textContent` evaluated to the text and threw on
44
+ `querySelectorAll('0')`. They now write the property.
45
+ - **`@lokascript/semantic`**: a template-literal URL in a Korean handler was read as a custom
46
+ event name.
47
+
48
+ ## [3.2.0] - 2026-09-30
49
+
50
+ A correctness release for multilingual hyperscript. The semantic parser behind
51
+ `translate()`, `@lokascript/hyperscript-adapter`, MCP `translate_code` and hyperfixi's
52
+ non-English path now keeps whole programs that it used to shorten or drop: values,
53
+ conditions, loops and handler heads. Every value shape in a generated matrix of 4,205
54
+ (executed in 24 languages, directly and through the adapter, against upstream
55
+ \_hyperscript) now matches upstream, apart from two documented differences. Core
56
+ matches upstream \_hyperscript on more forms.
57
+
58
+ ### Added
59
+
60
+ - **Counted loops are written in each language's own words**: es `repetir 3 veces`,
61
+ fr `répéter 3 fois`, ja `3 回 を 繰り返し`, hi `3 बार को दोहराएं`, where translations
62
+ wrote English `times` in 18 languages and English `repeat` in the SOV six. English
63
+ `times`/`repeat` and each language's other count words (es `vez`, ru `раза`, …) still
64
+ read. 18 i18n dictionaries gain a `temporal.times` word.
65
+ - **Variable names that are words of the target language.** A variable spelled like a
66
+ structure word (es `si`, pl `w`, tr `al`) is written in parentheses, `(si)`, where the
67
+ plain translation would be misread, and read back as the variable. The language server
68
+ warns (`name-collision`, with a rename quick fix), MCP `validate_hyperscript` reports
69
+ `NAME_COLLISION`, and `translate_code` warns when a variable reads as a value word in
70
+ the target (tl `ako` is `me`). `@lokascript/semantic` exports `nameCollision`,
71
+ `findNameCollisions` and `findTranslationCollisions`.
72
+ - Event-handler modifiers (`once`, `debounced at`, `throttled at`, `from`) render in every
73
+ language and are scored by the fidelity signals.
74
+ - `of`-paths and pseudo-commands (`reset() the closest <form/>`) survive translation.
75
+ - `@lokascript/hyperscript-adapter`'s host gate also rejects a translation that reads a
76
+ reference as a handler's event (`on click on me toggle .active`, an empty click handler
77
+ plus a handler for an event named `me`), falling back to the author's text with a
78
+ warning; such a translation used to parse cleanly and do nothing.
79
+ - **Localized htmx attributes: all 23 non-English languages now cover
80
+ every attribute the _Hypermedia Systems_ book's code listings use** —
81
+ `hx-vals`, `hx-select`, `hx-swap-oob` and `hx-sync` join the Contact.app
82
+ twelve (`hx-値`, `hx-valores`, `hx-값`, `hx-werte`, `hx-選択`, `hx-selección`,
83
+ `hx-seçim`, `hx-auswahl`, `hx-sélection`, `hx-选择`, …). tr/de/fr/zh are new
84
+ in the table: the Contact.app seven they lacked (`hx-sil`, `hx-löschen`,
85
+ `hx-supprimer`, `hx-删除`, …) plus audited trigger/swap primaries following
86
+ loka-js (`hx-tetikleyici`, `hx-auslöser`, `hx-déclencheur`, `hx-替换`; the
87
+ profile words `hx-tetikle`, `hx-auslösen`, `hx-déclencher`, `hx-交换` still
88
+ resolve). de gains a lowercase `hx-ziel`: the profile's `hx-Ziel` could never
89
+ match a parsed attribute, since HTML lowercases attribute names. The other
90
+ 15 languages (ar, bn, he, hi, id, it, ms, pl, qu, ru, sw, th, tl, uk, vi)
91
+ are unreviewed drafts, every entry flagged `lowConfidence`; they also gain
92
+ the trigger heads their dictionaries left in English (ms and tl all six,
93
+ vi/he/ar some). id gains `hx-sasaran` for a target that was the English
94
+ identity; tl keeps `hx-target` on purpose. `hx-ext` is left in English on
95
+ purpose: htmx 4 removed it. The vocab table gains an `events` block for
96
+ trigger heads the i18n dictionaries do not name; ja, es, pt and ko author
97
+ the DOM `search` event (`検索`, `buscar`, `검색`), which the book's and
98
+ Contact.app's search box fires. htmx's own trigger words (`revealed`,
99
+ `every`) stay English, like the `delay:` / `from:` modifiers.
100
+ - Adapter-only vocab keys now resolve from the authored table alone, never
101
+ from a semantic profile: 22 profiles carry a `select` keyword that means
102
+ mark/highlight text (de `markieren`, tr `vurgula`), and it would otherwise
103
+ have shipped as `hx-select` in 22 unreviewed languages.
104
+
105
+ - **Localized htmx attributes: ja, es, pt and ko now cover every attribute the
106
+ _Hypermedia Systems_ Contact.app uses** — `hx-post`, `hx-delete`,
107
+ `hx-confirm`, `hx-push-url`, `hx-boost`, `hx-indicator` and `hx-include` join
108
+ the existing five (e.g. `hx-削除`, `hx-eliminar`, `hx-excluir`, `hx-삭제`).
109
+ `hx-indicator` / `hx-include` localize under `@hyperfixi/htmx-adapter` (stock
110
+ htmx implements them; the embedded layer does not).
111
+ - **Vocab aliases.** Several localized names may map to one attribute or event;
112
+ the first is the form to teach, later ones keep already-authored pages
113
+ working. Core's embedded orchestrator now reads whichever form an element
114
+ carries (it previously kept a single name per attribute).
115
+
116
+ ### Changed
117
+
118
+ - **Rendered vocabulary** (owner decisions): `null` is written `null` in ar, hi, id, qu,
119
+ sw, th and tr, where it shared the word for `empty`; hi writes `no` as `कोई नहीं`; qu
120
+ writes `and` as `hinallataq`; tl and tr write `includes` with `contains`' word (both
121
+ engines read the two as one operator).
122
+ - `@lokascript/semantic` exports its real `VERSION` (it was `0.1.0` since the first
123
+ release); browser bundles report `<version>-<bundle>`.
124
+ - MCP `translate_hyperscript` is described accurately: it uses the same semantic engine as
125
+ `translate_code`, without the verification.
126
+ - **Audited primaries for `hx-trigger` / `hx-swap`** in ja (`hx-トリガー`,
127
+ `hx-置換`), es (`hx-disparador`, `hx-intercambio`), pt (`hx-gatilho`,
128
+ `hx-troca`) and ko (`hx-교체`), following loka-js's terminology reviews. The
129
+ names that shipped before (`hx-引き金`, `hx-disparar`, …) still resolve.
130
+ - The vocab modules are regenerated against the current i18n dictionaries.
131
+ **No working name was removed** — 141 retired event names and three attribute
132
+ names are kept as aliases. One name changed meaning with its dictionary: sw
133
+ `panya_juu` is now `mouseup` (was `mouseover`).
134
+
135
+ ### Fixed
136
+
137
+ - **Translations that lost code** (semantic parse and render, in every language):
138
+ - `get` of a literal (`get "hello"`, `get 3`, `get true`) dropped the whole command; an
139
+ object literal after `get` was cut to `get {`.
140
+ - Loops keep their extent and body; `repeat until` stops; a loop keeps its index
141
+ variable, the tail after an `if` inside it, and the command after an empty loop its own
142
+ `end` closes; a count may be any value (a variable, `$n`, `it`, a possessive), where
143
+ some translations read `forever`, a silent infinite loop.
144
+ - Handler heads keep `or` events, filters and event parameters; a translated
145
+ `wait for <event>` waits instead of throwing; behaviors keep their handlers and init
146
+ blocks; an else-if chain shares one `end`.
147
+ - Values keep their comparison phrases (`is equal to`, `includes`, `is an Element`, …),
148
+ `and`/`or`/`not`, `mod`, possessive and `of` chains, object and array literals, every
149
+ conversion core supports (`as Int`, `as Fixed:2`, piped conversions), unary minus and
150
+ property paths; `true`/`false`/`null` keep their meaning.
151
+ - Commands keep `put … into <variable>`, `tell … to show`, fetch's `do not throw` and
152
+ response type, event-source/socket URLs, show/hide `with` strategies, `go`/`scroll`
153
+ positions, `${…}` URLs, core's `has`, and a class query's `in` scope.
154
+ - **`@hyperfixi/core` follows upstream \_hyperscript**:
155
+ - `X of Y` binds as property access, and the X of a null target is null.
156
+ - `increment` reads its amount and counter as upstream does, and writing an object's
157
+ property through `'s`/`of` works.
158
+ - A loop's count reads as upstream's `index < times` does: `"6.5"` loops 7 times and
159
+ `"6abc"` none, in the full runtime and in the hybrid bundles (`hyperfixi-hx.js`,
160
+ plugin bundles), which read `parseInt`.
161
+ - Blocks close at the end of input; `morph … to`; `render … with name: value`; `beep!`
162
+ as an expression; `transition`'s owners, several properties, `from` and `using`; three
163
+ handler forms from _Hypermedia Systems_; four upstream-valid forms that compiled and
164
+ then failed at run time; `.item in #list` keeps its scope; `on click once` fires on
165
+ the first click.
166
+ - htmx: camelCase event names survive an attribute name.
167
+ - MCP: the validators report what the parsers actually did.
168
+ - The AOT compiler (experimental): loops were compiled to `while (true)`; targets,
169
+ property access, scoped queries, `empty` and variable scope now compile to what they
170
+ mean.
171
+ - `@lokascript/hyperscript-adapter` and `@hyperscript-tools/multilingual` READMEs: the
172
+ Japanese quickstart example was a dead button, and the multi-language example loaded
173
+ only the Spanish bundle; the examples are now native and run in a test. Bundle sizes are
174
+ measured.
175
+ - Bare CDN URLs now serve a browser bundle: `unpkg.com/@lokascript/hyperscript-adapter@3` (and
176
+ jsDelivr) served `dist/index.cjs`, which throws in a `<script>` tag. The adapter,
177
+ `@hyperscript-tools/multilingual`, `@lokascript/semantic`, `@hyperfixi/core` and
178
+ `@lokascript/htmx-adapter` declare `unpkg`/`jsdelivr` entries.
179
+ - `hyperfixi-multilingual.js`'s error names the semantic bundle that exists (the full
180
+ `browser.global.js`, the only one that defines `LokaScriptSemantic`).
181
+ - READMEs: `@lokascript/i18n` no longer claims to translate (and its `/lsp` export and CLI,
182
+ which do not exist, are gone); `@lokascript/semantic`, `@hyperfixi/vite-plugin` and
183
+ `@hyperfixi/core` have correct package names, API calls, language lists (24) and measured
184
+ bundle sizes.
185
+ - Dependencies: the js-yaml and smol-toml advisories; Vite 8, vitest 5 and happy-dom 20.14.
186
+ - **Vietnamese `hx-get` / `hx-target` / `hx-swap` / `hx-trigger` / `sse-swap`
187
+ never worked**: the names were emitted with spaces (`hx-lấy giá trị`), which
188
+ HTML reads as three attributes. They are now hyphen-joined
189
+ (`hx-lấy-giá-trị`), like vi's existing `hx-trực-tiếp`.
190
+ - 29 multi-word event names (ar, vi) were removed from the vocab modules: an
191
+ event is a single token of an `hx-trigger` value, so they could never match.
192
+
10
193
  ## [3.1.1] - 2026-09-04
11
194
 
12
195
  ### Fixed
@@ -716,7 +899,9 @@ _Synchronized version release. See git history for details._
716
899
  - npm access token stored in GitHub Secrets
717
900
  - 2FA recommended for npm organization
718
901
 
719
- [Unreleased]: https://github.com/codetalcott/hyperfixi/compare/v3.1.0...HEAD
902
+ [Unreleased]: https://github.com/codetalcott/hyperfixi/compare/v3.3.0...HEAD
903
+ [3.3.0]: https://github.com/codetalcott/hyperfixi/compare/v3.2.0...v3.3.0
904
+ [3.2.0]: https://github.com/codetalcott/hyperfixi/compare/v3.1.0...v3.2.0
720
905
  [3.1.0]: https://github.com/codetalcott/hyperfixi/compare/v3.0.0...v3.1.0
721
906
  [2.10.0]: https://github.com/codetalcott/hyperfixi/compare/v2.9.0...v2.10.0
722
907
  [2.9.0]: https://github.com/codetalcott/hyperfixi/compare/v2.8.0...v2.9.0
package/dist/index.cjs CHANGED
@@ -745,7 +745,7 @@ function varRefInfo(node) {
745
745
  }
746
746
 
747
747
  // src/version.ts
748
- var VERSION = "3.1.1";
748
+ var VERSION = "3.3.0";
749
749
 
750
750
  // src/index.ts
751
751
  var reactivityPlugin = {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/signals.ts","../src/caret-var.ts","../src/live.ts","../src/when.ts","../src/bind.ts","../src/version.ts"],"sourcesContent":["/**\n * @hyperfixi/reactivity — signal-based reactive features for hyperfixi.\n *\n * Adds four constructs from upstream _hyperscript 0.9.90:\n *\n * live [commandList] end reactive block — body re-runs\n * when tracked reads change.\n *\n * when <expr> [or <expr>]* changes observer — body runs when any\n * [commandList] end watched expression changes.\n *\n * bind <var> to <element> two-way DOM ⇄ var binding.\n *\n * ^name [on <target>] DOM-scoped inherited variable\n * (read + write with `^`).\n *\n * Install:\n *\n * ```ts\n * import { createRuntime, installPlugin } from '@hyperfixi/core';\n * import { reactivityPlugin } from '@hyperfixi/reactivity';\n *\n * const runtime = createRuntime();\n * installPlugin(runtime, reactivityPlugin);\n * ```\n */\n\nimport type { HyperfixiPlugin, HyperfixiPluginContext } from '@hyperfixi/core';\nimport { reactive } from './signals';\nimport { parseCaretPrefix, makeEvaluateCaretVar, makeWriteCaretVar } from './caret-var';\nimport { parseLiveFeature, makeEvaluateLiveFeature } from './live';\nimport { parseWhenFeature, makeEvaluateWhenFeature } from './when';\nimport { parseBindFeature, makeEvaluateBindFeature } from './bind';\nimport { VERSION } from './version.js';\n\nexport { reactive } from './signals';\nexport type { CaretVarNode } from './caret-var';\nexport type { LiveFeatureNode } from './live';\nexport type { WhenFeatureNode } from './when';\nexport type { BindFeatureNode } from './bind';\n\n/**\n * The plugin object. Install once at app startup; re-installing is idempotent\n * (guarded via a `parserExtensions.hasFeature('live')` check).\n *\n * Registers:\n * - Features: `live`, `when`, `bind` (top-level features with `end` bodies)\n * - Prefix op: `^` (primary expression for DOM-scoped vars)\n * - Node evaluators: `liveFeature`, `whenFeature`, `bindFeature`, `caretVar`\n * - A node writer for `caretVar` so `set ^X to Y` flows through `reactive.writeCaret`\n * - Global read/write hooks so `$name` reads track and writes notify\n *\n * Effect cleanup: each effect-creating evaluator calls\n * `context.registerCleanup(owner, stop, ...)` so the core runtime tears effects\n * down when their owning element is cleaned up. There is no separate plugin-level\n * cleanup hook; `reactive.stopElementEffects(el)` is exposed for explicit teardown\n * by tests and consumers that manage element lifecycle outside the runtime.\n */\nexport const reactivityPlugin: HyperfixiPlugin & { version: string } = {\n name: '@hyperfixi/reactivity',\n version: VERSION,\n install(ctx: HyperfixiPluginContext) {\n const { parserExtensions, runtime } = ctx;\n\n // Idempotency: the parser-extension registry is process-singleton, so\n // re-installing into a fresh runtime would otherwise stack additional\n // global read/write hooks on every call. `snapshot()`/`restore()` clears\n // the feature registry — when tests roll back the registry, this guard\n // re-enables a fresh install.\n if (parserExtensions.hasFeature('live')) return;\n\n // Parser hooks — three block features plus a primary-expression caret.\n parserExtensions.registerFeature('live', parseLiveFeature as never);\n parserExtensions.registerFeature('when', parseWhenFeature as never);\n parserExtensions.registerFeature('bind', parseBindFeature as never);\n parserExtensions.registerPrefixOperator('^', 85, parseCaretPrefix as never);\n\n // Runtime evaluators. Features capture `runtime` so effect re-runs can\n // dispatch body commands without going through module-scope state.\n parserExtensions.registerNodeEvaluator(\n 'liveFeature',\n makeEvaluateLiveFeature(runtime as never) as never\n );\n parserExtensions.registerNodeEvaluator(\n 'whenFeature',\n makeEvaluateWhenFeature(runtime as never) as never\n );\n parserExtensions.registerNodeEvaluator(\n 'bindFeature',\n makeEvaluateBindFeature(runtime as never) as never\n );\n parserExtensions.registerNodeEvaluator(\n 'caretVar',\n makeEvaluateCaretVar(runtime as never) as never\n );\n\n // Caret-var write: lets the core `set` command dispatch `set ^X to Y`\n // through `reactive.writeCaret`. Resolves `on <target>` via the captured\n // runtime — no global-scope indirection.\n parserExtensions.registerNodeWriter('caretVar', makeWriteCaretVar(runtime as never) as never);\n\n // Global-write hook: notify the reactive graph whenever `$name` is set.\n // The returned disposer is intentionally discarded — the install-time\n // idempotency guard above is the sole gate against double-registration.\n parserExtensions.registerGlobalWriteHook((name: string, _value: unknown, _context: unknown) => {\n reactive.notifyGlobal(name);\n });\n\n // Global-read hook: track the read against the current effect (if any)\n // so effects re-run when the global changes.\n parserExtensions.registerGlobalReadHook((name: string, _context: unknown) => {\n reactive.trackGlobal(name);\n });\n\n // Local hooks — analogous to globals, but keyed by `context.me` so each\n // element holds independent state. A `set :foo to ...` running in a\n // handler with `me=button1` notifies effects subscribed to (button1, 'foo');\n // a `set :foo` in a different `me` won't reach them. This matches how\n // locals already work — they're never cross-context — and lets two\n // components hold independent `:foo` state without interference.\n parserExtensions.registerLocalWriteHook((name: string, _value: unknown, context) => {\n const owner = (context as { me?: Element | null }).me ?? null;\n if (owner) reactive.notifyElement(owner, name);\n });\n\n parserExtensions.registerLocalReadHook((name: string, context) => {\n const owner = (context as { me?: Element | null }).me ?? null;\n if (owner) reactive.trackElement(owner, name);\n });\n },\n};\n\nexport default reactivityPlugin;\n","/**\n * Signal/effect reactive core.\n *\n * Models three dependency kinds for v1:\n * - `global` — `$name` variables in `context.globals`\n * - `element` — `^name` DOM-scoped variables (per-element storage)\n * - `dom` — DOM properties read via `input`/`change` listeners\n *\n * Inspired by upstream _hyperscript 0.9.91's `src/core/runtime/reactivity.js`\n * but reimplemented in TypeScript against hyperfixi's types. Batched via\n * `queueMicrotask` so synchronous writes coalesce into a single flush. Cycle\n * detection halts at >100 consecutive triggers (matching upstream).\n *\n * External packages never see `Effect` or `Reactive` directly — they hold\n * the disposer returned by `createEffect()` and call it when the owning\n * element is cleaned up.\n */\n\nexport type DepKind = 'global' | 'element' | 'dom';\n\n/**\n * Whether reactive debug logging is enabled. Mirrors the core convention:\n * `localStorage.setItem('hyperfixi:debug', '*')` enables, anything else (or no\n * `localStorage`) disables. Cheap call — no caching, since users toggle this\n * interactively in the console.\n */\nfunction debugEnabled(): boolean {\n try {\n return typeof localStorage !== 'undefined' && localStorage.getItem('hyperfixi:debug') !== null;\n } catch {\n return false;\n }\n}\n\nfunction debugWarn(message: string, err: unknown): void {\n if (!debugEnabled()) return;\n if (typeof console === 'undefined') return;\n console.warn(`[@hyperfixi/reactivity] ${message}`, err);\n}\n\nexport interface Dep {\n readonly key: string;\n readonly kind: DepKind;\n readonly name: string;\n readonly element: Element | null;\n}\n\nconst globalDepKey = (name: string): string => `global:${name}`;\nconst elementDepKey = (el: Element, name: string): string => `element:${reactiveIdFor(el)}:${name}`;\nconst domDepKey = (el: Element, prop: string): string => `dom:${reactiveIdFor(el)}:${prop}`;\n\n// WeakMap-based id assignment so we don't monkey-patch DOM nodes. Ids are\n// per-process and only used to construct stable string keys for an effect's\n// per-effect dependency dedup map.\nconst _reactiveIds = new WeakMap<Element, number>();\nlet _idCounter = 0;\nfunction reactiveIdFor(el: Element): number {\n const existing = _reactiveIds.get(el);\n if (existing !== undefined) return existing;\n const id = ++_idCounter;\n _reactiveIds.set(el, id);\n return id;\n}\n\nexport class Effect {\n readonly dependencies = new Map<string, Dep>();\n private lastValue: unknown = undefined;\n private _stopped = false;\n private _consecutiveTriggers = 0;\n\n constructor(\n private readonly r: Reactive,\n readonly expression: () => unknown | Promise<unknown>,\n readonly handler: (value: unknown) => void | Promise<void>,\n readonly element: Element | null\n ) {}\n\n get stopped(): boolean {\n return this._stopped;\n }\n\n /**\n * Run the effect for the first time: collect dependencies, subscribe, call\n * handler with the initial value. Errors in expression evaluation are caught\n * and silently swallowed (matches upstream's tolerance).\n */\n async initialize(): Promise<void> {\n if (this._stopped) return;\n try {\n const value = await this.r._runWithEffect(this, this.expression);\n this.lastValue = value;\n if (value !== undefined && value !== null) {\n await this.handler(value);\n }\n } catch (err) {\n // Swallow — expression evaluation failed during initial read. Surface\n // via debug log so users diagnosing \"my effect never fires\" can see why.\n debugWarn('effect.initialize failed', err);\n }\n }\n\n /**\n * Re-run the effect after a notify. Cycle-detects (halts at 101 consecutive\n * triggers). If the new value is Object.is-equal to the last, the handler\n * is skipped. If the owning element has disconnected, the effect stops\n * itself and returns.\n */\n async run(): Promise<void> {\n if (this._stopped) return;\n if (this.element && !this.element.isConnected) {\n this.stop();\n return;\n }\n this._consecutiveTriggers++;\n if (this._consecutiveTriggers > 100) {\n if (typeof console !== 'undefined') {\n console.error(\n '[@hyperfixi/reactivity] Effect halted: > 100 consecutive triggers (cycle detected).'\n );\n }\n this.stop();\n return;\n }\n try {\n const value = await this.r._runWithEffect(this, this.expression);\n if (Object.is(value, this.lastValue)) return;\n this.lastValue = value;\n await this.handler(value);\n } catch (err) {\n // Swallow — expression/handler errors don't break the microtask flush.\n debugWarn('effect.run failed', err);\n }\n }\n\n resetTriggerCount(): void {\n this._consecutiveTriggers = 0;\n }\n\n /**\n * Stop the effect and remove it from all dependency subscriptions. Safe to\n * call multiple times.\n */\n stop(): void {\n if (this._stopped) return;\n this._stopped = true;\n this.r._unsubscribeEffect(this);\n this.dependencies.clear();\n }\n}\n\ninterface ElementState {\n symbolSubs: Map<string, Set<Effect>>;\n caretVars: Map<string, unknown>;\n domHandlers: Map<string, DomHandler>;\n effects: Set<Effect>;\n}\n\ninterface DomHandler {\n subs: Set<Effect>;\n detach: () => void;\n}\n\nexport class Reactive {\n private currentEffect: Effect | null = null;\n /**\n * Tail of the effect-evaluation chain. `_runWithEffect` links onto this so\n * only one effect owns the `currentEffect` slot at a time — see the comment\n * there for why concurrent async effects otherwise lose their dependencies.\n */\n private trackingQueue: Promise<void> = Promise.resolve();\n private pending = new Set<Effect>();\n private scheduled = false;\n\n private globalSubs = new Map<string, Set<Effect>>();\n private elementState = new WeakMap<Element, ElementState>();\n\n private getElementState(el: Element): ElementState {\n let s = this.elementState.get(el);\n if (!s) {\n s = {\n symbolSubs: new Map(),\n caretVars: new Map(),\n domHandlers: new Map(),\n effects: new Set(),\n };\n this.elementState.set(el, s);\n }\n return s;\n }\n\n /**\n * Internal helper invoked by Effect — installs `this` as the current effect,\n * runs the expression, then restores the previous current-effect pointer.\n * Track* methods (called from read-paths) consult `currentEffect` to know\n * which effect to subscribe.\n */\n async _runWithEffect(e: Effect, fn: () => unknown | Promise<unknown>): Promise<unknown> {\n // Dependency capture keys off ONE `currentEffect` slot, but effect\n // expressions are async (a `live` body awaits each command). Two effects\n // evaluating concurrently would interleave across their await points and\n // attribute each other's reads — or, once the inner one restored `prev`,\n // leave the outer one tracking nothing at all. Three `live` blocks on a\n // page (they all start in the same tick) reliably lost their\n // subscriptions that way and froze.\n //\n // Serialize instead: each run waits for the previous one to finish, so\n // the slot belongs to exactly one effect for the whole of its evaluation.\n // Effect bodies are short DOM updates, and this only orders effect\n // evaluation — it does not block the page.\n const runExclusive = async (): Promise<unknown> => {\n const prev = this.currentEffect;\n this.currentEffect = e;\n // Unsubscribe from previous deps before re-running. The expression will\n // re-record whatever it actually reads this time, avoiding stale subs.\n this._unsubscribeEffect(e);\n e.dependencies.clear();\n try {\n return await fn();\n } finally {\n this.currentEffect = prev;\n }\n };\n\n // Chain onto the tail of the queue, and keep the queue alive whether this\n // run resolves or throws (callers already swallow effect errors).\n const result = this.trackingQueue.then(runExclusive, runExclusive);\n this.trackingQueue = result.then(\n () => undefined,\n () => undefined\n );\n return result;\n }\n\n // ---------------------------------------------------------------------------\n // Track (read-path) — invoked from interceptors / evaluators.\n // ---------------------------------------------------------------------------\n\n trackGlobal(name: string): void {\n const e = this.currentEffect;\n if (!e) return;\n const key = globalDepKey(name);\n if (e.dependencies.has(key)) return;\n e.dependencies.set(key, { key, kind: 'global', name, element: null });\n let subs = this.globalSubs.get(name);\n if (!subs) {\n subs = new Set();\n this.globalSubs.set(name, subs);\n }\n subs.add(e);\n }\n\n trackElement(el: Element, name: string): void {\n const e = this.currentEffect;\n if (!e) return;\n const key = elementDepKey(el, name);\n if (e.dependencies.has(key)) return;\n e.dependencies.set(key, { key, kind: 'element', name, element: el });\n const state = this.getElementState(el);\n let subs = state.symbolSubs.get(name);\n if (!subs) {\n subs = new Set();\n state.symbolSubs.set(name, subs);\n }\n subs.add(e);\n state.effects.add(e);\n }\n\n trackDomProperty(el: Element, prop: string): void {\n const e = this.currentEffect;\n if (!e) return;\n const key = domDepKey(el, prop);\n if (e.dependencies.has(key)) return;\n e.dependencies.set(key, { key, kind: 'dom', name: prop, element: el });\n const state = this.getElementState(el);\n let handler = state.domHandlers.get(prop);\n if (!handler) {\n const subs = new Set<Effect>();\n const listener = (): void => {\n for (const effect of subs) this.schedule(effect);\n };\n // Use both input and change to cover radios/selects which don't fire input.\n el.addEventListener('input', listener);\n el.addEventListener('change', listener);\n handler = {\n subs,\n detach: () => {\n el.removeEventListener('input', listener);\n el.removeEventListener('change', listener);\n },\n };\n state.domHandlers.set(prop, handler);\n }\n handler.subs.add(e);\n state.effects.add(e);\n }\n\n // ---------------------------------------------------------------------------\n // Notify (write-path) — schedules dependent effects for re-run.\n // ---------------------------------------------------------------------------\n\n notifyGlobal(name: string): void {\n const subs = this.globalSubs.get(name);\n if (!subs) return;\n for (const e of subs) this.schedule(e);\n }\n\n notifyElement(el: Element, name: string): void {\n const state = this.elementState.get(el);\n if (!state) return;\n const subs = state.symbolSubs.get(name);\n if (!subs) return;\n for (const e of subs) this.schedule(e);\n }\n\n // ---------------------------------------------------------------------------\n // Effect lifecycle.\n // ---------------------------------------------------------------------------\n\n /**\n * Create + initialize an effect. Returns a disposer that stops the effect.\n * Callers are expected to register the disposer with the core runtime's\n * cleanup registry so it fires on element removal.\n */\n createEffect(\n expression: () => unknown | Promise<unknown>,\n handler: (value: unknown) => void | Promise<void>,\n owner: Element | null\n ): () => void {\n const e = new Effect(this, expression, handler, owner);\n if (owner) this.getElementState(owner).effects.add(e);\n // Fire initialize asynchronously so the caller can set up cleanup registration\n // before the first run touches the DOM.\n queueMicrotask(() => {\n void e.initialize();\n });\n return () => e.stop();\n }\n\n /**\n * Stop all effects owned by an element. Called by the reactivity plugin's\n * cleanup hook registered via `runtime.getCleanupRegistry()`.\n */\n stopElementEffects(el: Element): void {\n const state = this.elementState.get(el);\n if (!state) return;\n for (const e of Array.from(state.effects)) e.stop();\n state.effects.clear();\n }\n\n /**\n * Internal: remove an effect's entries from all subscriber sets. Called by\n * `Effect.stop()`.\n */\n _unsubscribeEffect(e: Effect): void {\n for (const dep of e.dependencies.values()) {\n if (dep.kind === 'global') {\n const subs = this.globalSubs.get(dep.name);\n subs?.delete(e);\n } else if (dep.kind === 'element' && dep.element) {\n const state = this.elementState.get(dep.element);\n const subs = state?.symbolSubs.get(dep.name);\n subs?.delete(e);\n state?.effects.delete(e);\n } else if (dep.kind === 'dom' && dep.element) {\n const state = this.elementState.get(dep.element);\n const handler = state?.domHandlers.get(dep.name);\n handler?.subs.delete(e);\n if (handler && handler.subs.size === 0) {\n handler.detach();\n state?.domHandlers.delete(dep.name);\n }\n state?.effects.delete(e);\n }\n }\n }\n\n // ---------------------------------------------------------------------------\n // Caret-variable storage — `^name` reads/writes.\n // ---------------------------------------------------------------------------\n\n /**\n * Whether `el` is a `dom-scope=\"isolated\"` boundary. Walks of `^var`\n * lookups stop at boundary elements that don't define the var, so nested\n * components don't accidentally read or write each other's state.\n */\n private isIsolationBoundary(el: Element): boolean {\n return typeof el.getAttribute === 'function' && el.getAttribute('dom-scope') === 'isolated';\n }\n\n /**\n * Walk up the DOM tree from `lookupRoot`, returning the first element whose\n * state has `name` defined. Stops at any `dom-scope=\"isolated\"` boundary\n * that doesn't itself define the var. Returns `null` if no owner is found.\n */\n private findCaretOwner(lookupRoot: Element, name: string): Element | null {\n let el: Element | null = lookupRoot;\n while (el) {\n const state = this.elementState.get(el);\n if (state && state.caretVars.has(name)) return el;\n if (this.isIsolationBoundary(el)) return null;\n el = el.parentElement;\n }\n return null;\n }\n\n /**\n * Read a DOM-scoped variable. Walks up from `lookupRoot`, tracking each\n * element visited as an `element` dep (so writes at any ancestor notify\n * dependent effects). Stops at any `dom-scope=\"isolated\"` boundary that\n * doesn't itself define the var.\n */\n readCaret(lookupRoot: Element, name: string): unknown {\n let el: Element | null = lookupRoot;\n while (el) {\n this.trackElement(el, name);\n const state = this.elementState.get(el);\n if (state && state.caretVars.has(name)) {\n return state.caretVars.get(name);\n }\n if (this.isIsolationBoundary(el)) return undefined;\n el = el.parentElement;\n }\n return undefined;\n }\n\n /**\n * Write a DOM-scoped variable. If `target` is provided, writes there\n * directly; otherwise walks up from `lookupRoot` to find the existing owner,\n * falling back to `lookupRoot` itself if no owner exists.\n */\n writeCaret(lookupRoot: Element, name: string, value: unknown, target?: Element): void {\n const owner = target ?? this.findCaretOwner(lookupRoot, name) ?? lookupRoot;\n const state = this.getElementState(owner);\n state.caretVars.set(name, value);\n this.notifyElement(owner, name);\n }\n\n // ---------------------------------------------------------------------------\n // Scheduler — microtask-batched flush.\n // ---------------------------------------------------------------------------\n\n private schedule(e: Effect): void {\n if (e.stopped) return;\n this.pending.add(e);\n if (this.scheduled) return;\n this.scheduled = true;\n queueMicrotask(() => void this.flush());\n }\n\n private async flush(): Promise<void> {\n try {\n while (this.pending.size > 0) {\n const batch = Array.from(this.pending);\n this.pending.clear();\n for (const e of batch) {\n if (e.stopped) continue;\n await e.run();\n // If the effect's run did not synchronously re-schedule itself, its\n // trigger count is reset. Self-rescheduling (a real cycle) keeps the\n // counter climbing toward the 100 cap. Long-lived effects that fire\n // sporadically (e.g. user-input bindings) stay alive forever.\n if (!this.pending.has(e)) e.resetTriggerCount();\n }\n }\n } finally {\n this.scheduled = false;\n }\n }\n}\n\n// Module singleton. One reactive graph per process; mirrors upstream's\n// `runtime.reactivity` pattern.\nexport const reactive = new Reactive();\n","/**\n * `^name` — DOM-scoped inherited variable.\n *\n * Upstream syntax:\n * ^counter → read from nearest ancestor that has `counter` set\n * ^counter on #target → read from (or near) #target\n * set ^counter to 42 → write to owner (or lookupRoot if not yet defined)\n *\n * Parse side: register `^` as a Pratt prefix operator. The handler consumes\n * the identifier token and an optional `on <target>` clause, emitting a\n * `caretVar` AST node.\n *\n * Eval side: `caretVar` node evaluator calls `reactive.readCaret(anchor,\n * name)` which walks the DOM tree for the owner and records deps as it goes.\n */\n\nimport type { ASTNode, ExecutionContext } from './types';\nimport { reactive } from './signals';\n\nexport interface CaretVarNode extends ASTNode {\n type: 'caretVar';\n name: string;\n onTarget: ASTNode | null;\n}\n\ninterface CaretVarRuntime {\n execute(node: ASTNode, ctx: ExecutionContext): Promise<unknown>;\n}\n\n/**\n * Coerce a runtime-evaluated `on <target>` result to a single Element.\n * Selector expressions resolve to an array of matched elements; bare\n * `me`/`it`/`you`/identifier references resolve to single Elements. Take the\n * first element of an array, or the value itself if it's already an Element.\n */\nfunction coerceToElement(value: unknown): Element | null {\n if (value instanceof Element) return value;\n if (Array.isArray(value) && value[0] instanceof Element) return value[0];\n return null;\n}\n\n/**\n * Pratt prefix handler for `^`. Consumes the following identifier token and\n * an optional `on <expr>` clause.\n */\nexport function parseCaretPrefix(token: unknown, ctx: unknown): ASTNode {\n // `ctx` is hyperfixi's PrattContext: { peek, advance, parseExpr, isStopToken, atEnd }.\n const pctx = ctx as {\n peek(): { value?: string; kind?: string } | undefined;\n advance(): { value: string; kind?: string };\n parseExpr(minBp: number): ASTNode;\n };\n const ident = pctx.advance();\n if (!ident || !ident.value) {\n throw new Error(\"Expected identifier after '^'\");\n }\n let onTarget: ASTNode | null = null;\n const next = pctx.peek();\n if (next && next.value === 'on') {\n pctx.advance(); // consume 'on'\n // Binding power 86 — higher than standard comparisons so `^x on me` doesn't\n // over-consume into surrounding expressions.\n onTarget = pctx.parseExpr(86);\n }\n const startTok = token as { start?: number; end?: number; line?: number; column?: number };\n return {\n type: 'caretVar',\n name: ident.value,\n onTarget,\n start: startTok?.start ?? 0,\n end: startTok?.end ?? 0,\n line: startTok?.line,\n column: startTok?.column,\n } as CaretVarNode;\n}\n\n/**\n * Build a node evaluator for `caretVar` bound to a specific runtime. The\n * evaluator walks up from the resolved anchor element, tracking every element\n * visited so writes at any ancestor notify dependent effects.\n *\n * Capturing `runtime` via a factory closure (matching live/when/bind) keeps\n * the evaluator independent of any module-scope state.\n */\nexport function makeEvaluateCaretVar(\n runtime: CaretVarRuntime\n): (node: ASTNode, ctx: unknown) => Promise<unknown> {\n return async function evaluateCaretVar(node, ctx) {\n const n = node as CaretVarNode;\n const context = ctx as ExecutionContext;\n let anchor: Element | null = (context.me as Element | null) ?? null;\n\n if (n.onTarget) {\n const resolved = await runtime.execute(n.onTarget, context);\n const el = coerceToElement(resolved);\n if (el) anchor = el;\n }\n\n if (!anchor) return undefined;\n return reactive.readCaret(anchor, n.name);\n };\n}\n\n/**\n * Build a node writer for `caretVar` bound to a specific runtime. Used by the\n * core `set` command via `parserExtensions.registerNodeWriter`.\n */\nexport function makeWriteCaretVar(\n runtime: CaretVarRuntime\n): (node: ASTNode, value: unknown, ctx: unknown) => Promise<void> {\n return async function writeCaretVar(node, value, ctx) {\n const n = node as CaretVarNode;\n const context = ctx as ExecutionContext;\n const anchor: Element | null = (context.me as Element | null) ?? null;\n if (!anchor) return;\n\n let target: Element | undefined;\n if (n.onTarget) {\n const resolved = await runtime.execute(n.onTarget, context);\n const el = coerceToElement(resolved);\n if (el) target = el;\n }\n reactive.writeCaret(anchor, n.name, value, target);\n };\n}\n","/**\n * `live ... end` — reactive block. Body re-runs whenever any dependency read\n * during its execution changes.\n *\n * Upstream syntax:\n * live [commandList] end\n *\n * Each command in the body is re-executed as a single effect. The entire\n * body shares one effect instance; its dependency set is the union of every\n * read performed during body execution.\n */\n\nimport type { ASTNode, ExecutionContext, FeatureParserCtx } from './types';\nimport { reactive } from './signals';\n\nexport interface LiveFeatureNode extends ASTNode {\n type: 'liveFeature';\n body: ASTNode[];\n}\n\n/**\n * Parse `live ... end`. The `live` keyword has already been consumed by the\n * parser dispatcher; we parse the body and expect a trailing `end`.\n */\nexport function parseLiveFeature(ctx: unknown, token: unknown): ASTNode {\n const pctx = ctx as FeatureParserCtx;\n const body = pctx.parseCommandListUntilEnd();\n // parseCommandListUntilEnd stops when it sees `end` (but doesn't consume it).\n if (!pctx.isAtEnd() && pctx.check('end')) pctx.match('end');\n const tok = token as { start?: number; end?: number; line?: number; column?: number };\n return {\n type: 'liveFeature',\n body,\n start: tok?.start ?? 0,\n end: pctx.getPosition().end,\n line: tok?.line,\n column: tok?.column,\n } as LiveFeatureNode;\n}\n\n/**\n * Create an evaluator bound to a runtime reference. The plugin captures\n * `runtime` at install time and passes it in so effect re-runs can dispatch\n * the body commands without going through module-scope state.\n */\nexport function makeEvaluateLiveFeature(runtime: {\n execute(node: ASTNode, ctx: ExecutionContext): Promise<unknown>;\n}): (node: ASTNode, ctx: unknown) => unknown | Promise<unknown> {\n return async function evaluateLiveFeature(node, ctx) {\n const context = ctx as ExecutionContext;\n const owner = (context.me as Element) ?? document.body;\n const n = node as LiveFeatureNode;\n\n const stop = reactive.createEffect(\n async () => {\n for (const cmd of n.body) {\n await runtime.execute(cmd, context);\n }\n },\n () => {\n /* no-op — side effects happened inside the expression */\n },\n owner\n );\n context.registerCleanup?.(owner, stop, 'live-effect');\n return undefined;\n };\n}\n","/**\n * `when <expr> [or <expr>]* changes [commandList] end` — observer feature.\n *\n * Runs the body when any watched expression's value changes (Object.is\n * semantics). One effect is created per watched expression so writes to a\n * given dep only re-run that watcher.\n */\n\nimport type { ASTNode, ExecutionContext, FeatureParserCtx } from './types';\nimport { reactive } from './signals';\n\nexport interface WhenFeatureNode extends ASTNode {\n type: 'whenFeature';\n watched: ASTNode[];\n body: ASTNode[];\n}\n\nexport function parseWhenFeature(ctx: unknown, token: unknown): ASTNode {\n const pctx = ctx as FeatureParserCtx;\n const watched: ASTNode[] = [pctx.parseExpression()];\n while (pctx.match('or')) {\n watched.push(pctx.parseExpression());\n }\n pctx.consume('changes', \"Expected 'changes' after when expression list\");\n const body = pctx.parseCommandListUntilEnd();\n if (!pctx.isAtEnd() && pctx.check('end')) pctx.match('end');\n const tok = token as { start?: number; end?: number; line?: number; column?: number };\n return {\n type: 'whenFeature',\n watched,\n body,\n start: tok?.start ?? 0,\n end: pctx.getPosition().end,\n line: tok?.line,\n column: tok?.column,\n } as WhenFeatureNode;\n}\n\nexport function makeEvaluateWhenFeature(runtime: {\n execute(node: ASTNode, ctx: ExecutionContext): Promise<unknown>;\n evaluateExpressionWithResult?: (\n node: ASTNode,\n ctx: ExecutionContext\n ) => Promise<{ value: unknown }>;\n}): (node: ASTNode, ctx: unknown) => unknown | Promise<unknown> {\n return async function evaluateWhenFeature(node, ctx) {\n const context = ctx as ExecutionContext;\n const owner = (context.me as Element) ?? document.body;\n const n = node as WhenFeatureNode;\n\n for (const watchedExpr of n.watched) {\n const stop = reactive.createEffect(\n async () => {\n // Evaluate the watched expression as a regular expression through the\n // runtime so that trackGlobal/trackElement fire via the global-write\n // hook and caret-var reads.\n return await runtime.execute(watchedExpr, context);\n },\n async newValue => {\n // Fire the body with `it` / `result` bound to the new value.\n const subCtx: ExecutionContext = { ...context, it: newValue, result: newValue };\n for (const cmd of n.body) {\n await runtime.execute(cmd, subCtx);\n }\n },\n owner\n );\n context.registerCleanup?.(owner, stop, 'when-effect');\n }\n return undefined;\n };\n}\n","/**\n * `bind X [to|and|with] Y` — two-way binding.\n *\n * Creates two effects (registration order is load-bearing — both initialize\n * via `queueMicrotask` and run in registration order):\n * 1. DOM → var: read DOM, write var. DOM \"wins\" on init.\n * 2. var → DOM: read var, write DOM. Fires on programmatic var writes\n * after init. On its own initial run, var === DOM (Effect 1 just synced\n * them), so the write is a no-op.\n *\n * Two binding forms:\n *\n * 1. Auto-detected (DOM side is a bare element expression):\n *\n * bind $name to #input -- detects `value`\n * bind $checked to me -- detects `checked` on a checkbox\n *\n * Auto-detected property by element type:\n * - INPUT[type=checkbox|radio] → `checked`\n * - INPUT[type=number|range] → `valueAsNumber`\n * - INPUT|TEXTAREA|SELECT → `value`\n * - contenteditable=\"true\" → `textContent`\n * - Custom elements with own `value` → `value`\n *\n * 2. Explicit property (DOM side is a member or possessive expression):\n *\n * bind $color to #picker's value -- possessive (preferred — reads in any language)\n * bind $color to #picker.value -- dot (JS-style alternative)\n * bind $text to #div's textContent -- non-form properties: var→DOM only\n *\n * For form-like elements, both directions work. For non-form elements\n * (e.g., binding a div's `textContent`), only var→DOM fires — there are\n * no input/change events to drive DOM→var, so user mutations of the\n * property via devtools won't propagate back.\n */\n\nimport type { ASTNode, ExecutionContext, FeatureParserCtx } from './types';\nimport { reactive } from './signals';\n\nexport interface BindFeatureNode extends ASTNode {\n type: 'bindFeature';\n left: ASTNode;\n right: ASTNode;\n}\n\nexport function parseBindFeature(ctx: unknown, token: unknown): ASTNode {\n const pctx = ctx as FeatureParserCtx;\n const left = pctx.parseExpression();\n // Accept any of `to` / `and` / `with` as the separator.\n if (!(pctx.match('to') || pctx.match('and') || pctx.match('with'))) {\n throw new Error(\"bind requires 'to', 'and', or 'with' between the two sides\");\n }\n const right = pctx.parseExpression();\n // Optional `end` terminator (matches upstream which allows both forms).\n if (pctx.check('end')) pctx.match('end');\n const tok = token as { start?: number; end?: number; line?: number; column?: number };\n return {\n type: 'bindFeature',\n left,\n right,\n start: tok?.start ?? 0,\n end: pctx.getPosition().end,\n line: tok?.line,\n column: tok?.column,\n } as BindFeatureNode;\n}\n\n/**\n * Auto-detect the DOM property to bind by element type. Returns null if the\n * target isn't a recognized form/editable element.\n */\nfunction detectProperty(el: Element): string | null {\n const tag = el.tagName;\n if (tag === 'INPUT') {\n const type = (el as HTMLInputElement).type;\n if (type === 'checkbox' || type === 'radio') return 'checked';\n if (type === 'number' || type === 'range') return 'valueAsNumber';\n return 'value';\n }\n if (tag === 'TEXTAREA' || tag === 'SELECT') return 'value';\n const ce = (el as HTMLElement).contentEditable;\n if (ce === 'true') return 'textContent';\n // Custom elements with own `value` prop.\n if ('value' in el) return 'value';\n return null;\n}\n\n/**\n * If `node` is a `memberExpression` or `possessiveExpression` with a static\n * identifier property (`#el.value` or `#el's value`), return the inner element\n * expression and the property name. Returns null for anything else.\n *\n * Computed member access (`#el[prop]`) is intentionally rejected — we'd have to\n * evaluate the index dynamically and the binding direction would be unclear.\n * Chained property access (`#el.dataset.value`) is also not unpacked — we only\n * peel one level. Multi-level support is a future arc.\n */\nfunction unwrapExplicitProperty(node: ASTNode): { element: ASTNode; propertyName: string } | null {\n if (!node || (node.type !== 'memberExpression' && node.type !== 'possessiveExpression')) {\n return null;\n }\n if (node.type === 'memberExpression' && node.computed === true) return null;\n const property = node.property as ASTNode | undefined;\n const object = node.object as ASTNode | undefined;\n if (!property || !object || property.type !== 'identifier') return null;\n const name = (property.name as string) ?? '';\n if (!name) return null;\n return { element: object, propertyName: name };\n}\n\n/**\n * Detect a chained property access on the bind RHS — e.g.\n * `me.style.backgroundColor` or `#el's dataset's value`.\n *\n * After `unwrapExplicitProperty` peels the outermost member, the returned\n * `element` is the object of that member. If that object is itself a\n * member/possessive expression, we have a multi-level chain that v1 of\n * bind doesn't support. Used only for the error path; the parsed AST is\n * left intact.\n */\nfunction isChainedMember(node: ASTNode | undefined): boolean {\n if (!node) return false;\n return node.type === 'memberExpression' || node.type === 'possessiveExpression';\n}\n\n/**\n * Whether the input/change listener installed by `trackDomProperty` would\n * actually fire for this element. Used to decide whether to wire up the\n * DOM→var direction when an explicit property is given.\n */\nfunction isFormLikeElement(el: Element): boolean {\n const tag = el.tagName;\n if (tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT') return true;\n if ((el as HTMLElement).contentEditable === 'true') return true;\n return false;\n}\n\n/**\n * Whether debug logging is enabled. Mirrors signals.ts — keeps a single\n * convention for the package.\n */\nfunction debugEnabled(): boolean {\n try {\n return typeof localStorage !== 'undefined' && localStorage.getItem('hyperfixi:debug') !== null;\n } catch {\n return false;\n }\n}\n\n/**\n * Create the bind evaluator. Because `bind` has unusual parse shape (AST\n * nodes for left/right that may be identifiers, `$name` references, or DOM\n * element lookups), we rely on the runtime to evaluate them.\n */\nexport function makeEvaluateBindFeature(runtime: {\n execute(node: ASTNode, ctx: ExecutionContext): Promise<unknown>;\n}): (node: ASTNode, ctx: unknown) => unknown | Promise<unknown> {\n return async function evaluateBindFeature(node, ctx) {\n const context = ctx as ExecutionContext;\n const owner = (context.me as Element) ?? document.body;\n const n = node as BindFeatureNode;\n\n // Dynamically import the element-scope accessor from core. A static import\n // would pull core's full index into this module's load graph — and core's\n // index eagerly evaluates `morphlex` (which touches `Element.prototype` at\n // module scope), throwing `Element is not defined` when @hyperfixi/reactivity\n // is imported in bare Node/SSR. This evaluator only runs in a browser (it\n // uses `document.body` above), so deferring the import keeps the package\n // importable in Node while `:name` binds still address the core store.\n const { getElementScopeMap } = await import('@hyperfixi/core');\n\n // Resolve sides. One is a var reference (read/write a symbol), the other\n // a DOM target (read/write a property). Determine direction from node shape.\n const leftIsVar = isVarRef(n.left);\n const rightIsVar = isVarRef(n.right);\n\n // We need a DOM-side descriptor (element + property) and a var-side name.\n let varSide: { name: string; isGlobal: boolean } | null = null;\n let domSide: { exprNode: ASTNode } | null = null;\n\n if (leftIsVar && !rightIsVar) {\n varSide = varRefInfo(n.left);\n domSide = { exprNode: n.right };\n } else if (rightIsVar && !leftIsVar) {\n varSide = varRefInfo(n.right);\n domSide = { exprNode: n.left };\n } else if (leftIsVar && rightIsVar) {\n // var-to-var binding not supported in v1 (no DOM side).\n throw new Error('bind: cannot bind two symbols together (need a DOM side)');\n } else {\n throw new Error('bind: could not identify a symbol side');\n }\n\n // Unwrap explicit property syntax: `#el.value` or `#el's value` becomes\n // (element-expression, propertyName). Otherwise, evaluate the whole side\n // and rely on auto-detection.\n const explicit = unwrapExplicitProperty(domSide.exprNode);\n // Multi-level property access (`#el.style.background`, `me's dataset's id`)\n // is not supported in v1. Surface that diagnosis up front; otherwise the\n // user gets the generic \"did not resolve to an element\" message and no\n // hint that the chain is the problem.\n if (explicit && isChainedMember(explicit.element)) {\n throw new Error(\n 'bind: multi-level property access (e.g., `#el.a.b`) is not supported in v1 — restructure to a single property write or pass the element directly for auto-detection (e.g., `bind $x to #el`).'\n );\n }\n const elementExpr = explicit ? explicit.element : domSide.exprNode;\n const domValue = await runtime.execute(elementExpr, context);\n if (!(domValue instanceof Element)) {\n const valueType =\n domValue === null ? 'null' : domValue === undefined ? 'undefined' : typeof domValue;\n const snippet =\n domValue !== null && domValue !== undefined ? ` \"${String(domValue).slice(0, 40)}\"` : '';\n const suggestion = explicit\n ? ''\n : \" If you meant to write to a property, use the explicit form: `<selector>'s <property>`.\";\n throw new Error(\n `bind: right-hand side did not resolve to an element (got ${valueType}${snippet}).${suggestion}`\n );\n }\n const el = domValue;\n // Explicit property bypasses auto-detect; the user takes responsibility for\n // picking something readable from the element. For DOM→var to work without\n // explicit property, fall back to auto-detect.\n const prop = explicit ? explicit.propertyName : detectProperty(el);\n if (!prop) {\n throw new Error(\n `bind: could not auto-detect property for <${el.tagName.toLowerCase()}> — use explicit \\`<expr>'s <property>\\` form`\n );\n }\n // For explicit-property mode on non-form elements, DOM→var can't sync via\n // input/change events. Skip the listener install; only var→DOM runs.\n const installDomToVar = !explicit || isFormLikeElement(el);\n if (explicit && !installDomToVar && debugEnabled() && typeof console !== 'undefined') {\n console.warn(\n `[@hyperfixi/reactivity] bind: DOM→var skipped for <${el.tagName.toLowerCase()}>.${prop} — no input/change event source.`\n );\n }\n\n // Initial sync: DOM → var.\n const readDom = (): unknown => {\n if (prop === 'valueAsNumber') return (el as HTMLInputElement).valueAsNumber;\n if (prop === 'checked') return (el as HTMLInputElement).checked;\n if (prop === 'textContent') return el.textContent ?? '';\n return (el as any)[prop];\n };\n const writeDom = (value: unknown): void => {\n if (prop === 'valueAsNumber') {\n const n = Number(value);\n (el as HTMLInputElement).valueAsNumber = Number.isNaN(n) ? (null as never) : n;\n } else if (prop === 'checked') {\n (el as HTMLInputElement).checked = Boolean(value);\n } else if (prop === 'textContent') {\n el.textContent = value == null ? '' : String(value);\n } else {\n (el as any)[prop] = value;\n }\n };\n const readVar = (): unknown => {\n if (varSide!.isGlobal) return context.globals?.get(varSide!.name);\n // `:name` is element-scoped — read from the owner element's store, the same\n // place the core `set :name` writes. (Direct Map access, not getElementVar,\n // so we don't double-fire the read hook; bind tracks via trackElement.)\n return getElementScopeMap(owner).get(varSide!.name);\n };\n const writeVar = (value: unknown): void => {\n if (varSide!.isGlobal) {\n context.globals?.set(varSide!.name, value);\n // Bypass the global-write hook (we're touching the Map directly), so\n // notify the reactive graph manually for the var→DOM effect.\n reactive.notifyGlobal(varSide!.name);\n } else {\n // Element-scoped `:name`: write to the owner element's store (same place\n // core's `set :name` writes), then notify manually — the direct Map write\n // skips the localWriteHook.\n getElementScopeMap(owner).set(varSide!.name, value);\n reactive.notifyElement(owner, varSide!.name);\n }\n };\n\n // Effect 1: DOM → var (fires on user input). Skipped for explicit-property\n // bindings on non-form elements — no input/change events would drive it.\n let stopDomToVar: (() => void) | null = null;\n if (installDomToVar) {\n stopDomToVar = reactive.createEffect(\n () => {\n reactive.trackDomProperty(el, prop);\n return readDom();\n },\n newValue => {\n writeVar(newValue);\n },\n owner\n );\n }\n\n // Effect 2: var → DOM (fires on programmatic var writes — for globals\n // via the core's globalWriteHook, for locals via the localWriteHook the\n // reactivity plugin registers).\n const stopVarToDom = reactive.createEffect(\n () => {\n if (varSide!.isGlobal) reactive.trackGlobal(varSide!.name);\n else reactive.trackElement(owner, varSide!.name);\n return readVar();\n },\n newValue => {\n if (newValue === undefined) return;\n writeDom(newValue);\n },\n owner\n );\n\n if (stopDomToVar) context.registerCleanup?.(owner, stopDomToVar, 'bind-dom-to-var');\n context.registerCleanup?.(owner, stopVarToDom, 'bind-var-to-dom');\n return undefined;\n };\n}\n\n/**\n * Is this AST node a var reference we can bind to?\n *\n * The hyperfixi parser emits `identifier` nodes for both globals and locals,\n * but with different shapes:\n * - `$foo` → `{ type: 'identifier', name: '$foo' }` (no scope field)\n * - `:foo` → `{ type: 'identifier', name: 'foo', scope: 'element' }` (element-scoped)\n * - `::foo` → `{ type: 'identifier', name: 'foo', scope: 'global' }`\n *\n * We accept all of them. The legacy `$`-prefix sniff stays as a fallback because\n * `parseExpression` doesn't always set `scope`.\n */\nfunction isVarRef(node: ASTNode): boolean {\n if (!node) return false;\n if (node.type !== 'identifier') return false;\n const scope = node.scope as string | undefined;\n if (scope === 'local' || scope === 'element' || scope === 'global') return true;\n const name = (node.name as string) ?? '';\n return name.startsWith('$') || name.startsWith(':');\n}\n\nfunction varRefInfo(node: ASTNode): { name: string; isGlobal: boolean } {\n const rawName = (node.name as string) ?? '';\n const scope = node.scope as string | undefined;\n // Prefer the parser-emitted scope marker. Fall back to prefix sniffing for\n // shapes where scope isn't set (e.g. `$foo` arriving as bare identifier\n // with name '$foo').\n let isGlobal: boolean;\n let name: string;\n if (scope === 'global') {\n isGlobal = true;\n name = rawName;\n } else if (scope === 'local' || scope === 'element') {\n isGlobal = false;\n name = rawName;\n } else if (rawName.startsWith('$')) {\n isGlobal = true;\n name = rawName.slice(1);\n } else if (rawName.startsWith(':')) {\n isGlobal = false;\n name = rawName.slice(1);\n } else {\n isGlobal = false;\n name = rawName;\n }\n if (!name) {\n throw new Error('bind: variable reference has empty name');\n }\n return { name, isGlobal };\n}\n","/**\n * The version reported by this package's plugin descriptor.\n *\n * GENERATED — do not edit by hand. `scripts/set-version.cjs` rewrites this file\n * alongside the `package.json` files it already touches.\n *\n * The literal used to sit inline in the plugin object, where nothing kept it\n * honest: `HyperfixiPlugin` does not declare `version` (the plugins widen the\n * type themselves), so no consumer and no test ever read it. Both froze at the\n * release they were written in — reactivity at 2.3.1, realtime at 2.6.0 — and\n * shipped that way inside the browser bundle for many releases. The equality\n * check in `version.test.ts` is what keeps this one honest.\n */\nexport const VERSION = '3.1.1';\n"],"mappings":"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","names":["debugEnabled","n"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/signals.ts","../src/caret-var.ts","../src/live.ts","../src/when.ts","../src/bind.ts","../src/version.ts"],"sourcesContent":["/**\n * @hyperfixi/reactivity — signal-based reactive features for hyperfixi.\n *\n * Adds four constructs from upstream _hyperscript 0.9.90:\n *\n * live [commandList] end reactive block — body re-runs\n * when tracked reads change.\n *\n * when <expr> [or <expr>]* changes observer — body runs when any\n * [commandList] end watched expression changes.\n *\n * bind <var> to <element> two-way DOM ⇄ var binding.\n *\n * ^name [on <target>] DOM-scoped inherited variable\n * (read + write with `^`).\n *\n * Install:\n *\n * ```ts\n * import { createRuntime, installPlugin } from '@hyperfixi/core';\n * import { reactivityPlugin } from '@hyperfixi/reactivity';\n *\n * const runtime = createRuntime();\n * installPlugin(runtime, reactivityPlugin);\n * ```\n */\n\nimport type { HyperfixiPlugin, HyperfixiPluginContext } from '@hyperfixi/core';\nimport { reactive } from './signals';\nimport { parseCaretPrefix, makeEvaluateCaretVar, makeWriteCaretVar } from './caret-var';\nimport { parseLiveFeature, makeEvaluateLiveFeature } from './live';\nimport { parseWhenFeature, makeEvaluateWhenFeature } from './when';\nimport { parseBindFeature, makeEvaluateBindFeature } from './bind';\nimport { VERSION } from './version.js';\n\nexport { reactive } from './signals';\nexport type { CaretVarNode } from './caret-var';\nexport type { LiveFeatureNode } from './live';\nexport type { WhenFeatureNode } from './when';\nexport type { BindFeatureNode } from './bind';\n\n/**\n * The plugin object. Install once at app startup; re-installing is idempotent\n * (guarded via a `parserExtensions.hasFeature('live')` check).\n *\n * Registers:\n * - Features: `live`, `when`, `bind` (top-level features with `end` bodies)\n * - Prefix op: `^` (primary expression for DOM-scoped vars)\n * - Node evaluators: `liveFeature`, `whenFeature`, `bindFeature`, `caretVar`\n * - A node writer for `caretVar` so `set ^X to Y` flows through `reactive.writeCaret`\n * - Global read/write hooks so `$name` reads track and writes notify\n *\n * Effect cleanup: each effect-creating evaluator calls\n * `context.registerCleanup(owner, stop, ...)` so the core runtime tears effects\n * down when their owning element is cleaned up. There is no separate plugin-level\n * cleanup hook; `reactive.stopElementEffects(el)` is exposed for explicit teardown\n * by tests and consumers that manage element lifecycle outside the runtime.\n */\nexport const reactivityPlugin: HyperfixiPlugin & { version: string } = {\n name: '@hyperfixi/reactivity',\n version: VERSION,\n install(ctx: HyperfixiPluginContext) {\n const { parserExtensions, runtime } = ctx;\n\n // Idempotency: the parser-extension registry is process-singleton, so\n // re-installing into a fresh runtime would otherwise stack additional\n // global read/write hooks on every call. `snapshot()`/`restore()` clears\n // the feature registry — when tests roll back the registry, this guard\n // re-enables a fresh install.\n if (parserExtensions.hasFeature('live')) return;\n\n // Parser hooks — three block features plus a primary-expression caret.\n parserExtensions.registerFeature('live', parseLiveFeature as never);\n parserExtensions.registerFeature('when', parseWhenFeature as never);\n parserExtensions.registerFeature('bind', parseBindFeature as never);\n parserExtensions.registerPrefixOperator('^', 85, parseCaretPrefix as never);\n\n // Runtime evaluators. Features capture `runtime` so effect re-runs can\n // dispatch body commands without going through module-scope state.\n parserExtensions.registerNodeEvaluator(\n 'liveFeature',\n makeEvaluateLiveFeature(runtime as never) as never\n );\n parserExtensions.registerNodeEvaluator(\n 'whenFeature',\n makeEvaluateWhenFeature(runtime as never) as never\n );\n parserExtensions.registerNodeEvaluator(\n 'bindFeature',\n makeEvaluateBindFeature(runtime as never) as never\n );\n parserExtensions.registerNodeEvaluator(\n 'caretVar',\n makeEvaluateCaretVar(runtime as never) as never\n );\n\n // Caret-var write: lets the core `set` command dispatch `set ^X to Y`\n // through `reactive.writeCaret`. Resolves `on <target>` via the captured\n // runtime — no global-scope indirection.\n parserExtensions.registerNodeWriter('caretVar', makeWriteCaretVar(runtime as never) as never);\n\n // Global-write hook: notify the reactive graph whenever `$name` is set.\n // The returned disposer is intentionally discarded — the install-time\n // idempotency guard above is the sole gate against double-registration.\n parserExtensions.registerGlobalWriteHook((name: string, _value: unknown, _context: unknown) => {\n reactive.notifyGlobal(name);\n });\n\n // Global-read hook: track the read against the current effect (if any)\n // so effects re-run when the global changes.\n parserExtensions.registerGlobalReadHook((name: string, _context: unknown) => {\n reactive.trackGlobal(name);\n });\n\n // Local hooks — analogous to globals, but keyed by `context.me` so each\n // element holds independent state. A `set :foo to ...` running in a\n // handler with `me=button1` notifies effects subscribed to (button1, 'foo');\n // a `set :foo` in a different `me` won't reach them. This matches how\n // locals already work — they're never cross-context — and lets two\n // components hold independent `:foo` state without interference.\n parserExtensions.registerLocalWriteHook((name: string, _value: unknown, context) => {\n const owner = (context as { me?: Element | null }).me ?? null;\n if (owner) reactive.notifyElement(owner, name);\n });\n\n parserExtensions.registerLocalReadHook((name: string, context) => {\n const owner = (context as { me?: Element | null }).me ?? null;\n if (owner) reactive.trackElement(owner, name);\n });\n },\n};\n\nexport default reactivityPlugin;\n","/**\n * Signal/effect reactive core.\n *\n * Models three dependency kinds for v1:\n * - `global` — `$name` variables in `context.globals`\n * - `element` — `^name` DOM-scoped variables (per-element storage)\n * - `dom` — DOM properties read via `input`/`change` listeners\n *\n * Inspired by upstream _hyperscript 0.9.91's `src/core/runtime/reactivity.js`\n * but reimplemented in TypeScript against hyperfixi's types. Batched via\n * `queueMicrotask` so synchronous writes coalesce into a single flush. Cycle\n * detection halts at >100 consecutive triggers (matching upstream).\n *\n * External packages never see `Effect` or `Reactive` directly — they hold\n * the disposer returned by `createEffect()` and call it when the owning\n * element is cleaned up.\n */\n\nexport type DepKind = 'global' | 'element' | 'dom';\n\n/**\n * Whether reactive debug logging is enabled. Mirrors the core convention:\n * `localStorage.setItem('hyperfixi:debug', '*')` enables, anything else (or no\n * `localStorage`) disables. Cheap call — no caching, since users toggle this\n * interactively in the console.\n */\nfunction debugEnabled(): boolean {\n try {\n return typeof localStorage !== 'undefined' && localStorage.getItem('hyperfixi:debug') !== null;\n } catch {\n return false;\n }\n}\n\nfunction debugWarn(message: string, err: unknown): void {\n if (!debugEnabled()) return;\n if (typeof console === 'undefined') return;\n console.warn(`[@hyperfixi/reactivity] ${message}`, err);\n}\n\nexport interface Dep {\n readonly key: string;\n readonly kind: DepKind;\n readonly name: string;\n readonly element: Element | null;\n}\n\nconst globalDepKey = (name: string): string => `global:${name}`;\nconst elementDepKey = (el: Element, name: string): string => `element:${reactiveIdFor(el)}:${name}`;\nconst domDepKey = (el: Element, prop: string): string => `dom:${reactiveIdFor(el)}:${prop}`;\n\n// WeakMap-based id assignment so we don't monkey-patch DOM nodes. Ids are\n// per-process and only used to construct stable string keys for an effect's\n// per-effect dependency dedup map.\nconst _reactiveIds = new WeakMap<Element, number>();\nlet _idCounter = 0;\nfunction reactiveIdFor(el: Element): number {\n const existing = _reactiveIds.get(el);\n if (existing !== undefined) return existing;\n const id = ++_idCounter;\n _reactiveIds.set(el, id);\n return id;\n}\n\nexport class Effect {\n readonly dependencies = new Map<string, Dep>();\n private lastValue: unknown = undefined;\n private _stopped = false;\n private _consecutiveTriggers = 0;\n\n constructor(\n private readonly r: Reactive,\n readonly expression: () => unknown | Promise<unknown>,\n readonly handler: (value: unknown) => void | Promise<void>,\n readonly element: Element | null\n ) {}\n\n get stopped(): boolean {\n return this._stopped;\n }\n\n /**\n * Run the effect for the first time: collect dependencies, subscribe, call\n * handler with the initial value. Errors in expression evaluation are caught\n * and silently swallowed (matches upstream's tolerance).\n */\n async initialize(): Promise<void> {\n if (this._stopped) return;\n try {\n const value = await this.r._runWithEffect(this, this.expression);\n this.lastValue = value;\n if (value !== undefined && value !== null) {\n await this.handler(value);\n }\n } catch (err) {\n // Swallow — expression evaluation failed during initial read. Surface\n // via debug log so users diagnosing \"my effect never fires\" can see why.\n debugWarn('effect.initialize failed', err);\n }\n }\n\n /**\n * Re-run the effect after a notify. Cycle-detects (halts at 101 consecutive\n * triggers). If the new value is Object.is-equal to the last, the handler\n * is skipped. If the owning element has disconnected, the effect stops\n * itself and returns.\n */\n async run(): Promise<void> {\n if (this._stopped) return;\n if (this.element && !this.element.isConnected) {\n this.stop();\n return;\n }\n this._consecutiveTriggers++;\n if (this._consecutiveTriggers > 100) {\n if (typeof console !== 'undefined') {\n console.error(\n '[@hyperfixi/reactivity] Effect halted: > 100 consecutive triggers (cycle detected).'\n );\n }\n this.stop();\n return;\n }\n try {\n const value = await this.r._runWithEffect(this, this.expression);\n if (Object.is(value, this.lastValue)) return;\n this.lastValue = value;\n await this.handler(value);\n } catch (err) {\n // Swallow — expression/handler errors don't break the microtask flush.\n debugWarn('effect.run failed', err);\n }\n }\n\n resetTriggerCount(): void {\n this._consecutiveTriggers = 0;\n }\n\n /**\n * Stop the effect and remove it from all dependency subscriptions. Safe to\n * call multiple times.\n */\n stop(): void {\n if (this._stopped) return;\n this._stopped = true;\n this.r._unsubscribeEffect(this);\n this.dependencies.clear();\n }\n}\n\ninterface ElementState {\n symbolSubs: Map<string, Set<Effect>>;\n caretVars: Map<string, unknown>;\n domHandlers: Map<string, DomHandler>;\n effects: Set<Effect>;\n}\n\ninterface DomHandler {\n subs: Set<Effect>;\n detach: () => void;\n}\n\nexport class Reactive {\n private currentEffect: Effect | null = null;\n /**\n * Tail of the effect-evaluation chain. `_runWithEffect` links onto this so\n * only one effect owns the `currentEffect` slot at a time — see the comment\n * there for why concurrent async effects otherwise lose their dependencies.\n */\n private trackingQueue: Promise<void> = Promise.resolve();\n private pending = new Set<Effect>();\n private scheduled = false;\n\n private globalSubs = new Map<string, Set<Effect>>();\n private elementState = new WeakMap<Element, ElementState>();\n\n private getElementState(el: Element): ElementState {\n let s = this.elementState.get(el);\n if (!s) {\n s = {\n symbolSubs: new Map(),\n caretVars: new Map(),\n domHandlers: new Map(),\n effects: new Set(),\n };\n this.elementState.set(el, s);\n }\n return s;\n }\n\n /**\n * Internal helper invoked by Effect — installs `this` as the current effect,\n * runs the expression, then restores the previous current-effect pointer.\n * Track* methods (called from read-paths) consult `currentEffect` to know\n * which effect to subscribe.\n */\n async _runWithEffect(e: Effect, fn: () => unknown | Promise<unknown>): Promise<unknown> {\n // Dependency capture keys off ONE `currentEffect` slot, but effect\n // expressions are async (a `live` body awaits each command). Two effects\n // evaluating concurrently would interleave across their await points and\n // attribute each other's reads — or, once the inner one restored `prev`,\n // leave the outer one tracking nothing at all. Three `live` blocks on a\n // page (they all start in the same tick) reliably lost their\n // subscriptions that way and froze.\n //\n // Serialize instead: each run waits for the previous one to finish, so\n // the slot belongs to exactly one effect for the whole of its evaluation.\n // Effect bodies are short DOM updates, and this only orders effect\n // evaluation — it does not block the page.\n const runExclusive = async (): Promise<unknown> => {\n const prev = this.currentEffect;\n this.currentEffect = e;\n // Unsubscribe from previous deps before re-running. The expression will\n // re-record whatever it actually reads this time, avoiding stale subs.\n this._unsubscribeEffect(e);\n e.dependencies.clear();\n try {\n return await fn();\n } finally {\n this.currentEffect = prev;\n }\n };\n\n // Chain onto the tail of the queue, and keep the queue alive whether this\n // run resolves or throws (callers already swallow effect errors).\n const result = this.trackingQueue.then(runExclusive, runExclusive);\n this.trackingQueue = result.then(\n () => undefined,\n () => undefined\n );\n return result;\n }\n\n // ---------------------------------------------------------------------------\n // Track (read-path) — invoked from interceptors / evaluators.\n // ---------------------------------------------------------------------------\n\n trackGlobal(name: string): void {\n const e = this.currentEffect;\n if (!e) return;\n const key = globalDepKey(name);\n if (e.dependencies.has(key)) return;\n e.dependencies.set(key, { key, kind: 'global', name, element: null });\n let subs = this.globalSubs.get(name);\n if (!subs) {\n subs = new Set();\n this.globalSubs.set(name, subs);\n }\n subs.add(e);\n }\n\n trackElement(el: Element, name: string): void {\n const e = this.currentEffect;\n if (!e) return;\n const key = elementDepKey(el, name);\n if (e.dependencies.has(key)) return;\n e.dependencies.set(key, { key, kind: 'element', name, element: el });\n const state = this.getElementState(el);\n let subs = state.symbolSubs.get(name);\n if (!subs) {\n subs = new Set();\n state.symbolSubs.set(name, subs);\n }\n subs.add(e);\n state.effects.add(e);\n }\n\n trackDomProperty(el: Element, prop: string): void {\n const e = this.currentEffect;\n if (!e) return;\n const key = domDepKey(el, prop);\n if (e.dependencies.has(key)) return;\n e.dependencies.set(key, { key, kind: 'dom', name: prop, element: el });\n const state = this.getElementState(el);\n let handler = state.domHandlers.get(prop);\n if (!handler) {\n const subs = new Set<Effect>();\n const listener = (): void => {\n for (const effect of subs) this.schedule(effect);\n };\n // Use both input and change to cover radios/selects which don't fire input.\n el.addEventListener('input', listener);\n el.addEventListener('change', listener);\n handler = {\n subs,\n detach: () => {\n el.removeEventListener('input', listener);\n el.removeEventListener('change', listener);\n },\n };\n state.domHandlers.set(prop, handler);\n }\n handler.subs.add(e);\n state.effects.add(e);\n }\n\n // ---------------------------------------------------------------------------\n // Notify (write-path) — schedules dependent effects for re-run.\n // ---------------------------------------------------------------------------\n\n notifyGlobal(name: string): void {\n const subs = this.globalSubs.get(name);\n if (!subs) return;\n for (const e of subs) this.schedule(e);\n }\n\n notifyElement(el: Element, name: string): void {\n const state = this.elementState.get(el);\n if (!state) return;\n const subs = state.symbolSubs.get(name);\n if (!subs) return;\n for (const e of subs) this.schedule(e);\n }\n\n // ---------------------------------------------------------------------------\n // Effect lifecycle.\n // ---------------------------------------------------------------------------\n\n /**\n * Create + initialize an effect. Returns a disposer that stops the effect.\n * Callers are expected to register the disposer with the core runtime's\n * cleanup registry so it fires on element removal.\n */\n createEffect(\n expression: () => unknown | Promise<unknown>,\n handler: (value: unknown) => void | Promise<void>,\n owner: Element | null\n ): () => void {\n const e = new Effect(this, expression, handler, owner);\n if (owner) this.getElementState(owner).effects.add(e);\n // Fire initialize asynchronously so the caller can set up cleanup registration\n // before the first run touches the DOM.\n queueMicrotask(() => {\n void e.initialize();\n });\n return () => e.stop();\n }\n\n /**\n * Stop all effects owned by an element. Called by the reactivity plugin's\n * cleanup hook registered via `runtime.getCleanupRegistry()`.\n */\n stopElementEffects(el: Element): void {\n const state = this.elementState.get(el);\n if (!state) return;\n for (const e of Array.from(state.effects)) e.stop();\n state.effects.clear();\n }\n\n /**\n * Internal: remove an effect's entries from all subscriber sets. Called by\n * `Effect.stop()`.\n */\n _unsubscribeEffect(e: Effect): void {\n for (const dep of e.dependencies.values()) {\n if (dep.kind === 'global') {\n const subs = this.globalSubs.get(dep.name);\n subs?.delete(e);\n } else if (dep.kind === 'element' && dep.element) {\n const state = this.elementState.get(dep.element);\n const subs = state?.symbolSubs.get(dep.name);\n subs?.delete(e);\n state?.effects.delete(e);\n } else if (dep.kind === 'dom' && dep.element) {\n const state = this.elementState.get(dep.element);\n const handler = state?.domHandlers.get(dep.name);\n handler?.subs.delete(e);\n if (handler && handler.subs.size === 0) {\n handler.detach();\n state?.domHandlers.delete(dep.name);\n }\n state?.effects.delete(e);\n }\n }\n }\n\n // ---------------------------------------------------------------------------\n // Caret-variable storage — `^name` reads/writes.\n // ---------------------------------------------------------------------------\n\n /**\n * Whether `el` is a `dom-scope=\"isolated\"` boundary. Walks of `^var`\n * lookups stop at boundary elements that don't define the var, so nested\n * components don't accidentally read or write each other's state.\n */\n private isIsolationBoundary(el: Element): boolean {\n return typeof el.getAttribute === 'function' && el.getAttribute('dom-scope') === 'isolated';\n }\n\n /**\n * Walk up the DOM tree from `lookupRoot`, returning the first element whose\n * state has `name` defined. Stops at any `dom-scope=\"isolated\"` boundary\n * that doesn't itself define the var. Returns `null` if no owner is found.\n */\n private findCaretOwner(lookupRoot: Element, name: string): Element | null {\n let el: Element | null = lookupRoot;\n while (el) {\n const state = this.elementState.get(el);\n if (state && state.caretVars.has(name)) return el;\n if (this.isIsolationBoundary(el)) return null;\n el = el.parentElement;\n }\n return null;\n }\n\n /**\n * Read a DOM-scoped variable. Walks up from `lookupRoot`, tracking each\n * element visited as an `element` dep (so writes at any ancestor notify\n * dependent effects). Stops at any `dom-scope=\"isolated\"` boundary that\n * doesn't itself define the var.\n */\n readCaret(lookupRoot: Element, name: string): unknown {\n let el: Element | null = lookupRoot;\n while (el) {\n this.trackElement(el, name);\n const state = this.elementState.get(el);\n if (state && state.caretVars.has(name)) {\n return state.caretVars.get(name);\n }\n if (this.isIsolationBoundary(el)) return undefined;\n el = el.parentElement;\n }\n return undefined;\n }\n\n /**\n * Write a DOM-scoped variable. If `target` is provided, writes there\n * directly; otherwise walks up from `lookupRoot` to find the existing owner,\n * falling back to `lookupRoot` itself if no owner exists.\n */\n writeCaret(lookupRoot: Element, name: string, value: unknown, target?: Element): void {\n const owner = target ?? this.findCaretOwner(lookupRoot, name) ?? lookupRoot;\n const state = this.getElementState(owner);\n state.caretVars.set(name, value);\n this.notifyElement(owner, name);\n }\n\n // ---------------------------------------------------------------------------\n // Scheduler — microtask-batched flush.\n // ---------------------------------------------------------------------------\n\n private schedule(e: Effect): void {\n if (e.stopped) return;\n this.pending.add(e);\n if (this.scheduled) return;\n this.scheduled = true;\n queueMicrotask(() => void this.flush());\n }\n\n private async flush(): Promise<void> {\n try {\n while (this.pending.size > 0) {\n const batch = Array.from(this.pending);\n this.pending.clear();\n for (const e of batch) {\n if (e.stopped) continue;\n await e.run();\n // If the effect's run did not synchronously re-schedule itself, its\n // trigger count is reset. Self-rescheduling (a real cycle) keeps the\n // counter climbing toward the 100 cap. Long-lived effects that fire\n // sporadically (e.g. user-input bindings) stay alive forever.\n if (!this.pending.has(e)) e.resetTriggerCount();\n }\n }\n } finally {\n this.scheduled = false;\n }\n }\n}\n\n// Module singleton. One reactive graph per process; mirrors upstream's\n// `runtime.reactivity` pattern.\nexport const reactive = new Reactive();\n","/**\n * `^name` — DOM-scoped inherited variable.\n *\n * Upstream syntax:\n * ^counter → read from nearest ancestor that has `counter` set\n * ^counter on #target → read from (or near) #target\n * set ^counter to 42 → write to owner (or lookupRoot if not yet defined)\n *\n * Parse side: register `^` as a Pratt prefix operator. The handler consumes\n * the identifier token and an optional `on <target>` clause, emitting a\n * `caretVar` AST node.\n *\n * Eval side: `caretVar` node evaluator calls `reactive.readCaret(anchor,\n * name)` which walks the DOM tree for the owner and records deps as it goes.\n */\n\nimport type { ASTNode, ExecutionContext } from './types';\nimport { reactive } from './signals';\n\nexport interface CaretVarNode extends ASTNode {\n type: 'caretVar';\n name: string;\n onTarget: ASTNode | null;\n}\n\ninterface CaretVarRuntime {\n execute(node: ASTNode, ctx: ExecutionContext): Promise<unknown>;\n}\n\n/**\n * Coerce a runtime-evaluated `on <target>` result to a single Element.\n * Selector expressions resolve to an array of matched elements; bare\n * `me`/`it`/`you`/identifier references resolve to single Elements. Take the\n * first element of an array, or the value itself if it's already an Element.\n */\nfunction coerceToElement(value: unknown): Element | null {\n if (value instanceof Element) return value;\n if (Array.isArray(value) && value[0] instanceof Element) return value[0];\n return null;\n}\n\n/**\n * Pratt prefix handler for `^`. Consumes the following identifier token and\n * an optional `on <expr>` clause.\n */\nexport function parseCaretPrefix(token: unknown, ctx: unknown): ASTNode {\n // `ctx` is hyperfixi's PrattContext: { peek, advance, parseExpr, isStopToken, atEnd }.\n const pctx = ctx as {\n peek(): { value?: string; kind?: string } | undefined;\n advance(): { value: string; kind?: string };\n parseExpr(minBp: number): ASTNode;\n };\n const ident = pctx.advance();\n if (!ident || !ident.value) {\n throw new Error(\"Expected identifier after '^'\");\n }\n let onTarget: ASTNode | null = null;\n const next = pctx.peek();\n if (next && next.value === 'on') {\n pctx.advance(); // consume 'on'\n // Binding power 86 — higher than standard comparisons so `^x on me` doesn't\n // over-consume into surrounding expressions.\n onTarget = pctx.parseExpr(86);\n }\n const startTok = token as { start?: number; end?: number; line?: number; column?: number };\n return {\n type: 'caretVar',\n name: ident.value,\n onTarget,\n start: startTok?.start ?? 0,\n end: startTok?.end ?? 0,\n line: startTok?.line,\n column: startTok?.column,\n } as CaretVarNode;\n}\n\n/**\n * Build a node evaluator for `caretVar` bound to a specific runtime. The\n * evaluator walks up from the resolved anchor element, tracking every element\n * visited so writes at any ancestor notify dependent effects.\n *\n * Capturing `runtime` via a factory closure (matching live/when/bind) keeps\n * the evaluator independent of any module-scope state.\n */\nexport function makeEvaluateCaretVar(\n runtime: CaretVarRuntime\n): (node: ASTNode, ctx: unknown) => Promise<unknown> {\n return async function evaluateCaretVar(node, ctx) {\n const n = node as CaretVarNode;\n const context = ctx as ExecutionContext;\n let anchor: Element | null = (context.me as Element | null) ?? null;\n\n if (n.onTarget) {\n const resolved = await runtime.execute(n.onTarget, context);\n const el = coerceToElement(resolved);\n if (el) anchor = el;\n }\n\n if (!anchor) return undefined;\n return reactive.readCaret(anchor, n.name);\n };\n}\n\n/**\n * Build a node writer for `caretVar` bound to a specific runtime. Used by the\n * core `set` command via `parserExtensions.registerNodeWriter`.\n */\nexport function makeWriteCaretVar(\n runtime: CaretVarRuntime\n): (node: ASTNode, value: unknown, ctx: unknown) => Promise<void> {\n return async function writeCaretVar(node, value, ctx) {\n const n = node as CaretVarNode;\n const context = ctx as ExecutionContext;\n const anchor: Element | null = (context.me as Element | null) ?? null;\n if (!anchor) return;\n\n let target: Element | undefined;\n if (n.onTarget) {\n const resolved = await runtime.execute(n.onTarget, context);\n const el = coerceToElement(resolved);\n if (el) target = el;\n }\n reactive.writeCaret(anchor, n.name, value, target);\n };\n}\n","/**\n * `live ... end` — reactive block. Body re-runs whenever any dependency read\n * during its execution changes.\n *\n * Upstream syntax:\n * live [commandList] end\n *\n * Each command in the body is re-executed as a single effect. The entire\n * body shares one effect instance; its dependency set is the union of every\n * read performed during body execution.\n */\n\nimport type { ASTNode, ExecutionContext, FeatureParserCtx } from './types';\nimport { reactive } from './signals';\n\nexport interface LiveFeatureNode extends ASTNode {\n type: 'liveFeature';\n body: ASTNode[];\n}\n\n/**\n * Parse `live ... end`. The `live` keyword has already been consumed by the\n * parser dispatcher; we parse the body and expect a trailing `end`.\n */\nexport function parseLiveFeature(ctx: unknown, token: unknown): ASTNode {\n const pctx = ctx as FeatureParserCtx;\n const body = pctx.parseCommandListUntilEnd();\n // parseCommandListUntilEnd stops when it sees `end` (but doesn't consume it).\n if (!pctx.isAtEnd() && pctx.check('end')) pctx.match('end');\n const tok = token as { start?: number; end?: number; line?: number; column?: number };\n return {\n type: 'liveFeature',\n body,\n start: tok?.start ?? 0,\n end: pctx.getPosition().end,\n line: tok?.line,\n column: tok?.column,\n } as LiveFeatureNode;\n}\n\n/**\n * Create an evaluator bound to a runtime reference. The plugin captures\n * `runtime` at install time and passes it in so effect re-runs can dispatch\n * the body commands without going through module-scope state.\n */\nexport function makeEvaluateLiveFeature(runtime: {\n execute(node: ASTNode, ctx: ExecutionContext): Promise<unknown>;\n}): (node: ASTNode, ctx: unknown) => unknown | Promise<unknown> {\n return async function evaluateLiveFeature(node, ctx) {\n const context = ctx as ExecutionContext;\n const owner = (context.me as Element) ?? document.body;\n const n = node as LiveFeatureNode;\n\n const stop = reactive.createEffect(\n async () => {\n for (const cmd of n.body) {\n await runtime.execute(cmd, context);\n }\n },\n () => {\n /* no-op — side effects happened inside the expression */\n },\n owner\n );\n context.registerCleanup?.(owner, stop, 'live-effect');\n return undefined;\n };\n}\n","/**\n * `when <expr> [or <expr>]* changes [commandList] end` — observer feature.\n *\n * Runs the body when any watched expression's value changes (Object.is\n * semantics). One effect is created per watched expression so writes to a\n * given dep only re-run that watcher.\n */\n\nimport type { ASTNode, ExecutionContext, FeatureParserCtx } from './types';\nimport { reactive } from './signals';\n\nexport interface WhenFeatureNode extends ASTNode {\n type: 'whenFeature';\n watched: ASTNode[];\n body: ASTNode[];\n}\n\nexport function parseWhenFeature(ctx: unknown, token: unknown): ASTNode {\n const pctx = ctx as FeatureParserCtx;\n const watched: ASTNode[] = [pctx.parseExpression()];\n while (pctx.match('or')) {\n watched.push(pctx.parseExpression());\n }\n pctx.consume('changes', \"Expected 'changes' after when expression list\");\n const body = pctx.parseCommandListUntilEnd();\n if (!pctx.isAtEnd() && pctx.check('end')) pctx.match('end');\n const tok = token as { start?: number; end?: number; line?: number; column?: number };\n return {\n type: 'whenFeature',\n watched,\n body,\n start: tok?.start ?? 0,\n end: pctx.getPosition().end,\n line: tok?.line,\n column: tok?.column,\n } as WhenFeatureNode;\n}\n\nexport function makeEvaluateWhenFeature(runtime: {\n execute(node: ASTNode, ctx: ExecutionContext): Promise<unknown>;\n evaluateExpressionWithResult?: (\n node: ASTNode,\n ctx: ExecutionContext\n ) => Promise<{ value: unknown }>;\n}): (node: ASTNode, ctx: unknown) => unknown | Promise<unknown> {\n return async function evaluateWhenFeature(node, ctx) {\n const context = ctx as ExecutionContext;\n const owner = (context.me as Element) ?? document.body;\n const n = node as WhenFeatureNode;\n\n for (const watchedExpr of n.watched) {\n const stop = reactive.createEffect(\n async () => {\n // Evaluate the watched expression as a regular expression through the\n // runtime so that trackGlobal/trackElement fire via the global-write\n // hook and caret-var reads.\n return await runtime.execute(watchedExpr, context);\n },\n async newValue => {\n // Fire the body with `it` / `result` bound to the new value.\n const subCtx: ExecutionContext = { ...context, it: newValue, result: newValue };\n for (const cmd of n.body) {\n await runtime.execute(cmd, subCtx);\n }\n },\n owner\n );\n context.registerCleanup?.(owner, stop, 'when-effect');\n }\n return undefined;\n };\n}\n","/**\n * `bind X [to|and|with] Y` — two-way binding.\n *\n * Creates two effects (registration order is load-bearing — both initialize\n * via `queueMicrotask` and run in registration order):\n * 1. DOM → var: read DOM, write var. DOM \"wins\" on init.\n * 2. var → DOM: read var, write DOM. Fires on programmatic var writes\n * after init. On its own initial run, var === DOM (Effect 1 just synced\n * them), so the write is a no-op.\n *\n * Two binding forms:\n *\n * 1. Auto-detected (DOM side is a bare element expression):\n *\n * bind $name to #input -- detects `value`\n * bind $checked to me -- detects `checked` on a checkbox\n *\n * Auto-detected property by element type:\n * - INPUT[type=checkbox|radio] → `checked`\n * - INPUT[type=number|range] → `valueAsNumber`\n * - INPUT|TEXTAREA|SELECT → `value`\n * - contenteditable=\"true\" → `textContent`\n * - Custom elements with own `value` → `value`\n *\n * 2. Explicit property (DOM side is a member or possessive expression):\n *\n * bind $color to #picker's value -- possessive (preferred — reads in any language)\n * bind $color to #picker.value -- dot (JS-style alternative)\n * bind $text to #div's textContent -- non-form properties: var→DOM only\n *\n * For form-like elements, both directions work. For non-form elements\n * (e.g., binding a div's `textContent`), only var→DOM fires — there are\n * no input/change events to drive DOM→var, so user mutations of the\n * property via devtools won't propagate back.\n */\n\nimport type { ASTNode, ExecutionContext, FeatureParserCtx } from './types';\nimport { reactive } from './signals';\n\nexport interface BindFeatureNode extends ASTNode {\n type: 'bindFeature';\n left: ASTNode;\n right: ASTNode;\n}\n\nexport function parseBindFeature(ctx: unknown, token: unknown): ASTNode {\n const pctx = ctx as FeatureParserCtx;\n const left = pctx.parseExpression();\n // Accept any of `to` / `and` / `with` as the separator.\n if (!(pctx.match('to') || pctx.match('and') || pctx.match('with'))) {\n throw new Error(\"bind requires 'to', 'and', or 'with' between the two sides\");\n }\n const right = pctx.parseExpression();\n // Optional `end` terminator (matches upstream which allows both forms).\n if (pctx.check('end')) pctx.match('end');\n const tok = token as { start?: number; end?: number; line?: number; column?: number };\n return {\n type: 'bindFeature',\n left,\n right,\n start: tok?.start ?? 0,\n end: pctx.getPosition().end,\n line: tok?.line,\n column: tok?.column,\n } as BindFeatureNode;\n}\n\n/**\n * Auto-detect the DOM property to bind by element type. Returns null if the\n * target isn't a recognized form/editable element.\n */\nfunction detectProperty(el: Element): string | null {\n const tag = el.tagName;\n if (tag === 'INPUT') {\n const type = (el as HTMLInputElement).type;\n if (type === 'checkbox' || type === 'radio') return 'checked';\n if (type === 'number' || type === 'range') return 'valueAsNumber';\n return 'value';\n }\n if (tag === 'TEXTAREA' || tag === 'SELECT') return 'value';\n const ce = (el as HTMLElement).contentEditable;\n if (ce === 'true') return 'textContent';\n // Custom elements with own `value` prop.\n if ('value' in el) return 'value';\n return null;\n}\n\n/**\n * If `node` is a `memberExpression` or `possessiveExpression` with a static\n * identifier property (`#el.value` or `#el's value`), return the inner element\n * expression and the property name. Returns null for anything else.\n *\n * Computed member access (`#el[prop]`) is intentionally rejected — we'd have to\n * evaluate the index dynamically and the binding direction would be unclear.\n * Chained property access (`#el.dataset.value`) is also not unpacked — we only\n * peel one level. Multi-level support is a future arc.\n */\nfunction unwrapExplicitProperty(node: ASTNode): { element: ASTNode; propertyName: string } | null {\n if (!node || (node.type !== 'memberExpression' && node.type !== 'possessiveExpression')) {\n return null;\n }\n if (node.type === 'memberExpression' && node.computed === true) return null;\n const property = node.property as ASTNode | undefined;\n const object = node.object as ASTNode | undefined;\n if (!property || !object || property.type !== 'identifier') return null;\n const name = (property.name as string) ?? '';\n if (!name) return null;\n return { element: object, propertyName: name };\n}\n\n/**\n * Detect a chained property access on the bind RHS — e.g.\n * `me.style.backgroundColor` or `#el's dataset's value`.\n *\n * After `unwrapExplicitProperty` peels the outermost member, the returned\n * `element` is the object of that member. If that object is itself a\n * member/possessive expression, we have a multi-level chain that v1 of\n * bind doesn't support. Used only for the error path; the parsed AST is\n * left intact.\n */\nfunction isChainedMember(node: ASTNode | undefined): boolean {\n if (!node) return false;\n return node.type === 'memberExpression' || node.type === 'possessiveExpression';\n}\n\n/**\n * Whether the input/change listener installed by `trackDomProperty` would\n * actually fire for this element. Used to decide whether to wire up the\n * DOM→var direction when an explicit property is given.\n */\nfunction isFormLikeElement(el: Element): boolean {\n const tag = el.tagName;\n if (tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT') return true;\n if ((el as HTMLElement).contentEditable === 'true') return true;\n return false;\n}\n\n/**\n * Whether debug logging is enabled. Mirrors signals.ts — keeps a single\n * convention for the package.\n */\nfunction debugEnabled(): boolean {\n try {\n return typeof localStorage !== 'undefined' && localStorage.getItem('hyperfixi:debug') !== null;\n } catch {\n return false;\n }\n}\n\n/**\n * Create the bind evaluator. Because `bind` has unusual parse shape (AST\n * nodes for left/right that may be identifiers, `$name` references, or DOM\n * element lookups), we rely on the runtime to evaluate them.\n */\nexport function makeEvaluateBindFeature(runtime: {\n execute(node: ASTNode, ctx: ExecutionContext): Promise<unknown>;\n}): (node: ASTNode, ctx: unknown) => unknown | Promise<unknown> {\n return async function evaluateBindFeature(node, ctx) {\n const context = ctx as ExecutionContext;\n const owner = (context.me as Element) ?? document.body;\n const n = node as BindFeatureNode;\n\n // Dynamically import the element-scope accessor from core. A static import\n // would pull core's full index into this module's load graph — and core's\n // index eagerly evaluates `morphlex` (which touches `Element.prototype` at\n // module scope), throwing `Element is not defined` when @hyperfixi/reactivity\n // is imported in bare Node/SSR. This evaluator only runs in a browser (it\n // uses `document.body` above), so deferring the import keeps the package\n // importable in Node while `:name` binds still address the core store.\n const { getElementScopeMap } = await import('@hyperfixi/core');\n\n // Resolve sides. One is a var reference (read/write a symbol), the other\n // a DOM target (read/write a property). Determine direction from node shape.\n const leftIsVar = isVarRef(n.left);\n const rightIsVar = isVarRef(n.right);\n\n // We need a DOM-side descriptor (element + property) and a var-side name.\n let varSide: { name: string; isGlobal: boolean } | null = null;\n let domSide: { exprNode: ASTNode } | null = null;\n\n if (leftIsVar && !rightIsVar) {\n varSide = varRefInfo(n.left);\n domSide = { exprNode: n.right };\n } else if (rightIsVar && !leftIsVar) {\n varSide = varRefInfo(n.right);\n domSide = { exprNode: n.left };\n } else if (leftIsVar && rightIsVar) {\n // var-to-var binding not supported in v1 (no DOM side).\n throw new Error('bind: cannot bind two symbols together (need a DOM side)');\n } else {\n throw new Error('bind: could not identify a symbol side');\n }\n\n // Unwrap explicit property syntax: `#el.value` or `#el's value` becomes\n // (element-expression, propertyName). Otherwise, evaluate the whole side\n // and rely on auto-detection.\n const explicit = unwrapExplicitProperty(domSide.exprNode);\n // Multi-level property access (`#el.style.background`, `me's dataset's id`)\n // is not supported in v1. Surface that diagnosis up front; otherwise the\n // user gets the generic \"did not resolve to an element\" message and no\n // hint that the chain is the problem.\n if (explicit && isChainedMember(explicit.element)) {\n throw new Error(\n 'bind: multi-level property access (e.g., `#el.a.b`) is not supported in v1 — restructure to a single property write or pass the element directly for auto-detection (e.g., `bind $x to #el`).'\n );\n }\n const elementExpr = explicit ? explicit.element : domSide.exprNode;\n const domValue = await runtime.execute(elementExpr, context);\n if (!(domValue instanceof Element)) {\n const valueType =\n domValue === null ? 'null' : domValue === undefined ? 'undefined' : typeof domValue;\n const snippet =\n domValue !== null && domValue !== undefined ? ` \"${String(domValue).slice(0, 40)}\"` : '';\n const suggestion = explicit\n ? ''\n : \" If you meant to write to a property, use the explicit form: `<selector>'s <property>`.\";\n throw new Error(\n `bind: right-hand side did not resolve to an element (got ${valueType}${snippet}).${suggestion}`\n );\n }\n const el = domValue;\n // Explicit property bypasses auto-detect; the user takes responsibility for\n // picking something readable from the element. For DOM→var to work without\n // explicit property, fall back to auto-detect.\n const prop = explicit ? explicit.propertyName : detectProperty(el);\n if (!prop) {\n throw new Error(\n `bind: could not auto-detect property for <${el.tagName.toLowerCase()}> — use explicit \\`<expr>'s <property>\\` form`\n );\n }\n // For explicit-property mode on non-form elements, DOM→var can't sync via\n // input/change events. Skip the listener install; only var→DOM runs.\n const installDomToVar = !explicit || isFormLikeElement(el);\n if (explicit && !installDomToVar && debugEnabled() && typeof console !== 'undefined') {\n console.warn(\n `[@hyperfixi/reactivity] bind: DOM→var skipped for <${el.tagName.toLowerCase()}>.${prop} — no input/change event source.`\n );\n }\n\n // Initial sync: DOM → var.\n const readDom = (): unknown => {\n if (prop === 'valueAsNumber') return (el as HTMLInputElement).valueAsNumber;\n if (prop === 'checked') return (el as HTMLInputElement).checked;\n if (prop === 'textContent') return el.textContent ?? '';\n return (el as any)[prop];\n };\n const writeDom = (value: unknown): void => {\n if (prop === 'valueAsNumber') {\n const n = Number(value);\n (el as HTMLInputElement).valueAsNumber = Number.isNaN(n) ? (null as never) : n;\n } else if (prop === 'checked') {\n (el as HTMLInputElement).checked = Boolean(value);\n } else if (prop === 'textContent') {\n el.textContent = value == null ? '' : String(value);\n } else {\n (el as any)[prop] = value;\n }\n };\n const readVar = (): unknown => {\n if (varSide!.isGlobal) return context.globals?.get(varSide!.name);\n // `:name` is element-scoped — read from the owner element's store, the same\n // place the core `set :name` writes. (Direct Map access, not getElementVar,\n // so we don't double-fire the read hook; bind tracks via trackElement.)\n return getElementScopeMap(owner).get(varSide!.name);\n };\n const writeVar = (value: unknown): void => {\n if (varSide!.isGlobal) {\n context.globals?.set(varSide!.name, value);\n // Bypass the global-write hook (we're touching the Map directly), so\n // notify the reactive graph manually for the var→DOM effect.\n reactive.notifyGlobal(varSide!.name);\n } else {\n // Element-scoped `:name`: write to the owner element's store (same place\n // core's `set :name` writes), then notify manually — the direct Map write\n // skips the localWriteHook.\n getElementScopeMap(owner).set(varSide!.name, value);\n reactive.notifyElement(owner, varSide!.name);\n }\n };\n\n // Effect 1: DOM → var (fires on user input). Skipped for explicit-property\n // bindings on non-form elements — no input/change events would drive it.\n let stopDomToVar: (() => void) | null = null;\n if (installDomToVar) {\n stopDomToVar = reactive.createEffect(\n () => {\n reactive.trackDomProperty(el, prop);\n return readDom();\n },\n newValue => {\n writeVar(newValue);\n },\n owner\n );\n }\n\n // Effect 2: var → DOM (fires on programmatic var writes — for globals\n // via the core's globalWriteHook, for locals via the localWriteHook the\n // reactivity plugin registers).\n const stopVarToDom = reactive.createEffect(\n () => {\n if (varSide!.isGlobal) reactive.trackGlobal(varSide!.name);\n else reactive.trackElement(owner, varSide!.name);\n return readVar();\n },\n newValue => {\n if (newValue === undefined) return;\n writeDom(newValue);\n },\n owner\n );\n\n if (stopDomToVar) context.registerCleanup?.(owner, stopDomToVar, 'bind-dom-to-var');\n context.registerCleanup?.(owner, stopVarToDom, 'bind-var-to-dom');\n return undefined;\n };\n}\n\n/**\n * Is this AST node a var reference we can bind to?\n *\n * The hyperfixi parser emits `identifier` nodes for both globals and locals,\n * but with different shapes:\n * - `$foo` → `{ type: 'identifier', name: '$foo' }` (no scope field)\n * - `:foo` → `{ type: 'identifier', name: 'foo', scope: 'element' }` (element-scoped)\n * - `::foo` → `{ type: 'identifier', name: 'foo', scope: 'global' }`\n *\n * We accept all of them. The legacy `$`-prefix sniff stays as a fallback because\n * `parseExpression` doesn't always set `scope`.\n */\nfunction isVarRef(node: ASTNode): boolean {\n if (!node) return false;\n if (node.type !== 'identifier') return false;\n const scope = node.scope as string | undefined;\n if (scope === 'local' || scope === 'element' || scope === 'global') return true;\n const name = (node.name as string) ?? '';\n return name.startsWith('$') || name.startsWith(':');\n}\n\nfunction varRefInfo(node: ASTNode): { name: string; isGlobal: boolean } {\n const rawName = (node.name as string) ?? '';\n const scope = node.scope as string | undefined;\n // Prefer the parser-emitted scope marker. Fall back to prefix sniffing for\n // shapes where scope isn't set (e.g. `$foo` arriving as bare identifier\n // with name '$foo').\n let isGlobal: boolean;\n let name: string;\n if (scope === 'global') {\n isGlobal = true;\n name = rawName;\n } else if (scope === 'local' || scope === 'element') {\n isGlobal = false;\n name = rawName;\n } else if (rawName.startsWith('$')) {\n isGlobal = true;\n name = rawName.slice(1);\n } else if (rawName.startsWith(':')) {\n isGlobal = false;\n name = rawName.slice(1);\n } else {\n isGlobal = false;\n name = rawName;\n }\n if (!name) {\n throw new Error('bind: variable reference has empty name');\n }\n return { name, isGlobal };\n}\n","/**\n * The version reported by this package's plugin descriptor.\n *\n * GENERATED — do not edit by hand. `scripts/set-version.cjs` rewrites this file\n * alongside the `package.json` files it already touches.\n *\n * The literal used to sit inline in the plugin object, where nothing kept it\n * honest: `HyperfixiPlugin` does not declare `version` (the plugins widen the\n * type themselves), so no consumer and no test ever read it. Both froze at the\n * release they were written in — reactivity at 2.3.1, realtime at 2.6.0 — and\n * shipped that way inside the browser bundle for many releases. The equality\n * check in `version.test.ts` is what keeps this one honest.\n */\nexport const VERSION = '3.3.0';\n"],"mappings":"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","names":["debugEnabled","n"]}
package/dist/index.js CHANGED
@@ -707,7 +707,7 @@ function varRefInfo(node) {
707
707
  }
708
708
 
709
709
  // src/version.ts
710
- var VERSION = "3.1.1";
710
+ var VERSION = "3.3.0";
711
711
 
712
712
  // src/index.ts
713
713
  var reactivityPlugin = {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/signals.ts","../src/caret-var.ts","../src/live.ts","../src/when.ts","../src/bind.ts","../src/version.ts","../src/index.ts"],"sourcesContent":["/**\n * Signal/effect reactive core.\n *\n * Models three dependency kinds for v1:\n * - `global` — `$name` variables in `context.globals`\n * - `element` — `^name` DOM-scoped variables (per-element storage)\n * - `dom` — DOM properties read via `input`/`change` listeners\n *\n * Inspired by upstream _hyperscript 0.9.91's `src/core/runtime/reactivity.js`\n * but reimplemented in TypeScript against hyperfixi's types. Batched via\n * `queueMicrotask` so synchronous writes coalesce into a single flush. Cycle\n * detection halts at >100 consecutive triggers (matching upstream).\n *\n * External packages never see `Effect` or `Reactive` directly — they hold\n * the disposer returned by `createEffect()` and call it when the owning\n * element is cleaned up.\n */\n\nexport type DepKind = 'global' | 'element' | 'dom';\n\n/**\n * Whether reactive debug logging is enabled. Mirrors the core convention:\n * `localStorage.setItem('hyperfixi:debug', '*')` enables, anything else (or no\n * `localStorage`) disables. Cheap call — no caching, since users toggle this\n * interactively in the console.\n */\nfunction debugEnabled(): boolean {\n try {\n return typeof localStorage !== 'undefined' && localStorage.getItem('hyperfixi:debug') !== null;\n } catch {\n return false;\n }\n}\n\nfunction debugWarn(message: string, err: unknown): void {\n if (!debugEnabled()) return;\n if (typeof console === 'undefined') return;\n console.warn(`[@hyperfixi/reactivity] ${message}`, err);\n}\n\nexport interface Dep {\n readonly key: string;\n readonly kind: DepKind;\n readonly name: string;\n readonly element: Element | null;\n}\n\nconst globalDepKey = (name: string): string => `global:${name}`;\nconst elementDepKey = (el: Element, name: string): string => `element:${reactiveIdFor(el)}:${name}`;\nconst domDepKey = (el: Element, prop: string): string => `dom:${reactiveIdFor(el)}:${prop}`;\n\n// WeakMap-based id assignment so we don't monkey-patch DOM nodes. Ids are\n// per-process and only used to construct stable string keys for an effect's\n// per-effect dependency dedup map.\nconst _reactiveIds = new WeakMap<Element, number>();\nlet _idCounter = 0;\nfunction reactiveIdFor(el: Element): number {\n const existing = _reactiveIds.get(el);\n if (existing !== undefined) return existing;\n const id = ++_idCounter;\n _reactiveIds.set(el, id);\n return id;\n}\n\nexport class Effect {\n readonly dependencies = new Map<string, Dep>();\n private lastValue: unknown = undefined;\n private _stopped = false;\n private _consecutiveTriggers = 0;\n\n constructor(\n private readonly r: Reactive,\n readonly expression: () => unknown | Promise<unknown>,\n readonly handler: (value: unknown) => void | Promise<void>,\n readonly element: Element | null\n ) {}\n\n get stopped(): boolean {\n return this._stopped;\n }\n\n /**\n * Run the effect for the first time: collect dependencies, subscribe, call\n * handler with the initial value. Errors in expression evaluation are caught\n * and silently swallowed (matches upstream's tolerance).\n */\n async initialize(): Promise<void> {\n if (this._stopped) return;\n try {\n const value = await this.r._runWithEffect(this, this.expression);\n this.lastValue = value;\n if (value !== undefined && value !== null) {\n await this.handler(value);\n }\n } catch (err) {\n // Swallow — expression evaluation failed during initial read. Surface\n // via debug log so users diagnosing \"my effect never fires\" can see why.\n debugWarn('effect.initialize failed', err);\n }\n }\n\n /**\n * Re-run the effect after a notify. Cycle-detects (halts at 101 consecutive\n * triggers). If the new value is Object.is-equal to the last, the handler\n * is skipped. If the owning element has disconnected, the effect stops\n * itself and returns.\n */\n async run(): Promise<void> {\n if (this._stopped) return;\n if (this.element && !this.element.isConnected) {\n this.stop();\n return;\n }\n this._consecutiveTriggers++;\n if (this._consecutiveTriggers > 100) {\n if (typeof console !== 'undefined') {\n console.error(\n '[@hyperfixi/reactivity] Effect halted: > 100 consecutive triggers (cycle detected).'\n );\n }\n this.stop();\n return;\n }\n try {\n const value = await this.r._runWithEffect(this, this.expression);\n if (Object.is(value, this.lastValue)) return;\n this.lastValue = value;\n await this.handler(value);\n } catch (err) {\n // Swallow — expression/handler errors don't break the microtask flush.\n debugWarn('effect.run failed', err);\n }\n }\n\n resetTriggerCount(): void {\n this._consecutiveTriggers = 0;\n }\n\n /**\n * Stop the effect and remove it from all dependency subscriptions. Safe to\n * call multiple times.\n */\n stop(): void {\n if (this._stopped) return;\n this._stopped = true;\n this.r._unsubscribeEffect(this);\n this.dependencies.clear();\n }\n}\n\ninterface ElementState {\n symbolSubs: Map<string, Set<Effect>>;\n caretVars: Map<string, unknown>;\n domHandlers: Map<string, DomHandler>;\n effects: Set<Effect>;\n}\n\ninterface DomHandler {\n subs: Set<Effect>;\n detach: () => void;\n}\n\nexport class Reactive {\n private currentEffect: Effect | null = null;\n /**\n * Tail of the effect-evaluation chain. `_runWithEffect` links onto this so\n * only one effect owns the `currentEffect` slot at a time — see the comment\n * there for why concurrent async effects otherwise lose their dependencies.\n */\n private trackingQueue: Promise<void> = Promise.resolve();\n private pending = new Set<Effect>();\n private scheduled = false;\n\n private globalSubs = new Map<string, Set<Effect>>();\n private elementState = new WeakMap<Element, ElementState>();\n\n private getElementState(el: Element): ElementState {\n let s = this.elementState.get(el);\n if (!s) {\n s = {\n symbolSubs: new Map(),\n caretVars: new Map(),\n domHandlers: new Map(),\n effects: new Set(),\n };\n this.elementState.set(el, s);\n }\n return s;\n }\n\n /**\n * Internal helper invoked by Effect — installs `this` as the current effect,\n * runs the expression, then restores the previous current-effect pointer.\n * Track* methods (called from read-paths) consult `currentEffect` to know\n * which effect to subscribe.\n */\n async _runWithEffect(e: Effect, fn: () => unknown | Promise<unknown>): Promise<unknown> {\n // Dependency capture keys off ONE `currentEffect` slot, but effect\n // expressions are async (a `live` body awaits each command). Two effects\n // evaluating concurrently would interleave across their await points and\n // attribute each other's reads — or, once the inner one restored `prev`,\n // leave the outer one tracking nothing at all. Three `live` blocks on a\n // page (they all start in the same tick) reliably lost their\n // subscriptions that way and froze.\n //\n // Serialize instead: each run waits for the previous one to finish, so\n // the slot belongs to exactly one effect for the whole of its evaluation.\n // Effect bodies are short DOM updates, and this only orders effect\n // evaluation — it does not block the page.\n const runExclusive = async (): Promise<unknown> => {\n const prev = this.currentEffect;\n this.currentEffect = e;\n // Unsubscribe from previous deps before re-running. The expression will\n // re-record whatever it actually reads this time, avoiding stale subs.\n this._unsubscribeEffect(e);\n e.dependencies.clear();\n try {\n return await fn();\n } finally {\n this.currentEffect = prev;\n }\n };\n\n // Chain onto the tail of the queue, and keep the queue alive whether this\n // run resolves or throws (callers already swallow effect errors).\n const result = this.trackingQueue.then(runExclusive, runExclusive);\n this.trackingQueue = result.then(\n () => undefined,\n () => undefined\n );\n return result;\n }\n\n // ---------------------------------------------------------------------------\n // Track (read-path) — invoked from interceptors / evaluators.\n // ---------------------------------------------------------------------------\n\n trackGlobal(name: string): void {\n const e = this.currentEffect;\n if (!e) return;\n const key = globalDepKey(name);\n if (e.dependencies.has(key)) return;\n e.dependencies.set(key, { key, kind: 'global', name, element: null });\n let subs = this.globalSubs.get(name);\n if (!subs) {\n subs = new Set();\n this.globalSubs.set(name, subs);\n }\n subs.add(e);\n }\n\n trackElement(el: Element, name: string): void {\n const e = this.currentEffect;\n if (!e) return;\n const key = elementDepKey(el, name);\n if (e.dependencies.has(key)) return;\n e.dependencies.set(key, { key, kind: 'element', name, element: el });\n const state = this.getElementState(el);\n let subs = state.symbolSubs.get(name);\n if (!subs) {\n subs = new Set();\n state.symbolSubs.set(name, subs);\n }\n subs.add(e);\n state.effects.add(e);\n }\n\n trackDomProperty(el: Element, prop: string): void {\n const e = this.currentEffect;\n if (!e) return;\n const key = domDepKey(el, prop);\n if (e.dependencies.has(key)) return;\n e.dependencies.set(key, { key, kind: 'dom', name: prop, element: el });\n const state = this.getElementState(el);\n let handler = state.domHandlers.get(prop);\n if (!handler) {\n const subs = new Set<Effect>();\n const listener = (): void => {\n for (const effect of subs) this.schedule(effect);\n };\n // Use both input and change to cover radios/selects which don't fire input.\n el.addEventListener('input', listener);\n el.addEventListener('change', listener);\n handler = {\n subs,\n detach: () => {\n el.removeEventListener('input', listener);\n el.removeEventListener('change', listener);\n },\n };\n state.domHandlers.set(prop, handler);\n }\n handler.subs.add(e);\n state.effects.add(e);\n }\n\n // ---------------------------------------------------------------------------\n // Notify (write-path) — schedules dependent effects for re-run.\n // ---------------------------------------------------------------------------\n\n notifyGlobal(name: string): void {\n const subs = this.globalSubs.get(name);\n if (!subs) return;\n for (const e of subs) this.schedule(e);\n }\n\n notifyElement(el: Element, name: string): void {\n const state = this.elementState.get(el);\n if (!state) return;\n const subs = state.symbolSubs.get(name);\n if (!subs) return;\n for (const e of subs) this.schedule(e);\n }\n\n // ---------------------------------------------------------------------------\n // Effect lifecycle.\n // ---------------------------------------------------------------------------\n\n /**\n * Create + initialize an effect. Returns a disposer that stops the effect.\n * Callers are expected to register the disposer with the core runtime's\n * cleanup registry so it fires on element removal.\n */\n createEffect(\n expression: () => unknown | Promise<unknown>,\n handler: (value: unknown) => void | Promise<void>,\n owner: Element | null\n ): () => void {\n const e = new Effect(this, expression, handler, owner);\n if (owner) this.getElementState(owner).effects.add(e);\n // Fire initialize asynchronously so the caller can set up cleanup registration\n // before the first run touches the DOM.\n queueMicrotask(() => {\n void e.initialize();\n });\n return () => e.stop();\n }\n\n /**\n * Stop all effects owned by an element. Called by the reactivity plugin's\n * cleanup hook registered via `runtime.getCleanupRegistry()`.\n */\n stopElementEffects(el: Element): void {\n const state = this.elementState.get(el);\n if (!state) return;\n for (const e of Array.from(state.effects)) e.stop();\n state.effects.clear();\n }\n\n /**\n * Internal: remove an effect's entries from all subscriber sets. Called by\n * `Effect.stop()`.\n */\n _unsubscribeEffect(e: Effect): void {\n for (const dep of e.dependencies.values()) {\n if (dep.kind === 'global') {\n const subs = this.globalSubs.get(dep.name);\n subs?.delete(e);\n } else if (dep.kind === 'element' && dep.element) {\n const state = this.elementState.get(dep.element);\n const subs = state?.symbolSubs.get(dep.name);\n subs?.delete(e);\n state?.effects.delete(e);\n } else if (dep.kind === 'dom' && dep.element) {\n const state = this.elementState.get(dep.element);\n const handler = state?.domHandlers.get(dep.name);\n handler?.subs.delete(e);\n if (handler && handler.subs.size === 0) {\n handler.detach();\n state?.domHandlers.delete(dep.name);\n }\n state?.effects.delete(e);\n }\n }\n }\n\n // ---------------------------------------------------------------------------\n // Caret-variable storage — `^name` reads/writes.\n // ---------------------------------------------------------------------------\n\n /**\n * Whether `el` is a `dom-scope=\"isolated\"` boundary. Walks of `^var`\n * lookups stop at boundary elements that don't define the var, so nested\n * components don't accidentally read or write each other's state.\n */\n private isIsolationBoundary(el: Element): boolean {\n return typeof el.getAttribute === 'function' && el.getAttribute('dom-scope') === 'isolated';\n }\n\n /**\n * Walk up the DOM tree from `lookupRoot`, returning the first element whose\n * state has `name` defined. Stops at any `dom-scope=\"isolated\"` boundary\n * that doesn't itself define the var. Returns `null` if no owner is found.\n */\n private findCaretOwner(lookupRoot: Element, name: string): Element | null {\n let el: Element | null = lookupRoot;\n while (el) {\n const state = this.elementState.get(el);\n if (state && state.caretVars.has(name)) return el;\n if (this.isIsolationBoundary(el)) return null;\n el = el.parentElement;\n }\n return null;\n }\n\n /**\n * Read a DOM-scoped variable. Walks up from `lookupRoot`, tracking each\n * element visited as an `element` dep (so writes at any ancestor notify\n * dependent effects). Stops at any `dom-scope=\"isolated\"` boundary that\n * doesn't itself define the var.\n */\n readCaret(lookupRoot: Element, name: string): unknown {\n let el: Element | null = lookupRoot;\n while (el) {\n this.trackElement(el, name);\n const state = this.elementState.get(el);\n if (state && state.caretVars.has(name)) {\n return state.caretVars.get(name);\n }\n if (this.isIsolationBoundary(el)) return undefined;\n el = el.parentElement;\n }\n return undefined;\n }\n\n /**\n * Write a DOM-scoped variable. If `target` is provided, writes there\n * directly; otherwise walks up from `lookupRoot` to find the existing owner,\n * falling back to `lookupRoot` itself if no owner exists.\n */\n writeCaret(lookupRoot: Element, name: string, value: unknown, target?: Element): void {\n const owner = target ?? this.findCaretOwner(lookupRoot, name) ?? lookupRoot;\n const state = this.getElementState(owner);\n state.caretVars.set(name, value);\n this.notifyElement(owner, name);\n }\n\n // ---------------------------------------------------------------------------\n // Scheduler — microtask-batched flush.\n // ---------------------------------------------------------------------------\n\n private schedule(e: Effect): void {\n if (e.stopped) return;\n this.pending.add(e);\n if (this.scheduled) return;\n this.scheduled = true;\n queueMicrotask(() => void this.flush());\n }\n\n private async flush(): Promise<void> {\n try {\n while (this.pending.size > 0) {\n const batch = Array.from(this.pending);\n this.pending.clear();\n for (const e of batch) {\n if (e.stopped) continue;\n await e.run();\n // If the effect's run did not synchronously re-schedule itself, its\n // trigger count is reset. Self-rescheduling (a real cycle) keeps the\n // counter climbing toward the 100 cap. Long-lived effects that fire\n // sporadically (e.g. user-input bindings) stay alive forever.\n if (!this.pending.has(e)) e.resetTriggerCount();\n }\n }\n } finally {\n this.scheduled = false;\n }\n }\n}\n\n// Module singleton. One reactive graph per process; mirrors upstream's\n// `runtime.reactivity` pattern.\nexport const reactive = new Reactive();\n","/**\n * `^name` — DOM-scoped inherited variable.\n *\n * Upstream syntax:\n * ^counter → read from nearest ancestor that has `counter` set\n * ^counter on #target → read from (or near) #target\n * set ^counter to 42 → write to owner (or lookupRoot if not yet defined)\n *\n * Parse side: register `^` as a Pratt prefix operator. The handler consumes\n * the identifier token and an optional `on <target>` clause, emitting a\n * `caretVar` AST node.\n *\n * Eval side: `caretVar` node evaluator calls `reactive.readCaret(anchor,\n * name)` which walks the DOM tree for the owner and records deps as it goes.\n */\n\nimport type { ASTNode, ExecutionContext } from './types';\nimport { reactive } from './signals';\n\nexport interface CaretVarNode extends ASTNode {\n type: 'caretVar';\n name: string;\n onTarget: ASTNode | null;\n}\n\ninterface CaretVarRuntime {\n execute(node: ASTNode, ctx: ExecutionContext): Promise<unknown>;\n}\n\n/**\n * Coerce a runtime-evaluated `on <target>` result to a single Element.\n * Selector expressions resolve to an array of matched elements; bare\n * `me`/`it`/`you`/identifier references resolve to single Elements. Take the\n * first element of an array, or the value itself if it's already an Element.\n */\nfunction coerceToElement(value: unknown): Element | null {\n if (value instanceof Element) return value;\n if (Array.isArray(value) && value[0] instanceof Element) return value[0];\n return null;\n}\n\n/**\n * Pratt prefix handler for `^`. Consumes the following identifier token and\n * an optional `on <expr>` clause.\n */\nexport function parseCaretPrefix(token: unknown, ctx: unknown): ASTNode {\n // `ctx` is hyperfixi's PrattContext: { peek, advance, parseExpr, isStopToken, atEnd }.\n const pctx = ctx as {\n peek(): { value?: string; kind?: string } | undefined;\n advance(): { value: string; kind?: string };\n parseExpr(minBp: number): ASTNode;\n };\n const ident = pctx.advance();\n if (!ident || !ident.value) {\n throw new Error(\"Expected identifier after '^'\");\n }\n let onTarget: ASTNode | null = null;\n const next = pctx.peek();\n if (next && next.value === 'on') {\n pctx.advance(); // consume 'on'\n // Binding power 86 — higher than standard comparisons so `^x on me` doesn't\n // over-consume into surrounding expressions.\n onTarget = pctx.parseExpr(86);\n }\n const startTok = token as { start?: number; end?: number; line?: number; column?: number };\n return {\n type: 'caretVar',\n name: ident.value,\n onTarget,\n start: startTok?.start ?? 0,\n end: startTok?.end ?? 0,\n line: startTok?.line,\n column: startTok?.column,\n } as CaretVarNode;\n}\n\n/**\n * Build a node evaluator for `caretVar` bound to a specific runtime. The\n * evaluator walks up from the resolved anchor element, tracking every element\n * visited so writes at any ancestor notify dependent effects.\n *\n * Capturing `runtime` via a factory closure (matching live/when/bind) keeps\n * the evaluator independent of any module-scope state.\n */\nexport function makeEvaluateCaretVar(\n runtime: CaretVarRuntime\n): (node: ASTNode, ctx: unknown) => Promise<unknown> {\n return async function evaluateCaretVar(node, ctx) {\n const n = node as CaretVarNode;\n const context = ctx as ExecutionContext;\n let anchor: Element | null = (context.me as Element | null) ?? null;\n\n if (n.onTarget) {\n const resolved = await runtime.execute(n.onTarget, context);\n const el = coerceToElement(resolved);\n if (el) anchor = el;\n }\n\n if (!anchor) return undefined;\n return reactive.readCaret(anchor, n.name);\n };\n}\n\n/**\n * Build a node writer for `caretVar` bound to a specific runtime. Used by the\n * core `set` command via `parserExtensions.registerNodeWriter`.\n */\nexport function makeWriteCaretVar(\n runtime: CaretVarRuntime\n): (node: ASTNode, value: unknown, ctx: unknown) => Promise<void> {\n return async function writeCaretVar(node, value, ctx) {\n const n = node as CaretVarNode;\n const context = ctx as ExecutionContext;\n const anchor: Element | null = (context.me as Element | null) ?? null;\n if (!anchor) return;\n\n let target: Element | undefined;\n if (n.onTarget) {\n const resolved = await runtime.execute(n.onTarget, context);\n const el = coerceToElement(resolved);\n if (el) target = el;\n }\n reactive.writeCaret(anchor, n.name, value, target);\n };\n}\n","/**\n * `live ... end` — reactive block. Body re-runs whenever any dependency read\n * during its execution changes.\n *\n * Upstream syntax:\n * live [commandList] end\n *\n * Each command in the body is re-executed as a single effect. The entire\n * body shares one effect instance; its dependency set is the union of every\n * read performed during body execution.\n */\n\nimport type { ASTNode, ExecutionContext, FeatureParserCtx } from './types';\nimport { reactive } from './signals';\n\nexport interface LiveFeatureNode extends ASTNode {\n type: 'liveFeature';\n body: ASTNode[];\n}\n\n/**\n * Parse `live ... end`. The `live` keyword has already been consumed by the\n * parser dispatcher; we parse the body and expect a trailing `end`.\n */\nexport function parseLiveFeature(ctx: unknown, token: unknown): ASTNode {\n const pctx = ctx as FeatureParserCtx;\n const body = pctx.parseCommandListUntilEnd();\n // parseCommandListUntilEnd stops when it sees `end` (but doesn't consume it).\n if (!pctx.isAtEnd() && pctx.check('end')) pctx.match('end');\n const tok = token as { start?: number; end?: number; line?: number; column?: number };\n return {\n type: 'liveFeature',\n body,\n start: tok?.start ?? 0,\n end: pctx.getPosition().end,\n line: tok?.line,\n column: tok?.column,\n } as LiveFeatureNode;\n}\n\n/**\n * Create an evaluator bound to a runtime reference. The plugin captures\n * `runtime` at install time and passes it in so effect re-runs can dispatch\n * the body commands without going through module-scope state.\n */\nexport function makeEvaluateLiveFeature(runtime: {\n execute(node: ASTNode, ctx: ExecutionContext): Promise<unknown>;\n}): (node: ASTNode, ctx: unknown) => unknown | Promise<unknown> {\n return async function evaluateLiveFeature(node, ctx) {\n const context = ctx as ExecutionContext;\n const owner = (context.me as Element) ?? document.body;\n const n = node as LiveFeatureNode;\n\n const stop = reactive.createEffect(\n async () => {\n for (const cmd of n.body) {\n await runtime.execute(cmd, context);\n }\n },\n () => {\n /* no-op — side effects happened inside the expression */\n },\n owner\n );\n context.registerCleanup?.(owner, stop, 'live-effect');\n return undefined;\n };\n}\n","/**\n * `when <expr> [or <expr>]* changes [commandList] end` — observer feature.\n *\n * Runs the body when any watched expression's value changes (Object.is\n * semantics). One effect is created per watched expression so writes to a\n * given dep only re-run that watcher.\n */\n\nimport type { ASTNode, ExecutionContext, FeatureParserCtx } from './types';\nimport { reactive } from './signals';\n\nexport interface WhenFeatureNode extends ASTNode {\n type: 'whenFeature';\n watched: ASTNode[];\n body: ASTNode[];\n}\n\nexport function parseWhenFeature(ctx: unknown, token: unknown): ASTNode {\n const pctx = ctx as FeatureParserCtx;\n const watched: ASTNode[] = [pctx.parseExpression()];\n while (pctx.match('or')) {\n watched.push(pctx.parseExpression());\n }\n pctx.consume('changes', \"Expected 'changes' after when expression list\");\n const body = pctx.parseCommandListUntilEnd();\n if (!pctx.isAtEnd() && pctx.check('end')) pctx.match('end');\n const tok = token as { start?: number; end?: number; line?: number; column?: number };\n return {\n type: 'whenFeature',\n watched,\n body,\n start: tok?.start ?? 0,\n end: pctx.getPosition().end,\n line: tok?.line,\n column: tok?.column,\n } as WhenFeatureNode;\n}\n\nexport function makeEvaluateWhenFeature(runtime: {\n execute(node: ASTNode, ctx: ExecutionContext): Promise<unknown>;\n evaluateExpressionWithResult?: (\n node: ASTNode,\n ctx: ExecutionContext\n ) => Promise<{ value: unknown }>;\n}): (node: ASTNode, ctx: unknown) => unknown | Promise<unknown> {\n return async function evaluateWhenFeature(node, ctx) {\n const context = ctx as ExecutionContext;\n const owner = (context.me as Element) ?? document.body;\n const n = node as WhenFeatureNode;\n\n for (const watchedExpr of n.watched) {\n const stop = reactive.createEffect(\n async () => {\n // Evaluate the watched expression as a regular expression through the\n // runtime so that trackGlobal/trackElement fire via the global-write\n // hook and caret-var reads.\n return await runtime.execute(watchedExpr, context);\n },\n async newValue => {\n // Fire the body with `it` / `result` bound to the new value.\n const subCtx: ExecutionContext = { ...context, it: newValue, result: newValue };\n for (const cmd of n.body) {\n await runtime.execute(cmd, subCtx);\n }\n },\n owner\n );\n context.registerCleanup?.(owner, stop, 'when-effect');\n }\n return undefined;\n };\n}\n","/**\n * `bind X [to|and|with] Y` — two-way binding.\n *\n * Creates two effects (registration order is load-bearing — both initialize\n * via `queueMicrotask` and run in registration order):\n * 1. DOM → var: read DOM, write var. DOM \"wins\" on init.\n * 2. var → DOM: read var, write DOM. Fires on programmatic var writes\n * after init. On its own initial run, var === DOM (Effect 1 just synced\n * them), so the write is a no-op.\n *\n * Two binding forms:\n *\n * 1. Auto-detected (DOM side is a bare element expression):\n *\n * bind $name to #input -- detects `value`\n * bind $checked to me -- detects `checked` on a checkbox\n *\n * Auto-detected property by element type:\n * - INPUT[type=checkbox|radio] → `checked`\n * - INPUT[type=number|range] → `valueAsNumber`\n * - INPUT|TEXTAREA|SELECT → `value`\n * - contenteditable=\"true\" → `textContent`\n * - Custom elements with own `value` → `value`\n *\n * 2. Explicit property (DOM side is a member or possessive expression):\n *\n * bind $color to #picker's value -- possessive (preferred — reads in any language)\n * bind $color to #picker.value -- dot (JS-style alternative)\n * bind $text to #div's textContent -- non-form properties: var→DOM only\n *\n * For form-like elements, both directions work. For non-form elements\n * (e.g., binding a div's `textContent`), only var→DOM fires — there are\n * no input/change events to drive DOM→var, so user mutations of the\n * property via devtools won't propagate back.\n */\n\nimport type { ASTNode, ExecutionContext, FeatureParserCtx } from './types';\nimport { reactive } from './signals';\n\nexport interface BindFeatureNode extends ASTNode {\n type: 'bindFeature';\n left: ASTNode;\n right: ASTNode;\n}\n\nexport function parseBindFeature(ctx: unknown, token: unknown): ASTNode {\n const pctx = ctx as FeatureParserCtx;\n const left = pctx.parseExpression();\n // Accept any of `to` / `and` / `with` as the separator.\n if (!(pctx.match('to') || pctx.match('and') || pctx.match('with'))) {\n throw new Error(\"bind requires 'to', 'and', or 'with' between the two sides\");\n }\n const right = pctx.parseExpression();\n // Optional `end` terminator (matches upstream which allows both forms).\n if (pctx.check('end')) pctx.match('end');\n const tok = token as { start?: number; end?: number; line?: number; column?: number };\n return {\n type: 'bindFeature',\n left,\n right,\n start: tok?.start ?? 0,\n end: pctx.getPosition().end,\n line: tok?.line,\n column: tok?.column,\n } as BindFeatureNode;\n}\n\n/**\n * Auto-detect the DOM property to bind by element type. Returns null if the\n * target isn't a recognized form/editable element.\n */\nfunction detectProperty(el: Element): string | null {\n const tag = el.tagName;\n if (tag === 'INPUT') {\n const type = (el as HTMLInputElement).type;\n if (type === 'checkbox' || type === 'radio') return 'checked';\n if (type === 'number' || type === 'range') return 'valueAsNumber';\n return 'value';\n }\n if (tag === 'TEXTAREA' || tag === 'SELECT') return 'value';\n const ce = (el as HTMLElement).contentEditable;\n if (ce === 'true') return 'textContent';\n // Custom elements with own `value` prop.\n if ('value' in el) return 'value';\n return null;\n}\n\n/**\n * If `node` is a `memberExpression` or `possessiveExpression` with a static\n * identifier property (`#el.value` or `#el's value`), return the inner element\n * expression and the property name. Returns null for anything else.\n *\n * Computed member access (`#el[prop]`) is intentionally rejected — we'd have to\n * evaluate the index dynamically and the binding direction would be unclear.\n * Chained property access (`#el.dataset.value`) is also not unpacked — we only\n * peel one level. Multi-level support is a future arc.\n */\nfunction unwrapExplicitProperty(node: ASTNode): { element: ASTNode; propertyName: string } | null {\n if (!node || (node.type !== 'memberExpression' && node.type !== 'possessiveExpression')) {\n return null;\n }\n if (node.type === 'memberExpression' && node.computed === true) return null;\n const property = node.property as ASTNode | undefined;\n const object = node.object as ASTNode | undefined;\n if (!property || !object || property.type !== 'identifier') return null;\n const name = (property.name as string) ?? '';\n if (!name) return null;\n return { element: object, propertyName: name };\n}\n\n/**\n * Detect a chained property access on the bind RHS — e.g.\n * `me.style.backgroundColor` or `#el's dataset's value`.\n *\n * After `unwrapExplicitProperty` peels the outermost member, the returned\n * `element` is the object of that member. If that object is itself a\n * member/possessive expression, we have a multi-level chain that v1 of\n * bind doesn't support. Used only for the error path; the parsed AST is\n * left intact.\n */\nfunction isChainedMember(node: ASTNode | undefined): boolean {\n if (!node) return false;\n return node.type === 'memberExpression' || node.type === 'possessiveExpression';\n}\n\n/**\n * Whether the input/change listener installed by `trackDomProperty` would\n * actually fire for this element. Used to decide whether to wire up the\n * DOM→var direction when an explicit property is given.\n */\nfunction isFormLikeElement(el: Element): boolean {\n const tag = el.tagName;\n if (tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT') return true;\n if ((el as HTMLElement).contentEditable === 'true') return true;\n return false;\n}\n\n/**\n * Whether debug logging is enabled. Mirrors signals.ts — keeps a single\n * convention for the package.\n */\nfunction debugEnabled(): boolean {\n try {\n return typeof localStorage !== 'undefined' && localStorage.getItem('hyperfixi:debug') !== null;\n } catch {\n return false;\n }\n}\n\n/**\n * Create the bind evaluator. Because `bind` has unusual parse shape (AST\n * nodes for left/right that may be identifiers, `$name` references, or DOM\n * element lookups), we rely on the runtime to evaluate them.\n */\nexport function makeEvaluateBindFeature(runtime: {\n execute(node: ASTNode, ctx: ExecutionContext): Promise<unknown>;\n}): (node: ASTNode, ctx: unknown) => unknown | Promise<unknown> {\n return async function evaluateBindFeature(node, ctx) {\n const context = ctx as ExecutionContext;\n const owner = (context.me as Element) ?? document.body;\n const n = node as BindFeatureNode;\n\n // Dynamically import the element-scope accessor from core. A static import\n // would pull core's full index into this module's load graph — and core's\n // index eagerly evaluates `morphlex` (which touches `Element.prototype` at\n // module scope), throwing `Element is not defined` when @hyperfixi/reactivity\n // is imported in bare Node/SSR. This evaluator only runs in a browser (it\n // uses `document.body` above), so deferring the import keeps the package\n // importable in Node while `:name` binds still address the core store.\n const { getElementScopeMap } = await import('@hyperfixi/core');\n\n // Resolve sides. One is a var reference (read/write a symbol), the other\n // a DOM target (read/write a property). Determine direction from node shape.\n const leftIsVar = isVarRef(n.left);\n const rightIsVar = isVarRef(n.right);\n\n // We need a DOM-side descriptor (element + property) and a var-side name.\n let varSide: { name: string; isGlobal: boolean } | null = null;\n let domSide: { exprNode: ASTNode } | null = null;\n\n if (leftIsVar && !rightIsVar) {\n varSide = varRefInfo(n.left);\n domSide = { exprNode: n.right };\n } else if (rightIsVar && !leftIsVar) {\n varSide = varRefInfo(n.right);\n domSide = { exprNode: n.left };\n } else if (leftIsVar && rightIsVar) {\n // var-to-var binding not supported in v1 (no DOM side).\n throw new Error('bind: cannot bind two symbols together (need a DOM side)');\n } else {\n throw new Error('bind: could not identify a symbol side');\n }\n\n // Unwrap explicit property syntax: `#el.value` or `#el's value` becomes\n // (element-expression, propertyName). Otherwise, evaluate the whole side\n // and rely on auto-detection.\n const explicit = unwrapExplicitProperty(domSide.exprNode);\n // Multi-level property access (`#el.style.background`, `me's dataset's id`)\n // is not supported in v1. Surface that diagnosis up front; otherwise the\n // user gets the generic \"did not resolve to an element\" message and no\n // hint that the chain is the problem.\n if (explicit && isChainedMember(explicit.element)) {\n throw new Error(\n 'bind: multi-level property access (e.g., `#el.a.b`) is not supported in v1 — restructure to a single property write or pass the element directly for auto-detection (e.g., `bind $x to #el`).'\n );\n }\n const elementExpr = explicit ? explicit.element : domSide.exprNode;\n const domValue = await runtime.execute(elementExpr, context);\n if (!(domValue instanceof Element)) {\n const valueType =\n domValue === null ? 'null' : domValue === undefined ? 'undefined' : typeof domValue;\n const snippet =\n domValue !== null && domValue !== undefined ? ` \"${String(domValue).slice(0, 40)}\"` : '';\n const suggestion = explicit\n ? ''\n : \" If you meant to write to a property, use the explicit form: `<selector>'s <property>`.\";\n throw new Error(\n `bind: right-hand side did not resolve to an element (got ${valueType}${snippet}).${suggestion}`\n );\n }\n const el = domValue;\n // Explicit property bypasses auto-detect; the user takes responsibility for\n // picking something readable from the element. For DOM→var to work without\n // explicit property, fall back to auto-detect.\n const prop = explicit ? explicit.propertyName : detectProperty(el);\n if (!prop) {\n throw new Error(\n `bind: could not auto-detect property for <${el.tagName.toLowerCase()}> — use explicit \\`<expr>'s <property>\\` form`\n );\n }\n // For explicit-property mode on non-form elements, DOM→var can't sync via\n // input/change events. Skip the listener install; only var→DOM runs.\n const installDomToVar = !explicit || isFormLikeElement(el);\n if (explicit && !installDomToVar && debugEnabled() && typeof console !== 'undefined') {\n console.warn(\n `[@hyperfixi/reactivity] bind: DOM→var skipped for <${el.tagName.toLowerCase()}>.${prop} — no input/change event source.`\n );\n }\n\n // Initial sync: DOM → var.\n const readDom = (): unknown => {\n if (prop === 'valueAsNumber') return (el as HTMLInputElement).valueAsNumber;\n if (prop === 'checked') return (el as HTMLInputElement).checked;\n if (prop === 'textContent') return el.textContent ?? '';\n return (el as any)[prop];\n };\n const writeDom = (value: unknown): void => {\n if (prop === 'valueAsNumber') {\n const n = Number(value);\n (el as HTMLInputElement).valueAsNumber = Number.isNaN(n) ? (null as never) : n;\n } else if (prop === 'checked') {\n (el as HTMLInputElement).checked = Boolean(value);\n } else if (prop === 'textContent') {\n el.textContent = value == null ? '' : String(value);\n } else {\n (el as any)[prop] = value;\n }\n };\n const readVar = (): unknown => {\n if (varSide!.isGlobal) return context.globals?.get(varSide!.name);\n // `:name` is element-scoped — read from the owner element's store, the same\n // place the core `set :name` writes. (Direct Map access, not getElementVar,\n // so we don't double-fire the read hook; bind tracks via trackElement.)\n return getElementScopeMap(owner).get(varSide!.name);\n };\n const writeVar = (value: unknown): void => {\n if (varSide!.isGlobal) {\n context.globals?.set(varSide!.name, value);\n // Bypass the global-write hook (we're touching the Map directly), so\n // notify the reactive graph manually for the var→DOM effect.\n reactive.notifyGlobal(varSide!.name);\n } else {\n // Element-scoped `:name`: write to the owner element's store (same place\n // core's `set :name` writes), then notify manually — the direct Map write\n // skips the localWriteHook.\n getElementScopeMap(owner).set(varSide!.name, value);\n reactive.notifyElement(owner, varSide!.name);\n }\n };\n\n // Effect 1: DOM → var (fires on user input). Skipped for explicit-property\n // bindings on non-form elements — no input/change events would drive it.\n let stopDomToVar: (() => void) | null = null;\n if (installDomToVar) {\n stopDomToVar = reactive.createEffect(\n () => {\n reactive.trackDomProperty(el, prop);\n return readDom();\n },\n newValue => {\n writeVar(newValue);\n },\n owner\n );\n }\n\n // Effect 2: var → DOM (fires on programmatic var writes — for globals\n // via the core's globalWriteHook, for locals via the localWriteHook the\n // reactivity plugin registers).\n const stopVarToDom = reactive.createEffect(\n () => {\n if (varSide!.isGlobal) reactive.trackGlobal(varSide!.name);\n else reactive.trackElement(owner, varSide!.name);\n return readVar();\n },\n newValue => {\n if (newValue === undefined) return;\n writeDom(newValue);\n },\n owner\n );\n\n if (stopDomToVar) context.registerCleanup?.(owner, stopDomToVar, 'bind-dom-to-var');\n context.registerCleanup?.(owner, stopVarToDom, 'bind-var-to-dom');\n return undefined;\n };\n}\n\n/**\n * Is this AST node a var reference we can bind to?\n *\n * The hyperfixi parser emits `identifier` nodes for both globals and locals,\n * but with different shapes:\n * - `$foo` → `{ type: 'identifier', name: '$foo' }` (no scope field)\n * - `:foo` → `{ type: 'identifier', name: 'foo', scope: 'element' }` (element-scoped)\n * - `::foo` → `{ type: 'identifier', name: 'foo', scope: 'global' }`\n *\n * We accept all of them. The legacy `$`-prefix sniff stays as a fallback because\n * `parseExpression` doesn't always set `scope`.\n */\nfunction isVarRef(node: ASTNode): boolean {\n if (!node) return false;\n if (node.type !== 'identifier') return false;\n const scope = node.scope as string | undefined;\n if (scope === 'local' || scope === 'element' || scope === 'global') return true;\n const name = (node.name as string) ?? '';\n return name.startsWith('$') || name.startsWith(':');\n}\n\nfunction varRefInfo(node: ASTNode): { name: string; isGlobal: boolean } {\n const rawName = (node.name as string) ?? '';\n const scope = node.scope as string | undefined;\n // Prefer the parser-emitted scope marker. Fall back to prefix sniffing for\n // shapes where scope isn't set (e.g. `$foo` arriving as bare identifier\n // with name '$foo').\n let isGlobal: boolean;\n let name: string;\n if (scope === 'global') {\n isGlobal = true;\n name = rawName;\n } else if (scope === 'local' || scope === 'element') {\n isGlobal = false;\n name = rawName;\n } else if (rawName.startsWith('$')) {\n isGlobal = true;\n name = rawName.slice(1);\n } else if (rawName.startsWith(':')) {\n isGlobal = false;\n name = rawName.slice(1);\n } else {\n isGlobal = false;\n name = rawName;\n }\n if (!name) {\n throw new Error('bind: variable reference has empty name');\n }\n return { name, isGlobal };\n}\n","/**\n * The version reported by this package's plugin descriptor.\n *\n * GENERATED — do not edit by hand. `scripts/set-version.cjs` rewrites this file\n * alongside the `package.json` files it already touches.\n *\n * The literal used to sit inline in the plugin object, where nothing kept it\n * honest: `HyperfixiPlugin` does not declare `version` (the plugins widen the\n * type themselves), so no consumer and no test ever read it. Both froze at the\n * release they were written in — reactivity at 2.3.1, realtime at 2.6.0 — and\n * shipped that way inside the browser bundle for many releases. The equality\n * check in `version.test.ts` is what keeps this one honest.\n */\nexport const VERSION = '3.1.1';\n","/**\n * @hyperfixi/reactivity — signal-based reactive features for hyperfixi.\n *\n * Adds four constructs from upstream _hyperscript 0.9.90:\n *\n * live [commandList] end reactive block — body re-runs\n * when tracked reads change.\n *\n * when <expr> [or <expr>]* changes observer — body runs when any\n * [commandList] end watched expression changes.\n *\n * bind <var> to <element> two-way DOM ⇄ var binding.\n *\n * ^name [on <target>] DOM-scoped inherited variable\n * (read + write with `^`).\n *\n * Install:\n *\n * ```ts\n * import { createRuntime, installPlugin } from '@hyperfixi/core';\n * import { reactivityPlugin } from '@hyperfixi/reactivity';\n *\n * const runtime = createRuntime();\n * installPlugin(runtime, reactivityPlugin);\n * ```\n */\n\nimport type { HyperfixiPlugin, HyperfixiPluginContext } from '@hyperfixi/core';\nimport { reactive } from './signals';\nimport { parseCaretPrefix, makeEvaluateCaretVar, makeWriteCaretVar } from './caret-var';\nimport { parseLiveFeature, makeEvaluateLiveFeature } from './live';\nimport { parseWhenFeature, makeEvaluateWhenFeature } from './when';\nimport { parseBindFeature, makeEvaluateBindFeature } from './bind';\nimport { VERSION } from './version.js';\n\nexport { reactive } from './signals';\nexport type { CaretVarNode } from './caret-var';\nexport type { LiveFeatureNode } from './live';\nexport type { WhenFeatureNode } from './when';\nexport type { BindFeatureNode } from './bind';\n\n/**\n * The plugin object. Install once at app startup; re-installing is idempotent\n * (guarded via a `parserExtensions.hasFeature('live')` check).\n *\n * Registers:\n * - Features: `live`, `when`, `bind` (top-level features with `end` bodies)\n * - Prefix op: `^` (primary expression for DOM-scoped vars)\n * - Node evaluators: `liveFeature`, `whenFeature`, `bindFeature`, `caretVar`\n * - A node writer for `caretVar` so `set ^X to Y` flows through `reactive.writeCaret`\n * - Global read/write hooks so `$name` reads track and writes notify\n *\n * Effect cleanup: each effect-creating evaluator calls\n * `context.registerCleanup(owner, stop, ...)` so the core runtime tears effects\n * down when their owning element is cleaned up. There is no separate plugin-level\n * cleanup hook; `reactive.stopElementEffects(el)` is exposed for explicit teardown\n * by tests and consumers that manage element lifecycle outside the runtime.\n */\nexport const reactivityPlugin: HyperfixiPlugin & { version: string } = {\n name: '@hyperfixi/reactivity',\n version: VERSION,\n install(ctx: HyperfixiPluginContext) {\n const { parserExtensions, runtime } = ctx;\n\n // Idempotency: the parser-extension registry is process-singleton, so\n // re-installing into a fresh runtime would otherwise stack additional\n // global read/write hooks on every call. `snapshot()`/`restore()` clears\n // the feature registry — when tests roll back the registry, this guard\n // re-enables a fresh install.\n if (parserExtensions.hasFeature('live')) return;\n\n // Parser hooks — three block features plus a primary-expression caret.\n parserExtensions.registerFeature('live', parseLiveFeature as never);\n parserExtensions.registerFeature('when', parseWhenFeature as never);\n parserExtensions.registerFeature('bind', parseBindFeature as never);\n parserExtensions.registerPrefixOperator('^', 85, parseCaretPrefix as never);\n\n // Runtime evaluators. Features capture `runtime` so effect re-runs can\n // dispatch body commands without going through module-scope state.\n parserExtensions.registerNodeEvaluator(\n 'liveFeature',\n makeEvaluateLiveFeature(runtime as never) as never\n );\n parserExtensions.registerNodeEvaluator(\n 'whenFeature',\n makeEvaluateWhenFeature(runtime as never) as never\n );\n parserExtensions.registerNodeEvaluator(\n 'bindFeature',\n makeEvaluateBindFeature(runtime as never) as never\n );\n parserExtensions.registerNodeEvaluator(\n 'caretVar',\n makeEvaluateCaretVar(runtime as never) as never\n );\n\n // Caret-var write: lets the core `set` command dispatch `set ^X to Y`\n // through `reactive.writeCaret`. Resolves `on <target>` via the captured\n // runtime — no global-scope indirection.\n parserExtensions.registerNodeWriter('caretVar', makeWriteCaretVar(runtime as never) as never);\n\n // Global-write hook: notify the reactive graph whenever `$name` is set.\n // The returned disposer is intentionally discarded — the install-time\n // idempotency guard above is the sole gate against double-registration.\n parserExtensions.registerGlobalWriteHook((name: string, _value: unknown, _context: unknown) => {\n reactive.notifyGlobal(name);\n });\n\n // Global-read hook: track the read against the current effect (if any)\n // so effects re-run when the global changes.\n parserExtensions.registerGlobalReadHook((name: string, _context: unknown) => {\n reactive.trackGlobal(name);\n });\n\n // Local hooks — analogous to globals, but keyed by `context.me` so each\n // element holds independent state. A `set :foo to ...` running in a\n // handler with `me=button1` notifies effects subscribed to (button1, 'foo');\n // a `set :foo` in a different `me` won't reach them. This matches how\n // locals already work — they're never cross-context — and lets two\n // components hold independent `:foo` state without interference.\n parserExtensions.registerLocalWriteHook((name: string, _value: unknown, context) => {\n const owner = (context as { me?: Element | null }).me ?? null;\n if (owner) reactive.notifyElement(owner, name);\n });\n\n parserExtensions.registerLocalReadHook((name: string, context) => {\n const owner = (context as { me?: Element | null }).me ?? null;\n if (owner) reactive.trackElement(owner, name);\n });\n },\n};\n\nexport default reactivityPlugin;\n"],"mappings":"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","names":["debugEnabled","n"]}
1
+ {"version":3,"sources":["../src/signals.ts","../src/caret-var.ts","../src/live.ts","../src/when.ts","../src/bind.ts","../src/version.ts","../src/index.ts"],"sourcesContent":["/**\n * Signal/effect reactive core.\n *\n * Models three dependency kinds for v1:\n * - `global` — `$name` variables in `context.globals`\n * - `element` — `^name` DOM-scoped variables (per-element storage)\n * - `dom` — DOM properties read via `input`/`change` listeners\n *\n * Inspired by upstream _hyperscript 0.9.91's `src/core/runtime/reactivity.js`\n * but reimplemented in TypeScript against hyperfixi's types. Batched via\n * `queueMicrotask` so synchronous writes coalesce into a single flush. Cycle\n * detection halts at >100 consecutive triggers (matching upstream).\n *\n * External packages never see `Effect` or `Reactive` directly — they hold\n * the disposer returned by `createEffect()` and call it when the owning\n * element is cleaned up.\n */\n\nexport type DepKind = 'global' | 'element' | 'dom';\n\n/**\n * Whether reactive debug logging is enabled. Mirrors the core convention:\n * `localStorage.setItem('hyperfixi:debug', '*')` enables, anything else (or no\n * `localStorage`) disables. Cheap call — no caching, since users toggle this\n * interactively in the console.\n */\nfunction debugEnabled(): boolean {\n try {\n return typeof localStorage !== 'undefined' && localStorage.getItem('hyperfixi:debug') !== null;\n } catch {\n return false;\n }\n}\n\nfunction debugWarn(message: string, err: unknown): void {\n if (!debugEnabled()) return;\n if (typeof console === 'undefined') return;\n console.warn(`[@hyperfixi/reactivity] ${message}`, err);\n}\n\nexport interface Dep {\n readonly key: string;\n readonly kind: DepKind;\n readonly name: string;\n readonly element: Element | null;\n}\n\nconst globalDepKey = (name: string): string => `global:${name}`;\nconst elementDepKey = (el: Element, name: string): string => `element:${reactiveIdFor(el)}:${name}`;\nconst domDepKey = (el: Element, prop: string): string => `dom:${reactiveIdFor(el)}:${prop}`;\n\n// WeakMap-based id assignment so we don't monkey-patch DOM nodes. Ids are\n// per-process and only used to construct stable string keys for an effect's\n// per-effect dependency dedup map.\nconst _reactiveIds = new WeakMap<Element, number>();\nlet _idCounter = 0;\nfunction reactiveIdFor(el: Element): number {\n const existing = _reactiveIds.get(el);\n if (existing !== undefined) return existing;\n const id = ++_idCounter;\n _reactiveIds.set(el, id);\n return id;\n}\n\nexport class Effect {\n readonly dependencies = new Map<string, Dep>();\n private lastValue: unknown = undefined;\n private _stopped = false;\n private _consecutiveTriggers = 0;\n\n constructor(\n private readonly r: Reactive,\n readonly expression: () => unknown | Promise<unknown>,\n readonly handler: (value: unknown) => void | Promise<void>,\n readonly element: Element | null\n ) {}\n\n get stopped(): boolean {\n return this._stopped;\n }\n\n /**\n * Run the effect for the first time: collect dependencies, subscribe, call\n * handler with the initial value. Errors in expression evaluation are caught\n * and silently swallowed (matches upstream's tolerance).\n */\n async initialize(): Promise<void> {\n if (this._stopped) return;\n try {\n const value = await this.r._runWithEffect(this, this.expression);\n this.lastValue = value;\n if (value !== undefined && value !== null) {\n await this.handler(value);\n }\n } catch (err) {\n // Swallow — expression evaluation failed during initial read. Surface\n // via debug log so users diagnosing \"my effect never fires\" can see why.\n debugWarn('effect.initialize failed', err);\n }\n }\n\n /**\n * Re-run the effect after a notify. Cycle-detects (halts at 101 consecutive\n * triggers). If the new value is Object.is-equal to the last, the handler\n * is skipped. If the owning element has disconnected, the effect stops\n * itself and returns.\n */\n async run(): Promise<void> {\n if (this._stopped) return;\n if (this.element && !this.element.isConnected) {\n this.stop();\n return;\n }\n this._consecutiveTriggers++;\n if (this._consecutiveTriggers > 100) {\n if (typeof console !== 'undefined') {\n console.error(\n '[@hyperfixi/reactivity] Effect halted: > 100 consecutive triggers (cycle detected).'\n );\n }\n this.stop();\n return;\n }\n try {\n const value = await this.r._runWithEffect(this, this.expression);\n if (Object.is(value, this.lastValue)) return;\n this.lastValue = value;\n await this.handler(value);\n } catch (err) {\n // Swallow — expression/handler errors don't break the microtask flush.\n debugWarn('effect.run failed', err);\n }\n }\n\n resetTriggerCount(): void {\n this._consecutiveTriggers = 0;\n }\n\n /**\n * Stop the effect and remove it from all dependency subscriptions. Safe to\n * call multiple times.\n */\n stop(): void {\n if (this._stopped) return;\n this._stopped = true;\n this.r._unsubscribeEffect(this);\n this.dependencies.clear();\n }\n}\n\ninterface ElementState {\n symbolSubs: Map<string, Set<Effect>>;\n caretVars: Map<string, unknown>;\n domHandlers: Map<string, DomHandler>;\n effects: Set<Effect>;\n}\n\ninterface DomHandler {\n subs: Set<Effect>;\n detach: () => void;\n}\n\nexport class Reactive {\n private currentEffect: Effect | null = null;\n /**\n * Tail of the effect-evaluation chain. `_runWithEffect` links onto this so\n * only one effect owns the `currentEffect` slot at a time — see the comment\n * there for why concurrent async effects otherwise lose their dependencies.\n */\n private trackingQueue: Promise<void> = Promise.resolve();\n private pending = new Set<Effect>();\n private scheduled = false;\n\n private globalSubs = new Map<string, Set<Effect>>();\n private elementState = new WeakMap<Element, ElementState>();\n\n private getElementState(el: Element): ElementState {\n let s = this.elementState.get(el);\n if (!s) {\n s = {\n symbolSubs: new Map(),\n caretVars: new Map(),\n domHandlers: new Map(),\n effects: new Set(),\n };\n this.elementState.set(el, s);\n }\n return s;\n }\n\n /**\n * Internal helper invoked by Effect — installs `this` as the current effect,\n * runs the expression, then restores the previous current-effect pointer.\n * Track* methods (called from read-paths) consult `currentEffect` to know\n * which effect to subscribe.\n */\n async _runWithEffect(e: Effect, fn: () => unknown | Promise<unknown>): Promise<unknown> {\n // Dependency capture keys off ONE `currentEffect` slot, but effect\n // expressions are async (a `live` body awaits each command). Two effects\n // evaluating concurrently would interleave across their await points and\n // attribute each other's reads — or, once the inner one restored `prev`,\n // leave the outer one tracking nothing at all. Three `live` blocks on a\n // page (they all start in the same tick) reliably lost their\n // subscriptions that way and froze.\n //\n // Serialize instead: each run waits for the previous one to finish, so\n // the slot belongs to exactly one effect for the whole of its evaluation.\n // Effect bodies are short DOM updates, and this only orders effect\n // evaluation — it does not block the page.\n const runExclusive = async (): Promise<unknown> => {\n const prev = this.currentEffect;\n this.currentEffect = e;\n // Unsubscribe from previous deps before re-running. The expression will\n // re-record whatever it actually reads this time, avoiding stale subs.\n this._unsubscribeEffect(e);\n e.dependencies.clear();\n try {\n return await fn();\n } finally {\n this.currentEffect = prev;\n }\n };\n\n // Chain onto the tail of the queue, and keep the queue alive whether this\n // run resolves or throws (callers already swallow effect errors).\n const result = this.trackingQueue.then(runExclusive, runExclusive);\n this.trackingQueue = result.then(\n () => undefined,\n () => undefined\n );\n return result;\n }\n\n // ---------------------------------------------------------------------------\n // Track (read-path) — invoked from interceptors / evaluators.\n // ---------------------------------------------------------------------------\n\n trackGlobal(name: string): void {\n const e = this.currentEffect;\n if (!e) return;\n const key = globalDepKey(name);\n if (e.dependencies.has(key)) return;\n e.dependencies.set(key, { key, kind: 'global', name, element: null });\n let subs = this.globalSubs.get(name);\n if (!subs) {\n subs = new Set();\n this.globalSubs.set(name, subs);\n }\n subs.add(e);\n }\n\n trackElement(el: Element, name: string): void {\n const e = this.currentEffect;\n if (!e) return;\n const key = elementDepKey(el, name);\n if (e.dependencies.has(key)) return;\n e.dependencies.set(key, { key, kind: 'element', name, element: el });\n const state = this.getElementState(el);\n let subs = state.symbolSubs.get(name);\n if (!subs) {\n subs = new Set();\n state.symbolSubs.set(name, subs);\n }\n subs.add(e);\n state.effects.add(e);\n }\n\n trackDomProperty(el: Element, prop: string): void {\n const e = this.currentEffect;\n if (!e) return;\n const key = domDepKey(el, prop);\n if (e.dependencies.has(key)) return;\n e.dependencies.set(key, { key, kind: 'dom', name: prop, element: el });\n const state = this.getElementState(el);\n let handler = state.domHandlers.get(prop);\n if (!handler) {\n const subs = new Set<Effect>();\n const listener = (): void => {\n for (const effect of subs) this.schedule(effect);\n };\n // Use both input and change to cover radios/selects which don't fire input.\n el.addEventListener('input', listener);\n el.addEventListener('change', listener);\n handler = {\n subs,\n detach: () => {\n el.removeEventListener('input', listener);\n el.removeEventListener('change', listener);\n },\n };\n state.domHandlers.set(prop, handler);\n }\n handler.subs.add(e);\n state.effects.add(e);\n }\n\n // ---------------------------------------------------------------------------\n // Notify (write-path) — schedules dependent effects for re-run.\n // ---------------------------------------------------------------------------\n\n notifyGlobal(name: string): void {\n const subs = this.globalSubs.get(name);\n if (!subs) return;\n for (const e of subs) this.schedule(e);\n }\n\n notifyElement(el: Element, name: string): void {\n const state = this.elementState.get(el);\n if (!state) return;\n const subs = state.symbolSubs.get(name);\n if (!subs) return;\n for (const e of subs) this.schedule(e);\n }\n\n // ---------------------------------------------------------------------------\n // Effect lifecycle.\n // ---------------------------------------------------------------------------\n\n /**\n * Create + initialize an effect. Returns a disposer that stops the effect.\n * Callers are expected to register the disposer with the core runtime's\n * cleanup registry so it fires on element removal.\n */\n createEffect(\n expression: () => unknown | Promise<unknown>,\n handler: (value: unknown) => void | Promise<void>,\n owner: Element | null\n ): () => void {\n const e = new Effect(this, expression, handler, owner);\n if (owner) this.getElementState(owner).effects.add(e);\n // Fire initialize asynchronously so the caller can set up cleanup registration\n // before the first run touches the DOM.\n queueMicrotask(() => {\n void e.initialize();\n });\n return () => e.stop();\n }\n\n /**\n * Stop all effects owned by an element. Called by the reactivity plugin's\n * cleanup hook registered via `runtime.getCleanupRegistry()`.\n */\n stopElementEffects(el: Element): void {\n const state = this.elementState.get(el);\n if (!state) return;\n for (const e of Array.from(state.effects)) e.stop();\n state.effects.clear();\n }\n\n /**\n * Internal: remove an effect's entries from all subscriber sets. Called by\n * `Effect.stop()`.\n */\n _unsubscribeEffect(e: Effect): void {\n for (const dep of e.dependencies.values()) {\n if (dep.kind === 'global') {\n const subs = this.globalSubs.get(dep.name);\n subs?.delete(e);\n } else if (dep.kind === 'element' && dep.element) {\n const state = this.elementState.get(dep.element);\n const subs = state?.symbolSubs.get(dep.name);\n subs?.delete(e);\n state?.effects.delete(e);\n } else if (dep.kind === 'dom' && dep.element) {\n const state = this.elementState.get(dep.element);\n const handler = state?.domHandlers.get(dep.name);\n handler?.subs.delete(e);\n if (handler && handler.subs.size === 0) {\n handler.detach();\n state?.domHandlers.delete(dep.name);\n }\n state?.effects.delete(e);\n }\n }\n }\n\n // ---------------------------------------------------------------------------\n // Caret-variable storage — `^name` reads/writes.\n // ---------------------------------------------------------------------------\n\n /**\n * Whether `el` is a `dom-scope=\"isolated\"` boundary. Walks of `^var`\n * lookups stop at boundary elements that don't define the var, so nested\n * components don't accidentally read or write each other's state.\n */\n private isIsolationBoundary(el: Element): boolean {\n return typeof el.getAttribute === 'function' && el.getAttribute('dom-scope') === 'isolated';\n }\n\n /**\n * Walk up the DOM tree from `lookupRoot`, returning the first element whose\n * state has `name` defined. Stops at any `dom-scope=\"isolated\"` boundary\n * that doesn't itself define the var. Returns `null` if no owner is found.\n */\n private findCaretOwner(lookupRoot: Element, name: string): Element | null {\n let el: Element | null = lookupRoot;\n while (el) {\n const state = this.elementState.get(el);\n if (state && state.caretVars.has(name)) return el;\n if (this.isIsolationBoundary(el)) return null;\n el = el.parentElement;\n }\n return null;\n }\n\n /**\n * Read a DOM-scoped variable. Walks up from `lookupRoot`, tracking each\n * element visited as an `element` dep (so writes at any ancestor notify\n * dependent effects). Stops at any `dom-scope=\"isolated\"` boundary that\n * doesn't itself define the var.\n */\n readCaret(lookupRoot: Element, name: string): unknown {\n let el: Element | null = lookupRoot;\n while (el) {\n this.trackElement(el, name);\n const state = this.elementState.get(el);\n if (state && state.caretVars.has(name)) {\n return state.caretVars.get(name);\n }\n if (this.isIsolationBoundary(el)) return undefined;\n el = el.parentElement;\n }\n return undefined;\n }\n\n /**\n * Write a DOM-scoped variable. If `target` is provided, writes there\n * directly; otherwise walks up from `lookupRoot` to find the existing owner,\n * falling back to `lookupRoot` itself if no owner exists.\n */\n writeCaret(lookupRoot: Element, name: string, value: unknown, target?: Element): void {\n const owner = target ?? this.findCaretOwner(lookupRoot, name) ?? lookupRoot;\n const state = this.getElementState(owner);\n state.caretVars.set(name, value);\n this.notifyElement(owner, name);\n }\n\n // ---------------------------------------------------------------------------\n // Scheduler — microtask-batched flush.\n // ---------------------------------------------------------------------------\n\n private schedule(e: Effect): void {\n if (e.stopped) return;\n this.pending.add(e);\n if (this.scheduled) return;\n this.scheduled = true;\n queueMicrotask(() => void this.flush());\n }\n\n private async flush(): Promise<void> {\n try {\n while (this.pending.size > 0) {\n const batch = Array.from(this.pending);\n this.pending.clear();\n for (const e of batch) {\n if (e.stopped) continue;\n await e.run();\n // If the effect's run did not synchronously re-schedule itself, its\n // trigger count is reset. Self-rescheduling (a real cycle) keeps the\n // counter climbing toward the 100 cap. Long-lived effects that fire\n // sporadically (e.g. user-input bindings) stay alive forever.\n if (!this.pending.has(e)) e.resetTriggerCount();\n }\n }\n } finally {\n this.scheduled = false;\n }\n }\n}\n\n// Module singleton. One reactive graph per process; mirrors upstream's\n// `runtime.reactivity` pattern.\nexport const reactive = new Reactive();\n","/**\n * `^name` — DOM-scoped inherited variable.\n *\n * Upstream syntax:\n * ^counter → read from nearest ancestor that has `counter` set\n * ^counter on #target → read from (or near) #target\n * set ^counter to 42 → write to owner (or lookupRoot if not yet defined)\n *\n * Parse side: register `^` as a Pratt prefix operator. The handler consumes\n * the identifier token and an optional `on <target>` clause, emitting a\n * `caretVar` AST node.\n *\n * Eval side: `caretVar` node evaluator calls `reactive.readCaret(anchor,\n * name)` which walks the DOM tree for the owner and records deps as it goes.\n */\n\nimport type { ASTNode, ExecutionContext } from './types';\nimport { reactive } from './signals';\n\nexport interface CaretVarNode extends ASTNode {\n type: 'caretVar';\n name: string;\n onTarget: ASTNode | null;\n}\n\ninterface CaretVarRuntime {\n execute(node: ASTNode, ctx: ExecutionContext): Promise<unknown>;\n}\n\n/**\n * Coerce a runtime-evaluated `on <target>` result to a single Element.\n * Selector expressions resolve to an array of matched elements; bare\n * `me`/`it`/`you`/identifier references resolve to single Elements. Take the\n * first element of an array, or the value itself if it's already an Element.\n */\nfunction coerceToElement(value: unknown): Element | null {\n if (value instanceof Element) return value;\n if (Array.isArray(value) && value[0] instanceof Element) return value[0];\n return null;\n}\n\n/**\n * Pratt prefix handler for `^`. Consumes the following identifier token and\n * an optional `on <expr>` clause.\n */\nexport function parseCaretPrefix(token: unknown, ctx: unknown): ASTNode {\n // `ctx` is hyperfixi's PrattContext: { peek, advance, parseExpr, isStopToken, atEnd }.\n const pctx = ctx as {\n peek(): { value?: string; kind?: string } | undefined;\n advance(): { value: string; kind?: string };\n parseExpr(minBp: number): ASTNode;\n };\n const ident = pctx.advance();\n if (!ident || !ident.value) {\n throw new Error(\"Expected identifier after '^'\");\n }\n let onTarget: ASTNode | null = null;\n const next = pctx.peek();\n if (next && next.value === 'on') {\n pctx.advance(); // consume 'on'\n // Binding power 86 — higher than standard comparisons so `^x on me` doesn't\n // over-consume into surrounding expressions.\n onTarget = pctx.parseExpr(86);\n }\n const startTok = token as { start?: number; end?: number; line?: number; column?: number };\n return {\n type: 'caretVar',\n name: ident.value,\n onTarget,\n start: startTok?.start ?? 0,\n end: startTok?.end ?? 0,\n line: startTok?.line,\n column: startTok?.column,\n } as CaretVarNode;\n}\n\n/**\n * Build a node evaluator for `caretVar` bound to a specific runtime. The\n * evaluator walks up from the resolved anchor element, tracking every element\n * visited so writes at any ancestor notify dependent effects.\n *\n * Capturing `runtime` via a factory closure (matching live/when/bind) keeps\n * the evaluator independent of any module-scope state.\n */\nexport function makeEvaluateCaretVar(\n runtime: CaretVarRuntime\n): (node: ASTNode, ctx: unknown) => Promise<unknown> {\n return async function evaluateCaretVar(node, ctx) {\n const n = node as CaretVarNode;\n const context = ctx as ExecutionContext;\n let anchor: Element | null = (context.me as Element | null) ?? null;\n\n if (n.onTarget) {\n const resolved = await runtime.execute(n.onTarget, context);\n const el = coerceToElement(resolved);\n if (el) anchor = el;\n }\n\n if (!anchor) return undefined;\n return reactive.readCaret(anchor, n.name);\n };\n}\n\n/**\n * Build a node writer for `caretVar` bound to a specific runtime. Used by the\n * core `set` command via `parserExtensions.registerNodeWriter`.\n */\nexport function makeWriteCaretVar(\n runtime: CaretVarRuntime\n): (node: ASTNode, value: unknown, ctx: unknown) => Promise<void> {\n return async function writeCaretVar(node, value, ctx) {\n const n = node as CaretVarNode;\n const context = ctx as ExecutionContext;\n const anchor: Element | null = (context.me as Element | null) ?? null;\n if (!anchor) return;\n\n let target: Element | undefined;\n if (n.onTarget) {\n const resolved = await runtime.execute(n.onTarget, context);\n const el = coerceToElement(resolved);\n if (el) target = el;\n }\n reactive.writeCaret(anchor, n.name, value, target);\n };\n}\n","/**\n * `live ... end` — reactive block. Body re-runs whenever any dependency read\n * during its execution changes.\n *\n * Upstream syntax:\n * live [commandList] end\n *\n * Each command in the body is re-executed as a single effect. The entire\n * body shares one effect instance; its dependency set is the union of every\n * read performed during body execution.\n */\n\nimport type { ASTNode, ExecutionContext, FeatureParserCtx } from './types';\nimport { reactive } from './signals';\n\nexport interface LiveFeatureNode extends ASTNode {\n type: 'liveFeature';\n body: ASTNode[];\n}\n\n/**\n * Parse `live ... end`. The `live` keyword has already been consumed by the\n * parser dispatcher; we parse the body and expect a trailing `end`.\n */\nexport function parseLiveFeature(ctx: unknown, token: unknown): ASTNode {\n const pctx = ctx as FeatureParserCtx;\n const body = pctx.parseCommandListUntilEnd();\n // parseCommandListUntilEnd stops when it sees `end` (but doesn't consume it).\n if (!pctx.isAtEnd() && pctx.check('end')) pctx.match('end');\n const tok = token as { start?: number; end?: number; line?: number; column?: number };\n return {\n type: 'liveFeature',\n body,\n start: tok?.start ?? 0,\n end: pctx.getPosition().end,\n line: tok?.line,\n column: tok?.column,\n } as LiveFeatureNode;\n}\n\n/**\n * Create an evaluator bound to a runtime reference. The plugin captures\n * `runtime` at install time and passes it in so effect re-runs can dispatch\n * the body commands without going through module-scope state.\n */\nexport function makeEvaluateLiveFeature(runtime: {\n execute(node: ASTNode, ctx: ExecutionContext): Promise<unknown>;\n}): (node: ASTNode, ctx: unknown) => unknown | Promise<unknown> {\n return async function evaluateLiveFeature(node, ctx) {\n const context = ctx as ExecutionContext;\n const owner = (context.me as Element) ?? document.body;\n const n = node as LiveFeatureNode;\n\n const stop = reactive.createEffect(\n async () => {\n for (const cmd of n.body) {\n await runtime.execute(cmd, context);\n }\n },\n () => {\n /* no-op — side effects happened inside the expression */\n },\n owner\n );\n context.registerCleanup?.(owner, stop, 'live-effect');\n return undefined;\n };\n}\n","/**\n * `when <expr> [or <expr>]* changes [commandList] end` — observer feature.\n *\n * Runs the body when any watched expression's value changes (Object.is\n * semantics). One effect is created per watched expression so writes to a\n * given dep only re-run that watcher.\n */\n\nimport type { ASTNode, ExecutionContext, FeatureParserCtx } from './types';\nimport { reactive } from './signals';\n\nexport interface WhenFeatureNode extends ASTNode {\n type: 'whenFeature';\n watched: ASTNode[];\n body: ASTNode[];\n}\n\nexport function parseWhenFeature(ctx: unknown, token: unknown): ASTNode {\n const pctx = ctx as FeatureParserCtx;\n const watched: ASTNode[] = [pctx.parseExpression()];\n while (pctx.match('or')) {\n watched.push(pctx.parseExpression());\n }\n pctx.consume('changes', \"Expected 'changes' after when expression list\");\n const body = pctx.parseCommandListUntilEnd();\n if (!pctx.isAtEnd() && pctx.check('end')) pctx.match('end');\n const tok = token as { start?: number; end?: number; line?: number; column?: number };\n return {\n type: 'whenFeature',\n watched,\n body,\n start: tok?.start ?? 0,\n end: pctx.getPosition().end,\n line: tok?.line,\n column: tok?.column,\n } as WhenFeatureNode;\n}\n\nexport function makeEvaluateWhenFeature(runtime: {\n execute(node: ASTNode, ctx: ExecutionContext): Promise<unknown>;\n evaluateExpressionWithResult?: (\n node: ASTNode,\n ctx: ExecutionContext\n ) => Promise<{ value: unknown }>;\n}): (node: ASTNode, ctx: unknown) => unknown | Promise<unknown> {\n return async function evaluateWhenFeature(node, ctx) {\n const context = ctx as ExecutionContext;\n const owner = (context.me as Element) ?? document.body;\n const n = node as WhenFeatureNode;\n\n for (const watchedExpr of n.watched) {\n const stop = reactive.createEffect(\n async () => {\n // Evaluate the watched expression as a regular expression through the\n // runtime so that trackGlobal/trackElement fire via the global-write\n // hook and caret-var reads.\n return await runtime.execute(watchedExpr, context);\n },\n async newValue => {\n // Fire the body with `it` / `result` bound to the new value.\n const subCtx: ExecutionContext = { ...context, it: newValue, result: newValue };\n for (const cmd of n.body) {\n await runtime.execute(cmd, subCtx);\n }\n },\n owner\n );\n context.registerCleanup?.(owner, stop, 'when-effect');\n }\n return undefined;\n };\n}\n","/**\n * `bind X [to|and|with] Y` — two-way binding.\n *\n * Creates two effects (registration order is load-bearing — both initialize\n * via `queueMicrotask` and run in registration order):\n * 1. DOM → var: read DOM, write var. DOM \"wins\" on init.\n * 2. var → DOM: read var, write DOM. Fires on programmatic var writes\n * after init. On its own initial run, var === DOM (Effect 1 just synced\n * them), so the write is a no-op.\n *\n * Two binding forms:\n *\n * 1. Auto-detected (DOM side is a bare element expression):\n *\n * bind $name to #input -- detects `value`\n * bind $checked to me -- detects `checked` on a checkbox\n *\n * Auto-detected property by element type:\n * - INPUT[type=checkbox|radio] → `checked`\n * - INPUT[type=number|range] → `valueAsNumber`\n * - INPUT|TEXTAREA|SELECT → `value`\n * - contenteditable=\"true\" → `textContent`\n * - Custom elements with own `value` → `value`\n *\n * 2. Explicit property (DOM side is a member or possessive expression):\n *\n * bind $color to #picker's value -- possessive (preferred — reads in any language)\n * bind $color to #picker.value -- dot (JS-style alternative)\n * bind $text to #div's textContent -- non-form properties: var→DOM only\n *\n * For form-like elements, both directions work. For non-form elements\n * (e.g., binding a div's `textContent`), only var→DOM fires — there are\n * no input/change events to drive DOM→var, so user mutations of the\n * property via devtools won't propagate back.\n */\n\nimport type { ASTNode, ExecutionContext, FeatureParserCtx } from './types';\nimport { reactive } from './signals';\n\nexport interface BindFeatureNode extends ASTNode {\n type: 'bindFeature';\n left: ASTNode;\n right: ASTNode;\n}\n\nexport function parseBindFeature(ctx: unknown, token: unknown): ASTNode {\n const pctx = ctx as FeatureParserCtx;\n const left = pctx.parseExpression();\n // Accept any of `to` / `and` / `with` as the separator.\n if (!(pctx.match('to') || pctx.match('and') || pctx.match('with'))) {\n throw new Error(\"bind requires 'to', 'and', or 'with' between the two sides\");\n }\n const right = pctx.parseExpression();\n // Optional `end` terminator (matches upstream which allows both forms).\n if (pctx.check('end')) pctx.match('end');\n const tok = token as { start?: number; end?: number; line?: number; column?: number };\n return {\n type: 'bindFeature',\n left,\n right,\n start: tok?.start ?? 0,\n end: pctx.getPosition().end,\n line: tok?.line,\n column: tok?.column,\n } as BindFeatureNode;\n}\n\n/**\n * Auto-detect the DOM property to bind by element type. Returns null if the\n * target isn't a recognized form/editable element.\n */\nfunction detectProperty(el: Element): string | null {\n const tag = el.tagName;\n if (tag === 'INPUT') {\n const type = (el as HTMLInputElement).type;\n if (type === 'checkbox' || type === 'radio') return 'checked';\n if (type === 'number' || type === 'range') return 'valueAsNumber';\n return 'value';\n }\n if (tag === 'TEXTAREA' || tag === 'SELECT') return 'value';\n const ce = (el as HTMLElement).contentEditable;\n if (ce === 'true') return 'textContent';\n // Custom elements with own `value` prop.\n if ('value' in el) return 'value';\n return null;\n}\n\n/**\n * If `node` is a `memberExpression` or `possessiveExpression` with a static\n * identifier property (`#el.value` or `#el's value`), return the inner element\n * expression and the property name. Returns null for anything else.\n *\n * Computed member access (`#el[prop]`) is intentionally rejected — we'd have to\n * evaluate the index dynamically and the binding direction would be unclear.\n * Chained property access (`#el.dataset.value`) is also not unpacked — we only\n * peel one level. Multi-level support is a future arc.\n */\nfunction unwrapExplicitProperty(node: ASTNode): { element: ASTNode; propertyName: string } | null {\n if (!node || (node.type !== 'memberExpression' && node.type !== 'possessiveExpression')) {\n return null;\n }\n if (node.type === 'memberExpression' && node.computed === true) return null;\n const property = node.property as ASTNode | undefined;\n const object = node.object as ASTNode | undefined;\n if (!property || !object || property.type !== 'identifier') return null;\n const name = (property.name as string) ?? '';\n if (!name) return null;\n return { element: object, propertyName: name };\n}\n\n/**\n * Detect a chained property access on the bind RHS — e.g.\n * `me.style.backgroundColor` or `#el's dataset's value`.\n *\n * After `unwrapExplicitProperty` peels the outermost member, the returned\n * `element` is the object of that member. If that object is itself a\n * member/possessive expression, we have a multi-level chain that v1 of\n * bind doesn't support. Used only for the error path; the parsed AST is\n * left intact.\n */\nfunction isChainedMember(node: ASTNode | undefined): boolean {\n if (!node) return false;\n return node.type === 'memberExpression' || node.type === 'possessiveExpression';\n}\n\n/**\n * Whether the input/change listener installed by `trackDomProperty` would\n * actually fire for this element. Used to decide whether to wire up the\n * DOM→var direction when an explicit property is given.\n */\nfunction isFormLikeElement(el: Element): boolean {\n const tag = el.tagName;\n if (tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT') return true;\n if ((el as HTMLElement).contentEditable === 'true') return true;\n return false;\n}\n\n/**\n * Whether debug logging is enabled. Mirrors signals.ts — keeps a single\n * convention for the package.\n */\nfunction debugEnabled(): boolean {\n try {\n return typeof localStorage !== 'undefined' && localStorage.getItem('hyperfixi:debug') !== null;\n } catch {\n return false;\n }\n}\n\n/**\n * Create the bind evaluator. Because `bind` has unusual parse shape (AST\n * nodes for left/right that may be identifiers, `$name` references, or DOM\n * element lookups), we rely on the runtime to evaluate them.\n */\nexport function makeEvaluateBindFeature(runtime: {\n execute(node: ASTNode, ctx: ExecutionContext): Promise<unknown>;\n}): (node: ASTNode, ctx: unknown) => unknown | Promise<unknown> {\n return async function evaluateBindFeature(node, ctx) {\n const context = ctx as ExecutionContext;\n const owner = (context.me as Element) ?? document.body;\n const n = node as BindFeatureNode;\n\n // Dynamically import the element-scope accessor from core. A static import\n // would pull core's full index into this module's load graph — and core's\n // index eagerly evaluates `morphlex` (which touches `Element.prototype` at\n // module scope), throwing `Element is not defined` when @hyperfixi/reactivity\n // is imported in bare Node/SSR. This evaluator only runs in a browser (it\n // uses `document.body` above), so deferring the import keeps the package\n // importable in Node while `:name` binds still address the core store.\n const { getElementScopeMap } = await import('@hyperfixi/core');\n\n // Resolve sides. One is a var reference (read/write a symbol), the other\n // a DOM target (read/write a property). Determine direction from node shape.\n const leftIsVar = isVarRef(n.left);\n const rightIsVar = isVarRef(n.right);\n\n // We need a DOM-side descriptor (element + property) and a var-side name.\n let varSide: { name: string; isGlobal: boolean } | null = null;\n let domSide: { exprNode: ASTNode } | null = null;\n\n if (leftIsVar && !rightIsVar) {\n varSide = varRefInfo(n.left);\n domSide = { exprNode: n.right };\n } else if (rightIsVar && !leftIsVar) {\n varSide = varRefInfo(n.right);\n domSide = { exprNode: n.left };\n } else if (leftIsVar && rightIsVar) {\n // var-to-var binding not supported in v1 (no DOM side).\n throw new Error('bind: cannot bind two symbols together (need a DOM side)');\n } else {\n throw new Error('bind: could not identify a symbol side');\n }\n\n // Unwrap explicit property syntax: `#el.value` or `#el's value` becomes\n // (element-expression, propertyName). Otherwise, evaluate the whole side\n // and rely on auto-detection.\n const explicit = unwrapExplicitProperty(domSide.exprNode);\n // Multi-level property access (`#el.style.background`, `me's dataset's id`)\n // is not supported in v1. Surface that diagnosis up front; otherwise the\n // user gets the generic \"did not resolve to an element\" message and no\n // hint that the chain is the problem.\n if (explicit && isChainedMember(explicit.element)) {\n throw new Error(\n 'bind: multi-level property access (e.g., `#el.a.b`) is not supported in v1 — restructure to a single property write or pass the element directly for auto-detection (e.g., `bind $x to #el`).'\n );\n }\n const elementExpr = explicit ? explicit.element : domSide.exprNode;\n const domValue = await runtime.execute(elementExpr, context);\n if (!(domValue instanceof Element)) {\n const valueType =\n domValue === null ? 'null' : domValue === undefined ? 'undefined' : typeof domValue;\n const snippet =\n domValue !== null && domValue !== undefined ? ` \"${String(domValue).slice(0, 40)}\"` : '';\n const suggestion = explicit\n ? ''\n : \" If you meant to write to a property, use the explicit form: `<selector>'s <property>`.\";\n throw new Error(\n `bind: right-hand side did not resolve to an element (got ${valueType}${snippet}).${suggestion}`\n );\n }\n const el = domValue;\n // Explicit property bypasses auto-detect; the user takes responsibility for\n // picking something readable from the element. For DOM→var to work without\n // explicit property, fall back to auto-detect.\n const prop = explicit ? explicit.propertyName : detectProperty(el);\n if (!prop) {\n throw new Error(\n `bind: could not auto-detect property for <${el.tagName.toLowerCase()}> — use explicit \\`<expr>'s <property>\\` form`\n );\n }\n // For explicit-property mode on non-form elements, DOM→var can't sync via\n // input/change events. Skip the listener install; only var→DOM runs.\n const installDomToVar = !explicit || isFormLikeElement(el);\n if (explicit && !installDomToVar && debugEnabled() && typeof console !== 'undefined') {\n console.warn(\n `[@hyperfixi/reactivity] bind: DOM→var skipped for <${el.tagName.toLowerCase()}>.${prop} — no input/change event source.`\n );\n }\n\n // Initial sync: DOM → var.\n const readDom = (): unknown => {\n if (prop === 'valueAsNumber') return (el as HTMLInputElement).valueAsNumber;\n if (prop === 'checked') return (el as HTMLInputElement).checked;\n if (prop === 'textContent') return el.textContent ?? '';\n return (el as any)[prop];\n };\n const writeDom = (value: unknown): void => {\n if (prop === 'valueAsNumber') {\n const n = Number(value);\n (el as HTMLInputElement).valueAsNumber = Number.isNaN(n) ? (null as never) : n;\n } else if (prop === 'checked') {\n (el as HTMLInputElement).checked = Boolean(value);\n } else if (prop === 'textContent') {\n el.textContent = value == null ? '' : String(value);\n } else {\n (el as any)[prop] = value;\n }\n };\n const readVar = (): unknown => {\n if (varSide!.isGlobal) return context.globals?.get(varSide!.name);\n // `:name` is element-scoped — read from the owner element's store, the same\n // place the core `set :name` writes. (Direct Map access, not getElementVar,\n // so we don't double-fire the read hook; bind tracks via trackElement.)\n return getElementScopeMap(owner).get(varSide!.name);\n };\n const writeVar = (value: unknown): void => {\n if (varSide!.isGlobal) {\n context.globals?.set(varSide!.name, value);\n // Bypass the global-write hook (we're touching the Map directly), so\n // notify the reactive graph manually for the var→DOM effect.\n reactive.notifyGlobal(varSide!.name);\n } else {\n // Element-scoped `:name`: write to the owner element's store (same place\n // core's `set :name` writes), then notify manually — the direct Map write\n // skips the localWriteHook.\n getElementScopeMap(owner).set(varSide!.name, value);\n reactive.notifyElement(owner, varSide!.name);\n }\n };\n\n // Effect 1: DOM → var (fires on user input). Skipped for explicit-property\n // bindings on non-form elements — no input/change events would drive it.\n let stopDomToVar: (() => void) | null = null;\n if (installDomToVar) {\n stopDomToVar = reactive.createEffect(\n () => {\n reactive.trackDomProperty(el, prop);\n return readDom();\n },\n newValue => {\n writeVar(newValue);\n },\n owner\n );\n }\n\n // Effect 2: var → DOM (fires on programmatic var writes — for globals\n // via the core's globalWriteHook, for locals via the localWriteHook the\n // reactivity plugin registers).\n const stopVarToDom = reactive.createEffect(\n () => {\n if (varSide!.isGlobal) reactive.trackGlobal(varSide!.name);\n else reactive.trackElement(owner, varSide!.name);\n return readVar();\n },\n newValue => {\n if (newValue === undefined) return;\n writeDom(newValue);\n },\n owner\n );\n\n if (stopDomToVar) context.registerCleanup?.(owner, stopDomToVar, 'bind-dom-to-var');\n context.registerCleanup?.(owner, stopVarToDom, 'bind-var-to-dom');\n return undefined;\n };\n}\n\n/**\n * Is this AST node a var reference we can bind to?\n *\n * The hyperfixi parser emits `identifier` nodes for both globals and locals,\n * but with different shapes:\n * - `$foo` → `{ type: 'identifier', name: '$foo' }` (no scope field)\n * - `:foo` → `{ type: 'identifier', name: 'foo', scope: 'element' }` (element-scoped)\n * - `::foo` → `{ type: 'identifier', name: 'foo', scope: 'global' }`\n *\n * We accept all of them. The legacy `$`-prefix sniff stays as a fallback because\n * `parseExpression` doesn't always set `scope`.\n */\nfunction isVarRef(node: ASTNode): boolean {\n if (!node) return false;\n if (node.type !== 'identifier') return false;\n const scope = node.scope as string | undefined;\n if (scope === 'local' || scope === 'element' || scope === 'global') return true;\n const name = (node.name as string) ?? '';\n return name.startsWith('$') || name.startsWith(':');\n}\n\nfunction varRefInfo(node: ASTNode): { name: string; isGlobal: boolean } {\n const rawName = (node.name as string) ?? '';\n const scope = node.scope as string | undefined;\n // Prefer the parser-emitted scope marker. Fall back to prefix sniffing for\n // shapes where scope isn't set (e.g. `$foo` arriving as bare identifier\n // with name '$foo').\n let isGlobal: boolean;\n let name: string;\n if (scope === 'global') {\n isGlobal = true;\n name = rawName;\n } else if (scope === 'local' || scope === 'element') {\n isGlobal = false;\n name = rawName;\n } else if (rawName.startsWith('$')) {\n isGlobal = true;\n name = rawName.slice(1);\n } else if (rawName.startsWith(':')) {\n isGlobal = false;\n name = rawName.slice(1);\n } else {\n isGlobal = false;\n name = rawName;\n }\n if (!name) {\n throw new Error('bind: variable reference has empty name');\n }\n return { name, isGlobal };\n}\n","/**\n * The version reported by this package's plugin descriptor.\n *\n * GENERATED — do not edit by hand. `scripts/set-version.cjs` rewrites this file\n * alongside the `package.json` files it already touches.\n *\n * The literal used to sit inline in the plugin object, where nothing kept it\n * honest: `HyperfixiPlugin` does not declare `version` (the plugins widen the\n * type themselves), so no consumer and no test ever read it. Both froze at the\n * release they were written in — reactivity at 2.3.1, realtime at 2.6.0 — and\n * shipped that way inside the browser bundle for many releases. The equality\n * check in `version.test.ts` is what keeps this one honest.\n */\nexport const VERSION = '3.3.0';\n","/**\n * @hyperfixi/reactivity — signal-based reactive features for hyperfixi.\n *\n * Adds four constructs from upstream _hyperscript 0.9.90:\n *\n * live [commandList] end reactive block — body re-runs\n * when tracked reads change.\n *\n * when <expr> [or <expr>]* changes observer — body runs when any\n * [commandList] end watched expression changes.\n *\n * bind <var> to <element> two-way DOM ⇄ var binding.\n *\n * ^name [on <target>] DOM-scoped inherited variable\n * (read + write with `^`).\n *\n * Install:\n *\n * ```ts\n * import { createRuntime, installPlugin } from '@hyperfixi/core';\n * import { reactivityPlugin } from '@hyperfixi/reactivity';\n *\n * const runtime = createRuntime();\n * installPlugin(runtime, reactivityPlugin);\n * ```\n */\n\nimport type { HyperfixiPlugin, HyperfixiPluginContext } from '@hyperfixi/core';\nimport { reactive } from './signals';\nimport { parseCaretPrefix, makeEvaluateCaretVar, makeWriteCaretVar } from './caret-var';\nimport { parseLiveFeature, makeEvaluateLiveFeature } from './live';\nimport { parseWhenFeature, makeEvaluateWhenFeature } from './when';\nimport { parseBindFeature, makeEvaluateBindFeature } from './bind';\nimport { VERSION } from './version.js';\n\nexport { reactive } from './signals';\nexport type { CaretVarNode } from './caret-var';\nexport type { LiveFeatureNode } from './live';\nexport type { WhenFeatureNode } from './when';\nexport type { BindFeatureNode } from './bind';\n\n/**\n * The plugin object. Install once at app startup; re-installing is idempotent\n * (guarded via a `parserExtensions.hasFeature('live')` check).\n *\n * Registers:\n * - Features: `live`, `when`, `bind` (top-level features with `end` bodies)\n * - Prefix op: `^` (primary expression for DOM-scoped vars)\n * - Node evaluators: `liveFeature`, `whenFeature`, `bindFeature`, `caretVar`\n * - A node writer for `caretVar` so `set ^X to Y` flows through `reactive.writeCaret`\n * - Global read/write hooks so `$name` reads track and writes notify\n *\n * Effect cleanup: each effect-creating evaluator calls\n * `context.registerCleanup(owner, stop, ...)` so the core runtime tears effects\n * down when their owning element is cleaned up. There is no separate plugin-level\n * cleanup hook; `reactive.stopElementEffects(el)` is exposed for explicit teardown\n * by tests and consumers that manage element lifecycle outside the runtime.\n */\nexport const reactivityPlugin: HyperfixiPlugin & { version: string } = {\n name: '@hyperfixi/reactivity',\n version: VERSION,\n install(ctx: HyperfixiPluginContext) {\n const { parserExtensions, runtime } = ctx;\n\n // Idempotency: the parser-extension registry is process-singleton, so\n // re-installing into a fresh runtime would otherwise stack additional\n // global read/write hooks on every call. `snapshot()`/`restore()` clears\n // the feature registry — when tests roll back the registry, this guard\n // re-enables a fresh install.\n if (parserExtensions.hasFeature('live')) return;\n\n // Parser hooks — three block features plus a primary-expression caret.\n parserExtensions.registerFeature('live', parseLiveFeature as never);\n parserExtensions.registerFeature('when', parseWhenFeature as never);\n parserExtensions.registerFeature('bind', parseBindFeature as never);\n parserExtensions.registerPrefixOperator('^', 85, parseCaretPrefix as never);\n\n // Runtime evaluators. Features capture `runtime` so effect re-runs can\n // dispatch body commands without going through module-scope state.\n parserExtensions.registerNodeEvaluator(\n 'liveFeature',\n makeEvaluateLiveFeature(runtime as never) as never\n );\n parserExtensions.registerNodeEvaluator(\n 'whenFeature',\n makeEvaluateWhenFeature(runtime as never) as never\n );\n parserExtensions.registerNodeEvaluator(\n 'bindFeature',\n makeEvaluateBindFeature(runtime as never) as never\n );\n parserExtensions.registerNodeEvaluator(\n 'caretVar',\n makeEvaluateCaretVar(runtime as never) as never\n );\n\n // Caret-var write: lets the core `set` command dispatch `set ^X to Y`\n // through `reactive.writeCaret`. Resolves `on <target>` via the captured\n // runtime — no global-scope indirection.\n parserExtensions.registerNodeWriter('caretVar', makeWriteCaretVar(runtime as never) as never);\n\n // Global-write hook: notify the reactive graph whenever `$name` is set.\n // The returned disposer is intentionally discarded — the install-time\n // idempotency guard above is the sole gate against double-registration.\n parserExtensions.registerGlobalWriteHook((name: string, _value: unknown, _context: unknown) => {\n reactive.notifyGlobal(name);\n });\n\n // Global-read hook: track the read against the current effect (if any)\n // so effects re-run when the global changes.\n parserExtensions.registerGlobalReadHook((name: string, _context: unknown) => {\n reactive.trackGlobal(name);\n });\n\n // Local hooks — analogous to globals, but keyed by `context.me` so each\n // element holds independent state. A `set :foo to ...` running in a\n // handler with `me=button1` notifies effects subscribed to (button1, 'foo');\n // a `set :foo` in a different `me` won't reach them. This matches how\n // locals already work — they're never cross-context — and lets two\n // components hold independent `:foo` state without interference.\n parserExtensions.registerLocalWriteHook((name: string, _value: unknown, context) => {\n const owner = (context as { me?: Element | null }).me ?? null;\n if (owner) reactive.notifyElement(owner, name);\n });\n\n parserExtensions.registerLocalReadHook((name: string, context) => {\n const owner = (context as { me?: Element | null }).me ?? null;\n if (owner) reactive.trackElement(owner, name);\n });\n },\n};\n\nexport default reactivityPlugin;\n"],"mappings":"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","names":["debugEnabled","n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hyperfixi/reactivity",
3
- "version": "3.1.1",
3
+ "version": "3.3.0",
4
4
  "description": "Reactive features for hyperfixi — adds `live`, `bind`, `when X changes`, and `^name` DOM-scoped vars (upstream _hyperscript 0.9.90).",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -22,14 +22,14 @@
22
22
  "typecheck": "tsc --noEmit"
23
23
  },
24
24
  "dependencies": {
25
- "@hyperfixi/core": "^3.1.1"
25
+ "@hyperfixi/core": "^3.3.0"
26
26
  },
27
27
  "devDependencies": {
28
- "@types/node": "^26.1.2",
29
- "happy-dom": "^20.10.6",
28
+ "@types/node": "^26.6.3",
29
+ "happy-dom": "^20.14.5",
30
30
  "tsup": "^8.0.0",
31
31
  "typescript": "^5.0.0",
32
- "vitest": "^4.1.5"
32
+ "vitest": "^5.0.2"
33
33
  },
34
34
  "files": [
35
35
  "dist",
package/src/version.ts CHANGED
@@ -11,4 +11,4 @@
11
11
  * shipped that way inside the browser bundle for many releases. The equality
12
12
  * check in `version.test.ts` is what keeps this one honest.
13
13
  */
14
- export const VERSION = '3.1.1';
14
+ export const VERSION = '3.3.0';