Imported from cxr-dev/Nasta (
AGENTS.md). Install upstream withnpx skills add cxr-dev/Nasta. Copyright stays with the author.
AGENTS.md — Nasta
Project
Frontend-only PWA for Stockholm SL commuter departures. Svelte 5 (Runes) + TypeScript strict. No SSR, no backend.
Commands
pnpm install # must use pnpm (packageManager: "pnpm@11.9.0")
pnpm run dev # Vite dev server, base="/". predev hook fetches events-data.json
pnpm run build # production build to dist/, base="/Nasta/". prebuild: generate-png-icons + fetch-events-data
pnpm run check # svelte-check type checking (run before test)
pnpm test # Vitest unit tests (jsdom)
pnpm run test:watch # Vitest in watch mode
pnpm run test:e2e # Playwright E2E — builds first, then tests against vite preview
pnpm run verify:build # post-build verification (checks /Nasta/ base path, no SSR runtime leaks)
pnpm run preview # preview production build locally
Deployment validation runs in .github/workflows/deploy.yml: the validate job runs check, unit tests, build, and Playwright E2E; the build job creates and verifies the Pages artifact; deployment is followed by a smoke test. Scheduled runs refresh events-data.json at 06:17 and 10:47 UTC.
Quirks & conventions
- pnpm only.
packageManager: "pnpm@11.9.0",node-linker=hoisted(.npmrc). Stalepackage-lock.jsonis gitignored — delete if found. - No linter, no formatter. TypeScript
strict: true+checkJs: trueis the only enforcement. - Svelte 5 Runes. All stores use module-level
$state()+ manual subscriber arrays — notsvelte/storewritable/derived. Stores export either an object with methods (departureStore,deviationStore) or bare functions (pageStore,settingsStore,localeStore,stopAreaStore). Rune module files use.svelte.tsextension. - Base path is
/in dev,/Nasta/in production (GitHub Pages). Always useimport.meta.env.BASE_URLfor asset/service worker paths.verify-build.mjsenforces this in CI. - PWA service worker only in preview/production —
vite-plugin-pwadevOptions.enabled: false. prebuildhook runsscripts/generate-png-icons.mjs+scripts/fetch-events-data.mjsautomatically before everybuild.predevhook runsfetch-events-data.mjstoo. Output:public/events-data.json(gitignored, NetworkFirst in SW runtime cache).- SL Transport API is public — no API key needed. Fetched directly from browser.
- Sjostadstrafiken ferries detected by stop name, served from hardcoded schedule (
staticTimetable.ts) instead of SL API. - Request ID routing — route changes generate
requestIdvalues; stale API responses from earlier routes silently dropped (prevents race conditions on fast route switching). - CSP is a
<meta>tag inindex.html(not an HTTP header). Updateconnect-srcwhen adding new API origins (Supabase, Overpass, Visit Stockholm, corsproxy.io). - No
<svelte:head>— all meta tags live inindex.html. - Theme default.
defaultSettings.theme = 'system'; the resolved palette follows the operating-system preference unless the user chooses Light or Dark. - Env vars all optional (
VITE_*). No.envfile needed.VITE_SUPABASE_ANON_KEY,VITE_SUPABASE_DEFAULT_CITY_ID,VITE_USE_CORS_PROXY,VITE_CORS_PROXY_BASEexist but optional. maplibre-gldynamically imported inMapPreview.svelte— code-split automatically.opening_hoursdependency used for venue hours parsing in feature discovery.- Feature discovery uses three external APIs: Visit Stockholm (events, CORS-enabled), Supabase Edge Function (beer venues), Overpass API (OSM wine/cocktail venues).
- CSS transformer is
lightningcss(not PostCSS). Vite config:css.transformer: "lightningcss". - SL timestamps are timezone-naive Stockholm local time.
parseSlTimestamp()inslApi.tshandles DST-aware conversion.
Testing
- Unit tests (
pnpm test): vitest with jsdom,resolve.conditions: ['browser']. Setup invitest.setup.tsmocksglobalThis.localStoragewith avi.fn()object. Tests are*.test.tsco-located with source.@testing-library/svelteavailable for component tests. - E2E tests (
pnpm run test:e2e): Playwright intests/e2e/.fullyParallel: false,workers: 1(CI) /2(local). Service workers blocked (serviceWorkers: "block") sopage.route()mocks work. Timezone:Europe/Stockholm, locale:en-US. CSS animations disabled inbeforeEach. LocalStorage seeded viapage.addInitScript()(bypasses onboarding). Web server:vite previewon port 4173. Mock pattern:**/*.integration.sl.se/**for SL API, also stubizrgqxgsuhogrukisfrd.supabase.cofor venue prefetches.
Design constraints
From .impeccable.md: no gradient text, no side-stripe borders, no glassmorphism. Mobile-first, glanceable UI. Light mode primary. No decorative fluff.
Architecture
App.svelte → stores (pageStore, departureStore, deviationStore, settingsStore, localeStore, stopAreaStore) → services (SL API, caching, geo, static timetable, deviation cache, venue/event prefetching) → components. Persistence: LocalStorage + IndexedDB (deviations cache). See docs/ARCHITECTURE.md for data flow.