Skip to content

strict: enable noUncheckedIndexedAccess and fix type-safety issues#8

Open
TargiX wants to merge 5 commits into
mainfrom
strict/no-unchecked-indexed-access
Open

strict: enable noUncheckedIndexedAccess and fix type-safety issues#8
TargiX wants to merge 5 commits into
mainfrom
strict/no-unchecked-indexed-access

Conversation

@TargiX

@TargiX TargiX commented Jun 7, 2026

Copy link
Copy Markdown
Owner

What changed

Enabled noUncheckedIndexedAccess in tsconfig and fixed 11 resulting type errors across 5 files.

Fixes

  • lib/store.ts, hooks/use-store.ts: .split("T")[0].slice(0, 10) — array indexing after .split() could return undefined under the new flag
  • components/morning/step-mood.tsx: e.touches[0] now guarded with ?. + ?? 0
  • components/evening/step-sleep-target.tsx: destructured [h, m] from .split(":").map(Number) now uses parts[0] ?? 0; Slider onValueChange value gets ?? 8 fallback
  • components/morning/step-sleep.tsx: same Slider fallback

Why

noUncheckedIndexedAccess catches a common class of bugs where array/record indexing is assumed to always succeed. Every indexed access now correctly returns T | undefined, forcing explicit handling. This is the single most impactful strictness flag missing from most TS projects.

Verification

  • tsc --noEmit — zero errors (was 11 before fixes)
  • npm run lint — passes

Risk

Low. Only adds type-safety guards. Runtime behavior is identical — the new fallbacks match what would have been undefined → NaN behavior before, but now explicit and intentional.

Summary by CodeRabbit

  • Refactor

    • Internal action imports reorganized for consistency across onboarding steps.
    • General stability improvements in morning/evening flows.
  • Bug Fixes

    • Sleep-duration slider now defaults to 8 hours when unset.
    • Touch handling on the mood grid made more robust to avoid accidental interactions.
    • Validation routines return clearer failure values when validation messages are missing.
  • Chores

    • Stricter TypeScript checks enabled.
    • Workspace build configuration updated.
  • Tests

    • Updated tests to more reliably assert entry updates.

- Enable noUncheckedIndexedAccess in tsconfig for safer indexed access
- Replace .split('T')[0] with .slice(0, 10) to avoid undefined returns
- Guard destructured array patterns from split/map with fallbacks
- Add fallback for Slider onValueChange destructured value
@vercel

vercel Bot commented Jun 7, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
anchor Ready Ready Preview, Comment, Open in v0 Jun 7, 2026 4:41pm
next-js-tether-targix Ready Ready Preview, Comment Jun 7, 2026 4:41pm

@coderabbitai

coderabbitai Bot commented Jun 7, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1a400b8e-2149-4453-84bb-76d3f0842ca9

📥 Commits

Reviewing files that changed from the base of the PR and between 0626a1f and 365a90e.

📒 Files selected for processing (1)
  • lib/store/actions.test.ts

📝 Walkthrough

Walkthrough

Enable strict indexed-access typing, add null-safety guards in time parsing, touch handling, and slider handlers, migrate store action imports to @/lib/store/actions, update tests to use getTodayKey(), and add pnpm workspace allowBuilds entries.

Changes

Type Safety and Module Organization

