3.5 KiB
Frontend tooling
Type-safety and quality tooling for the Vue 3 + TypeScript SPAs.
ts-reset
Installed in both apps/portal/ and apps/app/ as a dev-dependency.
Imported via src/reset.d.ts:
import '@total-typescript/ts-reset'
Both SPAs' tsconfig.json include ./src/**/*, so the .d.ts is
picked up automatically — no explicit include edit needed.
What it changes
ts-reset patches TypeScript's loosest default types:
Array.filter(Boolean)returns non-nullableArray<T>instead ofArray<T | null | undefined | false | 0 | "">JSON.parsereturnsunknowninstead ofanyfetch().then(r => r.json())returnsunknowninstead ofanyArray.includes()accepts narrower typesMap.get()returnsT | undefinedmore strictly- See https://github.com/total-typescript/ts-reset for the full list
Why this matters
The default TypeScript types for these built-ins lose information that real code relies on. ts-reset fixes the types to match runtime behavior, surfacing bugs that were previously hidden.
Adoption state
apps/app — 0 pre-install tsc errors in own code; install
surfaced 2 errors in src/stores/useImpersonationStore.ts (both
from JSON.parse on sessionStorage content). Both fixed inline via
as ImpersonationState assertions that make the existing trust-in-
sessionStorage explicit. A backlog entry (TECH-TS-IMPERSONATION)
tracks proper runtime validation of the stored shape.
apps/portal — 22 pre-existing tsc errors in own code (mostly
tiptap editor components — unrelated to ts-reset). ts-reset added
0 new errors in portal's own code. 4 additional errors surfaced in
tiptap's third-party .ts sources under node_modules (tiptap
ships uncompiled TypeScript and some of its internal JSON.parse
call sites fall under ts-reset's tightening). Not our code; left
as-is.
Neither SPA achieves tsc --noEmit clean today — that's a pre-
existing state unrelated to this work package. Build + vitest are
the actual CI-relevant gates and both remain green.
Bypassing
@ts-expect-error with comment justification at the call site —
never blanket-disable ts-reset. If a specific reset is genuinely
wrong for the project, document it; the upstream issue is the
backstop.
Vitest
Runs against apps/portal/ and apps/app/. apps/app/ has a
Vitest test suite (402 tests as of May 2026); run with pnpm test
from apps/app/.
Storybook
Storybook 10.x is installed in apps/app/ as a component development and documentation tool.
Scripts (run from apps/app/):
pnpm storybook— dev server on http://localhost:6006pnpm build-storybook— static build to storybook-static/
Config files:
- apps/app/.storybook/main.ts — framework, stories glob, addons, viteFinal aliases
- apps/app/.storybook/preview.ts — PrimeVue (via installPrimeVue plugin), Tailwind
Stories location: co-located with components (ComponentName.stories.ts next to ComponentName.vue). Exception: smoke-test stories live in src/stories/.
Addons (explicit, no addon-essentials):
- @storybook/addon-docs — autodocs, prop tables
- @storybook/addon-a11y — axe-core accessibility checks per story
Scope boundary: Storybook = isolated render, autodocs, a11y. Interaction testing (click, form submit, emit) stays in Playwright CT. Do NOT install @storybook/addon-interactions.
Not in Storybook: page-level views, components with direct useAuthStore / useOrganisationStore calls (these need to be decoupled to props first).