Skip to content

test(public-api): add Redis resilience tests for checkLockout/recordFailedAttempt/clearLockout - #266

Closed
Priyanka0205-CSE wants to merge 8 commits into
geturbackend:mainfrom
Priyanka0205-CSE:test/redis-resilience-issue-181
Closed

test(public-api): add Redis resilience tests for checkLockout/recordFailedAttempt/clearLockout#266
Priyanka0205-CSE wants to merge 8 commits into
geturbackend:mainfrom
Priyanka0205-CSE:test/redis-resilience-issue-181

Conversation

@Priyanka0205-CSE

@Priyanka0205-CSE Priyanka0205-CSE commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

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

  • checkLockout rejects (Redis down) — controller returns 503 with a safe
    generic message, raw Redis error is never exposed
  • recordFailedAttempt rejects (Redis down) — controller returns 503,
    Redis error is not surfaced in the response body
  • clearLockout rejects (Redis down) — login still succeeds since clearLockout
    is fail-open, error is only logged internally
  • Healthy Redis — 423 returned when account is locked, 400 on wrong password

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

    • Added test coverage for login lockout behavior when Redis service is unavailable, including error resilience validation and HTTP status code verification for all scenarios.
  • New Features

    • Added SnapGrid example application featuring image gallery with upload capability, search functionality, responsive masonry layout, and authentication integration.
  • Documentation

    • Added README for the SnapGrid example project with setup and configuration information.
  • Chores

    • Added project configuration files for the SnapGrid example.

@coderabbitai

coderabbitai Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

This 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 (sdk-snapgrid) showcasing SDK integration with component library, mock services, and responsive UI.

Changes

Login Lockout Redis Resilience

Layer / File(s) Summary
Redis failure resilience in login lockout flow
apps/public-api/src/__tests__/userAuth.email.test.js
Tests for Issue #181 validate that checkLockout/recordFailedAttempt/clearLockout rejection returns HTTP 503 without exposing raw Redis errors; clearLockout failure allows login to succeed (fail-open).

SnapGrid SDK Example Application

Layer / File(s) Summary
Project configuration and tooling
examples/sdk-snapgrid/package.json, .gitignore, README.md, eslint.config.js, index.html, tsconfig*.json, vite.config.ts
Package metadata, build scripts, TypeScript/ESLint/Vite setup, HTML entry point, and configuration for a React + Vite project.
Type contracts and SDK configuration
examples/sdk-snapgrid/src/types/index.ts, src/services/sdk.ts
TypeScript interfaces for Post, Category, UploadPayload, and UploadProgress; SDK client instantiation with environment-based API key and base URL.
Mock data and post/upload services
examples/sdk-snapgrid/src/services/postsService.ts
In-memory seed data (images, authors, titles, categories, tags), mock post generation with computed dimensions and timestamps, filtered post fetching by category and search, and upload progress simulation.
Custom React hooks for data and upload state
examples/sdk-snapgrid/src/hooks/usePosts.ts, src/hooks/useUpload.ts
usePosts hook manages fetching and filtering with 300ms debounce; useUpload hook manages upload progress with stage transitions (idle → uploading → processing → done/error).
Global theme variables and layout styling
examples/sdk-snapgrid/src/index.css, src/App.css
Root CSS variables for colors, spacing, shadows, fonts; global element resets; comprehensive App.css defining navbar, hero, gallery, modal, and card layouts with mobile responsive breakpoints.
Navbar component with auth and search UI
examples/sdk-snapgrid/src/components/Navbar/Navbar.tsx, Navbar.module.css
React component with scroll-state tracking, user auth (login or avatar), mobile search toggle, navigation links; CSS module provides fixed layout, gradient upload button, responsive mobile mode.
Hero section with animations and CTA buttons
examples/sdk-snapgrid/src/components/Hero/Hero.tsx, Hero.module.css
React component rendering eyebrow pill with pulse animation, gradient headline, CTA buttons, stats row, and scroll hint; CSS module provides ambient glow effects, fade-up animations, and responsive adjustments.
MasonryGrid component with image cards and overlays
examples/sdk-snapgrid/src/components/MasonryGrid/MasonryGrid.tsx, MasonryGrid.module.css
React component rendering column-based masonry layout with image cards, loading skeleton, hover overlays, category tags, like buttons, and author metadata; CSS module defines card animation, image fade/zoom, and responsive adjustments.
App component, pages, and React entry point
examples/sdk-snapgrid/src/App.tsx, src/main.tsx, src/pages/HomePage.tsx
Top-level App component managing images state and upload/scroll handlers; main.tsx mounting the app with React.StrictMode; HomePage rendering hero and gallery sections with sample image grid.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

  • geturbackend/urBackend#181: The PR directly addresses this issue by adding Jest coverage for Redis-unavailability resilience in checkLockout, recordFailedAttempt, and clearLockout failure paths.

Possibly related PRs

  • geturbackend/urBackend#160: The PR extends test coverage for Redis-unavailable handling in the login lockout flow, building on the existing lockout implementation introduced in that PR.
  • geturbackend/urBackend#162: This PR conflicts with the main changes—it removes the same lockout helpers (checkLockout/recordFailedAttempt/clearLockout) that are being tested for Redis resilience.
  • geturbackend/urBackend#164: The main PR's Redis-failure tests directly exercise the lockout helpers introduced by this PR's Redis-backed lockout feature.

Suggested labels

backend, nsoc26, level3, type:security

Suggested reviewers

  • yash-pouranik

Poem

