Skip to content

fix: restrict guard type and tighten typed state for router.go()#27

Merged
sv2dev merged 2 commits intomainfrom
fix/guard-type-restriction
Apr 1, 2026
Merged

fix: restrict guard type and tighten typed state for router.go()#27
sv2dev merged 2 commits intomainfrom
fix/guard-type-restriction

Conversation

@sv2dev
Copy link
Copy Markdown
Owner

@sv2dev sv2dev commented Apr 1, 2026

Summary

  • Restricts the "?" guard key in Routes to function type only, preventing accidental assignment of a sub-routes object as a guard
  • Tightens typed router.go() to omit the state option entirely when the target route handler declares no state type

Changes

  • Routes["?"] is now typed as Resolve<T, S> | undefined instead of Routes<T, S> | Resolve<T, S>, closing the type hole that allowed objects to be assigned as guards
  • router.go() overload for routes without a required state now uses Omit<NavMeta<...>, "state"> so that passing state is a compile-time error
  • Added a null-guard in route-resolver.ts after the virtual route lookup to handle undefined route values safely
  • visibilitychange listener moved from window to document (included via the merged bundle-size improvements)

Changelog

Fixed

  • Route guard property "?" is now restricted to function type only, preventing accidental assignment of sub-routes objects (closes Route guard properties should not only allow a guard function  #10)
  • router.go() no longer exposes the state option for routes where no state type is declared, preventing accidental state passing on untyped routes

Summary by CodeRabbit

  • Bug Fixes

    • Navigation now rejects a state option when routing to handlers that don't declare state, preventing accidental state passing.
  • Tests

    • Added type-safety tests to verify state parameter validation for routes without state types.
  • Documentation

    • Updated changelog with the state-option fix and added documentation for a new PR automation skill that documents release workflow behavior.

Prevent passing state to router.go() for routes where no state type is
declared. Previously, the optional NavMeta still exposed the state field,
allowing accidental state usage on untyped routes.
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 1, 2026

Caution

Review failed

Pull request was closed or merged during review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 9ce173f0-6ef4-478d-be57-51e9bdbb5679

📥 Commits

Reviewing files that changed from the base of the PR and between 5bec992 and 8793c4b.

📒 Files selected for processing (2)
  • .claude/skills/pr/SKILL.md
  • CHANGELOG.md
✅ Files skipped from review due to trivial changes (1)
  • CHANGELOG.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • .claude/skills/pr/SKILL.md

Walkthrough

Adds a new PR automation skill for creating release and doc PRs via gh, updates CHANGELOG to document router state handling, and tightens Router.go TypeScript overloads plus a test to prevent passing state for routes without declared state types.

Changes

Cohort / File(s) Summary
PR Automation Skill
\.claude/skills/pr/SKILL.md
New skill that inspects commits/diffs vs main, auto-commits untracked src/ changes, detects release bumps from package.json, validates/promotes CHANGELOG.md Unreleased entries, updates package READMEs if needed, reorders commits so version-bump is tip for release PRs, and creates a templated PR via gh.
Changelog
CHANGELOG.md
Added Unreleased → Fixed entry: router.go() no longer exposes a state option for routes without a declared state type.
Router Types
packages/esroute/src/router.ts
Refined Router.go<P> overload for routes that do not require state: opts now typed as Omit<NavMeta<StateOf<...>>, "state"> to exclude state at compile time.
Router Tests
packages/esroute/src/router.spec.ts
Added a type-level test using // @ts-expect-error`` to assert passing state to `router.go()` for an untyped route is a compile-time error.

Sequence Diagram(s)

sequenceDiagram
  participant Dev as Developer
  participant Repo as Local Repo (filesystem)
  participant Git as Git
  participant GH as GitHub (gh CLI)
  participant Files as Docs/Package files

  Dev->>Repo: run PR skill
  Repo->>Git: inspect commits & diff vs main
  alt no commits to PR
    Git-->>Repo: exit early
  else commits present
    Git->>Files: detect uncommitted src/ changes
    Files-->>Git: auto-commit docs or src per guidance
    Git->>Files: read packages/*/package.json for version bumps
    Files-->>Git: read/update CHANGELOG.md and READMEs
    Git->>Git: reorder commits if release bump (version-bump as tip)
    Git->>GH: push branch (maybe --force-with-lease)
    GH->>GH: create PR with templated title/body
    GH-->>Dev: return PR URL
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

Poem

🐰 Hopping through commits with a clever plan,

I nudge changelogs, README, then push with a fan.
Types tucked tidy, state tucked away,
A PR is born—hip hop hooray! ✨

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 inconclusive)

Check name Status Explanation Resolution
Linked Issues check ❓ Inconclusive PR summary references fixing issue #10 about guard type restriction, but the raw_summary and files provided do not show implementation of the guard type fix mentioned in PR objectives. Verify that guard type restriction changes (Routes["?"] modification) are included in actual code changes not shown in raw_summary, or clarify scope discrepancy with PR objectives.
Out of Scope Changes check ❓ Inconclusive PR objectives mention guard type restriction, null-guard in route-resolver.ts, and visibilitychange listener changes, but raw_summary only shows router.go() state typing and changelog/skill file updates. Clarify whether all mentioned changes in PR objectives are included; some appear missing from the provided raw_summary file list.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed Title clearly summarizes the main changes: restricting guard types and tightening typed state for router.go(), both primary objectives of this PR.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/guard-type-restriction

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
.claude/skills/pr/SKILL.md (1)

109-123: Git reorder logic lacks error handling and edge-case guards.

The cherry-pick reorder sequence can fail silently or leave the branch in an inconsistent state:

  1. Line 116: If VERSION_COMMIT contains regex metacharacters (unlikely but possible with commit hashes), grep -v could misbehave. Using grep -Fxv would be safer.
  2. Line 120: If OTHER_COMMITS is empty (version bump is the only commit), xargs -r is correct on GNU systems but -r is not POSIX and won't work on macOS. Consider adding a check.
  3. Lines 119-122: No error handling—if any cherry-pick fails (e.g., conflicts), the branch is left in a broken state with no recovery path.

Consider adding explicit checks and error trapping:

Suggested improvements
 # Rebuild branch on top of main with version bump last
 git checkout -B ${CURRENT_BRANCH}_reorder main
-echo "$OTHER_COMMITS" | xargs -r git cherry-pick
+if [ -n "$OTHER_COMMITS" ]; then
+  echo "$OTHER_COMMITS" | xargs git cherry-pick || { echo "Cherry-pick failed"; exit 1; }
+fi
 git cherry-pick $VERSION_COMMIT
 git branch -M ${CURRENT_BRANCH}_reorder $CURRENT_BRANCH
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.claude/skills/pr/SKILL.md around lines 109 - 123, The reorder script around
VERSION_COMMIT/OTHER_COMMITS and CURRENT_BRANCH lacks portability and error
recovery; update it to (1) compute OTHER_COMMITS using grep -Fxv to avoid regex
issues with VERSION_COMMIT, (2) detect an empty OTHER_COMMITS and skip the
xargs/cherry-pick step instead of relying on GNU xargs -r, (3) wrap the rebuild
sequence that uses git checkout -B, git cherry-pick and final git branch -M in a
safe trap/try sequence that on any error runs git cherry-pick --abort (if in
progress) and resets the branch (e.g., git reset --hard or git checkout back to
CURRENT_BRANCH) to avoid leaving the repo in a conflicted state, and (4) surface
non-zero exit codes so callers know when reorder failed. Reference the
variables/functions VERSION_COMMIT, OTHER_COMMITS, CURRENT_BRANCH and the git
commands git checkout -B, git cherry-pick, git branch -M when applying these
changes.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.claude/skills/pr/SKILL.md:
- Around line 17-19: The git diff command currently targets a non-existent root
src/ directory (`git diff main...HEAD -- src/`), so change that invocation to
target the actual package source directories by replacing `src/` with
`packages/esroute/src/ packages/esroute-lit/src/` (i.e., use `git diff
main...HEAD -- packages/esroute/src/ packages/esroute-lit/src/`) so source
changes in the monorepo are correctly shown.

---

Nitpick comments:
In @.claude/skills/pr/SKILL.md:
- Around line 109-123: The reorder script around VERSION_COMMIT/OTHER_COMMITS
and CURRENT_BRANCH lacks portability and error recovery; update it to (1)
compute OTHER_COMMITS using grep -Fxv to avoid regex issues with VERSION_COMMIT,
(2) detect an empty OTHER_COMMITS and skip the xargs/cherry-pick step instead of
relying on GNU xargs -r, (3) wrap the rebuild sequence that uses git checkout
-B, git cherry-pick and final git branch -M in a safe trap/try sequence that on
any error runs git cherry-pick --abort (if in progress) and resets the branch
(e.g., git reset --hard or git checkout back to CURRENT_BRANCH) to avoid leaving
the repo in a conflicted state, and (4) surface non-zero exit codes so callers
know when reorder failed. Reference the variables/functions VERSION_COMMIT,
OTHER_COMMITS, CURRENT_BRANCH and the git commands git checkout -B, git
cherry-pick, git branch -M when applying these changes.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 9be90e05-67e6-47ed-8cff-49d05a961b52

📥 Commits

Reviewing files that changed from the base of the PR and between 8c3d8e6 and 5bec992.

📒 Files selected for processing (4)
  • .claude/skills/pr/SKILL.md
  • CHANGELOG.md
  • packages/esroute/src/router.spec.ts
  • packages/esroute/src/router.ts

Comment on lines +17 to +19
- `git log main..HEAD --oneline` — commits on this branch
- `git diff main...HEAD -- src/` — what changed in source
- `git diff main...HEAD -- CHANGELOG.md README.md packages/esroute/README.md packages/esroute-lit/README.md` — what docs already changed
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify the source directory structure
fd -t d -d 2 'src' | head -20

Repository: sv2dev/esroute

Length of output: 40


🏁 Script executed:

#!/bin/bash
# Explore the overall structure of the repo
tree -L 2 -d --dirsfirst 2>/dev/null || find . -maxdepth 2 -type d -not -path '*/\.*' | sort | head -30

Repository: sv2dev/esroute

Length of output: 123


🏁 Script executed:

#!/bin/bash
# Read the SKILL.md file to see the actual commands
cat .claude/skills/pr/SKILL.md

Repository: sv2dev/esroute

Length of output: 5953


🏁 Script executed:

#!/bin/bash
# Check if packages/esroute/src exists
fd -t d 'src' packages/ 2>/dev/null || echo "No src found in packages"

Repository: sv2dev/esroute

Length of output: 105


Fix the diff command to target the actual source directories in the monorepo.

Line 18 uses git diff main...HEAD -- src/ but source files are in packages/esroute/src/ and packages/esroute-lit/src/. The root-level src/ directory does not exist, so this diff will always be empty even when source code changes. Update the command to:

git diff main...HEAD -- packages/esroute/src/ packages/esroute-lit/src/
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.claude/skills/pr/SKILL.md around lines 17 - 19, The git diff command
currently targets a non-existent root src/ directory (`git diff main...HEAD --
src/`), so change that invocation to target the actual package source
directories by replacing `src/` with `packages/esroute/src/
packages/esroute-lit/src/` (i.e., use `git diff main...HEAD --
packages/esroute/src/ packages/esroute-lit/src/`) so source changes in the
monorepo are correctly shown.

@sv2dev sv2dev force-pushed the fix/guard-type-restriction branch from 5bec992 to 8793c4b Compare April 1, 2026 22:59
@sv2dev sv2dev merged commit 757e168 into main Apr 1, 2026
1 of 2 checks passed
@sv2dev sv2dev deleted the fix/guard-type-restriction branch April 1, 2026 23:01
@coderabbitai coderabbitai bot mentioned this pull request Apr 2, 2026
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.

Route guard properties should not only allow a guard function

1 participant