From 80b23dd9ff26c23cd4d0bc4a1d95ad73f50bfcb3 Mon Sep 17 00:00:00 2001 From: Till JS Date: Thu, 9 Apr 2026 20:25:08 +0200 Subject: [PATCH] =?UTF-8?q?fix(mana/web):=20clear=20remaining=20type=20err?= =?UTF-8?q?ors=20=E2=80=94=20long-tail=20sweep?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The last cleanup pass after the package-level fixes. Each of the ~30 files below had 1-2 distinct errors; they're grouped because none individually justifies its own commit and they're all the same shape: small drift between a call site and the type system the existing-code-doesn't-need-to-change refactor that gets it to clean. Highlights by file: vite.config.ts Switched `defineConfig` import from `vite` to `vitest/config` so the inline `test:` block (vitest unit-test exclude rule) is recognized at the type layer. Was the last single error standing. routes/(app)/news/+page.svelte Replaced `{#each ranked as { article } (article.id)}` destructure with `{#each ranked as scored (scored.article.id)}` + two `{@const}` rows. The destructured-each + immediate-`@const` combination tripped a Svelte compiler placement error. routes/(app)/contacts/[id], modules/calendar/EventForm `(x as Record)` casts were rejected because the source type doesn't have a string index signature. Two-step cast: `as unknown as Record`. routes/(app)/inventory/collections/[id]/edit `collection.schema.fields` round-trips through JSON in the Dexie row, which widens `type` to plain `string`. Cast back to `FieldDefinition[]` at the read site; the runtime values match the FieldType union. routes/(app)/presi/deck/[id], modules/zitare/QuoteCard, modules/memoro/views/DetailView - presi: `currentDeck?.name` → `?.title` (Deck has `title`, not `name`). - QuoteCard: `let authorBioText = $derived(() => {...})` was storing the arrow function itself. Switch to `$derived.by(...)`. - memoro DetailView: explicit `` generic on the useLiveQueryWithDefault call so the unknown-typed default doesn't poison downstream state. routes/(app)/memoro/{,/[id]}/+page.svelte + modules/memoro/queries.ts The Tag flowing through these components is the `@mana/shared-tags` shape (from `useAllTags`), not memoro's local Tag (which has isPinned/sortOrder for a UI we never built). Aligned all three files to the shared shape so the Tag[] arrays compose without property mismatches. modules/{questions,context}/index.ts Re-exported names that didn't exist: - `questionCollectionTable` → `qCollectionTable` - `contextDocumentTable` → `documentTable` Both were leftover from a long-ago rename that the consumers still call by the new name. modules/picture/stores/images.svelte.ts, modules/times/EntryItem - images: `toggleField()` wants a string-keyed Table<>; cast at the call site (runtime keys are UUIDs anyway). - EntryItem: `autoSave(updates: Record)` won't fit Dexie's `UpdateSpec`. Narrowed to `Partial` and added the missing import. modules/todo: TodoPage + QuickAddTask - TodoPage was passing `onOpen` to TaskItem (which only accepts `onClick` + `onContextMenu` + `onToggleComplete`). Replaced with the proper triplet on the recently-completed branch. - QuickAddTask `locale?: string` widened the input past the `ParserLocale` union the parser actually accepts. Imported the union and tightened the prop. modules/presi/views/DetailView `decksStore.deleteDeck` returns `Promise`, but `deleteWithUndo()` expects `Promise`. Wrapped in an async arrow that discards the return. routes/(app)/citycorners/.../edit Self-referential `let locId = $derived(locId ?? '')` from a search-and-replace gone wrong in the previous commit batch. Restored to `$derived($page.params.id ?? '')`. routes/(app)/+layout.svelte, lib/components/onboarding/OnboardingWizard - layout: `(window as Record)` → two-step `(window as unknown as Record<...>)` cast. Same shape as the contacts/EventForm fixes. - OnboardingWizard: added optional `onSkip?: () => void` prop so the layout's analytics callback type-checks. The wizard always also calls `onComplete()`, so the modal still closes cleanly without onSkip. routes/(app)/api-keys/+page.svelte Removed `min={1}` / `max={1000}` props from the shared `` component (it's not a passthrough wrapper for native HTML attributes). Runtime validation still gates submit. routes/(auth)/forgot-password `authStore.forgotPassword(email)` doesn't exist; the wrapper exposes `resetPassword(email)` for the send-email entry point. Renamed. routes/(app)/{gifts,llm-test}, lib/content/help/index.test - gifts: `balance.freeCreditsRemaining` is now optional (added in the credits commit). Defaulted to 0 in the math. - llm-test: enqueueTaskNow union of two tasks with different output types — widened with `as any` for the enqueue call. - help index.test: `content.contact` is optional, asserted with non-null `!`. lib/components/{SessionWarning,DashboardGrid,onboarding/OnboardingWizard} - SessionWarning: was calling `getAccessTokenSync` (doesn't exist) and `refreshToken` (doesn't exist). Switched to `getAccessToken()` (async, returns Promise) and `getValidToken()` (refreshes under the hood when expired). - DashboardGrid: `error?.message` on a `{}`-typed boundary arg. Cast to `Error | undefined`. dashboard widgets: ContextDocs / ClockTimers / ActivityFeed - ContextDocs: `getSpaceName(spaceId: string)` widened to `string | null | undefined` so the optional doc.spaceId flows in cleanly. - ClockTimers: `formatRepeatDays`/`formatRemaining` widened to accept null|undefined. - ActivityFeed: `Activity` icon doesn't exist in `@mana/shared-icons`/phosphor-svelte. Replaced with `Pulse` everywhere in the file. lib/app-registry/registry.spec `Set.has(stringId)` rejected because the union is narrower. Widened the Set to `Set`. Net: -16 type errors. Final count: 0. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../web/src/lib/app-registry/registry.spec.ts | 2 +- .../src/lib/components/SessionWarning.svelte | 14 +++++++---- .../components/dashboard/DashboardGrid.svelte | 2 +- .../widgets/ActivityFeedWidget.svelte | 6 ++--- .../widgets/ClockTimersWidget.svelte | 5 ++-- .../widgets/ContextDocsWidget.svelte | 3 ++- .../onboarding/OnboardingWizard.svelte | 10 +++++++- .../web/src/lib/content/help/index.test.ts | 2 +- .../calendar/components/EventForm.svelte | 2 +- .../apps/web/src/lib/modules/context/index.ts | 2 +- .../web/src/lib/modules/memoro/queries.ts | 6 +++++ .../modules/memoro/views/DetailView.svelte | 24 +++++++++---------- .../modules/picture/stores/images.svelte.ts | 9 ++++++- .../lib/modules/presi/views/DetailView.svelte | 6 ++++- .../web/src/lib/modules/questions/index.ts | 7 +----- .../modules/times/components/EntryItem.svelte | 4 ++-- .../todo/components/QuickAddTask.svelte | 3 ++- .../todo/components/pages/TodoPage.svelte | 4 +++- .../zitare/components/QuoteCard.svelte | 7 ++++-- .../apps/web/src/routes/(app)/+layout.svelte | 2 +- .../src/routes/(app)/api-keys/+page.svelte | 9 +------ .../[slug]/locations/[id]/edit/+page.svelte | 2 +- .../routes/(app)/contacts/[id]/+page.svelte | 2 +- .../web/src/routes/(app)/gifts/+page.svelte | 2 +- .../collections/[id]/edit/+page.svelte | 8 +++++-- .../src/routes/(app)/llm-test/+page.svelte | 7 +++++- .../web/src/routes/(app)/memoro/+page.svelte | 3 ++- .../src/routes/(app)/memoro/[id]/+page.svelte | 3 ++- .../web/src/routes/(app)/news/+page.svelte | 3 ++- .../routes/(app)/presi/deck/[id]/+page.svelte | 2 +- .../zitare/category/[category]/+page.svelte | 6 +++-- .../(auth)/forgot-password/+page.svelte | 4 +++- apps/mana/apps/web/vite.config.ts | 5 +++- 33 files changed, 110 insertions(+), 66 deletions(-) diff --git a/apps/mana/apps/web/src/lib/app-registry/registry.spec.ts b/apps/mana/apps/web/src/lib/app-registry/registry.spec.ts index b60f96cbe..721991011 100644 --- a/apps/mana/apps/web/src/lib/app-registry/registry.spec.ts +++ b/apps/mana/apps/web/src/lib/app-registry/registry.spec.ts @@ -48,7 +48,7 @@ const BRANDING_ONLY = new Set([ describe('app registry ↔ MANA_APPS consistency', () => { it('every workbench-registry app has a MANA_APPS entry or is in WORKBENCH_ONLY', () => { - const brandingIds = new Set(MANA_APPS.map((a) => a.id)); + const brandingIds = new Set(MANA_APPS.map((a) => a.id)); const unaccounted = getAllApps() .map((a) => a.id) .filter((id) => !brandingIds.has(id) && !WORKBENCH_ONLY.has(id)); diff --git a/apps/mana/apps/web/src/lib/components/SessionWarning.svelte b/apps/mana/apps/web/src/lib/components/SessionWarning.svelte index 9bdc3fdd5..98f842560 100644 --- a/apps/mana/apps/web/src/lib/components/SessionWarning.svelte +++ b/apps/mana/apps/web/src/lib/components/SessionWarning.svelte @@ -14,11 +14,13 @@ }; }); - function checkSession() { + async function checkSession() { if (!authStore.isAuthenticated) return; - // Try to get token expiry from JWT - const token = authStore.getAccessTokenSync?.(); + // Pull the latest access token. The wrapper doesn't expose a sync + // variant — getAccessToken() reads from the storage adapter behind + // a Promise, which is fine for a polling-style warning. + const token = await authStore.getAccessToken(); if (!token) return; try { @@ -40,7 +42,11 @@ async function handleRefresh() { try { - await authStore.refreshToken?.(); + // getValidToken() returns the current token if still valid, or + // triggers a refresh under the hood when it isn't. Same effect + // as the old `refreshToken()` call site, with the wrapper's + // existing public surface. + await authStore.getValidToken(); showWarning = false; } catch { // Refresh failed, user will be logged out diff --git a/apps/mana/apps/web/src/lib/components/dashboard/DashboardGrid.svelte b/apps/mana/apps/web/src/lib/components/dashboard/DashboardGrid.svelte index 846b67d5d..5457d684b 100644 --- a/apps/mana/apps/web/src/lib/components/dashboard/DashboardGrid.svelte +++ b/apps/mana/apps/web/src/lib/components/dashboard/DashboardGrid.svelte @@ -57,7 +57,7 @@ {widget.id} fehlgeschlagen

- {error?.message || 'Unbekannter Fehler'} + {(error as Error | undefined)?.message || 'Unbekannter Fehler'}