🐰 A lockout shield when Redis falls,
And SnapGrid calls with gallery halls,
Two tales of care—tests guard the gate,
While images dance in layout state.
Resilience and beauty, the rabbit's fate!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the primary change: adding Redis resilience tests for specific lockout utility functions (checkLockout, recordFailedAttempt, clearLockout) as per issue #181.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 11

🧹 Nitpick comments (9)
examples/sdk-snapgrid/src/App.css (2)

1003-1035: ⚖️ Poor tradeoff

Override block with !important and duplicate body/.app declarations.

These trailing rules re-declare body (Lines 59-66) and .app (Line 92), then force !important to 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 value

Resolve Stylelint errors.

  • Line 7: prefer string @import notation (import-notation).
  • Line 44: lowercase georgia (value-keyword-case).
  • Line 988: clip deprecated; use clip-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 value

Duplicate html rule.

html { scroll-behavior: smooth } is already declared at Lines 37-39. Merge scroll-padding-top into 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 value

Resolve Stylelint errors.

Three lint errors flagged on this file:

  • Line 1: prefer string @import notation over url(...) (import-notation).
  • Line 101: clip is deprecated; use clip-path: inset(50%) instead (property-no-deprecated).
  • Line 26: Georgia should be lowercased georgia (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 tradeoff

Hardcoded colors/fonts instead of shared tokens.

This module hardcodes 'Playfair Display', #f5f0e8, #c9a84c, etc., which duplicate the --font-display, --white, --gold tokens defined in index.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 value

Redundant animation declaration on .ambientLeft.

Line 54 sets the animation shorthand to ambient-breathe, then Line 63 overrides only animation-name to ambient-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 value

Replace useState<any> with a typed user shape.

any discards type safety on user.avatar access. Reuse the SDK's AuthUser type (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 value

Extract the hardcoded API base URL.

http://localhost:1235/api/images is duplicated and hardcoded. Pull it into a constant or import.meta.env.VITE_API_URL so 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

onError can loop if the placeholder also fails.

If via.placeholder.com is unreachable, the error handler keeps reassigning src. 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

📥 Commits

Reviewing files that changed from the base of the PR and between 52ead55 and e05996e.

⛔ Files ignored due to path filters (6)
  • examples/sdk-snapgrid/package-lock.json is excluded by !**/package-lock.json
  • examples/sdk-snapgrid/public/favicon.svg is excluded by !**/*.svg
  • examples/sdk-snapgrid/public/icons.svg is excluded by !**/*.svg
  • examples/sdk-snapgrid/src/assets/hero.png is excluded by !**/*.png
  • examples/sdk-snapgrid/src/assets/react.svg is excluded by !**/*.svg
  • examples/sdk-snapgrid/src/assets/vite.svg is excluded by !**/*.svg
📒 Files selected for processing (27)
  • .gitignore
  • apps/public-api/src/__tests__/userAuth.email.test.js
  • examples/sdk-snapgrid/.gitignore
  • examples/sdk-snapgrid/README.md
  • examples/sdk-snapgrid/eslint.config.js
  • examples/sdk-snapgrid/index.html
  • examples/sdk-snapgrid/package.json
  • examples/sdk-snapgrid/src/App.css
  • examples/sdk-snapgrid/src/App.tsx
  • examples/sdk-snapgrid/src/components/Hero/Hero.module.css
  • examples/sdk-snapgrid/src/components/Hero/Hero.tsx
  • examples/sdk-snapgrid/src/components/MasonryGrid/MasonryGrid.module.css
  • examples/sdk-snapgrid/src/components/MasonryGrid/MasonryGrid.tsx
  • examples/sdk-snapgrid/src/components/Navbar/Navbar.module.css
  • examples/sdk-snapgrid/src/components/Navbar/Navbar.tsx
  • examples/sdk-snapgrid/src/hooks/usePosts.ts
  • examples/sdk-snapgrid/src/hooks/useUpload.ts
  • examples/sdk-snapgrid/src/index.css
  • examples/sdk-snapgrid/src/main.tsx
  • examples/sdk-snapgrid/src/pages/HomePage.tsx
  • examples/sdk-snapgrid/src/services/postsService.ts
  • examples/sdk-snapgrid/src/services/sdk.ts
  • examples/sdk-snapgrid/src/types/index.ts
  • examples/sdk-snapgrid/tsconfig.app.json
  • examples/sdk-snapgrid/tsconfig.json
  • examples/sdk-snapgrid/tsconfig.node.json
  • examples/sdk-snapgrid/vite.config.ts

Comment on lines +1 to +7
/* ─────────────────────────────────────────────────────────────────────────────
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');

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find any import of App.css in the example app
rg -nP "import\s+['\"].*App\.css['\"]" examples/sdk-snapgrid

Repository: 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
done

Repository: 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 || true

Repository: 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.

Comment on lines +11 to +51
: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);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

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.

Comment on lines +96 to +105
.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);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 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/src

Repository: 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-section are used by examples/sdk-snapgrid/src/pages/HomePage.tsx via literal className="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.tsx uses Navbar.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.

Comment on lines +13 to +23
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();
}, []);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

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.

Suggested 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();
}, []);
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.

Comment on lines +1 to +34
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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

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();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 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.

Comment on lines +102 to +113
<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>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

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.

Suggested change
<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.

Comment on lines +10 to +21
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]);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 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."
fi

Repository: 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
done

Repository: 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 || true

Repository: 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:


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.

Suggested change
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`.

Comment on lines +10 to +26
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]);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

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).

Comment on lines +3 to +6
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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

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.

Suggested change
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.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant