Files
rose-ash/spec/tests/hyperscript-feature-audit-0.9.90.md
giles fd1dfea9b3 HS tests: scrape v0.9.90 upstream in full, flip silent stubs to loud SKIPs
- scrape-hs-upstream.py: new scraper walks /tmp/hs-upstream/test/**/*.js
  and emits body-style records for all 1,496 v0.9.90 tests (up from 831).
  Widens coverage into 66 previously-missing categories — templates,
  reactivity, behavior, worker, classRef, make, throw, htmx, tailwind,
  viewTransition, and more.

- build-hs-manifest.py + hyperscript-upstream-manifest.{json,md}:
  coverage manifest tagging each upstream test with a status
  (runnable / skip-listed / untranslated / missing) and block reason.

- generate-sx-tests.py: emit (error "SKIP (...)") instead of silent
  (hs-cleanup!) no-op for both skip-listed tests and generator-
  untranslatable bodies. Stub counter now reports both buckets.

- hyperscript-feature-audit-0.9.90.md: gap audit against the 0.9.90
  spec; pre-0.9.90.json backs up prior 831-test snapshot.

New honest baseline (ocaml runner, test-hyperscript-behavioral):
  831 -> 1,496 tests; 645 -> 1,013 passing (67.7% conformance).
  483 failures split: 45 skip-list, 151 untranslated, 287 real.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 20:27:22 +00:00

29 KiB
Raw Blame History

_hyperscript 0.9.90 Gap Audit

Date: 2026-04-22 Target release: upstream 0.9.90 (2026-04-13), with awareness of 0.9.91 bugfixes (2026-04-14) Supersedes: hyperscript-feature-audit.md (2026-04-09)

Our implementation lives in /root/rose-ash/lib/hyperscript/:

  • tokenizer.sx (618 lines), parser.sx (2414 lines), compiler.sx (1739 lines), runtime.sx (1609 lines), integration.sx (109 lines).

Tests in spec/tests/test-hyperscript-*.sx + extracted fixture hyperscript-upstream-tests.json (831 entries).


1. Version alignment

What release is our JSON snapshot from?

The fixture spec/tests/hyperscript-upstream-tests.json and the prior audit were both written on 2026-04-09 (four days before 0.9.90 tagged on 2026-04-13). File timestamps confirm this; there is no earlier git history for the JSON (it first appears in commit 7492ceac).

The contents are clearly 0.9.90 (pre-tag) or equivalent:

  • 63 on tests, 44 bind, 41 when, 23 live, 10 liveTemplate, 19 component, 4 reactive-properties — all new-in-0.9.90 reactivity features
  • 10 morph, 8 reset, 13 empty, 10 dialog, 4 swap, 7 halt, 5 askAnswer, 3 focus, 8 scroll, 23 fetch including fetch:beforeRequest/fetch:error lifecycle — all 0.9.90 features or overhauls
  • Keywords between, ignoring (case), precedes/follows, where, sorted/mapped/split/joined by — 0.9.90 collection expressions
  • Total 831 tests vs upstream's "~1332 tests" claim for 0.9.90 — we have the subset that was non-sinon, non-script-tag, non-dialog-API when extracted

Conclusion: the JSON is a valid 0.9.90-era snapshot. No re-extraction is strictly required, but upstream added tests post-tag (changelog mentions 1332 tests at 0.9.90 release) — we may be missing late additions.

What 0.9.90 added over 0.9.8 / 0.9.14

Full changelog consulted at github.com/bigskysoftware/_hyperscript/blob/master/CHANGELOG.md.

Breaking changes in 0.9.90:

  • processNode()process()
  • as JSON now parses a string; old stringify behavior → as JSONString; as Values | JSONString / as Values | FormEncoded replace Values:JSON / Values:Form
  • default uses nullish check (no longer overwrites 0/false)
  • go to url ... deprecated → go to /path or go to "url"
  • go to the top of ... deprecated → scroll to the top of
  • async keyword removed (was confusing)
  • /* */ block comments removed
  • transition widthtransition *width (explicit * style ref)
  • fetch throws on non-2xx by default; do not throw or as Response restores old behavior
  • [@attr] bracket-style attribute access deprecated (still parsed, use @attr)

