vinterliste/package.json

35 lines
1.1 KiB
JSON
Raw Permalink Normal View History

Scaffold Vinterliste — end-to-end encrypted winter activity list Foundation for an E2E-encrypted activity list per winter-list-claude-code-prompt.md. Server (Bun + Hono): - bun:sqlite with WAL and the spec's schema (idempotent migration) - opaque server-stored sessions, httpOnly cookie - signup / challenge / login / logout / me / password / recovery-challenge / recovery-complete - activity CRUD with strict visibility rules: private uses ciphertext+nonce, semi never serializes owner_id, public attributes the owner - tag store with normalisation + autocomplete (semi/public only) Frontend (Svelte 5 + Vite): - libsodium-wrappers-sumo for client crypto (Argon2id + XChaCha20-Poly1305). SUMO is required because the standard build omits crypto_pwhash. - IndexedDB-backed private tag index (never leaves the browser) - in-memory DEK (no localStorage); page reload re-prompts for password - signup shows the recovery code once; tag input merges server + private sources with clear labelling - Bokmål UI Crypto module (shared/crypto.ts): - pure, runs in both Bun and the browser via a runtime-conditional loader that papers over libsodium-wrappers-sumo's broken ESM entry (createRequire on server, Vite alias in the browser) - DEK wrap/unwrap, AEAD payload encryption, recovery code generation with a visually-unambiguous alphabet Verification: - 22 crypto round-trip tests (wrap/unwrap, AEAD tamper rejection, password change preserves ciphertexts, recovery still works after rotation) - typecheck passes for server and frontend - Vite production build succeeds; libsodium SUMO chunk is ~315 KB gzipped Single-image Containerfile for podman: builds frontend in a builder stage, runs Bun in a slim runtime; one volume for the SQLite file; BUILD_DATE / GIT_REVISION baked into OCI labels and /etc/build-info. Known limitation deferred for this commit: the recovery endpoint has no server-side proof of the recovery code (anyone who knows an email can lock out the legitimate user, though they can't read any data). Closed in the next commit.
2026-05-25 12:27:14 +02:00
{
"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"
Scaffold Vinterliste — end-to-end encrypted winter activity list Foundation for an E2E-encrypted activity list per winter-list-claude-code-prompt.md. Server (Bun + Hono): - bun:sqlite with WAL and the spec's schema (idempotent migration) - opaque server-stored sessions, httpOnly cookie - signup / challenge / login / logout / me / password / recovery-challenge / recovery-complete - activity CRUD with strict visibility rules: private uses ciphertext+nonce, semi never serializes owner_id, public attributes the owner - tag store with normalisation + autocomplete (semi/public only) Frontend (Svelte 5 + Vite): - libsodium-wrappers-sumo for client crypto (Argon2id + XChaCha20-Poly1305). SUMO is required because the standard build omits crypto_pwhash. - IndexedDB-backed private tag index (never leaves the browser) - in-memory DEK (no localStorage); page reload re-prompts for password - signup shows the recovery code once; tag input merges server + private sources with clear labelling - Bokmål UI Crypto module (shared/crypto.ts): - pure, runs in both Bun and the browser via a runtime-conditional loader that papers over libsodium-wrappers-sumo's broken ESM entry (createRequire on server, Vite alias in the browser) - DEK wrap/unwrap, AEAD payload encryption, recovery code generation with a visually-unambiguous alphabet Verification: - 22 crypto round-trip tests (wrap/unwrap, AEAD tamper rejection, password change preserves ciphertexts, recovery still works after rotation) - typecheck passes for server and frontend - Vite production build succeeds; libsodium SUMO chunk is ~315 KB gzipped Single-image Containerfile for podman: builds frontend in a builder stage, runs Bun in a slim runtime; one volume for the SQLite file; BUILD_DATE / GIT_REVISION baked into OCI labels and /etc/build-info. Known limitation deferred for this commit: the recovery endpoint has no server-side proof of the recovery code (anyone who knows an email can lock out the legitimate user, though they can't read any data). Closed in the next commit.
2026-05-25 12:27:14 +02:00
},
"dependencies": {
feat(activity): Markdown rendering for descriptions 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.
2026-05-25 21:15:31 +02:00
"dompurify": "^3.4.5",
Scaffold Vinterliste — end-to-end encrypted winter activity list Foundation for an E2E-encrypted activity list per winter-list-claude-code-prompt.md. Server (Bun + Hono): - bun:sqlite with WAL and the spec's schema (idempotent migration) - opaque server-stored sessions, httpOnly cookie - signup / challenge / login / logout / me / password / recovery-challenge / recovery-complete - activity CRUD with strict visibility rules: private uses ciphertext+nonce, semi never serializes owner_id, public attributes the owner - tag store with normalisation + autocomplete (semi/public only) Frontend (Svelte 5 + Vite): - libsodium-wrappers-sumo for client crypto (Argon2id + XChaCha20-Poly1305). SUMO is required because the standard build omits crypto_pwhash. - IndexedDB-backed private tag index (never leaves the browser) - in-memory DEK (no localStorage); page reload re-prompts for password - signup shows the recovery code once; tag input merges server + private sources with clear labelling - Bokmål UI Crypto module (shared/crypto.ts): - pure, runs in both Bun and the browser via a runtime-conditional loader that papers over libsodium-wrappers-sumo's broken ESM entry (createRequire on server, Vite alias in the browser) - DEK wrap/unwrap, AEAD payload encryption, recovery code generation with a visually-unambiguous alphabet Verification: - 22 crypto round-trip tests (wrap/unwrap, AEAD tamper rejection, password change preserves ciphertexts, recovery still works after rotation) - typecheck passes for server and frontend - Vite production build succeeds; libsodium SUMO chunk is ~315 KB gzipped Single-image Containerfile for podman: builds frontend in a builder stage, runs Bun in a slim runtime; one volume for the SQLite file; BUILD_DATE / GIT_REVISION baked into OCI labels and /etc/build-info. Known limitation deferred for this commit: the recovery endpoint has no server-side proof of the recovery code (anyone who knows an email can lock out the legitimate user, though they can't read any data). Closed in the next commit.
2026-05-25 12:27:14 +02:00
"hono": "^4.6.0",
Drag-reorder works on touch via svelte-dnd-action HTML5 native drag-and-drop doesn't fire on touchscreens — mobile users couldn't reorder the list at all. Swapped the manual DnD wiring (dragstart/dragover/drop) for svelte-dnd-action, which uses Pointer Events and handles mouse, touch, AND keyboard reorder uniformly. Linear-quality reorder UX for ~11 KB gzipped. Replacement details: - bun add svelte-dnd-action (0.9.69) - Home.svelte: ~70 lines of manual handler code deleted, replaced with ~30 lines wiring up `use:dndzone` + `onconsider` + `onfinalize`. The midpoint-position math for sort_position is unchanged (finalize gives us the new neighbour list directly). - ActivityRow.svelte: the drag handle's `onpointerdown` flips a parent-owned dragDisabled flag to false — the library then takes over. Standard "handle-only drag" recipe; clicks on the title/buttons inside the card don't initiate drag because dragDisabled stays true everywhere else. - dndItems is a buffer copy of `filtered` that the library mutates during a drag. An $effect re-syncs it from `filtered` between drags (so new activities still float to the top, etc). - Shadow item (the library's placeholder while dragging) is rendered at 30% opacity so the drop target is visible without flashing. Accessibility wins for free: - Keyboard reorder: focus an item, press Space to "pick up", arrow keys to move, Space to drop. Screen readers get polite-live announcements of each move from the library. - Touch reorder works on iOS Safari and Android Chrome. 96 tests still pass; typecheck clean; build ok. Bundle: 122 KB → 154 KB (gzipped 42 → 53 KB, ~+11 KB).
2026-05-25 16:59:43 +02:00
"libsodium-wrappers-sumo": "^0.7.15",
feat(activity): Markdown rendering for descriptions 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.
2026-05-25 21:15:31 +02:00
"marked": "^18.0.4",
Drag-reorder works on touch via svelte-dnd-action HTML5 native drag-and-drop doesn't fire on touchscreens — mobile users couldn't reorder the list at all. Swapped the manual DnD wiring (dragstart/dragover/drop) for svelte-dnd-action, which uses Pointer Events and handles mouse, touch, AND keyboard reorder uniformly. Linear-quality reorder UX for ~11 KB gzipped. Replacement details: - bun add svelte-dnd-action (0.9.69) - Home.svelte: ~70 lines of manual handler code deleted, replaced with ~30 lines wiring up `use:dndzone` + `onconsider` + `onfinalize`. The midpoint-position math for sort_position is unchanged (finalize gives us the new neighbour list directly). - ActivityRow.svelte: the drag handle's `onpointerdown` flips a parent-owned dragDisabled flag to false — the library then takes over. Standard "handle-only drag" recipe; clicks on the title/buttons inside the card don't initiate drag because dragDisabled stays true everywhere else. - dndItems is a buffer copy of `filtered` that the library mutates during a drag. An $effect re-syncs it from `filtered` between drags (so new activities still float to the top, etc). - Shadow item (the library's placeholder while dragging) is rendered at 30% opacity so the drop target is visible without flashing. Accessibility wins for free: - Keyboard reorder: focus an item, press Space to "pick up", arrow keys to move, Space to drop. Screen readers get polite-live announcements of each move from the library. - Touch reorder works on iOS Safari and Android Chrome. 96 tests still pass; typecheck clean; build ok. Bundle: 122 KB → 154 KB (gzipped 42 → 53 KB, ~+11 KB).
2026-05-25 16:59:43 +02:00
"svelte-dnd-action": "^0.9.69"
Scaffold Vinterliste — end-to-end encrypted winter activity list Foundation for an E2E-encrypted activity list per winter-list-claude-code-prompt.md. Server (Bun + Hono): - bun:sqlite with WAL and the spec's schema (idempotent migration) - opaque server-stored sessions, httpOnly cookie - signup / challenge / login / logout / me / password / recovery-challenge / recovery-complete - activity CRUD with strict visibility rules: private uses ciphertext+nonce, semi never serializes owner_id, public attributes the owner - tag store with normalisation + autocomplete (semi/public only) Frontend (Svelte 5 + Vite): - libsodium-wrappers-sumo for client crypto (Argon2id + XChaCha20-Poly1305). SUMO is required because the standard build omits crypto_pwhash. - IndexedDB-backed private tag index (never leaves the browser) - in-memory DEK (no localStorage); page reload re-prompts for password - signup shows the recovery code once; tag input merges server + private sources with clear labelling - Bokmål UI Crypto module (shared/crypto.ts): - pure, runs in both Bun and the browser via a runtime-conditional loader that papers over libsodium-wrappers-sumo's broken ESM entry (createRequire on server, Vite alias in the browser) - DEK wrap/unwrap, AEAD payload encryption, recovery code generation with a visually-unambiguous alphabet Verification: - 22 crypto round-trip tests (wrap/unwrap, AEAD tamper rejection, password change preserves ciphertexts, recovery still works after rotation) - typecheck passes for server and frontend - Vite production build succeeds; libsodium SUMO chunk is ~315 KB gzipped Single-image Containerfile for podman: builds frontend in a builder stage, runs Bun in a slim runtime; one volume for the SQLite file; BUILD_DATE / GIT_REVISION baked into OCI labels and /etc/build-info. Known limitation deferred for this commit: the recovery endpoint has no server-side proof of the recovery code (anyone who knows an email can lock out the legitimate user, though they can't read any data). Closed in the next commit.
2026-05-25 12:27:14 +02:00
},
"devDependencies": {
feat(activity): Markdown rendering for descriptions 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.
2026-05-25 21:15:31 +02:00
"@happy-dom/global-registrator": "^20.9.0",
Scaffold Vinterliste — end-to-end encrypted winter activity list Foundation for an E2E-encrypted activity list per winter-list-claude-code-prompt.md. Server (Bun + Hono): - bun:sqlite with WAL and the spec's schema (idempotent migration) - opaque server-stored sessions, httpOnly cookie - signup / challenge / login / logout / me / password / recovery-challenge / recovery-complete - activity CRUD with strict visibility rules: private uses ciphertext+nonce, semi never serializes owner_id, public attributes the owner - tag store with normalisation + autocomplete (semi/public only) Frontend (Svelte 5 + Vite): - libsodium-wrappers-sumo for client crypto (Argon2id + XChaCha20-Poly1305). SUMO is required because the standard build omits crypto_pwhash. - IndexedDB-backed private tag index (never leaves the browser) - in-memory DEK (no localStorage); page reload re-prompts for password - signup shows the recovery code once; tag input merges server + private sources with clear labelling - Bokmål UI Crypto module (shared/crypto.ts): - pure, runs in both Bun and the browser via a runtime-conditional loader that papers over libsodium-wrappers-sumo's broken ESM entry (createRequire on server, Vite alias in the browser) - DEK wrap/unwrap, AEAD payload encryption, recovery code generation with a visually-unambiguous alphabet Verification: - 22 crypto round-trip tests (wrap/unwrap, AEAD tamper rejection, password change preserves ciphertexts, recovery still works after rotation) - typecheck passes for server and frontend - Vite production build succeeds; libsodium SUMO chunk is ~315 KB gzipped Single-image Containerfile for podman: builds frontend in a builder stage, runs Bun in a slim runtime; one volume for the SQLite file; BUILD_DATE / GIT_REVISION baked into OCI labels and /etc/build-info. Known limitation deferred for this commit: the recovery endpoint has no server-side proof of the recovery code (anyone who knows an email can lock out the legitimate user, though they can't read any data). Closed in the next commit.
2026-05-25 12:27:14 +02:00
"@sveltejs/vite-plugin-svelte": "^5.0.0",
"@tsconfig/svelte": "^5.0.4",
"@types/bun": "^1.1.0",
feat(activity): Markdown rendering for descriptions 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.
2026-05-25 21:15:31 +02:00
"@types/dompurify": "^3.2.0",
Scaffold Vinterliste — end-to-end encrypted winter activity list Foundation for an E2E-encrypted activity list per winter-list-claude-code-prompt.md. Server (Bun + Hono): - bun:sqlite with WAL and the spec's schema (idempotent migration) - opaque server-stored sessions, httpOnly cookie - signup / challenge / login / logout / me / password / recovery-challenge / recovery-complete - activity CRUD with strict visibility rules: private uses ciphertext+nonce, semi never serializes owner_id, public attributes the owner - tag store with normalisation + autocomplete (semi/public only) Frontend (Svelte 5 + Vite): - libsodium-wrappers-sumo for client crypto (Argon2id + XChaCha20-Poly1305). SUMO is required because the standard build omits crypto_pwhash. - IndexedDB-backed private tag index (never leaves the browser) - in-memory DEK (no localStorage); page reload re-prompts for password - signup shows the recovery code once; tag input merges server + private sources with clear labelling - Bokmål UI Crypto module (shared/crypto.ts): - pure, runs in both Bun and the browser via a runtime-conditional loader that papers over libsodium-wrappers-sumo's broken ESM entry (createRequire on server, Vite alias in the browser) - DEK wrap/unwrap, AEAD payload encryption, recovery code generation with a visually-unambiguous alphabet Verification: - 22 crypto round-trip tests (wrap/unwrap, AEAD tamper rejection, password change preserves ciphertexts, recovery still works after rotation) - typecheck passes for server and frontend - Vite production build succeeds; libsodium SUMO chunk is ~315 KB gzipped Single-image Containerfile for podman: builds frontend in a builder stage, runs Bun in a slim runtime; one volume for the SQLite file; BUILD_DATE / GIT_REVISION baked into OCI labels and /etc/build-info. Known limitation deferred for this commit: the recovery endpoint has no server-side proof of the recovery code (anyone who knows an email can lock out the legitimate user, though they can't read any data). Closed in the next commit.
2026-05-25 12:27:14 +02:00
"@types/libsodium-wrappers-sumo": "^0.7.8",
"svelte": "^5.0.0",
"svelte-check": "^4.0.0",
"typescript": "^5.6.0",
"vite": "^6.0.0"
}
}