sveltekit-app-setup
| Type | Skill |
| Plugin | awl-web · v0.0.6 |
| Invoke | /awl-web:sveltekit-app-setup |
| Tools | Read, Write, Edit, Bash, Glob, Grep |
| Source | plugins/awl-web/skills/sveltekit-app-setup/SKILL.md |
When Claude uses it
Section titled “When Claude uses it”Wires a new or existing SvelteKit app with the internal @awl/* packages in the order the docs imply - registry, OIDC login, session, i18n, OpenTelemetry metrics, HTTP client, Tailwind theme and a Playwright smoke test. Use for “set up sveltekit app”, “new awl web project”, “wire @awl packages”, “add oidc login”, “awl session”, “sveltekit i18n”, “opentelemetry sveltekit”, “hooks.server.ts”, “+layout.server.ts guard”, “awl registry npmrc”. NOT for a single package’s API (use awl-npm-packages), Payload CMS plugins (use payload-plugins) or writing the test suite itself (use awl-testing testing-web).
Trigger phrases: set up sveltekit app · new awl web project · wire @awl packages · add oidc login · awl session · sveltekit i18n · opentelemetry sveltekit · hooks.server.ts · +layout.server.ts guard · awl registry npmrc
Definition
Section titled “Definition”Layers go in this order because each one reads what the previous one puts on
event.locals: session before OIDC, session before i18n’s fromSession()
detector, metrics before anything that records. Every snippet below is copied
from the Storybook at https://npm-packages.staging.appswithlove.net; when a
detail isn’t there, this skill says so instead of guessing. Per-package API
tables live in awl-npm-packages/references/<package>.md.
Which package solves what
Section titled “Which package solves what”| Need | Package | Read |
|---|---|---|
| Login, logout, token refresh, route guards | @awl/oidc (/sveltekit) + @awl/session, or @awl/sveltekit-oidc + @awl/sveltekit-utils |
references/auth-oidc.md |
| Server session, flash messages, typed session data | @awl/session |
references/session-and-i18n.md |
Translations, locale detection, <html lang> |
@awl/i18n |
references/session-and-i18n.md |
| Run a handle hook on some routes only | only from @awl/sveltekit-utils/hooks |
references/auth-oidc.md |
| Counters and gauges exported to VictoriaMetrics or Prometheus | @awl/opentelemetry |
references/observability-and-request.md |
| Typed HTTP client with bearer auth and JSON:API | @awl/request |
references/observability-and-request.md |
Error messages, safe tuples, cookies, search params, proxy |
@awl/utils |
references/observability-and-request.md |
| px-based Tailwind theme and AWL breakpoints | @awl/tailwindcss |
references/env-and-config.md |
URL-synced state (ReactiveSearchParam) |
@awl/svelte-utils |
references/env-and-config.md |
| Hydration and a11y matchers for Playwright | @awl/playwright-sveltekit, @awl/playwright-a11y |
references/env-and-config.md |
Skip a layer the project doesn’t need. A content site without login needs no OIDC or session; an internal tool without translations needs no i18n.
Workflow
Section titled “Workflow”1. Inspect the project
Section titled “1. Inspect the project”Check package.json, src/hooks.server.ts, src/app.d.ts and
src/routes/+layout.server.ts before writing anything. An existing hook
sequence has to be extended, not replaced, and App.Locals has to be merged
into one declaration.
2. Registry and install
Section titled “2. Registry and install”Point the @awl scope at the GitLab package registry (project 3919) in the
user-level .npmrc, so the token stays out of the repo. CI uses
CI_JOB_TOKEN, Docker builds mount .npmrc as a secret. Commands and the
Dockerfile block: references/env-and-config.md.
pnpm add @awl/oidc @awl/session @awl/i18n @awl/opentelemetry @awl/request @awl/utils @awl/tailwindcsspnpm add -D @awl/playwright-sveltekit @awl/playwright-a11yThe docs use npm install; the registry config applies to pnpm as well. Pick
the OIDC package first (step 3) and drop the ones you don’t use.
3. Auth
Section titled “3. Auth”Decide between the two documented integrations, see the comparison table in
references/auth-oidc.md. With @awl/oidc, the
hook order is fixed:
import { sequence } from "@sveltejs/kit/hooks";import { session } from "$lib/config/session";import { oidc } from "$lib/config/oidc";
export const handle = sequence( session.handler, // make sure this runs before oidc.authenticate, // refreshes access token silently on expiry);authenticate only refreshes tokens. Guard routes from the server file that
owns them, because a guard protects only what calls it:
// src/routes/(app)/+layout.server.tsimport { oidc } from "$lib/config/oidc";
export const load = async (event) => { const user = await oidc.protect(event); return { user };};Form actions and +server.ts handlers call oidc.protect(event) themselves.
The catch-all action route is src/routes/oidc/[action]/+server.ts with
export const GET = oidc.actionHandler.
With @awl/sveltekit-oidc the hook is oidc.authenticate() and guards are
only(oidc.protect()).path('/admin*') in the same sequence.
4. Session
Section titled “4. Session”Already done in step 3 when using @awl/oidc. Standalone, the handler is
createSession({ storage }) from @awl/session/sveltekit and the session
is locals.session with get, set, once, remember, destroy. Use
MemoryStorage in dev and RedisStorage in production.
5. i18n
Section titled “5. i18n”Build the instance in src/lib/i18n.ts with createI18n<typeof en>(), the
nesting, interpolation and sveltekit plugins, and .hook({ detect, create }).
Then wire the two exports and the %locale% placeholder:
// src/hooks.server.ts (add to the sequence after session.handler)import { handle as i18nHandle } from '$lib/i18n'export { layoutServerLoad as load } from '$lib/i18n'<html lang="%locale%">The docs show export { handle } from '$lib/i18n' as the only hook; the
combined sequence and the alias above follow from that. Keep the root
layout as this re-export and put the OIDC guard in a group layout, because
the docs don’t show how to merge layoutServerLoad with another load.
6. Observability
Section titled “6. Observability”Create src/lib/server/metrics.ts with createMetrics({...}, { namespace, service, environment, readers })
and import it at the top of hooks.server.ts. Readers must exist before the
first measurement, and setReaders replaces all earlier readers. Use
@awl/opentelemetry/otlp-proto for VictoriaMetrics and install the matching
@opentelemetry/exporter-* peer dependency. The package covers metrics
only.
7. HTTP client and errors
Section titled “7. HTTP client and errors”Compose request(url).plugin(vars).plugin(options).plugin(auth).plugin(fetchPlugin)
in src/lib/server/api.ts and call .bearer(token).fetch() in loads. Catch
RequestError for status and URL, getMessage from @awl/utils/errors for
a display string, safe from @awl/utils for tuple-style handling.
8. Utils and Tailwind
Section titled “8. Utils and Tailwind”Replace @import "tailwindcss" with @import "@awl/tailwindcss" in the
global CSS. Add optimizeDeps.exclude: ['@awl/svelte-utils'] to
vite.config.ts when that package is installed.
9. Smoke test
Section titled “9. Smoke test”Extend expect with toBeHydrated and toBeAccessible, open /, assert
both. The Playwright config itself is in the awl-testing plugin’s
testing-web skill.
Verify
Section titled “Verify”pnpm installresolves every@awl/*package from the GitLab registry.pnpm checkpasses with the mergedApp.Locals(session,i18n, oruserandaccessToken).pnpm dev, then open/oidc/login: the browser is redirected to the issuer. After login, a guarded page renders and/oidc/logoutclears it.- A page under the guard returns
302for a browser request and401forfetchwithout a session. - Switching the locale cookie changes the server-rendered
langattribute and the translated strings. - The metrics reader exports at least one counter (VictoriaMetrics, Prometheus scrape, or the
debuglog). - The Playwright smoke test passes against
pnpm preview.
Env variables
Section titled “Env variables”| Variable | Used by |
|---|---|
OIDC_CLIENT_ID, OIDC_CLIENT_SECRET |
both OIDC packages |
OIDC_SECRET |
cookie encryption, secret option or CookieDataStorage |
NODE_ENV |
@awl/opentelemetry environment |
| issuer URL, Redis URL, API base URL, OTLP endpoint | not named in the docs, pick your own |
Read them via $env/dynamic/private in SvelteKit server modules. Full table
and reasons: references/env-and-config.md.
What the docs leave open
Section titled “What the docs leave open”- Which OIDC package the team prefers for greenfield apps. Neither is marked deprecated.
- With
@awl/oidc, how to read the access token for outbound API calls. - The combined hook
sequencewith session, OIDC and i18n; only pairs are shown. - Merging
layoutServerLoadwith your own root layoutload. - OTLP endpoint and Prometheus port configuration for the exporter wrappers.
- Tailwind version and Vite plugin prerequisites for
@awl/tailwindcss. - Playwright config,
webServerand CI job.
When one of these blocks you, check the live page:
https://npm-packages.staging.appswithlove.net/?path=/docs/<id> with ids
general-oidc--readme, sveltekit-sveltekit-oidc--readme,
general-session--readme, general-i18n--readme,
general-opentelemetry--readme, utils-request--docs, readme--docs.

