Skip to content

feat: add labelhash parsing and normalization utilities#1688

Merged
djstrong merged 7 commits intomainfrom
211-normalize-labelhash-for-ensrainbow-heal-request-2
Feb 27, 2026
Merged

feat: add labelhash parsing and normalization utilities#1688
djstrong merged 7 commits intomainfrom
211-normalize-labelhash-for-ensrainbow-heal-request-2

Conversation

@djstrong
Copy link
Contributor

@djstrong djstrong commented Feb 26, 2026

Lite PR

Tip: Review docs on the ENSNode PR process

Summary

  • Add parseLabelHash, parseEncodedLabelHash, and parseLabelHashOrEncodedLabelHash utilities to ensnode-sdk that normalize labelhash inputs to a canonical LabelHash (adds 0x prefix if missing, normalizes uppercase to lowercase, pads odd-length hex, validates 32-byte length).
  • Update EnsRainbowApiClient.heal() to accept LabelHash | EncodedLabelHash | string and normalize the input before making the API request; returns a HealBadRequestError if the input cannot be normalized.

Why

  • Closes #211 — callers previously had to supply a perfectly formatted labelhash; now the client handles common variants (missing 0x, uppercase hex, bracket-enclosed encoded labelhash) gracefully.

Testing

  • Comprehensive unit tests added for all three parse functions covering: valid inputs with/without 0x prefix, uppercase normalization, odd-length padding, bracket-enclosed format, and error cases (non-hex characters, wrong length, malformed brackets, empty string).
  • Existing EnsRainbowApiClient tests updated to cover the new normalization/error path.

Notes for Reviewer (Optional)

  • parseLabelHashOrEncodedLabelHash is intentionally more lenient than isEncodedLabelHash — it accepts [0xhash] (non-canonical bracket format) in addition to [hash].
  • Normalization errors surface as HealBadRequestError rather than thrown exceptions, keeping the heal() return type consistent.

Pre-Review Checklist (Blocking)

  • This PR does not introduce significant changes and is low-risk to review quickly.
  • Relevant changesets are included (or are not required)

- Introduced `parse-labelhash.ts` with functions to normalize label hashes and encoded label hashes.
- Added tests for label hash parsing in `parse-labelhash.test.ts`.
- Updated `ens/index.ts` to export the new parsing functions.
- Enhanced `EnsRainbowApiClient` to accept and normalize label hashes before processing requests.
Copilot AI review requested due to automatic review settings February 26, 2026 09:32
@vercel
Copy link
Contributor

vercel bot commented Feb 26, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
admin.ensnode.io Ready Ready Preview, Comment Feb 27, 2026 5:33pm
ensnode.io Ready Ready Preview, Comment Feb 27, 2026 5:33pm
ensrainbow.io Ready Ready Preview, Comment Feb 27, 2026 5:33pm

@changeset-bot
Copy link

changeset-bot bot commented Feb 26, 2026

🦋 Changeset detected

Latest commit: 95621e8

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 19 packages
Name Type
@ensnode/ensnode-sdk Patch
@ensnode/ensrainbow-sdk Patch
ensadmin Patch
ensapi Patch
ensindexer Patch
ensrainbow Patch
fallback-ensapi Patch
@namehash/ens-referrals Patch
@ensnode/ensnode-react Patch
@namehash/namehash-ui Patch
@ensnode/ponder-metadata Patch
@ensnode/datasources Patch
@ensnode/ensnode-schema Patch
@ensnode/ponder-sdk Patch
@ensnode/ponder-subgraph Patch
@ensnode/shared-configs Patch
@docs/ensnode Patch
@docs/ensrainbow Patch
@docs/mintlify Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 26, 2026

📝 Walkthrough

Walkthrough

Adds label-hash parsing utilities and re-exports them; updates EnsRainbowApiClient.heal to accept and normalize a wider set of labelHash inputs (including bracket-encoded and odd-length hex), uses the normalized hash for cache keys and request URLs, and adds/updates tests for parsing and client-side validation/normalization.

Changes

