fix(13-02): eliminate all ESLint violations — pnpm lint exits 0

- eslint.config.js: disable React Compiler rules (v7 flat.recommended enables
  them; codebase does not use the Compiler); add e2e/ to disableTypeChecked
  block; promote exhaustive-deps to error
- API broker: remove redundant as-casts (outboxWorker, poller, reminderScheduler,
  expand, sync, vevent, spike); add targeted ical.js no-unsafe-assignment/argument
  disables with justifying comments inside try blocks
- API routes/sse.ts: fix no-misused-promises on async writeSSE callback with
  void+IIFE+catch pattern
- API routes/lists.ts: let → const for updateValues
- API tests: remove unused imports (beforeEach, eq, vi); rename unused vars
  with _ prefix; remove unused lastActiveId assignment
- PWA components: void navigate() and void queryClient.invalidateQueries() on
  all fire-and-forget call sites; fix CalendarShell explicit-type-casts;
  Couldn't → HTML entity
- PWA test files: as unknown as Response for partial mock objects; string | null
  type annotation on mockLastSyncedUid; remove async from test callbacks without
  await; act(() => {}) not await act(async () => {}) for sync ops
- sw.ts: restructure Notification.data?.url access as let+if so disable
  comments land on the exact violation lines; void self.skipWaiting()
This commit is contained in:
Lucas Berger
2026-06-11 20:23:38 -04:00
parent 39e26561cf
commit 03e953158a
31 changed files with 176 additions and 121 deletions
+10 -10
View File
@@ -11,8 +11,8 @@
* Uses React Query's QueryClient directly (no mocked server).
*/
import { describe, it, expect, vi, beforeEach } from 'vitest'
import { renderHook, act } from '@testing-library/react'
import { describe, it, expect, beforeEach } from 'vitest'
import { act } from '@testing-library/react'
import { QueryClient } from '@tanstack/react-query'
import type { ListItem, ListItemsResponse } from '../api/listsClient.js'
@@ -80,7 +80,7 @@ describe('ListDetail — D-07 optimistic update + rollback', () => {
expect(updated?.items[0].checked).toBe(false)
})
it('rolls back the checked state if the server PATCH returns an error', async () => {
it('rolls back the checked state if the server PATCH returns an error', () => {
const item = makeItem({ checked: false })
queryClient.setQueryData<ListItemsResponse>(['list', LIST_ID], makeItemsResponse([item]))
@@ -88,7 +88,7 @@ describe('ListDetail — D-07 optimistic update + rollback', () => {
const previous = queryClient.getQueryData<ListItemsResponse>(['list', LIST_ID])
// Step 2: apply optimistic update
await act(async () => {
act(() => {
queryClient.setQueryData<ListItemsResponse>(['list', LIST_ID], (old) => ({
items: (old?.items ?? []).map((i) =>
i.id === item.id ? { ...i, checked: true } : i,
@@ -101,7 +101,7 @@ describe('ListDetail — D-07 optimistic update + rollback', () => {
expect(after?.items[0].checked).toBe(true)
// Step 3: simulate onError rollback
await act(async () => {
act(() => {
if (previous) {
queryClient.setQueryData(['list', LIST_ID], previous)
}
@@ -112,7 +112,7 @@ describe('ListDetail — D-07 optimistic update + rollback', () => {
expect(rolledBack?.items[0].checked).toBe(false)
})
it('adding an item shows it in the list immediately (optimistic insert)', async () => {
it('adding an item shows it in the list immediately (optimistic insert)', () => {
queryClient.setQueryData<ListItemsResponse>(['list', LIST_ID], makeItemsResponse([]))
const optimisticItem: ListItem = {
@@ -123,7 +123,7 @@ describe('ListDetail — D-07 optimistic update + rollback', () => {
rank: 'a0',
}
await act(async () => {
act(() => {
queryClient.setQueryData<ListItemsResponse>(['list', LIST_ID], (old) => ({
items: [...(old?.items ?? []), optimisticItem],
}))
@@ -136,7 +136,7 @@ describe('ListDetail — D-07 optimistic update + rollback', () => {
expect(data?.items[0].id).toBeLessThan(0)
})
it('removes the optimistically-added item if the server POST returns an error', async () => {
it('removes the optimistically-added item if the server POST returns an error', () => {
const existingItem = makeItem({ id: 1 })
queryClient.setQueryData<ListItemsResponse>(
['list', LIST_ID],
@@ -155,7 +155,7 @@ describe('ListDetail — D-07 optimistic update + rollback', () => {
rank: 'a1',
}
await act(async () => {
act(() => {
queryClient.setQueryData<ListItemsResponse>(['list', LIST_ID], (old) => ({
items: [...(old?.items ?? []), optimisticItem],
}))
@@ -166,7 +166,7 @@ describe('ListDetail — D-07 optimistic update + rollback', () => {
).toHaveLength(2)
// Simulate rollback on error
await act(async () => {
act(() => {
if (previous) queryClient.setQueryData(['list', LIST_ID], previous)
})
+9 -5
View File
@@ -181,7 +181,8 @@ export function ListDetail() {
}
},
onSettled: () => {
queryClient.invalidateQueries({ queryKey: ['list', parsedListId] })
// fire-and-forget: cache invalidation; React Query handles the refetch lifecycle
void queryClient.invalidateQueries({ queryKey: ['list', parsedListId] })
},
})
@@ -207,7 +208,8 @@ export function ListDetail() {
}
},
onSettled: () => {
queryClient.invalidateQueries({ queryKey: ['list', parsedListId] })
// fire-and-forget: cache invalidation; React Query handles the refetch lifecycle
void queryClient.invalidateQueries({ queryKey: ['list', parsedListId] })
},
})
@@ -223,7 +225,8 @@ export function ListDetail() {
},
// No onError rollback — delete-wins (D-09)
onSettled: () => {
queryClient.invalidateQueries({ queryKey: ['list', parsedListId] })
// fire-and-forget: cache invalidation; React Query handles the refetch lifecycle
void queryClient.invalidateQueries({ queryKey: ['list', parsedListId] })
},
})
@@ -251,7 +254,8 @@ export function ListDetail() {
}
},
onSettled: () => {
queryClient.invalidateQueries({ queryKey: ['list', parsedListId] })
// fire-and-forget: cache invalidation; React Query handles the refetch lifecycle
void queryClient.invalidateQueries({ queryKey: ['list', parsedListId] })
},
})
@@ -351,7 +355,7 @@ export function ListDetail() {
}}
>
<button
onClick={() => navigate('/lists')}
onClick={() => { void navigate('/lists') }}
aria-label="Back to lists"
style={{
background: 'none',
+4 -3
View File
@@ -66,10 +66,11 @@ export function ListsIndex() {
// TODO: surface "Couldn't delete. Try again." toast (Plan 06 / notification layer)
},
onSettled: () => {
queryClient.invalidateQueries({ queryKey: ['lists'] })
// fire-and-forget: cache invalidation; React Query handles the refetch lifecycle
void queryClient.invalidateQueries({ queryKey: ['lists'] })
},
onSuccess: () => {
navigate('/lists')
void navigate('/lists')
setDeleteTarget(null)
},
})
@@ -195,7 +196,7 @@ export function ListsIndex() {
</div>
<button
type="button"
onClick={() => refetch()}
onClick={() => { void refetch() }}
style={{
background: 'var(--color-member-0)',
color: '#fff',