diff --git a/.woodpecker/ci.yml b/.woodpecker/ci.yml index ff088c8..d21298f 100644 --- a/.woodpecker/ci.yml +++ b/.woodpecker/ci.yml @@ -1,4 +1,4 @@ -# CI: runs on every push. Lint, check, test, build (dev), e2e. +# CI: runs on every push. Install, lint, check, test, build (dev), e2e. when: - event: pull_request - event: push @@ -7,15 +7,75 @@ when: - event: manual steps: - build: + install: + image: node:22-bookworm-slim + commands: + - corepack enable + - corepack prepare pnpm@latest --activate + - pnpm install --frozen-lockfile + + lint: image: node:22-bookworm-slim commands: - corepack enable - corepack prepare pnpm@latest --activate - pnpm install --frozen-lockfile - pnpm run lint + depends_on: + - install + + check: + image: node:22-bookworm-slim + commands: + - corepack enable + - corepack prepare pnpm@latest --activate + - pnpm install --frozen-lockfile - pnpm run check + depends_on: + - install + + test: + image: node:22-bookworm-slim + commands: + - corepack enable + - corepack prepare pnpm@latest --activate + - pnpm install --frozen-lockfile - pnpm run test:run + depends_on: + - install + + build: + image: node:22-bookworm-slim + commands: + - corepack enable + - corepack prepare pnpm@latest --activate + - pnpm install --frozen-lockfile + - pnpm run build + depends_on: + - install + + build-full: + image: node:22-bookworm-slim + commands: + - apt-get update + - apt-get install -y --no-install-recommends ca-certificates libasound2 libatk-bridge2.0-0 libatk1.0-0 libcups2 libdrm2 libgbm1 libgtk-3-0 libnss3 libxcomposite1 libxdamage1 libxfixes3 libxkbcommon0 libxrandr2 + - rm -rf /var/lib/apt/lists/* + - corepack enable + - corepack prepare pnpm@latest --activate + - pnpm install --frozen-lockfile + - pnpm run critical-css:install + - pnpm run build:full + depends_on: + - install + + e2e: + image: node:22-bookworm-slim + commands: + - corepack enable + - corepack prepare pnpm@latest --activate + - pnpm install --frozen-lockfile - pnpm run build - pnpm exec playwright install chromium --with-deps - pnpm run test:e2e + depends_on: + - build diff --git a/AGENTS.md b/AGENTS.md index 32ffa41..23ffa2b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -10,7 +10,8 @@ This repo is a **one-page static** Linktree-style site for mifi.dev. It is **not - **TypeScript** (always) - **pnpm** (only); do not use npm or yarn - **PostCSS** for CSS (nesting + preset-env) -- **Critical CSS** via post-build script (`scripts/critical-css.mjs`); full build with critical CSS is `pnpm run build:full` (requires Chromium) +- **Critical CSS** via post-build script (`scripts/critical-css.mjs`); full build with critical CSS is `pnpm run build:full` (run `pnpm run critical-css:install` once to install Chromium) +- **CSP-safe scripts:** Post-build `scripts/externalize-inline-script.mjs` moves SvelteKit’s inline bootstrap script to `_app/immutable/bootstrap.[hash].js` so CSP can use `script-src 'self'` without `unsafe-inline` - **Content:** JSON in `src/lib/data/` (e.g. `links.json`), loaded in `+page.server.ts` at build time - **CSP:** Set by Traefik middleware; do not add CSP in app code @@ -19,8 +20,8 @@ This repo is a **one-page static** Linktree-style site for mifi.dev. It is **not - Use **semantic HTML** and **JSON-LD** for SEO; target **WCAG 2.2 AAA** for accessibility. - **No unsafe-inline** scripts; all JS via `'; + while (pos < html.length) { + if (escape) { + escape = false; + pos++; + continue; + } + if (inString) { + if (html[pos] === '\\') { + escape = true; + pos++; + continue; + } + if (html[pos] === inString) { + inString = null; + } + pos++; + continue; + } + if (html[pos] === '"' || html[pos] === "'") { + inString = html[pos]; + pos++; + continue; + } + if (html.slice(pos, pos + endTag.length) === endTag) { + return { + start: scriptStart, + end: pos + endTag.length, + content: html.slice(contentStart, pos), + }; + } + pos++; + } + return null; +} + +try { + let html = readFileSync(htmlPath, 'utf-8'); + const found = findSvelteKitInlineScript(html); + if (!found) { + console.log('No SvelteKit inline bootstrap script found in', htmlPath); + process.exit(0); + } + const content = found.content; + const hash = createHash('sha256').update(content).digest('hex').slice(0, 8); + const filename = `bootstrap.${hash}.js`; + const immutableDir = join(buildDir, '_app', 'immutable'); + mkdirSync(immutableDir, { recursive: true }); + const scriptPath = join(immutableDir, filename); + writeFileSync(scriptPath, content.trimStart(), 'utf-8'); + const scriptTag = ``; + html = + html.slice(0, found.start) + + scriptTag + + html.slice(found.end); + writeFileSync(htmlPath, html, 'utf-8'); + console.log('Externalized SvelteKit bootstrap to', scriptPath); +} catch (err) { + console.error('externalize-inline-script failed:', err instanceof Error ? err.message : String(err)); + process.exit(1); +} diff --git a/src/app.html b/src/app.html index a29ebdf..7b757ff 100644 --- a/src/app.html +++ b/src/app.html @@ -3,12 +3,10 @@ - - mifi.dev + %sveltekit.head% -
%sveltekit.body%
diff --git a/src/lib/components/ThemeToggle.svelte b/src/lib/components/ThemeToggle.svelte index d43ecdc..416c8b8 100644 --- a/src/lib/components/ThemeToggle.svelte +++ b/src/lib/components/ThemeToggle.svelte @@ -1,5 +1,4 @@ + + + + + + + + + + + + + + {@html jsonLdHtml} {#if personLdHtml} @@ -44,4 +89,4 @@ - +{@render children()} diff --git a/src/routes/+layout.ts b/src/routes/+layout.ts index 189f71e..8383188 100644 --- a/src/routes/+layout.ts +++ b/src/routes/+layout.ts @@ -1 +1,2 @@ export const prerender = true; +export const ssr = true; \ No newline at end of file diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index e240001..9e340c0 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -22,6 +22,30 @@ {data.site.title} + + + {#if data.site.person?.name} + + {/if} + + + + + + + + + + + + + + + + + + +
diff --git a/static/.well-known/bio.com.chrome.devtools.json b/static/.well-known/bio.com.chrome.devtools.json new file mode 100644 index 0000000..e31fa76 --- /dev/null +++ b/static/.well-known/bio.com.chrome.devtools.json @@ -0,0 +1,12 @@ +{ + "name": "mifi.bio", + "url": "https://mifi.bio", + "description": "mifi.bio", + "icons": [ + { + "src": "https://mifi.dev/assets/images/apple-touch-icon.png", + "sizes": "512x512", + "type": "image/png" + } + ] +} diff --git a/static/.well-known/bio.security.txt b/static/.well-known/bio.security.txt new file mode 100644 index 0000000..42430a2 --- /dev/null +++ b/static/.well-known/bio.security.txt @@ -0,0 +1,11 @@ +# Canonical URL for this file (recommended for validation) +Canonical: https://mifi.bio/.well-known/security.txt + +# Contact for reporting security vulnerabilities (required) +Contact: mailto:security@mifi.holdings + +# Optional: link to your vulnerability disclosure policy when you have one +# Policy: https://mifi.bio/security + +# Date after which this file is considered stale (required; RFC 3339; max 1 year recommended) +Expires: 2027-02-01T00:00:00.000Z diff --git a/static/.well-known/dev.com.chrome.devtools.json b/static/.well-known/dev.com.chrome.devtools.json new file mode 100644 index 0000000..c65a4cf --- /dev/null +++ b/static/.well-known/dev.com.chrome.devtools.json @@ -0,0 +1,12 @@ +{ + "name": "mifi.dev", + "url": "https://mifi.dev", + "description": "mifi.dev", + "icons": [ + { + "src": "https://mifi.dev/assets/images/apple-touch-icon.png", + "sizes": "512x512", + "type": "image/png" + } + ] +} diff --git a/static/.well-known/dev.security.txt b/static/.well-known/dev.security.txt new file mode 100644 index 0000000..34b3ca0 --- /dev/null +++ b/static/.well-known/dev.security.txt @@ -0,0 +1,11 @@ +# Canonical URL for this file (recommended for validation) +Canonical: https://mifi.dev/.well-known/security.txt + +# Contact for reporting security vulnerabilities (required) +Contact: mailto:security@mifi.holdings + +# Optional: link to your vulnerability disclosure policy when you have one +# Policy: https://mifi.dev/security + +# Date after which this file is considered stale (required; RFC 3339; max 1 year recommended) +Expires: 2027-02-01T00:00:00.000Z diff --git a/static/assets/images/apple-touch-icon.png b/static/assets/images/apple-touch-icon.png new file mode 100644 index 0000000..b00ad45 Binary files /dev/null and b/static/assets/images/apple-touch-icon.png differ diff --git a/static/assets/images/mifi-bio og-image.png b/static/assets/images/mifi-bio og-image.png new file mode 100644 index 0000000..0a471b8 Binary files /dev/null and b/static/assets/images/mifi-bio og-image.png differ diff --git a/static/assets/images/mifi-bio-og-image.webp b/static/assets/images/mifi-bio-og-image.webp new file mode 100644 index 0000000..59aadac Binary files /dev/null and b/static/assets/images/mifi-bio-og-image.webp differ diff --git a/static/assets/images/mifi-bio-twitter-image.png b/static/assets/images/mifi-bio-twitter-image.png new file mode 100644 index 0000000..b257f7c Binary files /dev/null and b/static/assets/images/mifi-bio-twitter-image.png differ diff --git a/static/assets/images/mifi-bio-twitter-image.webp b/static/assets/images/mifi-bio-twitter-image.webp new file mode 100644 index 0000000..5f2e375 Binary files /dev/null and b/static/assets/images/mifi-bio-twitter-image.webp differ diff --git a/static/assets/images/mifi-dev-og-image.png b/static/assets/images/mifi-dev-og-image.png new file mode 100644 index 0000000..ef4a804 Binary files /dev/null and b/static/assets/images/mifi-dev-og-image.png differ diff --git a/static/assets/images/mifi-dev-og-image.webp b/static/assets/images/mifi-dev-og-image.webp new file mode 100644 index 0000000..9004f29 Binary files /dev/null and b/static/assets/images/mifi-dev-og-image.webp differ diff --git a/static/assets/images/mifi-dev-twitter-image.png b/static/assets/images/mifi-dev-twitter-image.png new file mode 100644 index 0000000..451b3b0 Binary files /dev/null and b/static/assets/images/mifi-dev-twitter-image.png differ diff --git a/static/assets/images/mifi-dev-twitter-image.webp b/static/assets/images/mifi-dev-twitter-image.webp new file mode 100644 index 0000000..d00f29f Binary files /dev/null and b/static/assets/images/mifi-dev-twitter-image.webp differ diff --git a/static/favicon.ico b/static/favicon.ico new file mode 100644 index 0000000..89ca289 Binary files /dev/null and b/static/favicon.ico differ diff --git a/static/favicon.svg b/static/favicon.svg index 6a2944d..9cd55d7 100644 --- a/static/favicon.svg +++ b/static/favicon.svg @@ -1 +1,9 @@ - \ No newline at end of file + + + + \ No newline at end of file