johnandersen777.bsky.social/knotless
git that lives in your Atmosphere account
Forked from chadtmiller.com/knotless
0
0
import type { Handle, HandleFetch } from '@sveltejs/kit/hooks';
import { KNOTLESS_SERVE } from '$app/env/private';
import {
NIGHT,
NIGHT_PALETTE,
PALETTE,
THEME,
customOf,
halvesStyle,
paletteOf,
styleOf,
themeOf
} from '#lib/theme.ts';
/** What `<html>` says of the theme the cookies name: a theme of the viewer's own only with its palette, which is only ever colours as #rrggbb. */
function drawn(theme: string | null, said: string | undefined): string {
const palette = theme?.startsWith('custom-') ? paletteOf(said) : null;
if (palette) return ` data-theme="${customOf(palette)}" style="${styleOf(palette)}"`;
return theme && !theme.startsWith('custom-') ? ` data-theme="${theme}"` : '';
}
/** A half of a pair, as the cookies name it: its theme, and its palette for one of the viewer's own. */
function half(theme: string | null, said: string | undefined) {
const palette = theme?.startsWith('custom-') ? paletteOf(said) : null;
return theme && (palette || !theme.startsWith('custom-')) ? { id: theme, palette } : null;
}
/** What `<html>` says of a pair, by day and at night, and the <style> its palettes are drawn from: app.html's script shows the half the system says. */
function pair(day: ReturnType<typeof half>, night: ReturnType<typeof half>) {
if (!day || !night) return null;
const own = halvesStyle(day.palette, night.palette);
return {
html: ` data-day="${day.id}" data-night="${night.id}" data-theme="${day.id}" data-half="day"`,
head: own ? `<style id="theme-own">${own}</style>` : ''
};
}
export const handle: Handle = async ({ event, resolve }) => {
// A page is drawn in the theme its cookie names, from the first paint;
// or in a pair's, as the system is light or dark.
const both = pair(
half(themeOf(event.cookies.get(THEME)), event.cookies.get(PALETTE)),
half(themeOf(event.cookies.get(NIGHT)), event.cookies.get(NIGHT_PALETTE))
);
const theme = both?.html ?? drawn(themeOf(event.cookies.get(THEME)), event.cookies.get(PALETTE));
const res = await resolve(event, {
filterSerializedResponseHeaders: (name) => name === 'cache-control',
transformPageChunk: theme
? ({ html }) =>
html
.replace('<html lang="en">', `<html lang="en"${theme}>`)
.replace('</head>', `${both?.head ?? ''}</head>`)
: undefined
});
// A page's times are put in the zone its cookie names, and its colours in its theme.
res.headers.append('vary', 'cookie');
// Someone logged in is shown what is theirs: a page for no cache but theirs, and not kept.
if (event.cookies.get('knotless_session')) res.headers.set('cache-control', 'private, no-store');
return res;
};
/**
* Asked of this site, XRPC queries are asked of knotless-serve
* directly, and with the global fetch: knotless-serve can have the same
* origin as the site it fronts, which Kit's own would answer itself.
*/
export const handleFetch: HandleFetch = ({ event, request, fetch }) => {
const url = new URL(request.url);
if (url.origin === event.url.origin && url.pathname.startsWith('/xrpc/')) {
// The site it was asked of, for the addresses it answers in full.
const asked = new Request(`${KNOTLESS_SERVE}${url.pathname}${url.search}`, request);
asked.headers.set('x-forwarded-host', event.url.host);
asked.headers.set('x-forwarded-proto', event.url.protocol.slice(0, -1));
// Who asks, as the session cookie of someone logged in says: the
// global fetch does not send the page's cookies itself.
const cookie = event.request.headers.get('cookie');
if (cookie) asked.headers.set('cookie', cookie);
// The trace knotless-serve started for the page, for what it asks
// to be logged under the same one.
const trace = event.request.headers.get('traceparent');
if (trace) asked.headers.set('traceparent', trace);
// Named as the pages, which knotless-serve counts apart from the
// clients that ask it: node would call itself `node`, as scripts do.
asked.headers.set('user-agent', 'knotless-pages');
return globalThis.fetch(asked);
}
return fetch(request);
};