Skip to content

fix(documenting-html): use unique element IDs, keep same-name endpoints and escape search results - #54

Merged
CypherPotato merged 3 commits into
mainfrom
fix/documenting-html-ids-search-embed
Oct 16, 2026
Merged

CypherPotato merged 3 commits into
mainfrom
fix/documenting-html-ids-search-embed

Conversation

@CypherPotato

Copy link
Copy Markdown
Member

What

Fixes in the HTML documentation exporter around element IDs, endpoint de-duplication, the search dialog and embeds.

  • IDs: TransformId stripped every non-alphanumeric character, producing empty IDs (!!!), IDs that collide between an endpoint and a group (Users), and IDs that are invalid CSS selectors (2FA setup becomes 2FAsetup). The scroll spy used querySelector(href), which threw SyntaxError and aborted the whole page script. Endpoint IDs are now derived from method and route path with an ep- prefix, group IDs get a grp- prefix, and a short hash is appended so IDs are never empty or colliding. The scroll spy uses getElementById.
  • De-duplication: endpoints were de-duplicated by Method Name, so GET /items/a and GET /items/b annotated with the same name collapsed into one. They are now de-duplicated by Method Path, which still removes a route declared twice.
  • Embeds: the copy-embed button now carries the endpoint ID and embed-target resolves by ID, falling back to the name so embeds copied earlier keep working. Same-name endpoints can now be embedded separately.
  • Search: results were built with innerHTML from text read back through textContent, so a name such as List<T> or one containing markup became DOM. Values are now escaped, including around the <mark> highlights.

Verification

This extension has no test project, so I verified it with an external probe (not included in the PR): a small server with seven endpoints that cover empty, duplicate and invalid IDs, same-name routes and hostile names, driven by jsdom.

Build Checks passed
main 9 / 20
this branch 20 / 20

dotnet build of Sisk.Documenting.Html succeeds.

Notes and limitations

  • Anchors change: existing links such as #Users no longer resolve, because the ID scheme changed. Embed URLs that use the endpoint name still work through the fallback.
  • Not included: a hardening of the embed-target handler (r validation and JS encoding). I could not reproduce those problems on main: GetDocumentationUrl already requires an absolute http/https r, and the value is already passed through JavaScriptEncoder. The probe checks these cases and they pass on both builds.
  • Unknown embed-target values still return the existing console.error script; I did not change that response.

…ts and escape search results

Endpoint IDs were built by stripping every non-alphanumeric character from
the endpoint name, which produced empty IDs ("!!!"), IDs that collide
between an endpoint and a group ("Users"), and IDs that are invalid CSS
selectors ("2FA setup" -> "2FAsetup"). The scroll spy then threw a
SyntaxError from querySelector and aborted the page script.

- Derive endpoint IDs from method and route path with an ep- prefix, group
  IDs with a grp- prefix, and append a short hash so IDs are never empty or
  colliding. The scroll spy resolves them with getElementById.
- Deduplicate endpoints by method and route path instead of method and name,
  so distinct routes sharing a name are no longer dropped.
- The copy-embed button now carries the endpoint ID, and embed-target looks
  endpoints up by ID, falling back to the name for previously copied embeds.
- Escape endpoint names, paths, groups and methods before they are written
  to the search results with innerHTML.

Note: anchors produced by the previous ID scheme (for example #Users) no
longer resolve.
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 14, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-16T03:12:41.284887Z 1e4553d New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4f8761fe00

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +862 to +863
string hash = Convert.ToHexString ( SHA256.HashData ( Encoding.UTF8.GetBytes ( unsafeId ) ) ) [..8].ToLowerInvariant ();
return $"{new string ( unsafeId.Where ( char.IsLetterOrDigit ).ToArray () )}-{hash}";

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Use a non-truncated digest for endpoint IDs

The eight-character SHA-256 slice retains only 32 bits, so it cannot guarantee the uniqueness this change relies on. For example, Get ^/x--_-_-__-_-_____--z$ and Get ^/x__-___--____-_--_-z$ both sanitize to Getxz and have the same retained digest (dd883b3e); these are valid distinct regex route patterns. Documenting both produces duplicate summary IDs, causing navigation, search, and embed lookup to resolve only the first endpoint. Retain the full digest (or resolve collisions) when constructing the ID.

Useful? React with 👍 / 👎.

@CypherPotato
CypherPotato merged commit c58d53a into main Oct 16, 2026
5 checks 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.

1 participant