-
Notifications
You must be signed in to change notification settings - Fork 152
Prevent JavaScript in query selections #1021
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Adds detection and helpful error messages when users mistakenly use JavaScript operators (||, &&, ??) in query callbacks. These operators are evaluated at query construction time, not execution time, causing silent unexpected behavior. Changes: - Add JavaScriptOperatorInQueryError with helpful suggestions - Add Symbol.toPrimitive trap to RefProxy for primitive coercion - Add checkCallbackForJsOperators() to detect operators in callbacks - Integrate checks into select(), where(), and having() methods - Add comprehensive tests for the new error detection - Fix existing tests that incorrectly used JS operators
🦋 Changeset detectedLatest commit: 3f71f8e The changes in this PR will be included in the next version bump. This PR includes changesets to release 12 packages
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 |
More templates
@tanstack/angular-db
@tanstack/db
@tanstack/db-ivm
@tanstack/electric-db-collection
@tanstack/offline-transactions
@tanstack/powersync-db-collection
@tanstack/query-db-collection
@tanstack/react-db
@tanstack/rxdb-db-collection
@tanstack/solid-db
@tanstack/svelte-db
@tanstack/trailbase-db-collection
@tanstack/vue-db
commit: |
|
Size Change: +1.05 kB (+1.17%) Total Size: 91.1 kB
ℹ️ View Unchanged
|
|
Size Change: 0 B Total Size: 3.35 kB ℹ️ View Unchanged
|
- Add ternary operator (?:) detection to JS operator patterns - Add tests for ternary detection and regex literal edge case - Fix e2e test to use coalesce() instead of ?? - Add changeset 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
Cast coalesce result to string to satisfy lower() type requirement 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
LOWER(NULL) LIKE '%x%' is NULL (falsy) in SQL, so coalesce isn't needed when using OR with a title match that will find results. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
KyleAMathews
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code Review: PR #1021
Overall Assessment: Approve ✅
This is an excellent developer experience improvement. Like warning signs at the edge of a cliff, these checks prevent users from falling into a pit of silent query bugs. The dual detection strategy (static + runtime) provides comprehensive coverage.
Summary
The PR adds detection for JavaScript operators (||, &&, ??, ?:) in query callbacks that would silently produce incorrect results due to eager evaluation at construction time.
Strengths
-
Two-layer detection strategy:
- Static: Parse callback source code and detect patterns
- Runtime:
Symbol.toPrimitivetrap catches primitive coercion
-
Excellent error messages: The
JavaScriptOperatorInQueryErrorprovides:- Clear explanation of why it's wrong
- Specific suggestions for alternatives
- Before/after code examples
-
Smart string/comment stripping: The
stripStringsAndCommentsfunction prevents false positives from operators inside string literals. -
Thorough test coverage: Tests cover all operators, string literals, optional chaining, and the known regex limitation.
-
Documentation of limitations: The regex false positive is documented as a known limitation - this is the right call since regex literals in query callbacks are rare.
Technical Notes
-
ref-proxy.ts:283-287: The ternary pattern
\?[^.?][^:]*:correctly excludes optional chaining (?.) and nullish coalescing (??). -
Symbol.toPrimitive location: Correctly added to all three proxy creation sites (root, alias, and nested property access).
-
Test fixes: Good catch on the existing tests using
??- the fixes to usecoalesce()and!assertion are appropriate.
Minor Suggestions
-
Consider orderBy: Should
orderByalso get the check? Users might try({ users }) => users.name || 'zzz'for null-last sorting. -
Error message enhancement: Consider mentioning in the error that these operators are evaluated at construction time vs execution time - this is the key insight that helps users understand why.
-
Performance consideration: The
callback.toString()approach has minimal overhead since it only runs once per query construction, not per row. Good trade-off.
Question
For minified code (e.g., production builds), callback.toString() may return mangled code. The detection might not work, but the queries themselves still fail silently. Is this a concern, or is the assumption that developers catch these in development?
Great DX improvement that will save developers debugging time! 🛡️
Summary
Adds detection and helpful error messages when users accidentally use JavaScript operators (
||,&&,??,?:) in query callbacks. These operators are evaluated at query construction time rather than execution time, causing silent incorrect behavior.User-visible impact: Developers now get clear, actionable errors when making this common mistake, instead of silently broken queries.
Root Cause
Query callbacks like
.select(),.where(), and.having()receive RefProxy objects that record property access paths. When users write:The
||operator evaluates immediately at construction time. Sinceuser.nameis a RefProxy object (truthy), it always returns the RefProxy, ignoring'default'. The query silently produces unexpected results.Approach
Two-layer detection strategy:
.toString()and detect operator patterns after stripping string literals and commentsKey Invariants
?.) must NOT trigger false positivesNon-goals
/a||b/) will trigger false positives. This is documented as a known limitation since regex literals are rarely used in query callbacks.===,>,<) - These are handled separately in PR Fix QueryCompilationError with undefined expression type #1082Trade-offs
||/&&/??which return objectsThe dual approach catches the broadest range of mistakes with minimal overhead.
Verification
Files Changed
src/errors.tsJavaScriptOperatorInQueryErrorwith helpful message and examplessrc/query/builder/ref-proxy.tsSymbol.toPrimitivetrap,checkCallbackForJsOperators(), operator patternssrc/query/builder/index.tscheckCallbackForJsOperatorsinselect(),where(),having()tests/query/builder/ref-proxy.test.tstests/query/scheduler.test.tscoalesce()instead of??tests/query/live-query-collection.test.ts!assertion instead of??packages/db-collection-e2e/.../predicates.suite.ts!assertion