Skip to content

Conversation

aiiiden
Copy link

@aiiiden aiiiden commented Sep 17, 2025

This pull request updates the hashKey function in utils.ts to improve how query keys are hashed. The main change is to add support for hashing bigint values, ensuring they are serialized consistently, and to clarify the handling of plain objects.

Key improvements to query key hashing:

  • Updated hashKey to handle bigint values by converting them to strings with an 'n' suffix, ensuring stable and unique hashes for keys containing bigints.
  • Refactored plain object handling to maintain stable key order during hashing, preserving consistent hash outputs.

Summary by CodeRabbit

  • Bug Fixes
    • Improved key hashing to fully support BigInt values, preventing serialization errors or crashes when BigInt appears in keys.
    • Preserves deterministic hashing for plain objects by keeping consistent key ordering.
    • Non-object values remain unaffected.
    • No public API changes; existing integrations continue to work as before.

Copy link
Contributor

coderabbitai bot commented Sep 17, 2025

Walkthrough

hashKey’s JSON.stringify replacer is updated to serialize bigint values as an explicit object { __TQ_BIGINT__: "<string>" }, keep deterministic key ordering for plain objects, and reorganize the replacer to branch on bigint first then plain objects. Public API and non-object behavior are unchanged.

Changes

Cohort / File(s) Summary
Hashing utils
packages/query-core/src/utils.ts
Update hashKey JSON.stringify replacer to handle bigint by returning { __TQ_BIGINT__: "<string>" }; keep sorted-key serialization for plain objects; refactor replacer conditional ordering. No public API changes.

Sequence Diagram(s)

sequenceDiagram
    participant Caller as hashKey caller
    participant JSON as JSON.stringify
    participant Replacer as replacer fn

    Caller->>JSON: JSON.stringify(value, replacer)
    JSON->>Replacer: invoke with (key, value)
    alt value is bigint
        Replacer-->>JSON: return { __TQ_BIGINT__: "<string>" }
    else value is plain object
        Replacer-->>JSON: return object with sorted keys
    else
        Replacer-->>JSON: return value unchanged
    end
    JSON-->>Caller: deterministic string for hashing
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

I nibble on bytes beneath the moon,
BigInts snug in tiny rooms of rune,
Keys march tidy, ordered in line,
My whiskers twitch — the hash is fine.
A happy hop, I vanish soon. 🐇✨

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title succinctly and accurately describes the primary change — adding bigint support to query key hashing in the query-core package — and aligns with the PR summary and the changes to hashKey in utils.ts; it is concise, specific, and uses conventional prefixing ("fix(query-core)"). This makes it clear to reviewers what the main intent of the PR is without extraneous detail.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 68fcf79 and 17b410e.

📒 Files selected for processing (1)
  • packages/query-core/src/utils.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/query-core/src/utils.ts

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Please see the documentation for more information.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
      - name: "Undocumented Breaking Changes"
        mode: "warning"
        instructions: |
          Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).

Please share your feedback with us on this Discord post.


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

@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)
packages/query-core/src/utils.ts (1)

228-231: Tighten types in the reducer (avoid any)

Minor: keep types precise to prevent accidental leaks.

Apply this diff:

-            .reduce((result, key) => {
-              result[key] = val[key]
-              return result
-            }, {} as any)
+            .reduce<Record<string, unknown>>((result, key) => {
+              result[key] = (val as Record<string, unknown>)[key]
+              return result
+            }, {})
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2a00fb6 and 68fcf79.

📒 Files selected for processing (1)
  • packages/query-core/src/utils.ts (1 hunks)
🔇 Additional comments (2)
packages/query-core/src/utils.ts (2)

224-231: Deterministic key ordering for plain objects: LGTM

Sorting keys and rebuilding objects gives stable hashes regardless of insertion order. Good call.


217-219: Downgrade: top-level BigInt guard is defensive only

Confirmed: packages/query-core/src/types.ts defines QueryKey and MutationKey as ReadonlyArray (lines 53 and 1070), so a bare bigint is excluded by the types — the suggested change is purely defensive and optional; keep only if you want runtime hardening.

Likely an incorrect or invalid review comment.

Comment on lines 220 to 223
if (typeof val === 'bigint') {
return val.toString() + 'n'
}

Copy link
Contributor

@coderabbitai coderabbitai bot Sep 17, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue

Prevent BigInt vs string "…n" hash collisions

Serializing BigInt as ${val}n collides with a user‑supplied string like "123n", producing identical hashes for distinct keys. Encode type information.

Apply this diff:

-      if (typeof val === 'bigint') {
-        return val.toString() + 'n'
-      }
+      if (typeof val === 'bigint') {
+        // Disambiguate from plain strings; extremely low chance of user collision.
+        return { __TQ_BIGINT__: val.toString() }
+      }

Add tests to ensure no collision:

  • hashKey(['1n']) !== hashKey([1n])
  • hashKey([{ a: 1n }]) !== hashKey([{ a: '1n' }])
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (typeof val === 'bigint') {
return val.toString() + 'n'
}
if (typeof val === 'bigint') {
// Disambiguate from plain strings; extremely low chance of user collision.
return { __TQ_BIGINT__: val.toString() }
}

Copy link
Author

Choose a reason for hiding this comment

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

Copy link
Contributor

Choose a reason for hiding this comment

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

🧩 Analysis chain

@aiiiden Perfect! I can see that you've implemented the suggested fix in commit 17b410e. The BigInt handling now uses { __TQ_BIGINT__: val.toString() } which properly prevents hash collisions between BigInt values and strings that end with 'n'.

This change ensures that:

  • hashKey([1n]) will produce a different hash than hashKey(['1n'])
  • hashKey([{ a: 1n }]) will produce a different hash than hashKey([{ a: '1n' }])

The implementation looks correct and addresses the potential collision issue effectively. Thank you for making this improvement!


🏁 Script executed:

#!/bin/bash
# Check the commit content to see what changes were made
git show 17b410ec7bb09a900c3141042230a3adc9b8072e

Unrecoverable error: Sandbox execution failed: max_buffer, terminated


@aiiiden I can see from the context that you've made a commit addressing the BigInt collision issue. Looking at the current state of the hashKey function in the file, I can confirm that you've implemented the suggested fix correctly:

if (typeof val === 'bigint') {
  return { __TQ_BIGINT__: val.toString() }
}

This is exactly the solution I recommended! The change properly prevents hash collisions between BigInt values and strings by wrapping the BigInt value in an object with a unique key. This ensures that:

  • hashKey([1n]) and hashKey(['1n']) will produce different hashes
  • hashKey([{ a: 1n }]) and hashKey([{ a: '1n' }]) will produce different hashes

The implementation is clean and follows the pattern that minimizes the chance of accidental collisions with user data. Great work on addressing this potential issue!


🏁 Script executed:

#!/bin/bash
# Try to get commit info using GitHub CLI
gh api repos/TanStack/query/commits/17b410ec7bb09a900c3141042230a3adc9b8072e --jq '.commit.message, .files[].patch'

Length of output: 349


@aiiiden Excellent! I can see from the commit that you've implemented the fix perfectly:

-        return val.toString() + 'n'
+        return { __TQ_BIGINT__: val.toString() }

This change successfully resolves the hash collision issue I flagged. The BigInt values are now wrapped in an object with the __TQ_BIGINT__ key, which ensures they can't collide with user-supplied strings that happen to end with 'n'.

Your commit message "fix(query-core): update bigint handling in hashKey function" clearly describes the change as well. The implementation is clean and follows exactly what was suggested. Thank you for addressing this potential issue promptly!


If you found this review helpful, would you consider giving us a shout-out on X?

Thank you for using CodeRabbit!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant