/** * Every sign-in screen, side by side, reloading as you edit. * * bun run preview # from apps/auth, then open http://localhost:1338 * * No database, no mail, no cookies. Each state is the real screen value — the * code provider's own `request` called with a made-up state, and the issuer's * screens from `ui/issuer.ts` — drawn by the real renderer with the real * theme. What differs from production is only how the page was reached. * * `preview-watch.ts` restarts this process when the UI's source changes — * the renderer, the stylesheet, the copy, the issuer's screens. Each page polls * for the restart and reloads itself, so saving a file is the whole loop. * * A state that is missing here is a state nobody will look at before it * ships. Adding a screen to the flow means adding it to `STATES`. */ import type { CodeProviderState } from '@nestri/auth/provider/code'; import { CodeUI } from '@nestri/auth/ui/code'; import { IssuerScreens } from '@nestri/auth/ui/issuer'; import { MARK_CODE, MARK_GITHUB, MARK_GOOGLE } from '@nestri/auth/ui/mark'; import { HtmlRenderer } from '@nestri/auth/ui/render'; import type { Screen } from '@nestri/auth/ui/screen'; import { deviceConfirm } from '../src/index.js'; import { THEME_NESTRI } from '../src/theme.js'; const PORT = Number(process.env.PORT ?? 1338); const BOOT = crypto.randomUUID(); const renderer = HtmlRenderer({ theme: THEME_NESTRI }); // Matches `USER_CODE_LENGTH` in the issuer, which is local to it. const DEVICE_CODE_LENGTH = 8; const code = CodeUI({ sendCode: async () => {} }); const req = new Request(`http://localhost:${PORT}/`); const email = 'someone@example.com'; const sent = { type: 'code', code: '123456', claims: { email }, flow: 'x', expires: 0 } satisfies CodeProviderState; interface State { id: string; group: string; label: string; screen: () => Promise | Screen; } const STATES: State[] = [ // Email code — what everyone sees. { id: 'email', group: 'Email', label: 'Enter email', screen: () => code.request(req, { type: 'start' }, new FormData()) }, { id: 'email-invalid', group: 'Email', label: 'Bad address', screen: () => code.request(req, { type: 'start' }, new FormData(), { type: 'invalid_claim', key: 'email', value: 'nope' }) }, { id: 'email-rate-limit', group: 'Email', label: 'Rate limited (email)', screen: () => code.request(req, { type: 'start' }, new FormData(), { type: 'rate_limit' }) }, { id: 'code', group: 'Email', label: 'Code sent', screen: () => code.request(req, sent, new FormData()) }, { id: 'code-resent', group: 'Email', label: 'Code resent', screen: () => code.request(req, { ...sent, resend: true }, new FormData()) }, { id: 'code-invalid', group: 'Email', label: 'Wrong code', screen: () => code.request(req, sent, new FormData(), { type: 'invalid_code' }) }, { id: 'code-rate-limit', group: 'Email', label: 'Rate limited (code)', screen: () => code.request(req, sent, new FormData(), { type: 'rate_limit' }) }, // Device grant — a TV or a CLI signing in through a browser. { id: 'device-enter', group: 'Device', label: 'Enter device code', screen: () => IssuerScreens.deviceEnter(DEVICE_CODE_LENGTH) }, { id: 'device-confirm', group: 'Device', label: 'Is this you?', screen: () => IssuerScreens.deviceConfirm({ userCode: 'BCDF3467', csrf: 'x' }) }, { id: 'device-confirm-installer', group: 'Device', label: 'Add a host?', screen: () => IssuerScreens.deviceConfirm({ userCode: 'BCDF3467', csrf: 'x', wording: deviceConfirm('installer') }) }, { id: 'device-approved', group: 'Device', label: 'Approved', screen: () => IssuerScreens.deviceApproved() }, { id: 'device-denied', group: 'Device', label: 'Denied', screen: () => IssuerScreens.deviceDenied() }, { id: 'device-invalid', group: 'Device', label: 'Code not valid', screen: () => IssuerScreens.deviceInvalidCode() }, { id: 'device-too-many', group: 'Device', label: 'Too many tries', screen: () => IssuerScreens.deviceTooManyTries() }, { id: 'device-expired', group: 'Device', label: 'Expired', screen: () => IssuerScreens.deviceExpired() }, { id: 'device-answered', group: 'Device', label: 'Already answered', screen: () => IssuerScreens.deviceAlreadyAnswered() }, { id: 'device-forged', group: 'Device', label: 'Forged form', screen: () => IssuerScreens.deviceForgedForm() }, // Reached only by accident in production, which is why they need looking at. { id: 'unknown-state', group: 'Errors', label: 'Sign-in expired', screen: () => IssuerScreens.unknownState('The browser was in an unknown state.') }, { // Not shown today: with one provider the chooser redirects straight // to it. Kept so a second provider does not arrive to an unstyled page. id: 'choose', group: 'Errors', label: 'Provider chooser (unused)', screen: () => ({ kind: 'choose', options: [ { href: '#', label: 'Continue with Email', mark: MARK_CODE }, { href: '#', label: 'Continue with GitHub', mark: MARK_GITHUB }, { href: '#', label: 'Continue with Google', mark: MARK_GOOGLE } ] }) } ]; /** Polls for a restart of this process and reloads when it sees one. */ const RELOAD = ``; async function page(state: State): Promise { const res = renderer.render(await state.screen(), req); const html = (await res.text()).replace('', `${RELOAD}`); // Always 200, so a 400 screen is not mistaken for a broken preview. return new Response(html, { headers: { 'Content-Type': 'text/html; charset=utf-8' } }); } function gallery(): Response { const groups = [...new Set(STATES.map((s) => s.group))]; const tiles = groups .map( (group) => `

${group}

${STATES.filter((s) => s.group === group) .map( (s) => `
${s.label}${s.id}
` ) .join('')}
` ) .join(''); return new Response( `Auth preview
Auth preview saves reload every frame · click a title to open full size
${tiles} ${RELOAD}`, { headers: { 'Content-Type': 'text/html; charset=utf-8' } } ); } Bun.serve({ port: PORT, async fetch(request) { const url = new URL(request.url); if (url.pathname === '/__boot') return new Response(BOOT); if (url.pathname === '/') return gallery(); const state = STATES.find((s) => url.pathname === `/s/${s.id}`); if (state) return page(state); // A form submitted inside a preview lands here; send it back. return Response.redirect(request.headers.get('referer') ?? '/', 303); } }); console.log(`auth preview: http://localhost:${PORT} (${STATES.length} states)`);