Skip to content

Fix router // paths, ApiError.fromJson, range/search/SlotArray and inViewY - #10

Merged
soerenmeier merged 5 commits into
mainfrom
review-fixes-small
Oct 30, 2026
Merged

soerenmeier merged 5 commits into
mainfrom
review-fixes-small

Conversation

@noah-meierlabs

Copy link
Copy Markdown
Contributor

A smaller subset of #9. Fixes point 2, the ApiError.fromJson part of 12, points 16–18, and the inViewY part of 21 from #6. No API surface changes.

  • 2, // paths (chuchi): pushState/replaceState get location.origin + uri instead of the bare uri. A same-host link like https://site.ch//evil.com/x used to throw a SecurityError, because //evil.com/x is read as another host. The URL in the address bar stays the same. crelte has the same bug.
  • ApiError.fromJson (core): returns an OTHER error for null, numbers or {} instead of crashing. The XHR helpers pass null when the error body isn't JSON.
  • 16 range (utils): fixes the operator-precedence bug in the length. range(0, 10, 2) → [0,2,4,6,8], and range(-5, 5, 2) / range(5, 2) no longer throw.
  • 17 searchScore (utils): the right distance uses searchSpaceRight, so exact matches rank better: foo/foo = 1, foo/foobarbaz = 7 (before: both 4).
  • 18 SlotArray (utils): removing a slot twice no longer hands out the same index twice.
  • inViewY (legacy): true for an element taller than the viewport that covers it.

Testing

  • build, check-fmt, check, lint and check-jsr pass.
  • Router in headless Chromium: a same-host //evil.example/x link works. On main it throws SecurityError: Failed to execute 'pushState'…. Normal links behave as before.
  • Node scripts for fromJson, range, searchScore and SlotArray.

🤖 Generated with Claude Code

noah-meierlabs and others added 5 commits October 30, 2026 05:56
The history got the uri alone, and a path like //evil.com/x is read as
another host, so pushState threw a SecurityError after the click was
already prevented and the request was left half open. The history now
gets the full url on the current origin.

Refs #6

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…uble remove

range computed its length as end - start / step, so range(0, 10, 2) had
holes and range(-5, 5, 2) or range(5, 2) threw. It now collects the
values in a plain array.

searchScore measured the right distance with searchSpaceLeft, back over
the match itself, so an exact match scored like a match inside a longer
word. It now uses the unused searchSpaceRight from the last matched
character: foo/foo is 1, foo/foobarbaz is 7.

Removing a SlotArray slot twice added it to free twice, so two later
pushes got the same index and the first value was overwritten.

Refs #6

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The XHR helpers pass null when the error response was not json, which
threw "Cannot use 'in' operator" instead of returning an ApiError. Such
values now become OTHER.

Refs #6

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An element that covers the whole viewport has its top above and its
bottom below the viewport, so neither check matched.

Refs #6

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Collecting the values with push was about 3-4x slower than the old
preallocated array for 1000+ values. range now preallocates with the
correct length, which is as fast as before. A step of 0 or below
returns an empty array instead of looping forever.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@soerenmeier
soerenmeier merged commit ed3ad18 into main Oct 30, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants