From 0b7203df0fa6c4c61214b17e471b9303d629e133 Mon Sep 17 00:00:00 2001 From: Stefan Hildebrandt <695494+hildebrandttk@users.noreply.github.com> Date: Wed, 17 Jun 2026 23:40:40 +0200 Subject: [PATCH] chore: clear pre-commit eslint warnings (unused symbols, stale disables, test any) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cleans up the lint warnings the pre-commit hook surfaces, without any behavioral change: - Remove unused imports/vars/destructured props (parseISO, useEffect, format, durMin, roles, daysInYear, ALLOWED_PLUGIN_FILES, continuesBefore, isPushConnected, isSelected) and the now-unused parseDuration import. - Drop three stale `// eslint-disable-next-line no-undef` directives that no longer suppress anything (browser-navigation, smime/crypto-engine). - recurrence-expansion.test.ts: replace 39 `as any` casts with a cast-only `rule()` helper for partial recurrence-rule fixtures, typed access to utcStart/utcEnd (now on CalendarEvent), and the source's `Partial & { excluded?: boolean }` for the excluded override. No defaults are injected, so the expansion logic sees the same partial rules as before (35 tests still green). Remaining: 7 react-hooks/exhaustive-deps warnings are left as-is — adding the missing deps changes effect/memo timing and needs per-hook review, not a mechanical fix. tsc --noEmit clean; eslint 0 errors / 7 warnings. --- app/api/admin/marketplace/route.ts | 2 +- components/calendar/calendar-agenda-view.tsx | 3 +- components/calendar/event-card.tsx | 2 +- components/calendar/event-detail-popover.tsx | 2 +- components/calendar/event-modal.tsx | 1 - components/calendar/ical-import-modal.tsx | 2 +- components/contacts/contact-detail.tsx | 1 - components/contacts/contact-form.tsx | 2 +- components/layout/navigation-rail.tsx | 1 - components/layout/sidebar.tsx | 1 - lib/__tests__/recurrence-expansion.test.ts | 109 ++++++++++--------- lib/birthday-calendar.ts | 2 +- lib/browser-navigation.ts | 1 - lib/recurrence-expansion.ts | 1 - lib/smime/crypto-engine.ts | 2 - 15 files changed, 66 insertions(+), 66 deletions(-) diff --git a/app/api/admin/marketplace/route.ts b/app/api/admin/marketplace/route.ts index fe54a601..d44887ce 100644 --- a/app/api/admin/marketplace/route.ts +++ b/app/api/admin/marketplace/route.ts @@ -19,7 +19,7 @@ import { invalidateFrameOriginsCache, } from '@/lib/admin/csp-frame-origins'; import JSZip from 'jszip'; -import { MAX_PLUGIN_SIZE, MAX_THEME_SIZE, ALL_PERMISSIONS, ALLOWED_PLUGIN_FILES } from '@/lib/plugin-types'; +import { MAX_PLUGIN_SIZE, MAX_THEME_SIZE, ALL_PERMISSIONS } from '@/lib/plugin-types'; import { sanitizeThemeCSS, validateThemeCSSSafety } from '@/lib/theme-loader'; import { configManager } from '@/lib/admin/config-manager'; diff --git a/components/calendar/calendar-agenda-view.tsx b/components/calendar/calendar-agenda-view.tsx index c8c79db9..1aa0513c 100644 --- a/components/calendar/calendar-agenda-view.tsx +++ b/components/calendar/calendar-agenda-view.tsx @@ -5,7 +5,7 @@ import { useTranslations, useFormatter } from "next-intl"; import { format, isToday, isTomorrow, startOfDay } from "date-fns"; import { MapPin, Users } from "lucide-react"; import { cn } from "@/lib/utils"; -import { parseDuration, getEventColor } from "./event-card"; +import { getEventColor } from "./event-card"; import { getEventDayBounds, getEventEndDate, getEventStartDate, getPrimaryCalendarId } from "@/lib/calendar-utils"; import { getParticipantCount } from "@/lib/calendar-participants"; import type { CalendarEvent, Calendar } from "@/lib/jmap/types"; @@ -147,7 +147,6 @@ export function CalendarAgendaView({ const calendar = calId ? calendarMap.get(calId) : undefined; const color = getEventColor(ev, calendar); const start = getEventStartDate(ev); - const durMin = parseDuration(ev.duration); const end = getEventEndDate(ev); const locationName = ev.locations ? Object.values(ev.locations)[0]?.name diff --git a/components/calendar/event-card.tsx b/components/calendar/event-card.tsx index 46197d96..10808d0e 100644 --- a/components/calendar/event-card.tsx +++ b/components/calendar/event-card.tsx @@ -71,7 +71,7 @@ function createEventDragPreview(title: string, timeRange: string, color: string) return el; } -export function EventCard({ event, calendar, variant, onClick, onMouseEnter, onMouseLeave, onContextMenu, isSelected, draggable: isDraggable, continuesBefore = false, continuesAfter = false, className, style }: EventCardProps) { +export function EventCard({ event, calendar, variant, onClick, onMouseEnter, onMouseLeave, onContextMenu, isSelected, draggable: isDraggable, continuesAfter = false, className, style }: EventCardProps) { const t = useTranslations("calendar"); const [isBeingDragged, setIsBeingDragged] = useState(false); const color = getEventColor(event, calendar); diff --git a/components/calendar/event-detail-popover.tsx b/components/calendar/event-detail-popover.tsx index 23c21d43..79af4181 100644 --- a/components/calendar/event-detail-popover.tsx +++ b/components/calendar/event-detail-popover.tsx @@ -8,7 +8,7 @@ import { X, Clock, MapPin, Video, Users, Repeat, Bell, AlignLeft, Pencil, Trash2, Copy, Send, Check, } from "lucide-react"; -import { format, isSameDay, parseISO } from "date-fns"; +import { format, isSameDay } from "date-fns"; import { cn } from "@/lib/utils"; import type { CalendarEvent, Calendar, CalendarParticipant } from "@/lib/jmap/types"; import { parseDuration, getEventColor } from "./event-card"; diff --git a/components/calendar/event-modal.tsx b/components/calendar/event-modal.tsx index 5fc192e3..609613f1 100644 --- a/components/calendar/event-modal.tsx +++ b/components/calendar/event-modal.tsx @@ -617,7 +617,6 @@ export function EventModal({ if (isAttendeeMode && event) { const startD = getEventStartDate(event); - const durMin = parseDuration(event.duration); const endD = getEventEndDate(event); const locationName = event.locations ? Object.values(event.locations)[0]?.name : null; const participants = getParticipantList(event); diff --git a/components/calendar/ical-import-modal.tsx b/components/calendar/ical-import-modal.tsx index 0c987b96..36215829 100644 --- a/components/calendar/ical-import-modal.tsx +++ b/components/calendar/ical-import-modal.tsx @@ -4,7 +4,7 @@ import { useState, useCallback, useRef, useEffect } from "react"; import { useTranslations } from "next-intl"; import { Button } from "@/components/ui/button"; import { X, Upload, Check, Loader2, RefreshCw, Globe } from "lucide-react"; -import { format, parseISO } from "date-fns"; +import { format } from "date-fns"; import type { CalendarEvent, Calendar } from "@/lib/jmap/types"; import type { IJMAPClient } from '@/lib/jmap/client-interface'; import { getEventStartDate } from "@/lib/calendar-utils"; diff --git a/components/contacts/contact-detail.tsx b/components/contacts/contact-detail.tsx index b2f489d5..2dc70b10 100644 --- a/components/contacts/contact-detail.tsx +++ b/components/contacts/contact-detail.tsx @@ -203,7 +203,6 @@ export function ContactDetail({ contact, onEdit, onDelete, onAddToGroup, onDupli const notes = contact.notes ? Object.values(contact.notes) : []; const titles = contact.titles ? Object.values(contact.titles) : []; const jobTitles = titles.filter(t => t.kind !== "role"); - const roles = titles.filter(t => t.kind === "role"); const onlineServices = contact.onlineServices ? Object.values(contact.onlineServices) : []; const anniversaries = contact.anniversaries ? Object.values(contact.anniversaries) : []; const keywords = contact.keywords ? Object.keys(contact.keywords).filter(k => contact.keywords![k]) : []; diff --git a/components/contacts/contact-form.tsx b/components/contacts/contact-form.tsx index 462fdd9d..ebf6fe1c 100644 --- a/components/contacts/contact-form.tsx +++ b/components/contacts/contact-form.tsx @@ -1,6 +1,6 @@ "use client"; -import { useState, useMemo, useCallback, useEffect, useRef } from "react"; +import { useState, useMemo, useCallback, useRef } from "react"; import { useTranslations } from "next-intl"; import { X, Plus, ChevronDown, ChevronRight, User, Building, MapPin, Globe, Cake, Heart, Tag, StickyNote, Mail, Phone, Calendar, UserCircle, Book, Camera, Trash2 } from "lucide-react"; import { Button } from "@/components/ui/button"; diff --git a/components/layout/navigation-rail.tsx b/components/layout/navigation-rail.tsx index cbef6757..add5c4fa 100644 --- a/components/layout/navigation-rail.tsx +++ b/components/layout/navigation-rail.tsx @@ -166,7 +166,6 @@ export function NavigationRail({ collapsed = false, className, quota, - isPushConnected, onLogout, onShowShortcuts, onManageApps, diff --git a/components/layout/sidebar.tsx b/components/layout/sidebar.tsx index 93fbfbe8..53655202 100644 --- a/components/layout/sidebar.tsx +++ b/components/layout/sidebar.tsx @@ -169,7 +169,6 @@ function getIconClass(isSelected: boolean, isVirtual: boolean, colorful: boolean function SidebarRowCounts({ unread, total, - isSelected, onUnreadClick, }: { unread?: number; diff --git a/lib/__tests__/recurrence-expansion.test.ts b/lib/__tests__/recurrence-expansion.test.ts index 48d8f840..71e829f8 100644 --- a/lib/__tests__/recurrence-expansion.test.ts +++ b/lib/__tests__/recurrence-expansion.test.ts @@ -1,6 +1,15 @@ import { describe, it, expect } from 'vitest'; import { expandRecurringEvents } from '../recurrence-expansion'; -import type { CalendarEvent } from '@/lib/jmap/types'; +import type { CalendarEvent, CalendarRecurrenceRule } from '@/lib/jmap/types'; + +/** + * Helper: builds a recurrence-rule fixture from only the fields a test cares + * about. Cast-only (no defaults injected) so the expansion logic sees exactly + * the same partial rule the tests previously passed via `as any`. + */ +function rule(partial: Partial): CalendarRecurrenceRule { + return partial as CalendarRecurrenceRule; +} /** Helper: create a minimal CalendarEvent for testing recurrence */ function makeEvent(overrides: Partial = {}): CalendarEvent { @@ -41,7 +50,7 @@ describe('expandRecurringEvents', () => { describe('daily frequency', () => { it('expands daily events within range', () => { const event = makeEvent({ - recurrenceRules: [{ '@type': 'RecurrenceRule', frequency: 'daily' } as any], + recurrenceRules: [rule({ '@type': 'RecurrenceRule', frequency: 'daily' })], }); const result = expand(event, '2025-01-06T00:00:00', '2025-01-09T00:00:00'); expect(starts(result)).toEqual([ @@ -53,7 +62,7 @@ describe('expandRecurringEvents', () => { it('respects interval', () => { const event = makeEvent({ - recurrenceRules: [{ '@type': 'RecurrenceRule', frequency: 'daily', interval: 2 } as any], + recurrenceRules: [rule({ '@type': 'RecurrenceRule', frequency: 'daily', interval: 2 })], }); const result = expand(event, '2025-01-06T00:00:00', '2025-01-12T00:00:00'); expect(starts(result)).toEqual([ @@ -65,7 +74,7 @@ describe('expandRecurringEvents', () => { it('respects count', () => { const event = makeEvent({ - recurrenceRules: [{ '@type': 'RecurrenceRule', frequency: 'daily', count: 3 } as any], + recurrenceRules: [rule({ '@type': 'RecurrenceRule', frequency: 'daily', count: 3 })], }); const result = expand(event, '2025-01-06T00:00:00', '2025-12-31T00:00:00'); expect(result).toHaveLength(3); @@ -73,7 +82,7 @@ describe('expandRecurringEvents', () => { it('respects until', () => { const event = makeEvent({ - recurrenceRules: [{ '@type': 'RecurrenceRule', frequency: 'daily', until: '2025-01-08T09:00:00' } as any], + recurrenceRules: [rule({ '@type': 'RecurrenceRule', frequency: 'daily', until: '2025-01-08T09:00:00' })], }); const result = expand(event, '2025-01-06T00:00:00', '2025-12-31T00:00:00'); expect(result).toHaveLength(3); @@ -86,7 +95,7 @@ describe('expandRecurringEvents', () => { describe('weekly frequency', () => { it('expands weekly with implicit byDay (same weekday as start)', () => { const event = makeEvent({ - recurrenceRules: [{ '@type': 'RecurrenceRule', frequency: 'weekly' } as any], + recurrenceRules: [rule({ '@type': 'RecurrenceRule', frequency: 'weekly' })], }); // Jan 6 is Monday, so every Monday const result = expand(event, '2025-01-06T00:00:00', '2025-01-28T00:00:00'); @@ -100,11 +109,11 @@ describe('expandRecurringEvents', () => { it('expands weekly with explicit byDay (MWF)', () => { const event = makeEvent({ - recurrenceRules: [{ + recurrenceRules: [rule({ '@type': 'RecurrenceRule', frequency: 'weekly', byDay: [{ day: 'mo' }, { day: 'we' }, { day: 'fr' }], - } as any], + })], }); const result = expand(event, '2025-01-06T00:00:00', '2025-01-13T00:00:00'); expect(starts(result)).toEqual([ @@ -116,12 +125,12 @@ describe('expandRecurringEvents', () => { it('expands weekly with interval=2', () => { const event = makeEvent({ - recurrenceRules: [{ + recurrenceRules: [rule({ '@type': 'RecurrenceRule', frequency: 'weekly', interval: 2, byDay: [{ day: 'mo' }], - } as any], + })], }); const result = expand(event, '2025-01-06T00:00:00', '2025-02-03T00:00:00'); expect(starts(result)).toEqual([ @@ -138,7 +147,7 @@ describe('expandRecurringEvents', () => { it('expands monthly with implicit byMonthDay', () => { const event = makeEvent({ start: '2025-01-15T10:00:00', - recurrenceRules: [{ '@type': 'RecurrenceRule', frequency: 'monthly' } as any], + recurrenceRules: [rule({ '@type': 'RecurrenceRule', frequency: 'monthly' })], }); const result = expand(event, '2025-01-01T00:00:00', '2025-04-01T00:00:00'); expect(starts(result)).toEqual([ @@ -151,11 +160,11 @@ describe('expandRecurringEvents', () => { it('expands monthly with byMonthDay', () => { const event = makeEvent({ start: '2025-01-01T08:00:00', - recurrenceRules: [{ + recurrenceRules: [rule({ '@type': 'RecurrenceRule', frequency: 'monthly', byMonthDay: [1, 15], - } as any], + })], }); const result = expand(event, '2025-01-01T00:00:00', '2025-02-28T00:00:00'); expect(starts(result)).toEqual([ @@ -169,11 +178,11 @@ describe('expandRecurringEvents', () => { it('expands monthly with negative byMonthDay (-1 = last day)', () => { const event = makeEvent({ start: '2025-01-31T08:00:00', - recurrenceRules: [{ + recurrenceRules: [rule({ '@type': 'RecurrenceRule', frequency: 'monthly', byMonthDay: [-1], - } as any], + })], }); const result = expand(event, '2025-01-01T00:00:00', '2025-04-01T00:00:00'); const days = result.map(e => e.start.substring(0, 10)); @@ -183,11 +192,11 @@ describe('expandRecurringEvents', () => { it('expands monthly with byDay + nthOfPeriod (2nd Tuesday)', () => { const event = makeEvent({ start: '2025-01-14T09:00:00', // 2nd Tuesday - recurrenceRules: [{ + recurrenceRules: [rule({ '@type': 'RecurrenceRule', frequency: 'monthly', byDay: [{ day: 'tu', nthOfPeriod: 2 }], - } as any], + })], }); const result = expand(event, '2025-01-01T00:00:00', '2025-04-01T00:00:00'); const days = result.map(e => e.start.substring(0, 10)); @@ -197,11 +206,11 @@ describe('expandRecurringEvents', () => { it('expands monthly with byDay nthOfPeriod=-1 (last Friday)', () => { const event = makeEvent({ start: '2025-01-31T09:00:00', // last Friday of Jan - recurrenceRules: [{ + recurrenceRules: [rule({ '@type': 'RecurrenceRule', frequency: 'monthly', byDay: [{ day: 'fr', nthOfPeriod: -1 }], - } as any], + })], }); const result = expand(event, '2025-01-01T00:00:00', '2025-04-01T00:00:00'); const days = result.map(e => e.start.substring(0, 10)); @@ -216,7 +225,7 @@ describe('expandRecurringEvents', () => { it('expands yearly on the same date', () => { const event = makeEvent({ start: '2023-03-15T12:00:00', - recurrenceRules: [{ '@type': 'RecurrenceRule', frequency: 'yearly' } as any], + recurrenceRules: [rule({ '@type': 'RecurrenceRule', frequency: 'yearly' })], }); const result = expand(event, '2023-01-01T00:00:00', '2026-01-01T00:00:00'); expect(starts(result)).toEqual([ @@ -229,12 +238,12 @@ describe('expandRecurringEvents', () => { it('expands yearly with byMonth and byDay (last Friday of November = Thanksgiving-ish)', () => { const event = makeEvent({ start: '2025-11-28T09:00:00', // last Friday of Nov 2025 - recurrenceRules: [{ + recurrenceRules: [rule({ '@type': 'RecurrenceRule', frequency: 'yearly', byMonth: ['11'], byDay: [{ day: 'fr', nthOfPeriod: -1 }], - } as any], + })], }); const result = expand(event, '2025-01-01T00:00:00', '2028-01-01T00:00:00'); const days = result.map(e => e.start.substring(0, 10)); @@ -246,12 +255,12 @@ describe('expandRecurringEvents', () => { const event = makeEvent({ start: '2025-07-04T00:00:00', showWithoutTime: true, - recurrenceRules: [{ + recurrenceRules: [rule({ '@type': 'RecurrenceRule', frequency: 'yearly', byMonth: ['7'], byMonthDay: [4], - } as any], + })], }); const result = expand(event, '2025-01-01T00:00:00', '2028-01-01T00:00:00'); expect(result).toHaveLength(3); @@ -265,12 +274,12 @@ describe('expandRecurringEvents', () => { it('selects first and last from monthly byDay expansion', () => { const event = makeEvent({ start: '2025-01-06T10:00:00', - recurrenceRules: [{ + recurrenceRules: [rule({ '@type': 'RecurrenceRule', frequency: 'monthly', byDay: [{ day: 'mo' }, { day: 'tu' }, { day: 'we' }, { day: 'th' }, { day: 'fr' }], bySetPosition: [1, -1], // first and last weekday of month - } as any], + })], }); const result = expand(event, '2025-01-01T00:00:00', '2025-03-01T00:00:00'); const days = result.map(e => e.start.substring(0, 10)); @@ -289,7 +298,7 @@ describe('expandRecurringEvents', () => { describe('recurrenceOverrides', () => { it('applies overrides to matching occurrences', () => { const event = makeEvent({ - recurrenceRules: [{ '@type': 'RecurrenceRule', frequency: 'daily' } as any], + recurrenceRules: [rule({ '@type': 'RecurrenceRule', frequency: 'daily' })], recurrenceOverrides: { '2025-01-07T09:00:00': { title: 'Modified' }, }, @@ -301,9 +310,9 @@ describe('expandRecurringEvents', () => { it('excludes occurrences marked as excluded', () => { const event = makeEvent({ - recurrenceRules: [{ '@type': 'RecurrenceRule', frequency: 'daily' } as any], + recurrenceRules: [rule({ '@type': 'RecurrenceRule', frequency: 'daily' })], recurrenceOverrides: { - '2025-01-07T09:00:00': { excluded: true } as any, + '2025-01-07T09:00:00': { excluded: true } as Partial & { excluded?: boolean }, }, }); const result = expand(event, '2025-01-06T00:00:00', '2025-01-09T00:00:00'); @@ -313,7 +322,7 @@ describe('expandRecurringEvents', () => { it('adds RDATE-style overrides not generated by rules', () => { const event = makeEvent({ - recurrenceRules: [{ '@type': 'RecurrenceRule', frequency: 'weekly' } as any], + recurrenceRules: [rule({ '@type': 'RecurrenceRule', frequency: 'weekly' })], recurrenceOverrides: { '2025-01-08T09:00:00': { title: 'Extra Wednesday' }, // Not a Monday }, @@ -329,12 +338,12 @@ describe('expandRecurringEvents', () => { describe('excludedRecurrenceRules', () => { it('removes occurrences generated by excluded rules', () => { const event = makeEvent({ - recurrenceRules: [{ '@type': 'RecurrenceRule', frequency: 'daily' } as any], - excludedRecurrenceRules: [{ + recurrenceRules: [rule({ '@type': 'RecurrenceRule', frequency: 'daily' })], + excludedRecurrenceRules: [rule({ '@type': 'RecurrenceRule', frequency: 'weekly', byDay: [{ day: 'tu' }], - } as any], + })], }); // Jan 6-12 2025: Mon-Sun, Tuesday Jan 7 excluded const result = expand(event, '2025-01-06T00:00:00', '2025-01-13T00:00:00'); @@ -353,7 +362,7 @@ describe('expandRecurringEvents', () => { it('expands a daily series started years before the visible range', () => { const event = makeEvent({ start: '2022-01-03T09:00:00', - recurrenceRules: [{ '@type': 'RecurrenceRule', frequency: 'daily' } as any], + recurrenceRules: [rule({ '@type': 'RecurrenceRule', frequency: 'daily' })], }); const result = expand(event, '2025-06-02T00:00:00', '2025-06-05T00:00:00'); expect(starts(result)).toEqual([ @@ -366,7 +375,7 @@ describe('expandRecurringEvents', () => { it('expands a weekly series started years before the visible range', () => { const event = makeEvent({ start: '2020-01-06T09:00:00', // Monday - recurrenceRules: [{ '@type': 'RecurrenceRule', frequency: 'weekly' } as any], + recurrenceRules: [rule({ '@type': 'RecurrenceRule', frequency: 'weekly' })], }); const result = expand(event, '2025-06-01T00:00:00', '2025-06-30T00:00:00'); const days = result.map(e => e.start.substring(0, 10)); @@ -376,7 +385,7 @@ describe('expandRecurringEvents', () => { it('still respects count for old series (no fast-forward shortcut)', () => { const event = makeEvent({ start: '2025-01-06T09:00:00', - recurrenceRules: [{ '@type': 'RecurrenceRule', frequency: 'daily', count: 5 } as any], + recurrenceRules: [rule({ '@type': 'RecurrenceRule', frequency: 'daily', count: 5 })], }); // Range far after the 5 occurrences ran out const result = expand(event, '2025-06-01T00:00:00', '2025-06-30T00:00:00'); @@ -395,13 +404,13 @@ describe('expandRecurringEvents', () => { timeZone: 'America/New_York', utcStart: '2025-03-03T15:00:00Z', utcEnd: '2025-03-03T16:00:00Z', - recurrenceRules: [{ '@type': 'RecurrenceRule', frequency: 'weekly' } as any], + recurrenceRules: [rule({ '@type': 'RecurrenceRule', frequency: 'weekly' })], } as Partial); const result = expand(event, '2025-03-03T00:00:00', '2025-03-17T00:00:00'); - const utcStarts = result.map(e => (e as any).utcStart); + const utcStarts = result.map(e => e.utcStart); expect(utcStarts[0]).toBe('2025-03-03T15:00:00.000Z'); // EST: 10:00 -5 expect(utcStarts[1]).toBe('2025-03-10T14:00:00.000Z'); // EDT: 10:00 -4 - const utcEnds = result.map(e => (e as any).utcEnd); + const utcEnds = result.map(e => e.utcEnd); expect(utcEnds[1]).toBe('2025-03-10T15:00:00.000Z'); }); }); @@ -414,7 +423,7 @@ describe('expandRecurringEvents', () => { const event = makeEvent({ start: '2025-01-06T00:00:00', showWithoutTime: true, - recurrenceRules: [{ '@type': 'RecurrenceRule', frequency: 'weekly' } as any], + recurrenceRules: [rule({ '@type': 'RecurrenceRule', frequency: 'weekly' })], }); const result = expand(event, '2025-01-06T00:00:00', '2025-01-28T00:00:00'); expect(result).toHaveLength(4); // 4 Mondays: 6, 13, 20, 27 @@ -427,7 +436,7 @@ describe('expandRecurringEvents', () => { describe('edge cases', () => { it('does not exceed 500 occurrences', () => { const event = makeEvent({ - recurrenceRules: [{ '@type': 'RecurrenceRule', frequency: 'daily' } as any], + recurrenceRules: [rule({ '@type': 'RecurrenceRule', frequency: 'daily' })], }); const result = expand(event, '2025-01-01T00:00:00', '2030-01-01T00:00:00'); expect(result.length).toBeLessThanOrEqual(500); @@ -436,7 +445,7 @@ describe('expandRecurringEvents', () => { it('handles invalid start date gracefully', () => { const event = makeEvent({ start: 'invalid', - recurrenceRules: [{ '@type': 'RecurrenceRule', frequency: 'daily' } as any], + recurrenceRules: [rule({ '@type': 'RecurrenceRule', frequency: 'daily' })], }); const result = expand(event, '2025-01-01T00:00:00', '2025-02-01T00:00:00'); expect(result).toHaveLength(0); @@ -444,7 +453,7 @@ describe('expandRecurringEvents', () => { it('generates synthetic IDs for occurrences', () => { const event = makeEvent({ - recurrenceRules: [{ '@type': 'RecurrenceRule', frequency: 'daily' } as any], + recurrenceRules: [rule({ '@type': 'RecurrenceRule', frequency: 'daily' })], }); const result = expand(event, '2025-01-06T00:00:00', '2025-01-08T00:00:00'); expect(result[0].id).toBe('evt1:2025-01-06T09:00:00'); @@ -453,7 +462,7 @@ describe('expandRecurringEvents', () => { it('preserves originalId pointing to master', () => { const event = makeEvent({ - recurrenceRules: [{ '@type': 'RecurrenceRule', frequency: 'daily' } as any], + recurrenceRules: [rule({ '@type': 'RecurrenceRule', frequency: 'daily' })], }); const result = expand(event, '2025-01-06T00:00:00', '2025-01-08T00:00:00'); expect(result[0].originalId).toBe('evt1'); @@ -465,7 +474,7 @@ describe('expandRecurringEvents', () => { id: 'master1', uid: 'shared-uid', start: '2025-01-06T09:00:00', - recurrenceRules: [{ '@type': 'RecurrenceRule', frequency: 'weekly' } as any], + recurrenceRules: [rule({ '@type': 'RecurrenceRule', frequency: 'weekly' })], }); const override1 = makeEvent({ id: 'override1', @@ -527,10 +536,10 @@ describe('expandRecurringEvents', () => { const event = makeEvent({ start: '2026-09-01T12:00:00', utcStart: '2026-09-01T10:00:00Z', - recurrenceRules: [{ '@type': 'RecurrenceRule', frequency: 'weekly' } as any], + recurrenceRules: [rule({ '@type': 'RecurrenceRule', frequency: 'weekly' })], }); const result = expand(event, '2026-09-01T00:00:00', '2026-10-01T00:00:00'); - const utcStarts = result.map(e => (e as any).utcStart); + const utcStarts = result.map(e => e.utcStart); // Each occurrence should have a unique utcStart expect(new Set(utcStarts).size).toBe(result.length); // First occurrence keeps master's UTC offset relationship @@ -545,10 +554,10 @@ describe('expandRecurringEvents', () => { duration: 'PT1H', utcStart: '2026-09-01T10:00:00Z', utcEnd: '2026-09-01T11:00:00Z', - recurrenceRules: [{ '@type': 'RecurrenceRule', frequency: 'weekly' } as any], + recurrenceRules: [rule({ '@type': 'RecurrenceRule', frequency: 'weekly' })], }); const result = expand(event, '2026-09-01T00:00:00', '2026-10-01T00:00:00'); - const utcEnds = result.map(e => (e as any).utcEnd); + const utcEnds = result.map(e => e.utcEnd); // Each occurrence should have a unique utcEnd expect(new Set(utcEnds).size).toBe(result.length); expect(utcEnds[0]).toContain('2026-09-01'); @@ -559,7 +568,7 @@ describe('expandRecurringEvents', () => { it('does not set utcStart when master has none', () => { const event = makeEvent({ start: '2025-01-06T09:00:00', - recurrenceRules: [{ '@type': 'RecurrenceRule', frequency: 'daily' } as any], + recurrenceRules: [rule({ '@type': 'RecurrenceRule', frequency: 'daily' })], }); const result = expand(event, '2025-01-06T00:00:00', '2025-01-08T00:00:00'); expect(result[0].utcStart).toBeUndefined(); diff --git a/lib/birthday-calendar.ts b/lib/birthday-calendar.ts index efbd73ac..1b3f5eca 100644 --- a/lib/birthday-calendar.ts +++ b/lib/birthday-calendar.ts @@ -1,6 +1,6 @@ import type { ContactCard, CalendarEvent, Calendar, PartialDate, Timestamp } from '@/lib/jmap/types'; import { getContactDisplayName } from '@/stores/contact-store'; -import { format, eachYearOfInterval, parseISO } from 'date-fns'; +import { eachYearOfInterval, parseISO } from 'date-fns'; export const BIRTHDAY_CALENDAR_ID = '__birthday-calendar__'; export const BIRTHDAY_CALENDAR_COLOR = '#eab308'; // Yellow diff --git a/lib/browser-navigation.ts b/lib/browser-navigation.ts index 8cd0854a..39c5f0d7 100644 --- a/lib/browser-navigation.ts +++ b/lib/browser-navigation.ts @@ -67,7 +67,6 @@ export function getPathPrefix(locale?: string): string { * // Browser at /webmail/en/inbox → /webmail/api/jmap * // Browser at /en/inbox → /api/jmap */ -// eslint-disable-next-line no-undef export function apiFetch(input: string, init?: RequestInit): Promise { if (input.startsWith('/') && !input.startsWith('//')) { return fetch(getPathPrefix() + input, init); diff --git a/lib/recurrence-expansion.ts b/lib/recurrence-expansion.ts index a805bb0c..68963a89 100644 --- a/lib/recurrence-expansion.ts +++ b/lib/recurrence-expansion.ts @@ -784,7 +784,6 @@ function nthWeekdayInYear(date: Date, nth: number): number { const firstYd = getDayOfYear(first); return Math.floor((yd - firstYd) / 7) + 1; } else { - const daysInYear = isLeapYear(date.getFullYear()) ? 366 : 365; const dec31 = new Date(date.getFullYear(), 11, 31); let last = dec31; while (last.getDay() !== weekday) last = addDays(last, -1); diff --git a/lib/smime/crypto-engine.ts b/lib/smime/crypto-engine.ts index 0a37fe01..792b7a3d 100644 --- a/lib/smime/crypto-engine.ts +++ b/lib/smime/crypto-engine.ts @@ -225,7 +225,6 @@ class Pkcs12CryptoEngine extends pkijs.CryptoEngine { const cryptoKey = await this.importKey( 'raw', keyData, - // eslint-disable-next-line no-undef { name: algName, length: keyLen * 8 } as Algorithm, false, ['decrypt'], @@ -234,7 +233,6 @@ class Pkcs12CryptoEngine extends pkijs.CryptoEngine { // Decrypt const ciphertext = parameters.encryptedContentInfo.getEncryptedContent(); return this.decrypt( - // eslint-disable-next-line no-undef { name: algName, iv: ivBytes } as Algorithm, cryptoKey, ciphertext,