New features in 0.9.90:

  • Reactivity (live, when ... changes, bind) — dependency tracking + UI updates
  • Templates in corerender command + <template> elements + ${} interpolation + #for/#if/#else/#end
  • morph command (idiomorph-based DOM morph)
  • Components system<template component="name"> custom elements with slots
  • DOM-scoped variables (^name)
  • open / close, focus / blur, empty / clear, reset, swap, select, ask / answer, speak, breakpoint
  • toggle between attributes: toggle between [@data-state='active'] and [@data-state='inactive']
  • toggle x between a, b and c N-way cycle
  • Collection expressions: where, sorted by, mapped to, split by, joined by
  • Magic symbols: clipboard, selection
  • on resize (ResizeObserver), on first click (one-shot)
  • view transition command (View Transitions API)
  • intercept feature (service-worker DSL)
  • Pipe operator | for chained conversions (as Values | JSONString)
  • repeat ... until x end / repeat ... while x end (bottom-tested)
  • ignoring case modifier on comparisons
  • between, starts with, ends with comparisons
  • put null into @attr removes attribute
  • DOM expressions (#id, .class, <sel/>) are writable via .replaceWith()
  • set arr to arr + [1, 2] array concatenation
  • cleanup() API + hyperscript:before/after:init / :cleanup lifecycle events + data-hyperscript-powered

0.9.91 bugfixes (worth tracking):

  • on resize from window/document falls through to native resize (not ResizeObserver)
  • toggle ... for <duration> no longer consumes a following for-in
  • ${} template hang fix (debug log leftover)

2. Category coverage gap

Our behavioral test file test-hyperscript-behavioral.sx contains 67 defsuite blocks covering 52 unique JSON categories. The file has 831 eval-hs calls matching the 831 JSON entries — every upstream test has a behavioral entry, but 89 of them are replaced with safe no-op / NOT-IMPLEMENTED stubs (commit 71cf5b84).

Per-category upstream size vs. stub count:

Category Upstream tests Stubs in behavioral Simple-clean upstream (estimate) Status
on 63 32 ~30 many non-simple (event queue / debounced / throttled / intersection / mutation)
bind 44 ~44 1 almost entirely new-0.9.90, not implemented
when 41 ~36 5 when ... changes reactivity mostly missing
comparisonOperator 40 ~36 4 starts with / ends with / between / ignoring case mostly missing
put 38 8 37 implemented; stubs are complex positional forms
toggle 30 2 28 strong coverage
repeat 30 4 23 break / continue / until now work (recent commits)
def 27 7 3 basic only; async/catch variants partial
set 25 7 24 strong; stubs are array-concat and rare forms
dom-scope 25 23 23/25 upstream simple — ^var works
transition 23 1 22 strong
live 23 0 0/23 clean-simple — reactivity runtime missing
fetch 23 3 6 11/23 passing per recent commit 5c66095b; sinon-required tests stubbed
collectionExpressions 22 5 where / sorted by / mapped to / split by / joined bynew 0.9.90, mostly missing
increment 20 20 full
if 19 18 full
component 19 14 14/19 simple — <template component> custom-element bridge missing on JS host
add 19 4 18 full minus [@attr] / {css} forms
asExpression 17 0 0/17 simple — as JSON/JSONString/FormEncoded + pipe operator missing
remove 14 4 12 strong
hide 14 14 full
bootstrap 14 2 2/14 simple — lifecycle events / cleanup() / data-hyperscript-powered not covered
empty 13 12 full (recent commit 802ccd23)
append 13 5 13 strong
take 12 12 full
tell 10 6 10 parses; stubs are the multi-statement tell variants
morph 10 4 4/10 simple — string target variant works, element-target variants stubbed
liveTemplate 10 7 7/10 simple — no implementation of <template> + #for/#if
dialog 10 1 5 5/10 simple — dialog open/close partial
default 9 9 full
send 8 8 full
scroll 8 0 0/8 simple — scroll to command not implemented
reset 8 8 full (recent commit 802ccd23)
evalStatically 8 0 0/8 simple — static eval API not exposed
assignableElements 8 3 partial
wait 7 7 full
splitJoin 7 0 0/7 simple — split by / joined by absent
pick 7 0 0/7 simple — regex/char/item pick missing
parser 7 3 partial
halt 7 6 full (recent commit 802ccd23)
call 6 7 5 partial — call functions that return promises stubbed
no 5 1 no <expr> predicate mostly missing
mathOperator 5 0 0/5 simple — mod/abs + async math promise-aware ops missing
go 5 3 basic go to works; new scroll to form missing
askAnswer 5 0 0/5 simple — ask / answer not parsed
swap 4 4 full
socket 4 0 extension, out of scope
select 4 0 0/4 — select command not parsed (only compiler stub)
relativePositionalExpression 4 0 0/4 — first in, last in, random in collection-relative expressions missing
reactive-properties 4 0 0/4 — property-reactivity runtime not present
log 4 4 full
resize 3 0 0/3 — on resize synthetic event missing
logicalOperator 3 0 0/3 — async-safe and/or promise propagation absent
init 3 1 partial
focus 3 0 0/3 — focus / blur commands not wired
closest 3 1 partial
show 2 2 full
measure 2 0 0/2 — measure command stubbed
asyncError 2 0 0/2 — async error propagation
settle 1 1 full
scoping 1 1
queryRef 1 0 <sel/> selector refs — tokenizer skips
objectLiteral 1 0 JS-object literal in hyperscript expression missing
js 1 1 js ... end inline JS — we parse but don't emit real JS (no JS host)
in 1 0 in collection op missing
cookies 1 0 cookies.name magic symbol missing
attributeRef 1 0 @attr attribute-ref expressions

Categories with 0 simple-clean coverage (highest priority): asExpression (17), splitJoin (7), pick (7), mathOperator (5), askAnswer (5), scroll (8), evalStatically (8), socket (4), select (4), relativePositionalExpression (4), reactive-properties (4), resize (3), logicalOperator (3), focus (3), measure (2), asyncError (2), queryRef (1), objectLiteral (1), in (1), cookies (1), attributeRef (1), live (23), bind (43), collectionExpressions (17).


3. Feature gaps (parser / compiler / runtime)

Commands

Command tokenizer parser compiler runtime Notes
add yes yes yes yes complete minus [@attr] / {css-block} forms (new 0.9.90 block-CSS added in b23da319 ✓)
remove yes yes yes yes as above
toggle yes yes yes yes inc. between and N-way cycle (commit 00bf13a2)
set yes yes yes yes array concat form (set a to a + [1]) untested
put yes yes yes yes most positional forms
append yes yes yes yes landed c8aab54d
prepend no no no no not a 0.9.90 core command — only insert before via put, ignore
transition yes yes yes yes inc. possessives
show / hide yes yes yes yes when clause on hide (new 0.9.90) untested
wait / settle yes yes yes yes via perform IO suspension
send / trigger yes yes partial send lands in compiler dispatch; trigger alias
call / get yes yes yes partial call functions that return promises stubbed
log yes yes partial behavior OK
fetch yes yes yes yes 11/23 passing; non-2xx throw + as Response + do not throw missing
throw yes yes yes yes raise-based
continue / break yes yes yes yes landed f200418d
return / exit yes yes yes yes guard-based (commit 97818c6d)
halt yes yes yes yes modes landed 922e7a78
repeat yes yes yes yes forever, while, until, times, for-in, index
for yes yes yes yes alias of repeat for
if / unless / else / end / then / with yes yes yes yes unless recognized but not parsed as separate — unchanged from prior audit
js yes (no) (no) (no) embedded JS — we can't execute JS without a JS host
go yes yes yes yes basic go to <path>; go to the top/bottom of deprecated in 0.9.90 — replaced by scroll to
scroll yes yes yes partial scroll to the top/bottom of — parser has 2 hits, but 0/8 simple tests pass — runtime likely does not dispatch all forms
push/pull state no no no no history API — not in 0.9.90 core docs either
pick yes yes yes yes landed 41cfa562; 0/7 simple — pick command needs item/char/regex runtime
render yes yes parser has 8 hits but NO compiler emit — render + <template> interpolation not implemented
make yes yes yes yes hs-make + hs-make-object
increment / decrement yes yes yes yes full
measure yes yes yes partial 0/2 — runtime stub incomplete
tell yes yes partial parses, compiler handles single-cmd tell
take yes yes yes yes full
focus yes yes parser has 2 hits; no compiler emit, 0/3 simple pass
blur no no no no 0.9.90 addition, absent
hide the / show the alt grammar variant; not tracked separately
beep! yes yes yes yes (runtime no-op)
trim / cut / copy / paste no no no no clipboard magic symbols + commands absent
media no no no no not in 0.9.90 core
dialog no keyword yes yes partial parser parses dialog-open/close forms, 5/10 simple pass; commit 802ccd23 recent
morph yes yes yes yes landed 5b0c8569; 4/10 simple
empty / clear yes yes yes yes landed 802ccd23
reset yes yes yes yes landed (form reset 9d246f5c)
swap yes yes partial tokenizer has it; compiler dispatch missing — 4/4 simple currently pass? (verify)
open / close yes yes yes partial dialog/details/popover/fullscreen variants
select yes yes partial partial parser + compiler dispatch; select command 0/4 simple — runtime not complete
ask / answer no no no no 0/5 simple — new 0.9.90, absent
speak no no no no 0.9.90 addition, absent (Web Speech API)
breakpoint no no no no 0.9.90 addition, absent
view transition no no no no 0.9.90 addition, absent
intercept no no no no 0.9.90 service-worker DSL, out of scope

Expressions / operators

Expression Status Notes
me, my, it, its, result, event, detail, target yes tokenizer & parser
that no not in tokenizer — uncommon, low priority
element partial parser has 1 hit
body, document, window no not tokenized — high-value gap for e.g. on resize from window
you, yourself, sender partial you/sender tokenized, not parsed
response no fetch response magic — used in error handlers
clipboard, selection no 0.9.90 additions
first/last/random in partial tokens; random has 1 parser hit — 0/4 relativePositionalExpression tests pass
closest yes 1/3 simple
next, previous yes tokenized
parent of, children of no not tokenized — parent/children expressions missing
class ref (.foo), id ref (#foo) yes tokenizer handles
selector ref (<sel/>) partial tokenizer has < but not /> closer — 88 JSON tests use this
attribute ref (@attr) yes but [@attr] bracket form deprecated — we likely have mixed support
style ref (*prop) yes parser has 7 hits
CSS block ({color: red}) no 19 JSON tests use this
string interpolation ${...} no 21 JSON tests use this
time refs (1s, 1ms) yes parse-duration
math + - * / yes via evaluator
mod yes tok 0/5 simple — runtime doesn't propagate promises
abs no tok absent
floor/ceil/round no absent as hyperscript keywords (only via JS interop)
is / is not / == yes
matches / contains yes inc. ignore-case (ef5faa6b)
starts with / ends with yes parser has both with ignore-case variants
between ... and ... yes 9 parser hits; may not cover comparison form
precedes / follows yes 3 parser hits each
ignoring case yes 1 tokenizer hit; needs coverage per-primitive
in / not in yes-partial 3 tests
empty (predicate) yes
exists partial dropped short-circuit (ef5faa6b); 1 parser hit
no <expr> partial 1/5 simple
not, and, or yes short-circuit async missing → 0/3 logicalOperator simple
async and/or no 0/3 logicalOperator simple; needs promise propagation
some partial tokenized
collection: where yes tok 1 parser hit — 0 working simple tests
collection: sorted by / sorted by desc yes tok runtime hs-sorted-by / -desc present, not wired from parser for all forms
collection: mapped to yes tok
collection: split by / joined by yes tok runtime hs-split-by / hs-joined-by present — 0/7 splitJoin simple
type conversion as Foo partial 4/17 asExpression simple — JSONString / FormEncoded / **pipe `
pipe ` ` for conversions no
ternary / if ... else ... expression partial command-form only
object literal {key: val} partial 0/1 — special parser

Event syntax / feature forms

Feature Status Notes
on <event> yes core
on every <event> yes hs-on-every
on <event> from <target> yes inc. window / document textual — but tokenizer lacks window/document
event queueing (queue all/none/first/last) no 0 parser hits for queue — new in 0.9.90-era polish
debounced at <duration> no 0 parser hits
throttled at <duration> no 0 parser hits
halt the event / halt bubbling / halt default yes mode form in parser line 1953
on <event> when <cond> yes when-cond parser
on intersection / on mutation observers no needs IntersectionObserver/MutationObserver wiring
on resize (synthetic) no 0/3 resize tests; ResizeObserver adapter missing
on first <event> no one-shot modifier
on load / init yes in feat parser
on <event> in <selector> partial delegation
behavior Name ... install <Name> yes (parsed) behavior parsing at line 2390+
def name(args) ... end yes (commit 9d246f5c)
worker no extension, out of scope
live feature partial tokenizer has live, parser 3 hits; no reactivity runtime → 0/23 live simple
bind feature no tokenizer + parser both 0 hits — biggest single feature gap (44 upstream tests)
when <expr> changes partial changes is tokenized; reactive wiring missing

Scope

Feature Status Notes
locals (set x ...) yes full
element-scope (me.x, my x) yes my/your alias
globals ($x) yes
dotted names yes
local x partial tokenized
DOM-scope ^var yes 23/25 simple — strong coverage

4. Test complexity buckets we're NOT running

Bucket Upstream count Our coverage What's missing
simple 469 ~454 via behavioral (real) + ~89 stubs 15 skipped for pattern reasons
evaluate 125 0 as evaluate-typed tests; some merged into behavioral DOM-mutation-after-eval tests — need real browser, not sandbox
run-eval 83 0 distinct Mixed run+eval — possible via sandbox with state reads
promise 57 partial via perform Async promise chains — we have IO suspension but no sinon stub
eval-only 39 partial Pure expression tests — easy to add (just run eval-hs and assert result)
script-tag 36 0 <script type="text/hyperscript"> bootstrap path — we don't simulate the script-tag loader
sinon 17 0 Fetch stubs — our fetch tests mock via host-* server stubs, not sinon. 11/23 fetch pass, but the 17 sinon-specific tests aren't run
dialog 5 partial <dialog> element's showModal() / close() — needs dialog API mock

What each bucket would need:

  • eval-only and run-eval — cheapest wins. These just need eval-hs with result assertion. Missing infrastructure: none (we already have this in test-framework.sx). Gap is that the extractor tagged them eval-only so they weren't included in the "simple" migration.
  • evaluate — needs DOM mutation → DOM query. Our sandbox has mock DOM so most should be portable.
  • script-tag — needs script-tag loader: scan HTML for <script type="text/hyperscript">, compile and run. Separate from _= attribute bootstrap. Not hard — just a hook in integration.sx.
  • sinon — needs fetch mocking per-test. We have host-* server stubs globally. Fix: extend hs-fetch mock to support per-test route registration (some already done in 673be857).
  • dialog — needs HTMLDialogElement.showModal() / .close() + returnValue attribute. Minimal mock.
  • promise — most already work via perform. The ones that don't involve Promise.resolve(x) wrapping in JS (call functions that return promises). Needs awaiter mock.

5. Pattern classes the tokenizer skips

Global JSON counts (from html + check + action concatenation):

Pattern Upstream tests using it Upstream feature Our parser
[@attr="val"] bracket attribute 6 Deprecated in 0.9.90 — attribute-access/mutation syntax tokenizer doesn't emit [@ pair — 4 add, 4 toggle, 1 remove tests skipped
${...} template interpolation 21 String interpolation in expressions and templates Not tokenized — blocks all live/liveTemplate/render tests (~35 tests)
<sel/> selector ref 88 Inline CSS-selector reference as a value Tokenizer has < but no closing-slash detection
{prop:val;...} CSS block 19 CSS-property block in add/remove/toggle Tokenizer has {/} but no CSS-block parsing (partially landed b23da319 for add; not for remove/toggle)

Parser surface:

  • Attribute ref @foo — yes (as expression), 1/1 simple attributeRef test still 0 pass → compiler/runtime gap
  • Selector ref <sel/> — tokenizer returns "op" for /, parser has no rule to assemble as a single value
  • String interp ${x} — tokenizer returns "str" but doesn't split on ${...}; no parser for expression embedding
  • CSS block — tokenizer consumes {/} as punctuation; no recognition of CSS-property list inside

Recommendation:

  • [@attr] — since 0.9.90 deprecated it, don't invest
  • ${...} — required for live, liveTemplate, render, bind. High priority
  • <sel/> — 88 tests blocked; large ROI. Tokenizer change + parser rule
  • {css} — already partially done; extend to remove / toggle

6. Summary table

Category Upstream Impl (parse+compile+run) Simple tests passing Gap class
add 19 yes (minus [@/{css} forms) 18 skip patterns
remove 14 yes 12 skip patterns
toggle 30 yes (inc. between/cycle) 28 skip patterns
set 25 yes 24 array-concat
put 38 yes 37 complex positionals
append 13 yes 13
transition 23 yes 22
show 2 yes 2
hide 14 yes 14
wait 7 yes 7
send/trigger 8 yes 8
call 6 partial 5 promise await
fetch 23 partial 11 non-2xx throw, do not throw, pipe `
throw/catch/finally incl. def yes
if/unless/else 19 yes 18
repeat 30 yes 23 index, by step
for yes
def 27 partial 3 async/catch variants
behavior/install parses
init 3 partial 1 immediate init variants
increment/decrement 20 yes 20
take 12 yes 12
tell 10 partial 10 multi-stmt tell
halt 7 yes 6
empty/clear 13 yes 12
reset 8 yes 8
swap 4 partial-runtime 4
open/close partial dialog/popover
dialog 10 partial 5 dialog API mock
morph 10 yes 4 element-target forms
focus 3 no 0 not wired through compile
blur no absent
scroll 8 partial 0 runtime dispatch incomplete
measure 2 partial 0 runtime stub
pick 7 partial 0 runtime item/regex modes
select 4 partial 0 select-command runtime
bind 44 no 1 reactivity runtime
live 23 no 0 reactivity runtime
when-changes 41 partial 5 reactivity deps
reactive-properties 4 no 0 property reactivity
liveTemplate 10 no 7 ${} + <template>
component 19 partial 14 custom-element register
dom-scope (^var) 25 yes 23
collectionExpressions 22 partial 5 where/sorted/mapped/split/joined
comparisonOperator 40 partial 4 starts/ends/between/ic async
logicalOperator 3 partial 0 async and/or
mathOperator 5 partial 0 mod/abs + async
asExpression 17 partial 0 JSONString/FormEncoded + pipe
askAnswer 5 no 0 ask/answer keywords
speak no Web Speech API
breakpoint no devtools breakpoint
intercept no service-worker (out of scope)
view transition no View Transitions API
resize 3 no 0 ResizeObserver synthetic event
bootstrap 14 partial 2 lifecycle events + cleanup()
evalStatically 8 no 0 _hyperscript.evaluate(...) static API
relativePositionalExpression 4 partial 0 first in xs/last in xs
assignableElements 8 partial 3
go 5 partial 3 go to the top of deprecated → scroll to
in (operator) 1 partial 0
no (predicate) 5 partial 1
closest 3 partial 1
cookies 1 no 0 cookies.name magic
queryRef 1 no 0 <sel/> as value
objectLiteral 1 partial 0 JS-object literal expr
attributeRef 1 partial 0 @attr as expression
js (inline) 1 parsed 1
splitJoin 7 runtime-only 0 parser wiring
asyncError 2 partial 0 async catch paths
parser 7 partial 3 parse-error events
scoping 1 yes 1
settle 1 yes 1

Top 5 priority gaps (by upstream test count × foundational weight)

  1. Reactivity runtime (bind + live + when...changes + reactive-properties) — blocks 112 upstream tests (44 + 23 + 41 + 4). Requires: dependency-tracked evaluation, signal graph per-element, change propagation, two-way binding with dedup. Foundational: unlocks templates and components. Needs ${} string interp in parser.
  2. ${...} interpolation + <template> render + #for/#if/#else control flow (render + liveTemplate) — blocks ~35 tests but is foundational for templating. Tokenizer change required (${...} inside strings); new render-command runtime; template AST with # tags.
  3. Comparison + collection + conversion operator completion (comparisonOperator + collectionExpressions + asExpression + splitJoin) — 86 tests. Already have primitives (hs-split-by, hs-joined-by, hs-sorted-by, hs-starts-with-ic, etc.) but parser doesn't wire them. Lower-risk plumbing work.
  4. Missing simple commands: focus/blur, scroll to, ask/answer, speak, breakpoint, select (command), measure — ~25 tests, and small per-command scope. focus/blur + scroll to alone unblocks 11 tests. ask/answer need host-prompt/host-alert/host-confirm IO effects.
  5. Event syntax modifiers: queue, debounced at, throttled at, on first, on resize, on intersection, on mutation — 0 parser hits for any of these. Each is small in scope but together they affect test realism. on resize with ResizeObserver synth is the most valuable (0.9.91 bug in this area).

Secondary gaps (foundational but lower-count)

  • body/document/window magic symbols — blocks on resize from window and many scroll tests. Add to tokenizer.
  • parent of / children of expressions — not tokenized. Easy win.
  • Pipe operator | — required for 0.9.90 chained conversions.
  • fetch non-2xx throws + do not throw + as Response — 0.9.90 breaking change; 12 fetch tests blocked.
  • cleanup() API + lifecycle events — needed for bootstrap suite (14 tests) and htmx 4 interop.
  • Script-tag loader — 36 tests in script-tag bucket blocked for want of a <script type="text/hyperscript"> parser entry.

Non-priorities

  • worker, socket, eventsource — extensions, out of scope
  • intercept — service-worker DSL, out of scope
  • view transition — browser API, nice-to-have
  • [@attr] bracket form — deprecated upstream, don't invest
  • js ... end — can't execute real JS without a JS host; we parse but can't semantically emit