Activity descriptions now render through a small marked + DOMPurify pipeline. Client-side only — the server keeps storing raw markdown source, private descriptions stay inside the encrypted payload. frontend/src/lib/markdown.ts exposes a single renderMarkdown(src) helper. Allowlist: p / br / hr / strong / em / del / s / code / pre / ul / ol / li / blockquote / a / h3–h6. URL scheme allowlist: http(s) and mailto. Images are deliberately stripped — external image URLs leak the viewer's IP to the linker's host. Raw HTML pass-through is off. A DOMPurify afterSanitizeAttributes hook forces target="_blank" rel="noopener noreferrer ugc" on every <a>, matching the existing external-link pattern in PublicList. h1/h2 in the source get downshifted to h3 via walkTokens so the description's heading hierarchy doesn't collide with the SPA's own <h1>/<h2>. Render sites: the two description spots in ActivityRow.svelte (one for the decrypted private branch, one for non-private). New .md class in styles.css gives the rendered block tight spacing, discreet code/pre/blockquote treatment, accent-coloured links. UX: a "Du kan bruke Markdown — **fet**, *kursiv*, [lenke](https://…), lister med -" hint under the textarea in ActivityForm. No preview pane to keep scope contained. Tests: 14 cases in tests/markdown.test.ts covering the allowlist (bold/italic/strike/lists/links/mailto), the sanitisation surface (javascript: / data: / script / iframe / on* handlers / images), and the h1/h2 → h3 downshift. happy-dom is registered locally in beforeAll (and the markdown module dynamic-imported) rather than as a project-wide preload — the latter overrides fetch/Request/Response and breaks Bun-fetch-based API tests in other files. Bundle impact: marked + dompurify add ~60KB to the SPA bundle.
35 lines
1.1 KiB
JSON
35 lines
1.1 KiB
JSON
{
|
|
"name": "vinterliste",
|
|
"version": "0.1.0",
|
|
"description": "End-to-end encrypted winter activity list",
|
|
"private": true,
|
|
"type": "module",
|
|
"scripts": {
|
|
"dev:server": "bun run --hot server/index.ts",
|
|
"dev:frontend": "vite --config frontend/vite.config.ts",
|
|
"build:frontend": "vite build --config frontend/vite.config.ts",
|
|
"start": "NODE_ENV=production bun run server/index.ts",
|
|
"test": "bun test",
|
|
"typecheck": "tsc --noEmit && tsc --noEmit -p frontend/tsconfig.json",
|
|
"reset-password": "bun run server/reset-password.ts"
|
|
},
|
|
"dependencies": {
|
|
"dompurify": "^3.4.5",
|
|
"hono": "^4.6.0",
|
|
"libsodium-wrappers-sumo": "^0.7.15",
|
|
"marked": "^18.0.4",
|
|
"svelte-dnd-action": "^0.9.69"
|
|
},
|
|
"devDependencies": {
|
|
"@happy-dom/global-registrator": "^20.9.0",
|
|
"@sveltejs/vite-plugin-svelte": "^5.0.0",
|
|
"@tsconfig/svelte": "^5.0.4",
|
|
"@types/bun": "^1.1.0",
|
|
"@types/dompurify": "^3.2.0",
|
|
"@types/libsodium-wrappers-sumo": "^0.7.8",
|
|
"svelte": "^5.0.0",
|
|
"svelte-check": "^4.0.0",
|
|
"typescript": "^5.6.0",
|
|
"vite": "^6.0.0"
|
|
}
|
|
}
|