test(public-api): add Redis resilience tests for checkLockout/recordFailedAttempt/clearLockout - #266
Conversation
…ailedAttempt/clearLockout (geturbackend#181)
📝 WalkthroughWalkthroughThis PR contains two independent features: (1) Jest tests validating Redis-failure resilience in login lockout flow, ensuring HTTP 503 returns and error masking; and (2) a complete React + TypeScript + Vite example application ( ChangesLogin Lockout Redis Resilience
SnapGrid SDK Example Application
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related issues
Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 11
🧹 Nitpick comments (9)
examples/sdk-snapgrid/src/App.css (2)
1003-1035: ⚖️ Poor tradeoffOverride block with
!importantand duplicatebody/.appdeclarations.These trailing rules re-declare
body(Lines 59-66) and.app(Line 92), then force!importantto undo borders/pseudo-elements defined earlier in the same file. This is a fragile pattern—prefer fixing the original declarations instead of overriding them at the bottom.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@examples/sdk-snapgrid/src/App.css` around lines 1003 - 1035, The CSS file contains duplicate declarations for body and .app plus fragile overrides using !important on .hero, .gallery-section, and .navbar-inner and their pseudo-elements; instead of overriding at the end, remove the duplicated body and .app blocks and modify the original declarations that set borders/pseudo-elements so they reflect the intended layout (or increase selector specificity at the original location) so you can delete the trailing rules with !important (.hero, .gallery-section, .navbar-inner and their ::before/::after) — locate the original rules that add borders/pseudo-elements to .hero/.gallery-section/.navbar-inner and change them there (or narrow selectors) so the final overrides are unnecessary.
7-7: 💤 Low valueResolve Stylelint errors.
- Line 7: prefer string
@importnotation (import-notation).- Line 44: lowercase
georgia(value-keyword-case).- Line 988:
clipdeprecated; useclip-path: inset(50%)(property-no-deprecated).Also applies to: 44-44, 988-988
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@examples/sdk-snapgrid/src/App.css` at line 7, Replace the `@import` url(...) at the top with string notation by using `@import` "https://fonts.googleapis.com/..." (change the url(...) form to a quoted string) to satisfy import-notation; update any font-family declarations that use "Georgia" to lowercase "georgia" (search for font-family entries referencing Georgia) to satisfy value-keyword-case; and replace deprecated usage of the clip property (instances of "clip: ...") with the modern equivalent clip-path: inset(...) (e.g., clip-path: inset(50%)) to address property-no-deprecated.examples/sdk-snapgrid/src/index.css (2)
104-107: 💤 Low valueDuplicate
htmlrule.
html { scroll-behavior: smooth }is already declared at Lines 37-39. Mergescroll-padding-topinto the earlier block and drop this duplicate.♻️ Suggested consolidation
html { scroll-behavior: smooth; + scroll-padding-top: 100px; }And remove Lines 104-107.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@examples/sdk-snapgrid/src/index.css` around lines 104 - 107, There is a duplicate CSS selector block for html; open the earlier html rule (the one that already sets scroll-behavior: smooth at ~Lines 37-39) and add scroll-padding-top: 100px into that rule, then remove the later duplicate html { scroll-behavior: smooth; scroll-padding-top: 100px; } block so the html selector is declared only once.
1-1: 💤 Low valueResolve Stylelint errors.
Three lint errors flagged on this file:
- Line 1: prefer string
@importnotation overurl(...)(import-notation).- Line 101:
clipis deprecated; useclip-path: inset(50%)instead (property-no-deprecated).- Line 26:
Georgiashould be lowercasedgeorgia(value-keyword-case).Also applies to: 101-101
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@examples/sdk-snapgrid/src/index.css` at line 1, Update the CSS to satisfy Stylelint: change the `@import` from url(...) to string notation (replace `@import` url('https://fonts.googleapis.com/...') with `@import` 'https://fonts.googleapis.com/...'); find the rule using the deprecated clip property (look for "clip:" usage) and replace it with the modern equivalent clip-path: inset(50%) while preserving intent; and normalize the font-family value by lowercasing "Georgia" to "georgia" in the relevant font-family declaration so value-keyword-case is respected.examples/sdk-snapgrid/src/components/Hero/Hero.module.css (2)
129-134: ⚖️ Poor tradeoffHardcoded colors/fonts instead of shared tokens.
This module hardcodes
'Playfair Display',#f5f0e8,#c9a84c, etc., which duplicate the--font-display,--white,--goldtokens defined inindex.css. Referencing the tokens keeps the Hero consistent if the theme changes.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@examples/sdk-snapgrid/src/components/Hero/Hero.module.css` around lines 129 - 134, Replace the hardcoded font and color literals in the Hero module with the shared CSS tokens: change font-family 'Playfair Display' to use var(--font-display) (with an optional fallback), replace color: `#f5f0e8` with color: var(--white), and any other hardcoded color like `#c9a84c` to var(--gold); update the corresponding rules in Hero.module.css (the font-family and color declarations shown) so the component references the --font-display, --white, and --gold tokens from index.css while keeping the existing font-size, weight, line-height, and letter-spacing values.
54-67: 💤 Low valueRedundant animation declaration on
.ambientLeft.Line 54 sets the
animationshorthand toambient-breathe, then Line 63 overrides onlyanimation-nametoambient-breatheSm. Set the intended animation once to avoid confusion.♻️ Suggested fix
- animation:ambient-breathe 14s ease-in-out infinite reverse; + animation: ambient-breatheSm 14s ease-in-out infinite reverse; } @@ -/* The left blob doesn't translateX */ -.ambientLeft { animation-name: ambient-breatheSm; } `@keyframes` ambient-breatheSm {🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@examples/sdk-snapgrid/src/components/Hero/Hero.module.css` around lines 54 - 67, The .ambientLeft rule currently only overrides animation-name (ambient-breatheSm) while the base selector sets the full animation shorthand to ambient-breathe, which is confusing and may lead to unexpected combined properties; update the .ambientLeft declaration to set the full animation shorthand (e.g., animation: ambient-breatheSm 14s ease-in-out infinite reverse) so it explicitly uses the intended keyframes and timing, or alternatively remove the base shorthand and give each ambient variant its complete animation shorthand (referencing .ambientLeft, ambient-breathe, and ambient-breatheSm).examples/sdk-snapgrid/src/components/Navbar/Navbar.tsx (1)
28-28: 💤 Low valueReplace
useState<any>with a typed user shape.
anydiscards type safety onuser.avataraccess. Reuse the SDK'sAuthUsertype (or a minimal local interface).🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@examples/sdk-snapgrid/src/components/Navbar/Navbar.tsx` at line 28, The user state is typed as any which loses safety when accessing properties like user.avatar; update the Navbar component to use a concrete type (preferably the SDK's AuthUser) by importing AuthUser (or declare a minimal interface { id: string; name?: string; avatar?: string; ... }) and change the state declaration to use useState<AuthUser | null>(null) for the const [user, setUser] in Navbar so TypeScript enforces correct property access and null checks.examples/sdk-snapgrid/src/App.tsx (1)
15-15: 💤 Low valueExtract the hardcoded API base URL.
http://localhost:1235/api/imagesis duplicated and hardcoded. Pull it into a constant orimport.meta.env.VITE_API_URLso the example is configurable.Also applies to: 47-47
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@examples/sdk-snapgrid/src/App.tsx` at line 15, The fetch call using the hardcoded URL "http://localhost:1235/api/images" (seen in the fetch at the top of App.tsx and the duplicate at line ~47) should be refactored to use a single configurable base URL; create a constant (e.g. const API_BASE = import.meta.env.VITE_API_URL || "http://localhost:1235") or import it from env, then replace both occurrences so calls use `${API_BASE}/api/images` (update the fetch invocations that reference the variable `res` and any other uses to build the full endpoint from API_BASE).examples/sdk-snapgrid/src/components/MasonryGrid/MasonryGrid.tsx (1)
19-22: 💤 Low value
onErrorcan loop if the placeholder also fails.If
via.placeholder.comis unreachable, the error handler keeps reassigningsrc. Guard with a flag to fire once.🛡️ One-shot fallback
onError={(e) => { + if (e.currentTarget.dataset.fallback) return; + e.currentTarget.dataset.fallback = "1"; e.currentTarget.src = "https://via.placeholder.com/300x200?text=Image+Not+Loaded"; }}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@examples/sdk-snapgrid/src/components/MasonryGrid/MasonryGrid.tsx` around lines 19 - 22, The onError handler in MasonryGrid.tsx can create an infinite loop if the fallback URL also fails; update the onError callback used on the img element to be one-shot by checking a flag or dataset (e.g., data-has-fallback) or by removing the handler after first execution (use e.currentTarget.onerror = null or setAttribute data-has-fallback="true") before assigning the placeholder src so the fallback assignment only runs once and won’t re-trigger when the placeholder fails.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@examples/sdk-snapgrid/src/App.css`:
- Around line 11-51: App.css currently re-declares global :root tokens that
conflict with index.css (notably --nav-height, --font-display, --radius-sm,
--accent, --text-primary), causing silent overrides; remove the duplicate global
declarations from the :root block in App.css and either (a) rely on the
canonical tokens defined in index.css (keep only overrides that are
intentionally different and match expected values like --nav-height: 72px), or
(b) namespace app-specific tokens (e.g., --app-accent, --app-text-primary) and
update usages to those names; ensure Navbar/Hero keep using the canonical
--nav-height (72px) and the intended --font-display so the components remain
consistent.
- Around line 1-7: App.css (which contains the Google Fonts `@import` and
component styles) is never imported at runtime, so add an import for it where
global styles are loaded: open main.tsx and alongside the existing import
'./index.css' add import './App.css' (or alternatively add import './App.css' to
App.tsx) to ensure the styles and fonts are applied; confirm the relative path
matches the App.css location and rebuild.
- Around line 96-105: The global .navbar rule in App.css is unused because the
Navbar component (Navbar.tsx) uses CSS modules (styles.navbar) from
Navbar.module.css; either remove the .navbar block from App.css to avoid dead
global styles or update the Navbar component to use the global class by adding
className="navbar" and ensuring App.css is imported where the component is
rendered; reference the .navbar selector in App.css and the Navbar component
(Navbar.tsx / Navbar.module.css) when making this change.
In `@examples/sdk-snapgrid/src/App.tsx`:
- Around line 13-23: The fetchImages function inside useEffect lacks error
handling and assumes data.images exists; wrap the async body of fetchImages in
try/catch, check the fetch Response.ok and handle non-OK responses (log or
surface an error), validate that data and data.images are arrays before calling
data.images.map (fall back to an empty array), and ensure setImages is only
called with a safe array of URLs; reference fetchImages, useEffect, and
setImages when making the fixes.
In `@examples/sdk-snapgrid/src/components/MasonryGrid/MasonryGrid.tsx`:
- Around line 1-34: The component currently uses inline styles and a different
DOM structure so the provided MasonryGrid.module.css is never used; import the
CSS module at the top of the MasonryGrid component (e.g., import styles from
"./MasonryGrid.module.css") and replace the inline-styled elements with the
classNames expected by the stylesheet: apply styles.section to the root
<section>, styles.grid to the container instead of columns/gap, wrap each image
in a card element with styles.card and add the overlay element with
styles.overlay as the stylesheet expects; keep the images prop and the onError
handler on the <img> but remove the inline style and margin, and ensure you use
className={styles.someClass} references matching the module’s .section, .grid,
.card, .overlay names so the existing CSS is actually applied.
In `@examples/sdk-snapgrid/src/components/Navbar/Navbar.tsx`:
- Around line 102-113: The avatar logout control uses a non-focusable <div>
(styles.avatar) with an onClick that calls sdk.auth.logout() and setUser(null),
making it inaccessible via keyboard; change the element to a semantic <button>
(or if you must keep a div, add role="button", tabIndex={0}, and keydown handler
for Enter/Space) so it can be focused and activated by keyboard, and ensure the
click handler logic (calling sdk.auth.logout() and setUser) is moved to the
button's onClick and also invoked from the key handler when using the div
fallback.
- Line 26: The Navbar component destructures onUploadClick but never uses it;
add an upload control in the actions row that calls onUploadClick when
activated. Locate the Navbar function (export const Navbar:
React.FC<NavbarProps> = ({ onUploadClick, onSearch, searchValue }) => { ... })
and render an Upload button/icon alongside the other action controls in the
actions row, wiring its onClick/onPress handler to onUploadClick (ensuring
proper null/undefined guarding). Also ensure any accessible label/tooltip
matches existing styling and that props (NavbarProps) include onUploadClick so
App.tsx's handleUploadClick is invoked.
- Line 38: The call to sdk.auth.getUser() in Navbar.tsx is invalid because the
SDK exposes auth.me(token?) instead; replace the use of sdk.auth.getUser() with
sdk.auth.me() (pass the token if needed) to match the exported method name, or
confirm and use an existing alias if one is defined; update any surrounding
error handling or typings that referenced getUser to use me and its
Promise<AuthUser> return type.
In `@examples/sdk-snapgrid/src/hooks/usePosts.ts`:
- Around line 10-26: The load callback can write stale posts if an earlier
fetchPosts resolves after a later one; modify the effect to create an ignore
token (e.g., a local boolean "cancelled" or incrementing requestId tied to the
useEffect lifecycle) and pass that token into load (or have load capture it) so
that when a response arrives you only call setPosts / setError / setLoading if
the token indicates the request is still current; ensure the token is
set/cleared in the useEffect cleanup so in-flight results are ignored and
refetch behavior remains intact (referencing load, useEffect, fetchPosts,
setPosts, setError, setLoading).
- Around line 10-21: In the load callback inside usePosts (the async function
referenced as load that calls fetchPosts), the catch block declares an unused
binding catch (e) which violates the caughtErrors lint rule; change the catch
signature to catch (error) or catch (_e) and use it (e.g., include error.message
in the setError or log it via console.error/processLogger) or rename to _ to
mark intentionally unused—update the catch in the load function so the caught
variable is either used or named as an ignored binding to satisfy
`@typescript-eslint/no-unused-vars`.
In `@examples/sdk-snapgrid/src/services/sdk.ts`:
- Around line 3-6: The SDK is being initialized with possibly undefined env
vars; validate import.meta.env.VITE_URBACKEND_PUBLISHABLE_KEY and
import.meta.env.VITE_URBACKEND_URL before calling urBackend. Update the file
that exports sdk to check both variables (e.g., throw a clear Error or
console.error + process exit) if either is missing and only call urBackend({
apiKey, baseUrl }) when they are present; reference the exported const sdk and
the urBackend call when making the change.
---
Nitpick comments:
In `@examples/sdk-snapgrid/src/App.css`:
- Around line 1003-1035: The CSS file contains duplicate declarations for body
and .app plus fragile overrides using !important on .hero, .gallery-section, and
.navbar-inner and their pseudo-elements; instead of overriding at the end,
remove the duplicated body and .app blocks and modify the original declarations
that set borders/pseudo-elements so they reflect the intended layout (or
increase selector specificity at the original location) so you can delete the
trailing rules with !important (.hero, .gallery-section, .navbar-inner and their
::before/::after) — locate the original rules that add borders/pseudo-elements
to .hero/.gallery-section/.navbar-inner and change them there (or narrow
selectors) so the final overrides are unnecessary.
- Line 7: Replace the `@import` url(...) at the top with string notation by using
`@import` "https://fonts.googleapis.com/..." (change the url(...) form to a quoted
string) to satisfy import-notation; update any font-family declarations that use
"Georgia" to lowercase "georgia" (search for font-family entries referencing
Georgia) to satisfy value-keyword-case; and replace deprecated usage of the clip
property (instances of "clip: ...") with the modern equivalent clip-path:
inset(...) (e.g., clip-path: inset(50%)) to address property-no-deprecated.
In `@examples/sdk-snapgrid/src/App.tsx`:
- Line 15: The fetch call using the hardcoded URL
"http://localhost:1235/api/images" (seen in the fetch at the top of App.tsx and
the duplicate at line ~47) should be refactored to use a single configurable
base URL; create a constant (e.g. const API_BASE = import.meta.env.VITE_API_URL
|| "http://localhost:1235") or import it from env, then replace both occurrences
so calls use `${API_BASE}/api/images` (update the fetch invocations that
reference the variable `res` and any other uses to build the full endpoint from
API_BASE).
In `@examples/sdk-snapgrid/src/components/Hero/Hero.module.css`:
- Around line 129-134: Replace the hardcoded font and color literals in the Hero
module with the shared CSS tokens: change font-family 'Playfair Display' to use
var(--font-display) (with an optional fallback), replace color: `#f5f0e8` with
color: var(--white), and any other hardcoded color like `#c9a84c` to var(--gold);
update the corresponding rules in Hero.module.css (the font-family and color
declarations shown) so the component references the --font-display, --white, and
--gold tokens from index.css while keeping the existing font-size, weight,
line-height, and letter-spacing values.
- Around line 54-67: The .ambientLeft rule currently only overrides
animation-name (ambient-breatheSm) while the base selector sets the full
animation shorthand to ambient-breathe, which is confusing and may lead to
unexpected combined properties; update the .ambientLeft declaration to set the
full animation shorthand (e.g., animation: ambient-breatheSm 14s ease-in-out
infinite reverse) so it explicitly uses the intended keyframes and timing, or
alternatively remove the base shorthand and give each ambient variant its
complete animation shorthand (referencing .ambientLeft, ambient-breathe, and
ambient-breatheSm).
In `@examples/sdk-snapgrid/src/components/MasonryGrid/MasonryGrid.tsx`:
- Around line 19-22: The onError handler in MasonryGrid.tsx can create an
infinite loop if the fallback URL also fails; update the onError callback used
on the img element to be one-shot by checking a flag or dataset (e.g.,
data-has-fallback) or by removing the handler after first execution (use
e.currentTarget.onerror = null or setAttribute data-has-fallback="true") before
assigning the placeholder src so the fallback assignment only runs once and
won’t re-trigger when the placeholder fails.
In `@examples/sdk-snapgrid/src/components/Navbar/Navbar.tsx`:
- Line 28: The user state is typed as any which loses safety when accessing
properties like user.avatar; update the Navbar component to use a concrete type
(preferably the SDK's AuthUser) by importing AuthUser (or declare a minimal
interface { id: string; name?: string; avatar?: string; ... }) and change the
state declaration to use useState<AuthUser | null>(null) for the const [user,
setUser] in Navbar so TypeScript enforces correct property access and null
checks.
In `@examples/sdk-snapgrid/src/index.css`:
- Around line 104-107: There is a duplicate CSS selector block for html; open
the earlier html rule (the one that already sets scroll-behavior: smooth at
~Lines 37-39) and add scroll-padding-top: 100px into that rule, then remove the
later duplicate html { scroll-behavior: smooth; scroll-padding-top: 100px; }
block so the html selector is declared only once.
- Line 1: Update the CSS to satisfy Stylelint: change the `@import` from url(...)
to string notation (replace `@import` url('https://fonts.googleapis.com/...') with
`@import` 'https://fonts.googleapis.com/...'); find the rule using the deprecated
clip property (look for "clip:" usage) and replace it with the modern equivalent
clip-path: inset(50%) while preserving intent; and normalize the font-family
value by lowercasing "Georgia" to "georgia" in the relevant font-family
declaration so value-keyword-case is respected.
🪄 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: defaults
Review profile: CHILL
Plan: Pro
Run ID: c6acb2b2-984c-4e28-8c82-7214a741e0df
⛔ Files ignored due to path filters (6)
examples/sdk-snapgrid/package-lock.jsonis excluded by!**/package-lock.jsonexamples/sdk-snapgrid/public/favicon.svgis excluded by!**/*.svgexamples/sdk-snapgrid/public/icons.svgis excluded by!**/*.svgexamples/sdk-snapgrid/src/assets/hero.pngis excluded by!**/*.pngexamples/sdk-snapgrid/src/assets/react.svgis excluded by!**/*.svgexamples/sdk-snapgrid/src/assets/vite.svgis excluded by!**/*.svg
📒 Files selected for processing (27)
.gitignoreapps/public-api/src/__tests__/userAuth.email.test.jsexamples/sdk-snapgrid/.gitignoreexamples/sdk-snapgrid/README.mdexamples/sdk-snapgrid/eslint.config.jsexamples/sdk-snapgrid/index.htmlexamples/sdk-snapgrid/package.jsonexamples/sdk-snapgrid/src/App.cssexamples/sdk-snapgrid/src/App.tsxexamples/sdk-snapgrid/src/components/Hero/Hero.module.cssexamples/sdk-snapgrid/src/components/Hero/Hero.tsxexamples/sdk-snapgrid/src/components/MasonryGrid/MasonryGrid.module.cssexamples/sdk-snapgrid/src/components/MasonryGrid/MasonryGrid.tsxexamples/sdk-snapgrid/src/components/Navbar/Navbar.module.cssexamples/sdk-snapgrid/src/components/Navbar/Navbar.tsxexamples/sdk-snapgrid/src/hooks/usePosts.tsexamples/sdk-snapgrid/src/hooks/useUpload.tsexamples/sdk-snapgrid/src/index.cssexamples/sdk-snapgrid/src/main.tsxexamples/sdk-snapgrid/src/pages/HomePage.tsxexamples/sdk-snapgrid/src/services/postsService.tsexamples/sdk-snapgrid/src/services/sdk.tsexamples/sdk-snapgrid/src/types/index.tsexamples/sdk-snapgrid/tsconfig.app.jsonexamples/sdk-snapgrid/tsconfig.jsonexamples/sdk-snapgrid/tsconfig.node.jsonexamples/sdk-snapgrid/vite.config.ts
| /* ───────────────────────────────────────────────────────────────────────────── | ||
| SnapGrid — App.css | ||
| Design System: Dark Editorial / Luxury Gallery | ||
| Fonts: Fraunces (display) + DM Sans (body) | ||
| ───────────────────────────────────────────────────────────────────────────── */ | ||
|
|
||
| @import url('https://fonts.googleapis.com/css2?family=Fraunces:ital,opsz,wght@0,9..144,300;0,9..144,400;0,9..144,600;0,9..144,700;1,9..144,300;1,9..144,400&family=DM+Sans:ital,opsz,wght@0,9..40,300;0,9..40,400;0,9..40,500;0,9..40,600;1,9..40,300&display=swap'); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Find any import of App.css in the example app
rg -nP "import\s+['\"].*App\.css['\"]" examples/sdk-snapgridRepository: geturbackend/urBackend
Length of output: 48
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== Search for App.css mentions (case-sensitive) =="
rg -n "App\.css" examples/sdk-snapgrid || true
echo "== Search for app.css mentions (case-insensitive) =="
rg -ni "app\.css" examples/sdk-snapgrid || true
echo "== Search for CSS `@import` of App.css =="
rg -n "[@]import[^;]*App\.css" examples/sdk-snapgrid || true
echo "== Locate main.tsx and print import section =="
main_tsx="$(fd -a "main\.tsx$" examples/sdk-snapgrid | head -n 20 | cat)"
echo "main_tsx: $main_tsx"
if [ -n "$main_tsx" ]; then
sed -n '1,120p' "$main_tsx" | nl -ba
fi
echo "== Locate App.tsx (or equivalent) and print import section =="
app_tsx="$(fd -a "(App|app)\.tsx$" examples/sdk-snapgrid | head -n 20 | cat)"
echo "app_tsx candidates: $app_tsx"
for f in $app_tsx; do
echo "--- $f ---"
sed -n '1,160p' "$f" | nl -ba
doneRepository: geturbackend/urBackend
Length of output: 499
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== Search for App.css mentions (case-sensitive) =="
rg -n "App\.css" examples/sdk-snapgrid || true
echo "== Search for app.css mentions (case-insensitive) =="
rg -ni "app\.css" examples/sdk-snapgrid || true
echo "== Search for CSS `@import` of App.css =="
rg -n "[@]import[^;]*App\.css" examples/sdk-snapgrid || true
echo "== Locate main.tsx and print import/side-effect section =="
main_tsx="$(fd -a "main\.tsx$" examples/sdk-snapgrid | head -n 20 | cat)"
echo "main_tsx: $main_tsx"
if [ -n "${main_tsx:-}" ]; then
sed -n '1,140p' "$main_tsx" | cat -n
fi
echo "== Locate App.tsx / app.tsx and print import section =="
app_tsx_files="$(fd -a "(App|app)\.tsx$" examples/sdk-snapgrid | head -n 50 | cat)"
echo "app_tsx candidates:"
echo "$app_tsx_files"
if [ -n "${app_tsx_files:-}" ]; then
for f in $app_tsx_files; do
echo "--- $f ---"
sed -n '1,200p' "$f" | cat -n
done
fi
echo "== Search for any CSS imports in entry/components (quick sweep) =="
rg -nP "import\s+['\"].*\.css['\"]" examples/sdk-snapgrid/src || trueRepository: geturbackend/urBackend
Length of output: 3752
examples/sdk-snapgrid/src/App.css isn’t applied at runtime—import it if intended.
examples/sdk-snapgrid/src/main.tsx imports only ./index.css, and examples/sdk-snapgrid/src/App.tsx has no CSS imports. There are no other App.css imports in examples/sdk-snapgrid, so the App.css styles (and its Google Fonts @import) won’t load.
🧰 Tools
🪛 Stylelint (17.12.0)
[error] 7-7: Expected "url('https://fonts.googleapis.com/css2?family=Fraunces:ital,opsz,wght@0,9..144,300;0,9..144,400;0,9..144,600;0,9..144,700;1,9..144,300;1,9..144,400&family=DM+Sans:ital,opsz,wght@0,9..40,300;0,9..40,400;0,9..40,500;0,9..40,600;1,9..40,300&display=swap')" to be "'https://fonts.googleapis.com/css2?family=Fraunces:ital,opsz,wght@0,9..144,300;0,9..144,400;0,9..144,600;0,9..144,700;1,9..144,300;1,9..144,400&family=DM+Sans:ital,opsz,wght@0,9..40,300;0,9..40,400;0,9..40,500;0,9..40,600;1,9..40,300&display=swap'" (import-notation)
(import-notation)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@examples/sdk-snapgrid/src/App.css` around lines 1 - 7, App.css (which
contains the Google Fonts `@import` and component styles) is never imported at
runtime, so add an import for it where global styles are loaded: open main.tsx
and alongside the existing import './index.css' add import './App.css' (or
alternatively add import './App.css' to App.tsx) to ensure the styles and fonts
are applied; confirm the relative path matches the App.css location and rebuild.
| :root { | ||
| --bg-base: #0c0c0e; | ||
| --bg-surface: #131318; | ||
| --bg-elevated: #1a1a22; | ||
| --bg-overlay: #22222e; | ||
|
|
||
| --border-subtle: rgba(255, 255, 255, 0.06); | ||
| --border-mid: rgba(255, 255, 255, 0.11); | ||
| --border-strong: rgba(255, 255, 255, 0.22); | ||
|
|
||
| --text-primary: #f0ede8; | ||
| --text-secondary: rgba(240, 237, 232, 0.55); | ||
| --text-muted: rgba(240, 237, 232, 0.3); | ||
|
|
||
| --accent: #e8b84b; | ||
| --accent-dim: rgba(232, 184, 75, 0.15); | ||
| --accent-hover: #f2c85e; | ||
|
|
||
| --danger: #e85b5b; | ||
| --success: #4be8a0; | ||
|
|
||
| --glass-bg: rgba(26, 26, 34, 0.7); | ||
| --glass-border: rgba(255, 255, 255, 0.08); | ||
|
|
||
| --radius-sm: 8px; | ||
| --radius-md: 14px; | ||
| --radius-lg: 20px; | ||
| --radius-xl: 28px; | ||
|
|
||
| --shadow-card: 0 4px 24px rgba(0,0,0,0.5), 0 1px 3px rgba(0,0,0,0.3); | ||
| --shadow-hover: 0 12px 48px rgba(0,0,0,0.65), 0 2px 8px rgba(0,0,0,0.4); | ||
| --shadow-modal: 0 32px 96px rgba(0,0,0,0.8), 0 8px 32px rgba(0,0,0,0.5); | ||
|
|
||
| --font-display: 'Fraunces', Georgia, serif; | ||
| --font-body: 'DM Sans', system-ui, sans-serif; | ||
|
|
||
| --nav-height: 68px; | ||
|
|
||
| --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1); | ||
| --ease-out: cubic-bezier(0.16, 1, 0.3, 1); | ||
| } |
There was a problem hiding this comment.
Conflicting :root token systems with index.css.
index.css already defines global :root tokens (--gold, --white, --nav-height: 72px, --font-display: 'Playfair Display', --radius-sm: 6px). This file re-declares an entirely different token set on :root (--accent, --text-primary, --nav-height: 68px, --font-display: 'Fraunces', --radius-sm: 8px). If both stylesheets load, the later one silently wins, so --nav-height shifts to 68px while Hero/Navbar modules were built around 72px, and the two font systems clash. Consolidate to a single source of truth for tokens.
🧰 Tools
🪛 Stylelint (17.12.0)
[error] 44-44: Expected "Georgia" to be "georgia" (value-keyword-case)
(value-keyword-case)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@examples/sdk-snapgrid/src/App.css` around lines 11 - 51, App.css currently
re-declares global :root tokens that conflict with index.css (notably
--nav-height, --font-display, --radius-sm, --accent, --text-primary), causing
silent overrides; remove the duplicate global declarations from the :root block
in App.css and either (a) rely on the canonical tokens defined in index.css
(keep only overrides that are intentionally different and match expected values
like --nav-height: 72px), or (b) namespace app-specific tokens (e.g.,
--app-accent, --app-text-primary) and update usages to those names; ensure
Navbar/Hero keep using the canonical --nav-height (72px) and the intended
--font-display so the components remain consistent.
| .navbar { | ||
| position: fixed; | ||
| top: 0; left: 0; right: 0; | ||
| z-index: 100; | ||
| height: var(--nav-height); | ||
| background: var(--glass-bg); | ||
| backdrop-filter: blur(20px) saturate(1.5); | ||
| -webkit-backdrop-filter: blur(20px) saturate(1.5); | ||
| border-bottom: 1px solid var(--glass-border); | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Look for global className usage (string literals) vs CSS-module usage in the example
rg -nP "className=\"(navbar|hero|post-card|btn|gallery-section)" examples/sdk-snapgrid/src
rg -nP "styles\.(navbar|hero|postCard)" examples/sdk-snapgrid/srcRepository: geturbackend/urBackend
Length of output: 1311
🏁 Script executed:
#!/bin/bash
set -euo pipefail
repo_root="$(pwd)"
target_dir="examples/sdk-snapgrid/src"
# Show App.css sections that define the global selectors mentioned in the review
echo "=== App.css global selector definitions ==="
rg -n "^[[:space:]]*\.(navbar|hero|post-card|btn|gallery-section)[[:space:]]*\{" "$target_dir/App.css" || true
echo
echo "=== App.css content around .navbar/.hero/.post-card/.btn ==="
sed -n '70,140p' "$target_dir/App.css"
echo
echo "=== App.css content around lines ~484-520 (per review) ==="
sed -n '450,520p' "$target_dir/App.css"
echo
echo "=== Where global className strings are used ==="
rg -nP 'className="(navbar|hero|post-card|btn|gallery-section)\b' "$target_dir" || true
echo
echo "=== Where CSS-module style.* classes are used in Navbar/Hero ==="
# Look for Navbar/Hero imports and style usage
rg -n "from ['\"].*App\.css['\"]" "$target_dir" || true
rg -n "import\s+styles\s+from|import\s+\* as\s+styles|styles\." "$target_dir/components/Navbar/Navbar.tsx" "$target_dir/components/Hero/Hero.tsx" || true
sed -n '1,120p' "$target_dir/components/Navbar/Navbar.tsx"
sed -n '1,120p' "$target_dir/components/Hero/Hero.tsx"Repository: geturbackend/urBackend
Length of output: 15891
🏁 Script executed:
#!/bin/bash
set -euo pipefail
target="examples/sdk-snapgrid/src"
echo "=== HomePage.tsx (first 220 lines) ==="
sed -n '1,220p' "$target/pages/HomePage.tsx"
echo
echo "=== Any global className=\"navbar\" usage ==="
rg -nP 'className\s*=\s*"navbar\b' "$target" || true
echo
echo "=== Any global className=\"btn\" usage (string literal) ==="
rg -nP 'className\s*=\s*"[^"]*\bbtn\b' "$target" || true
echo
echo "=== Any global className=\"post-card\" usage ==="
rg -nP 'className\s*=\s*"post-card\b' "$target" || true
echo
echo "=== App.css .btn / .hero / .post-card / .navbar snippets ==="
for sel in navbar hero post-card btn; do
echo "---- .$sel ----"
rg -n "^[[:space:]]*\.$sel[[:space:]]*\{" "$target/App.css"
done
sed -n '80,125p' "$target/App.css"
sed -n '170,235p' "$target/App.css"
sed -n '245,320p' "$target/App.css"
sed -n '460,540p' "$target/App.css"Repository: geturbackend/urBackend
Length of output: 8524
Narrow the issue: only App.css’s global .navbar looks unused
App.css’s global.hero,.post-card,.btn, and.gallery-sectionare used byexamples/sdk-snapgrid/src/pages/HomePage.tsxvia literalclassName="hero",className="post-card",className="btn ...", etc., so those aren’t dead.App.css’s global.navbar(e.g., lines 96-105) doesn’t appear to be used by any markup in this example (className="navbar"not found);examples/sdk-snapgrid/src/components/Navbar/Navbar.tsxusesNavbar.module.css(styles.navbar) instead.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@examples/sdk-snapgrid/src/App.css` around lines 96 - 105, The global .navbar
rule in App.css is unused because the Navbar component (Navbar.tsx) uses CSS
modules (styles.navbar) from Navbar.module.css; either remove the .navbar block
from App.css to avoid dead global styles or update the Navbar component to use
the global class by adding className="navbar" and ensuring App.css is imported
where the component is rendered; reference the .navbar selector in App.css and
the Navbar component (Navbar.tsx / Navbar.module.css) when making this change.
| useEffect(() => { | ||
| const fetchImages = async () => { | ||
| const res = await fetch("http://localhost:1235/api/images"); | ||
| const data = await res.json(); | ||
|
|
||
| const urls = data.images.map((img: { url: string }) => img.url); | ||
| setImages(urls); | ||
| }; | ||
|
|
||
| fetchImages(); | ||
| }, []); |
There was a problem hiding this comment.
fetchImages has no error handling and assumes data.images exists.
Unlike the upload path, this mount fetch isn't wrapped in try/catch. A network failure produces an unhandled promise rejection, and a non-ok response or unexpected payload makes data.images.map throw (Cannot read properties of undefined).
🛡️ Guard the fetch
const fetchImages = async () => {
- const res = await fetch("http://localhost:1235/api/images");
- const data = await res.json();
-
- const urls = data.images.map((img: { url: string }) => img.url);
- setImages(urls);
+ try {
+ const res = await fetch("http://localhost:1235/api/images");
+ if (!res.ok) throw new Error(`Failed to load images: ${res.status}`);
+ const data = await res.json();
+ const urls = (data.images ?? []).map((img: { url: string }) => img.url);
+ setImages(urls);
+ } catch (err) {
+ console.error("Failed to fetch images:", err);
+ }
};📝 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.
| useEffect(() => { | |
| const fetchImages = async () => { | |
| const res = await fetch("http://localhost:1235/api/images"); | |
| const data = await res.json(); | |
| const urls = data.images.map((img: { url: string }) => img.url); | |
| setImages(urls); | |
| }; | |
| fetchImages(); | |
| }, []); | |
| useEffect(() => { | |
| const fetchImages = async () => { | |
| try { | |
| const res = await fetch("http://localhost:1235/api/images"); | |
| if (!res.ok) throw new Error(`Failed to load images: ${res.status}`); | |
| const data = await res.json(); | |
| const urls = (data.images ?? []).map((img: { url: string }) => img.url); | |
| setImages(urls); | |
| } catch (err) { | |
| console.error("Failed to fetch images:", err); | |
| } | |
| }; | |
| fetchImages(); | |
| }, []); |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@examples/sdk-snapgrid/src/App.tsx` around lines 13 - 23, The fetchImages
function inside useEffect lacks error handling and assumes data.images exists;
wrap the async body of fetchImages in try/catch, check the fetch Response.ok and
handle non-OK responses (log or surface an error), validate that data and
data.images are arrays before calling data.images.map (fall back to an empty
array), and ensure setImages is only called with a safe array of URLs; reference
fetchImages, useEffect, and setImages when making the fixes.
| type MasonryGridProps = { | ||
| images?: string[]; | ||
| }; | ||
|
|
||
| export function MasonryGrid({ images = [] }: MasonryGridProps) { | ||
| return ( | ||
| <section id="sg-gallery" style={{ padding: "30px" }}> | ||
| <h2>Uploaded Images</h2> | ||
|
|
||
| {images.length === 0 ? ( | ||
| <p>No images uploaded yet.</p> | ||
| ) : ( | ||
| <div style={{ columns: 3, gap: "16px" }}> | ||
| {images.map((url, index) => ( | ||
| <img | ||
| key={index} | ||
| src={url} | ||
| alt="Uploaded" | ||
| onError={(e) => { | ||
| e.currentTarget.src = | ||
| "https://via.placeholder.com/300x200?text=Image+Not+Loaded"; | ||
| }} | ||
| style={{ | ||
| width: "100%", | ||
| marginBottom: "16px", | ||
| borderRadius: "12px", | ||
| }} | ||
| /> | ||
| ))} | ||
| </div> | ||
| )} | ||
| </section> | ||
| ); | ||
| } No newline at end of file |
There was a problem hiding this comment.
Component renders inline styles and ignores the companion MasonryGrid.module.css.
The PR adds a comprehensive MasonryGrid.module.css (.section, .grid, .card, .overlay, etc.), but this component never imports it and instead uses inline styles with a different DOM structure (<h2>Uploaded Images</h2>, columns: 3). As written, the entire stylesheet is dead code and the documented masonry/overlay UI is never produced. Either wire the module in or drop it.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@examples/sdk-snapgrid/src/components/MasonryGrid/MasonryGrid.tsx` around
lines 1 - 34, The component currently uses inline styles and a different DOM
structure so the provided MasonryGrid.module.css is never used; import the CSS
module at the top of the MasonryGrid component (e.g., import styles from
"./MasonryGrid.module.css") and replace the inline-styled elements with the
classNames expected by the stylesheet: apply styles.section to the root
<section>, styles.grid to the container instead of columns/gap, wrap each image
in a card element with styles.card and add the overlay element with
styles.overlay as the stylesheet expects; keep the images prop and the onError
handler on the <img> but remove the inline style and margin, and ensure you use
className={styles.someClass} references matching the module’s .section, .grid,
.card, .overlay names so the existing CSS is actually applied.
|
|
||
| const checkUser = async () => { | ||
| try { | ||
| const currentUser = await sdk.auth.getUser(); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Confirm whether getUser (or me) is the public method on the SDK auth module
rg -nP '\b(getUser|me)\s*[:(=]' sdks/urbackend-sdk/src/modules/auth.ts
ast-grep --pattern 'public async getUser($$$) { $$$ }'Repository: geturbackend/urBackend
Length of output: 363
Fix sdk.auth.getUser() to use the SDK’s exposed auth method (me)
sdks/urbackend-sdk/src/modules/auth.ts exposes public async me(token?: string): Promise<AuthUser> and uses auth.me(...) in the module docs/examples; there’s no getUser() method there. Update sdk.auth.getUser() in examples/sdk-snapgrid/src/components/Navbar/Navbar.tsx to sdk.auth.me() (or confirm an alias exists elsewhere).
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@examples/sdk-snapgrid/src/components/Navbar/Navbar.tsx` at line 38, The call
to sdk.auth.getUser() in Navbar.tsx is invalid because the SDK exposes
auth.me(token?) instead; replace the use of sdk.auth.getUser() with
sdk.auth.me() (pass the token if needed) to match the exported method name, or
confirm and use an existing alias if one is defined; update any surrounding
error handling or typings that referenced getUser to use me and its
Promise<AuthUser> return type.
| <div | ||
| className={styles.avatar} | ||
| onClick={async () => { | ||
| await sdk.auth.logout(); | ||
| setUser(null); | ||
| }} | ||
| > | ||
| <img | ||
| src={user?.avatar || "https://i.pravatar.cc/36"} | ||
| alt="User avatar" | ||
| /> | ||
| </div> |
There was a problem hiding this comment.
Avatar logout control isn't keyboard accessible.
A <div onClick> can't be reached via Tab or activated with Enter/Space. Use a <button> (or add role="button", tabIndex={0}, and a key handler) so logout is operable without a mouse.
♿ Use a button element
- <div
- className={styles.avatar}
- onClick={async () => {
- await sdk.auth.logout();
- setUser(null);
- }}
->
- <img
- src={user?.avatar || "https://i.pravatar.cc/36"}
- alt="User avatar"
- />
-</div>
+ <button
+ type="button"
+ className={styles.avatar}
+ aria-label="Log out"
+ onClick={async () => {
+ await sdk.auth.logout();
+ setUser(null);
+ }}
+ >
+ <img
+ src={user?.avatar || "https://i.pravatar.cc/36"}
+ alt="User avatar"
+ />
+ </button>📝 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.
| <div | |
| className={styles.avatar} | |
| onClick={async () => { | |
| await sdk.auth.logout(); | |
| setUser(null); | |
| }} | |
| > | |
| <img | |
| src={user?.avatar || "https://i.pravatar.cc/36"} | |
| alt="User avatar" | |
| /> | |
| </div> | |
| <button | |
| type="button" | |
| className={styles.avatar} | |
| aria-label="Log out" | |
| onClick={async () => { | |
| await sdk.auth.logout(); | |
| setUser(null); | |
| }} | |
| > | |
| <img | |
| src={user?.avatar || "https://i.pravatar.cc/36"} | |
| alt="User avatar" | |
| /> | |
| </button> |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@examples/sdk-snapgrid/src/components/Navbar/Navbar.tsx` around lines 102 -
113, The avatar logout control uses a non-focusable <div> (styles.avatar) with
an onClick that calls sdk.auth.logout() and setUser(null), making it
inaccessible via keyboard; change the element to a semantic <button> (or if you
must keep a div, add role="button", tabIndex={0}, and keydown handler for
Enter/Space) so it can be focused and activated by keyboard, and ensure the
click handler logic (calling sdk.auth.logout() and setUser) is moved to the
button's onClick and also invoked from the key handler when using the div
fallback.
| const load = useCallback(async () => { | ||
| setLoading(true); | ||
| setError(null); | ||
| try { | ||
| const data = await fetchPosts(category, search); | ||
| setPosts(data); | ||
| } catch (e) { | ||
| setError('Failed to load posts. Please try again.'); | ||
| } finally { | ||
| setLoading(false); | ||
| } | ||
| }, [category, search]); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# 1) Locate the file and show the exact catch usage + surrounding lines
echo "=== usePosts.ts (around catch) ==="
ls -la examples/sdk-snapgrid/src/hooks/usePosts.ts
nl -ba examples/sdk-snapgrid/src/hooks/usePosts.ts | sed -n '1,120p'
# 2) Find ESLint config files and show the ones that configure ts-eslint
echo
echo "=== ESLint config files ==="
ls -la eslint.config.* package.json 2>/dev/null || true
# 3) Search for tseslint recommended usage and/or no-unused-vars options
echo
echo "=== Search tseslint configs / no-unused-vars settings ==="
rg -n "tseslint\.configs\.recommended|tseslint\.configs\.[a-zA-Z0-9_-]+|no-unused-vars|caughtErrors" -S . || true
# 4) Show the actual eslint.config.js (or closest equivalent) if present
echo
echo "=== eslint.config.js (or similar) ==="
if [ -f eslint.config.js ]; then
nl -ba eslint.config.js | sed -n '1,220p'
elif [ -f eslint.config.mjs ]; then
nl -ba eslint.config.mjs | sed -n '1,220p'
elif [ -f .eslintrc.* ]; then
echo "Found .eslintrc.*; dumping matching files:"
ls -la .eslintrc.*
for f in .eslintrc.*; do
echo "--- $f ---"
nl -ba "$f" | sed -n '1,220p'
done
else
echo "No root eslint config file found by those names."
fiRepository: geturbackend/urBackend
Length of output: 254
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# 1) Show catch usage with line numbers (no nl available)
echo "=== usePosts.ts (around catch) ==="
sed -n '1,160p' examples/sdk-snapgrid/src/hooks/usePosts.ts | cat -n | sed -n '1,140p'
echo
echo "=== Grep for catch bindings in usePosts.ts ==="
rg -n "catch\s*\(" examples/sdk-snapgrid/src/hooks/usePosts.ts || true
# 2) Locate eslint config files
echo
echo "=== ESLint config candidates in repo root ==="
ls -la | rg -n "eslint\.config(\.|$)|\.eslintrc|tseslint|eslint.*config" || true
ls -la eslint.config.* 2>/dev/null || true
ls -la .eslintrc* 2>/dev/null || true
# 3) Search for tseslint recommended usage / no-unused-vars settings
echo
echo "=== Search for tseslint recommended / caughtErrors / no-unused-vars ==="
rg -n "tseslint\.configs\.recommended|caughtErrors|no-unused-vars" -S . || true
# 4) If eslint config exists, dump it (limit output)
echo
echo "=== Dump eslint config file(s) if found ==="
for f in eslint.config.js eslint.config.mjs eslint.config.cjs .eslintrc.json .eslintrc.js .eslintrc.cjs; do
if [ -f "$f" ]; then
echo "--- $f ---"
head -n 260 "$f" | cat -n | sed -n '1,200p'
fi
doneRepository: geturbackend/urBackend
Length of output: 2471
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Show examples/sdk-snapgrid ESLint config (relevant parts only)
echo "=== examples/sdk-snapgrid/eslint.config.js (relevant parts) ==="
sed -n '1,220p' examples/sdk-snapgrid/eslint.config.js | cat -n
# Check whether lint script exists in example package.json
echo
echo "=== examples/sdk-snapgrid/package.json (lint script) ==="
if [ -f examples/sdk-snapgrid/package.json ]; then
cat examples/sdk-snapgrid/package.json | sed -n '1,200p' | cat -n
else
echo "No examples/sdk-snapgrid/package.json found"
fi
# Search within example for no-unused-vars / caughtErrors overrides
echo
echo "=== Search for caughtErrors/no-unused-vars overrides in examples/sdk-snapgrid ==="
rg -n "caughtErrors|no-unused-vars|`@typescript-eslint/no-unused-vars`" examples/sdk-snapgrid || trueRepository: geturbackend/urBackend
Length of output: 2099
🌐 Web query:
typescript-eslint v8 tseslint.configs.recommended @typescript-eslint/no-unused-vars caughtErrors default
💡 Result:
In typescript-eslint v8, the @typescript-eslint/no-unused-vars rule was updated to align its default behavior with ESLint 9 [1][2][3]. The caughtErrors option governs how unused variables in catch clauses are handled [1][4]. To match ESLint 9 standards and improve consistency, this rule was updated such that caught errors are now checked by default [2][3]. If you are using tseslint.configs.recommended and wish to ignore unused caught errors—reverting to behavior where they are not reported—you must explicitly set the caughtErrors option to 'none' in your ESLint configuration [1][4][5]: // eslint.config.mjs export default tseslint.config(...tseslint.configs.recommended, { rules: { '@typescript-eslint/no-unused-vars': [ 'error', { caughtErrors: 'none', }, ], }, },); This change was part of a broader effort to align typescript-eslint with upstream ESLint 9 defaults during the v8 release [2][6][3].
Citations:
- 1: https://typescript-eslint.io/blog/announcing-typescript-eslint-v8/
- 2: feat(eslint-plugin): [no-unused-vars] align catch behavior to ESLint 9 typescript-eslint/typescript-eslint#8971
- 3: typescript-eslint/typescript-eslint@1db993a
- 4: https://contextqmd.com/libraries/typescript-eslint/versions/8.58.1/pages/packages/website/blog/2024-05-27-announcing-typescript-eslint-v8-beta
- 5: https://typescript-eslint.io/blog/announcing-typescript-eslint-v8-beta
- 6: feat(eslint-plugin): [no-unused-vars] add
reportUnusedIgnorePatternoption typescript-eslint/typescript-eslint#9324
Fix unused catch (e) in usePosts
examples/sdk-snapgrid/eslint.config.js extends tseslint.configs.recommended, which enables @typescript-eslint/no-unused-vars with caughtErrors checking unused catch bindings. In examples/sdk-snapgrid/src/hooks/usePosts.ts, catch (e) doesn’t use e, so npm run lint should fail.
🔧 Proposed fix
- } catch (e) {
+ } catch {
setError('Failed to load posts. Please try again.');
} finally {📝 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.
| const load = useCallback(async () => { | |
| setLoading(true); | |
| setError(null); | |
| try { | |
| const data = await fetchPosts(category, search); | |
| setPosts(data); | |
| } catch (e) { | |
| setError('Failed to load posts. Please try again.'); | |
| } finally { | |
| setLoading(false); | |
| } | |
| }, [category, search]); | |
| const load = useCallback(async () => { | |
| setLoading(true); | |
| setError(null); | |
| try { | |
| const data = await fetchPosts(category, search); | |
| setPosts(data); | |
| } catch { | |
| setError('Failed to load posts. Please try again.'); | |
| } finally { | |
| setLoading(false); | |
| } | |
| }, [category, search]); |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@examples/sdk-snapgrid/src/hooks/usePosts.ts` around lines 10 - 21, In the
load callback inside usePosts (the async function referenced as load that calls
fetchPosts), the catch block declares an unused binding catch (e) which violates
the caughtErrors lint rule; change the catch signature to catch (error) or catch
(_e) and use it (e.g., include error.message in the setError or log it via
console.error/processLogger) or rename to _ to mark intentionally unused—update
the catch in the load function so the caught variable is either used or named as
an ignored binding to satisfy `@typescript-eslint/no-unused-vars`.
| const load = useCallback(async () => { | ||
| setLoading(true); | ||
| setError(null); | ||
| try { | ||
| const data = await fetchPosts(category, search); | ||
| setPosts(data); | ||
| } catch (e) { | ||
| setError('Failed to load posts. Please try again.'); | ||
| } finally { | ||
| setLoading(false); | ||
| } | ||
| }, [category, search]); | ||
|
|
||
| useEffect(() => { | ||
| const timer = setTimeout(load, 300); | ||
| return () => clearTimeout(timer); | ||
| }, [load]); |
There was a problem hiding this comment.
Guard against out-of-order responses to avoid stale posts.
fetchPosts resolves after ~900ms. If category/search change while a request is in flight, an earlier request can resolve after a newer one and overwrite posts with stale results. Add an ignore flag tied to the effect lifecycle.
♻️ Proposed fix
useEffect(() => {
- const timer = setTimeout(load, 300);
- return () => clearTimeout(timer);
+ let active = true;
+ const timer = setTimeout(async () => {
+ setLoading(true);
+ setError(null);
+ try {
+ const data = await fetchPosts(category, search);
+ if (active) setPosts(data);
+ } catch {
+ if (active) setError('Failed to load posts. Please try again.');
+ } finally {
+ if (active) setLoading(false);
+ }
+ }, 300);
+ return () => {
+ active = false;
+ clearTimeout(timer);
+ };
- }, [load]);
+ }, [category, search]);Note this would refactor load/refetch; an AbortController-style flag in the shared load callback is an alternative if you want to keep refetch wired to it.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@examples/sdk-snapgrid/src/hooks/usePosts.ts` around lines 10 - 26, The load
callback can write stale posts if an earlier fetchPosts resolves after a later
one; modify the effect to create an ignore token (e.g., a local boolean
"cancelled" or incrementing requestId tied to the useEffect lifecycle) and pass
that token into load (or have load capture it) so that when a response arrives
you only call setPosts / setError / setLoading if the token indicates the
request is still current; ensure the token is set/cleared in the useEffect
cleanup so in-flight results are ignored and refetch behavior remains intact
(referencing load, useEffect, fetchPosts, setPosts, setError, setLoading).
| export const sdk = urBackend({ | ||
| apiKey: import.meta.env.VITE_URBACKEND_PUBLISHABLE_KEY, | ||
| baseUrl: import.meta.env.VITE_URBACKEND_URL, | ||
| }); No newline at end of file |
There was a problem hiding this comment.
Add validation for required environment variables.
The SDK initialization reads VITE_URBACKEND_PUBLISHABLE_KEY and VITE_URBACKEND_URL from environment variables but doesn't validate they are defined. If these variables are missing, the SDK will be initialized with undefined values, causing cryptic runtime failures when methods are called.
🛡️ Proposed fix to add environment variable validation
import urBackend from "`@urbackend/sdk`";
+const apiKey = import.meta.env.VITE_URBACKEND_PUBLISHABLE_KEY;
+const baseUrl = import.meta.env.VITE_URBACKEND_URL;
+
+if (!apiKey || !baseUrl) {
+ throw new Error(
+ 'Missing required environment variables: VITE_URBACKEND_PUBLISHABLE_KEY and VITE_URBACKEND_URL must be defined'
+ );
+}
+
export const sdk = urBackend({
- apiKey: import.meta.env.VITE_URBACKEND_PUBLISHABLE_KEY,
- baseUrl: import.meta.env.VITE_URBACKEND_URL,
+ apiKey,
+ baseUrl,
});📝 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.
| export const sdk = urBackend({ | |
| apiKey: import.meta.env.VITE_URBACKEND_PUBLISHABLE_KEY, | |
| baseUrl: import.meta.env.VITE_URBACKEND_URL, | |
| }); | |
| import urBackend from "`@urbackend/sdk`"; | |
| const apiKey = import.meta.env.VITE_URBACKEND_PUBLISHABLE_KEY; | |
| const baseUrl = import.meta.env.VITE_URBACKEND_URL; | |
| if (!apiKey || !baseUrl) { | |
| throw new Error( | |
| 'Missing required environment variables: VITE_URBACKEND_PUBLISHABLE_KEY and VITE_URBACKEND_URL must be defined' | |
| ); | |
| } | |
| export const sdk = urBackend({ | |
| apiKey, | |
| baseUrl, | |
| }); |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@examples/sdk-snapgrid/src/services/sdk.ts` around lines 3 - 6, The SDK is
being initialized with possibly undefined env vars; validate
import.meta.env.VITE_URBACKEND_PUBLISHABLE_KEY and
import.meta.env.VITE_URBACKEND_URL before calling urBackend. Update the file
that exports sdk to check both variables (e.g., throw a clear Error or
console.error + process exit) if either is missing and only call urBackend({
apiKey, baseUrl }) when they are present; reference the exported const sdk and
the urBackend call when making the change.
What this PR does
This PR adds unit tests for the Redis rejection paths in the login lockout
utilities, as requested in issue #181.
The tests cover what happens when Redis goes down at each stage of the
login flow — making sure the controller handles failures gracefully without
crashing or leaking internal error details to the user.
Tests added in userAuth.email.test.js
generic message, raw Redis error is never exposed
Redis error is not surfaced in the response body
is fail-open, error is only logged internally
Test results
9 new tests added, all passing.
154 tests passing total across the suite.
Note: 2 tests in mail.controller.test.js are failing due to a pre-existing
bullmq config issue — unrelated to this PR and present before my changes.
Summary by CodeRabbit
Release Notes
Tests
New Features
Documentation
Chores