Cohort / File(s) Summary
Parsing module & public export
packages/ensnode-sdk/src/ens/parse-labelhash.ts, packages/ensnode-sdk/src/ens/index.ts
New parsing utilities: parseLabelHash, parseEncodedLabelHash, parseLabelHashOrEncodedLabelHash. index.ts now re-exports these to expand the public API.
Parsing tests
packages/ensnode-sdk/src/ens/parse-labelhash.test.ts
Comprehensive tests covering normalization, optional 0x handling, padding (63→64 hex chars), bracket-encoded inputs, case normalization, and multiple invalid-input error cases.
EnsRainbow client logic & tests
packages/ensrainbow-sdk/src/client.ts, packages/ensrainbow-sdk/src/client.test.ts
heal signature widened to accept `LabelHash
Indexer tests updated
apps/ensindexer/src/lib/graphnode-helpers.test.ts
Tests adjusted to expect client-side normalization (padding 63-char inputs, lowercasing) and to reflect when fetch is called vs. when invalid inputs are rejected client-side.
Changeset / metadata
.changeset/normalize-labelhash-heal.md
Documents API change: broadened heal input handling, normalization behavior, and newly exported parsing utilities and HealBadRequestError.

Sequence Diagram(s)

sequenceDiagram
  participant Client as EnsRainbowApiClient
  participant Parser as parseLabelHashOrEncodedLabelHash
  participant Cache as LocalCache
  participant Network as Fetch/API

  Client->>Parser: normalize(labelHashInput)
  Parser-->>Client: normalizedLabelHash or throw Error
  alt normalized
    Client->>Cache: lookup(normalizedLabelHash)
    Cache-->>Client: cachedResponse / miss
    alt miss
      Client->>Network: GET /heal/{normalizedLabelHash}
      Network-->>Client: response
      Client->>Cache: store(normalizedLabelHash, response)
      Client-->>Client: return response
    else cached
      Client-->>Client: return cachedResponse
    end
  else invalid
    Parser-->>Client: throws Error
    Client-->>Client: return/throw HealBadRequestError
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested labels

ensnode-internal

Poem

🐰 I hop through hex and brackets bright,
I pad the short, I lowercase the light,
I strip the brackets, add the 0x,
I hand a tidy hash to heal and fix,
Cache hums softly—rabbit's delight.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description check ✅ Passed The description follows the template with all required sections (Summary, Why, Testing, Notes, Checklist) properly completed with specific, actionable information.
Linked Issues check ✅ Passed The PR fully addresses issue #211 by implementing parseLabelHashOrEncodedLabelHash utility that normalizes labelhash formats and integrates it into heal() with HealBadRequestError handling for invalid inputs.
Out of Scope Changes check ✅ Passed All changes are scoped to adding label hash normalization utilities and updating heal() to use them; no unrelated refactoring or feature additions detected.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Title check ✅ Passed The title accurately describes the main change: adding labelhash parsing and normalization utilities to the codebase.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch 211-normalize-labelhash-for-ensrainbow-heal-request-2

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
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR introduces label hash parsing and normalization utilities to make the ENSRainbow SDK more user-friendly by accepting various labelHash input formats and automatically normalizing them.

Changes:

  • Added new parsing utilities (parseLabelHash, parseEncodedLabelHash, parseLabelHashOrEncodedLabelHash) that normalize labelHash inputs by handling missing 0x prefixes, uppercase hex characters, 63-char hex strings (odd-length padding), and bracket-enclosed encoded labelHashes
  • Updated EnsRainbowApiClient.heal() to accept LabelHash | EncodedLabelHash | string and perform client-side normalization and validation before making network requests
  • Added comprehensive test coverage for all normalization scenarios and error cases

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
packages/ensnode-sdk/src/ens/parse-labelhash.ts New utility module implementing labelHash parsing and normalization logic with three main functions and a custom error class
packages/ensnode-sdk/src/ens/parse-labelhash.test.ts Comprehensive test suite covering all normalization rules, edge cases, and error scenarios for the parsing utilities
packages/ensnode-sdk/src/ens/index.ts Export statement added to expose the new parsing utilities from the ENS module
packages/ensrainbow-sdk/src/client.ts Updated heal() method signature and implementation to accept flexible input formats, perform client-side normalization, and return errors for invalid inputs without making network calls
packages/ensrainbow-sdk/src/client.test.ts Added test cases for the new normalization behavior including uppercase, missing prefix, and encoded labelHash formats, plus verification that invalid inputs don't trigger network requests

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Contributor

@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: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/ensnode-sdk/src/ens/parse-labelhash.ts`:
- Around line 25-27: Remove the redundant `@returns` JSDoc tags in
packages/ensnode-sdk/src/ens/parse-labelhash.ts: for the JSDoc block around the
maybeLabelHash parameter (lines shown) and the other blocks at the indicated
locations (52-55, 73-76), delete the `@returns` lines that merely restate the
function summary so the docs are concise; keep the `@param` and `@throws` tags and
any `@returns` that add unique information, and ensure the remaining JSDoc text
still clearly describes the function behavior.
- Around line 8-13: Remove the custom InvalidLabelHashError class from
parse-labelhash.ts and replace any usages with plain Error instances: delete the
InvalidLabelHashError declaration and update all places that currently throw new
InvalidLabelHashError(...) (or reference that class) to throw new Error(...)
with the same message; ensure exports/imports are cleaned up so only plain Error
is used for parse failures (check functions like parseLabelHash or any callers
in this file/module and adjust their error handling accordingly).

ℹ️ Review info

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4da1d95 and c3ec3d0.

📒 Files selected for processing (5)
  • packages/ensnode-sdk/src/ens/index.ts
  • packages/ensnode-sdk/src/ens/parse-labelhash.test.ts
  • packages/ensnode-sdk/src/ens/parse-labelhash.ts
  • packages/ensrainbow-sdk/src/client.test.ts
  • packages/ensrainbow-sdk/src/client.ts

@vercel vercel bot temporarily deployed to Preview – admin.ensnode.io February 26, 2026 09:44 Inactive
@vercel vercel bot temporarily deployed to Preview – ensnode.io February 26, 2026 09:44 Inactive
@vercel vercel bot temporarily deployed to Preview – ensrainbow.io February 26, 2026 09:44 Inactive
Copy link
Contributor

@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: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/ensindexer/src/lib/graphnode-helpers.test.ts (1)

54-75: ⚠️ Potential issue | 🟠 Major

The "too short" test uses a valid labelHash and incorrectly mocks an API response.

The input 0x00ca5d0b4ef1129e04bfe7d35ac9def2f4f91daeb202cbe6e613f1dd17b2da0 is 64 hex chars (valid), not too short. Since parseLabelHash() validates client-side and the "too long" test correctly expects /Invalid labelHash length/i, either use an actually short input (e.g., 63 hex chars) or remove the API mock since client-side validation prevents the API call.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/ensindexer/src/lib/graphnode-helpers.test.ts` around lines 54 - 75, The
test for "too short" labelHash is using a valid 64-hex input and incorrectly
mocks fetch; update the test to trigger client-side validation by supplying an
actually short labelHash (e.g., 63 hex characters) when calling labelByLabelHash
so parseLabelHash runs and the fetch mock is not needed, or alternatively remove
the fetch mock and assert that labelByLabelHash rejects with /Invalid labelHash
length/i; reference labelByLabelHash and parseLabelHash to locate the validation
path to fix.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/ensindexer/src/lib/graphnode-helpers.test.ts`:
- Around line 77-92: Update the test that calls labelByLabelHash to also assert
that fetch was invoked with the normalized (lowercased) labelHash: keep the
existing mock and call to labelByLabelHash, then after awaiting the result add
an assertion that fetch was called with a URL or request body containing the
lowercased version of the input hash (use the same uppercased input string but
compare against its lowercase form) to verify labelByLabelHash performs
normalization before making the API call.
- Around line 70-75: The test for labelByLabelHash currently asserts that an
invalid-length labelHash rejects but doesn't verify fetch wasn't invoked; after
awaiting the rejection for labelByLabelHash("0x...65chars..."), add an explicit
assertion that the network helper was not called (e.g.,
expect(fetch).not.toHaveBeenCalled() or the project's mocked fetch helper) to
confirm validation occurs client-side and no fetch occurred. Ensure the fetch
mock is reset/available in this test so the not-to-have-been-called assertion is
reliable.
- Around line 94-101: The test for labelByLabelHash (the "throws an error for an
invalid labelHash missing 0x prefix and too long" case) claims client-side
validation but doesn't assert that no network call was made; after the await
expect(...).rejects.toThrow(...) add an assertion that the fetch mock was not
invoked (e.g., expect(globalThis.fetch).not.toHaveBeenCalled() or
expect(fetch).not.toHaveBeenCalled()) to explicitly verify fetch was not called.

---

Outside diff comments:
In `@apps/ensindexer/src/lib/graphnode-helpers.test.ts`:
- Around line 54-75: The test for "too short" labelHash is using a valid 64-hex
input and incorrectly mocks fetch; update the test to trigger client-side
validation by supplying an actually short labelHash (e.g., 63 hex characters)
when calling labelByLabelHash so parseLabelHash runs and the fetch mock is not
needed, or alternatively remove the fetch mock and assert that labelByLabelHash
rejects with /Invalid labelHash length/i; reference labelByLabelHash and
parseLabelHash to locate the validation path to fix.

ℹ️ Review info

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c3ec3d0 and 1dc8e2b.

📒 Files selected for processing (1)
  • apps/ensindexer/src/lib/graphnode-helpers.test.ts

…ndling and normalization

- Updated tests for `labelByLabelHash` to verify normalization of 63-char hex label hashes and proper error propagation for server responses.
- Refactored `parse-labelhash` functions to throw generic errors instead of custom `InvalidLabelHashError`, simplifying error handling across tests and implementations.
- Added additional test cases for empty strings and invalid formats in `parse-labelhash` tests.
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copilot AI review requested due to automatic review settings February 27, 2026 09:42
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@djstrong djstrong marked this pull request as ready for review February 27, 2026 09:50
@djstrong djstrong requested a review from a team as a code owner February 27, 2026 09:50
@greptile-apps
Copy link
Contributor

greptile-apps bot commented Feb 27, 2026

Greptile Summary

Adds robust labelhash normalization utilities that gracefully handle missing 0x prefixes, uppercase hex characters, odd-length hex strings, and bracket-enclosed encoded labelhashes. The heal() API now accepts flexible input formats and normalizes them before making requests, improving developer experience while maintaining backward compatibility.

  • parseLabelHash, parseEncodedLabelHash, and parseLabelHashOrEncodedLabelHash added with comprehensive validation
  • EnsRainbowApiClient.heal() updated to accept and normalize various labelhash formats
  • Cache lookups use normalized hashes ensuring consistent behavior across different input formats
  • Validation errors return HealBadRequestError without making network requests
  • All existing tests updated to reflect new normalization behavior

Confidence Score: 5/5

  • This PR is safe to merge with no identified issues
  • The implementation is well-tested with comprehensive coverage of edge cases, uses established validation libraries (viem's isHex), maintains backward compatibility, and follows defensive programming practices with clear error messages
  • No files require special attention

Important Files Changed

Filename Overview
packages/ensnode-sdk/src/ens/parse-labelhash.ts Adds three labelhash normalization utilities with clear validation and error handling
packages/ensnode-sdk/src/ens/parse-labelhash.test.ts Comprehensive test coverage for all parsing functions including edge cases and error scenarios
packages/ensrainbow-sdk/src/client.ts Updated heal() to normalize inputs and consistently use normalized hash for caching and requests
packages/ensrainbow-sdk/src/client.test.ts Added tests for normalization scenarios (uppercase, missing 0x, encoded format) and error handling

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[User calls heal with string] --> B{Input has brackets?}
    B -->|Yes| C[parseEncodedLabelHash]
    B -->|No| D[parseLabelHash]
    C --> E[Extract content from brackets]
    E --> D
    D --> F{Has 0x prefix?}
    F -->|No| G[Add 0x prefix]
    F -->|Yes| H[Extract hex part]
    G --> H
    H --> I{Valid hex?}
    I -->|No| J[Throw error: non-hex chars]
    I -->|Yes| K{Odd length?}
    K -->|Yes| L[Prepend 0 to hex part]
    K -->|No| M{Length = 64?}
    L --> M
    M -->|No| N[Throw error: invalid length]
    M -->|Yes| O[Lowercase and add 0x prefix]
    O --> P[Return normalized LabelHash]
    P --> Q[Check cache]
    Q -->|Hit| R[Return cached result]
    Q -->|Miss| S[Make API request with normalized hash]
    S --> T[Cache and return result]
Loading

Last reviewed commit: 9a92b99

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

6 files reviewed, no comments

Edit Code Review Agent Settings | Greptile

@djstrong djstrong changed the title feat: add label hash parsing and normalization utilities feat: add labelhash parsing and normalization utilities Feb 27, 2026
Copy link
Member

@lightwalker-eth lightwalker-eth left a comment

Choose a reason for hiding this comment

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

@djstrong Looks nice! Thank you

@djstrong djstrong merged commit 6f4d39b into main Feb 27, 2026
16 checks passed
@djstrong djstrong deleted the 211-normalize-labelhash-for-ensrainbow-heal-request-2 branch February 27, 2026 19:05
@github-actions github-actions bot mentioned this pull request Feb 27, 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.

Normalize labelhash for ENSRainbow Heal request

3 participants