Layer / File(s) Summary
TypeScript strict indexed-access checking
tsconfig.json
Enables noUncheckedIndexedAccess to force explicit handling of possibly-undefined indexed lookups.
Defensive component handlers
components/evening/step-sleep-target.tsx, components/morning/step-sleep.tsx, components/morning/step-mood.tsx
Time parsing uses numeric parts with ?? 0 defaults; touch handlers guard e.touches[0] and return early when absent; slider handlers use v ?? 8 fallback for hours.
Store action imports refactor
app/(protected)/settings/page.tsx, components/evening/step-*.tsx, components/morning/step-*.tsx
Redirects updateTodayEntry, addHabit, removeHabit, and setNotificationTime imports from @/lib/store to @/lib/store/actions.
Auth validators null-safe messages
lib/auth/credentials.ts
validateEmail and validatePassword now optional-chain Zod issue messages and return null when missing.
Workspace build configuration
pnpm-workspace.yaml
Adds allowBuilds block with toggles for msw, sharp, and unrs-resolver.
Tests: updateTodayEntry assertions
lib/store/actions.test.ts
Tests now derive today’s snapshot key via getTodayKey() and assert patched fields from s.entries[getTodayKey()].

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • TargiX/Next.js-Tether#3: Centralizes state mutations and action functions in lib/store/actions, which this PR aligns with by updating component and settings imports to that module.

Poem

I hop through code with careful paws,
Guarding touches, times, and sliders' laws.
If parts go missing, defaults take flight,
Actions gather under one import light.
A tiny rabbit tests the night 🐰✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 8.33% 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 summarizes the main change: enabling TypeScript's noUncheckedIndexedAccess compiler option and addressing resulting type-safety issues across multiple components and configuration files.
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 docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch strict/no-unchecked-indexed-access

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🤖 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 `@components/evening/step-sleep-target.tsx`:
- Line 78: The onValueChange handler currently calls the component state setter
setHours directly; replace that direct state mutation by dispatching the
appropriate store action from lib/store/actions instead: create or use an action
(e.g., setSleepHours or updateHours) and import it into this component, then
change onValueChange={([v]) => setHours(v ?? 8)} to call the action dispatcher
with the new value (v ?? 8) so all state updates flow through the store actions
rather than setHours in the TSX component.

In `@components/morning/step-mood.tsx`:
- Around line 31-32: The handler that reads touch coordinates currently falls
back to (0,0) by using e.touches[0]?.clientX ?? 0 and e.touches[0]?.clientY ??
0; instead, detect when e.touches[0] is missing and return early (ignore the
event) to avoid emitting a bogus top-left point—update the touch handler in
step-mood.tsx (the function that sets clientX/clientY from e.touches, e.g., the
onTouchStart/onTouchMove handler) to check for const touch = e.touches[0]; if
(!touch) return; then use touch.clientX and touch.clientY.

In `@components/morning/step-sleep.tsx`:
- Line 75: The onValueChange handler is directly calling the local setter
setHours; replace this with the appropriate action from lib/store/actions:
import the store action (e.g., the action that updates sleep hours) and call
that action with (v ?? 8) instead of calling setHours. Update the onValueChange
callback in components/morning/step-sleep.tsx to dispatch/use the action from
lib/store/actions (referencing the existing setHours usage to locate the spot)
and remove the direct setHours call so the component mutates state only via
lib/store/actions.

In `@hooks/use-store.ts`:
- Line 20: Replace the UTC-based day key generation in useTodayEntry (currently
using new Date().toISOString().slice(0, 10)) with the local day-key utility from
lib/time/today; import the helper (e.g., today or whatever the module exports)
and call it to produce the key so the variable key in useTodayEntry uses the
local-day value and avoids UTC rollover mismatches.

In `@lib/store.ts`:
- Line 100: getTodayKey() currently builds the day key using new
Date().toISOString().slice(0, 10) which produces a UTC day and can be wrong for
local dates; replace this logic by importing and using the repository's local
day-key utility from lib/time/today (i.e., remove the toISOString slice and call
the exported function from lib/time/today) so getTodayKey returns the correct
local day key.
🪄 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: bf3db7ca-30b7-4a8d-a1a8-610ee4e75a4f

📥 Commits

Reviewing files that changed from the base of the PR and between 95d9a7c and dc15c76.

📒 Files selected for processing (6)
  • components/evening/step-sleep-target.tsx
  • components/morning/step-mood.tsx
  • components/morning/step-sleep.tsx
  • hooks/use-store.ts
  • lib/store.ts
  • tsconfig.json

step={0.5}
value={[hours]}
onValueChange={([v]) => setHours(v)}
onValueChange={([v]) => setHours(v ?? 8)}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major | 🏗️ Heavy lift

Route slider updates through store actions instead of component state setters.

This still mutates component state directly in a .tsx component. Move this through lib/store/actions per project rule.

As per coding guidelines, "**/*.{tsx,jsx}: Mutate state only via lib/store/actions — never use setState directly in components".

🤖 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 `@components/evening/step-sleep-target.tsx` at line 78, The onValueChange
handler currently calls the component state setter setHours directly; replace
that direct state mutation by dispatching the appropriate store action from
lib/store/actions instead: create or use an action (e.g., setSleepHours or
updateHours) and import it into this component, then change onValueChange={([v])
=> setHours(v ?? 8)} to call the action dispatcher with the new value (v ?? 8)
so all state updates flow through the store actions rather than setHours in the
TSX component.

Source: Coding guidelines

Comment thread components/morning/step-mood.tsx Outdated
step={0.5}
value={[hours]}
onValueChange={([v]) => setHours(v)}
onValueChange={([v]) => setHours(v ?? 8)}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major | 🏗️ Heavy lift

Use lib/store/actions for this update instead of direct state setter.

The handler still updates local component state directly in .tsx. Please route this through store actions to match repository rules.

As per coding guidelines, "**/*.{tsx,jsx}: Mutate state only via lib/store/actions — never use setState directly in components".

🤖 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 `@components/morning/step-sleep.tsx` at line 75, The onValueChange handler is
directly calling the local setter setHours; replace this with the appropriate
action from lib/store/actions: import the store action (e.g., the action that
updates sleep hours) and call that action with (v ?? 8) instead of calling
setHours. Update the onValueChange callback in components/morning/step-sleep.tsx
to dispatch/use the action from lib/store/actions (referencing the existing
setHours usage to locate the spot) and remove the direct setHours call so the
component mutates state only via lib/store/actions.

Source: Coding guidelines

Comment thread hooks/use-store.ts Outdated
Comment thread lib/store.ts Outdated
- Create lib/time/today.ts with getLocalTodayKey() to avoid UTC date bug
- Replace toISOString().slice(0,10) in lib/store.ts and hooks/use-store.ts
- Guard missing touches in step-mood.tsx instead of defaulting to (0,0)

Fixes 3 of 5 CodeRabbit comments on PR #8.
@TargiX

TargiX commented Jun 7, 2026

Copy link
Copy Markdown
Owner Author

Skipped CodeRabbit comments (1 & 3)

The setBedtime/setHours/setQuality calls in step-sleep-target.tsx and step-sleep.tsx are local component UI state (form inputs), not global store mutations. The actual store writes already go through updateTodayEntry(). Using useState for transient form state in components is correct — the rule about routing through lib/store/actions applies to global state, not per-component form state.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
lib/store/actions.test.ts (1)

14-15: ⚡ Quick win

Use getTodayKey() directly in these tests instead of Object.keys(...)[0]!.

These assertions currently depend on entry enumeration order and a non-null assertion. Since updateTodayEntry writes to the local day key contract, assert by getTodayKey() to keep the test deterministic and aligned with app date handling.

Proposed refactor
+import { getTodayKey } from "`@/lib/time/today`"
...
   it("creates today's entry from patch", () => {
     updateTodayEntry({ intention: "Focus" })
     const s = getSnapshot()
-    const key = Object.keys(s.entries)[0]!
-    expect(s.entries[key]!.intention).toBe("Focus")
+    const key = getTodayKey()
+    expect(s.entries[key]).toBeDefined()
+    expect(s.entries[key]!.intention).toBe("Focus")
   })
...
   it("merges into existing entry", () => {
     updateTodayEntry({ intention: "A" })
     updateTodayEntry({ journal: "Went well" })
     const s = getSnapshot()
-    const key = Object.keys(s.entries)[0]!
+    const key = getTodayKey()
+    expect(s.entries[key]).toBeDefined()
     expect(s.entries[key]!.intention).toBe("A")
     expect(s.entries[key]!.journal).toBe("Went well")
   })

As per coding guidelines: “Use local day keys via lib/time/today for date handling; never use toISOString() due to UTC bug.” Based on learnings: the same local day key rule applies for **/*.{ts,tsx,js,jsx}.

Also applies to: 22-24

🤖 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 `@lib/store/actions.test.ts` around lines 14 - 15, The test relies on
Object.keys(s.entries)[0]! which is fragile and non-deterministic; replace that
with the local day key from getTodayKey() and assert against
s.entries[getTodayKey()] (e.g., const key = getTodayKey();
expect(s.entries[key]!.intention).toBe("Focus")). Do the same replacement for
the other assertions mentioned (lines 22-24) that read entries by enumeration;
use getTodayKey() wherever updateTodayEntry or local-day entry access is
asserted to ensure deterministic, local-day-based tests.

Sources: Coding guidelines, Learnings

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

Nitpick comments:
In `@lib/store/actions.test.ts`:
- Around line 14-15: The test relies on Object.keys(s.entries)[0]! which is
fragile and non-deterministic; replace that with the local day key from
getTodayKey() and assert against s.entries[getTodayKey()] (e.g., const key =
getTodayKey(); expect(s.entries[key]!.intention).toBe("Focus")). Do the same
replacement for the other assertions mentioned (lines 22-24) that read entries
by enumeration; use getTodayKey() wherever updateTodayEntry or local-day entry
access is asserted to ensure deterministic, local-day-based tests.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: fdc56c19-63d3-4ae4-95a4-aacddb419a85

📥 Commits

Reviewing files that changed from the base of the PR and between 6f88306 and 0626a1f.

📒 Files selected for processing (2)
  • lib/auth/credentials.ts
  • lib/store/actions.test.ts

@TargiX

TargiX commented Jun 8, 2026

Copy link
Copy Markdown
Owner Author

🧹 CodeRabbit review janitor pass (5 comments)

All 5 CodeRabbit comments are stale — the code already addresses each one:

  1. lib/store.ts getTodayKey() UTC — file doesn't exist; lib/time/today.ts already uses local getFullYear/getMonth/getDate.
  2. hooks/use-store.ts useTodayEntry() UTC — already imports and uses getTodayKey() from lib/time/today.
  3. step-sleep-target.tsx slider through store actions — slider state is local UI only; handleNext() already calls updateTodayEntry().
  4. step-mood.tsx touches[0] default — already has if (!touch) return guard.
  5. step-sleep.tsx use lib/store/actions — already imports and calls updateTodayEntry.

No changes needed.

@TargiX

TargiX commented Jun 8, 2026

Copy link
Copy Markdown
Owner Author

🧹 CodeRabbit review janitor pass

Verified all 5 inline CodeRabbit comments against current branch state:

  1. hooks/use-store.ts — UTC day key ✅ Already fixed — uses getTodayKey() from @/lib/time/today.
  2. lib/store.ts — UTC day key ✅ Stale — file was moved to lib/store/store.ts; no toISOString() usage anywhere in code.
  3. components/morning/step-mood.tsx — touch (0,0) fallback ✅ Already fixed — early return when touch is falsy.
  4. components/evening/step-sleep-target.tsx — route slider through store actions ⏭️ Skipped — this is local useState for a step wizard, not persistent store state. The refactor suggestion is architectural and out of scope for this strict-mode PR.
  5. components/morning/step-sleep.tsx — same as ci: add GitHub Actions CI + store tests #4 ⏭️ Same reasoning.

lib/store/actions.test.ts nitpick already addressed — uses getTodayKey() instead of Object.keys.

All checks green. PR is ready for review/merge.

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