The code for darkworld.download darkworld.download

feat: dangerous atproto sync, part 2

kris.darkworld.download ccb365a2 af5612e7

verified
+241 -39
+9 -2
build.ts
··· 13 13 }; 14 14 15 15 const outdir = path.join(process.cwd(), "dist"); 16 + const DIST_PUBLIC_PREFIX = "/.dist"; 16 17 const generatedDir = path.join(process.cwd(), "src", "runtime", "generated"); 17 18 const generatedLoadersPath = path.join(generatedDir, "island-loaders.ts"); 18 19 const manifestPath = path.join(outdir, "build-manifest.json"); ··· 92 93 await mkdir(generatedDir, { recursive: true }); 93 94 94 95 const lines = [ 95 - "export const islandLoaders: Record<string, () => Promise<Record<string, unknown>>> = {", 96 + "type IslandLoader = () => Promise<Record<string, unknown>>;", 97 + "", 98 + "const generatedIslandLoaders: Record<string, IslandLoader> = {", 96 99 ...modules.map((moduleId) => { 97 100 const importPath = "../" + path.relative(path.join(process.cwd(), "src", "runtime"), path.join(process.cwd(), moduleId)).replaceAll("\\", "/"); 98 101 return `\t${JSON.stringify(moduleId)}: () => import(${JSON.stringify(importPath)}),`; 99 102 }), 100 103 "};", 101 104 "", 105 + "export function getGeneratedIslandLoaders(): Record<string, IslandLoader> {", 106 + "\treturn generatedIslandLoaders;", 107 + "}", 108 + "", 102 109 ]; 103 110 104 111 await writeFile(generatedLoadersPath, lines.join("\n"), "utf8"); ··· 106 113 107 114 function outputHref(outputPath: string): string { 108 115 const relative = path.relative(outdir, outputPath).replaceAll("\\", "/"); 109 - return `/${relative}`; 116 + return `${DIST_PUBLIC_PREFIX}/${relative}`; 110 117 } 111 118 112 119 async function buildCss(): Promise<string | null> {
+67
lexicons/download.darkworld.site.getState.json
··· 1 + { 2 + "id": "download.darkworld.site.getState", 3 + "defs": { 4 + "main": { 5 + "type": "query", 6 + "description": "Return the normalized website state used by darkworld.download clients.", 7 + "output": { 8 + "encoding": "application/json", 9 + "schema": { 10 + "type": "ref", 11 + "ref": "#output" 12 + } 13 + } 14 + }, 15 + "output": { 16 + "type": "object", 17 + "required": [ 18 + "useSusieProphecy", 19 + "titleColors", 20 + "favoriteGames", 21 + "favoriteArtists", 22 + "favoriteAlbums", 23 + "favoriteDeltaruneCharacters" 24 + ], 25 + "properties": { 26 + "useSusieProphecy": { 27 + "type": "boolean", 28 + "description": "Whether to show Susie instead of Kris in prophecy content." 29 + }, 30 + "titleColors": { 31 + "type": "string", 32 + "description": "Named title color mode for the site.", 33 + "knownValues": [ 34 + "none", 35 + "enby", 36 + "trans" 37 + ] 38 + }, 39 + "favoriteGames": { 40 + "type": "array", 41 + "items": { 42 + "type": "string" 43 + } 44 + }, 45 + "favoriteArtists": { 46 + "type": "array", 47 + "items": { 48 + "type": "string" 49 + } 50 + }, 51 + "favoriteAlbums": { 52 + "type": "array", 53 + "items": { 54 + "type": "string" 55 + } 56 + }, 57 + "favoriteDeltaruneCharacters": { 58 + "type": "array", 59 + "items": { 60 + "type": "string" 61 + } 62 + } 63 + } 64 + } 65 + }, 66 + "lexicon": 1 67 + }
+4 -12
lexicons/download.darkworld.state.json
··· 39 39 "properties": { 40 40 "titleColors": { 41 41 "description": "TBD", 42 - "refs": [ 43 - "#titleColorsEnby", 44 - "#titleColorsTrans" 42 + "knownValues": [ 43 + "enby", 44 + "trans" 45 45 ], 46 - "type": "union" 46 + "type": "string" 47 47 }, 48 48 "susieProphecy": { 49 49 "description": "Swap out Kris with Susie in the prophecy panel.", ··· 85 85 } 86 86 } 87 87 } 88 - }, 89 - "titleColorsEnby": { 90 - "type": "object", 91 - "properties": {} 92 - }, 93 - "titleColorsTrans": { 94 - "type": "object", 95 - "properties": {} 96 88 } 97 89 }, 98 90 "lexicon": 1
+1 -1
scripts/publish-lexicons.ts
··· 32 32 password: a?.password! 33 33 }) 34 34 35 - console.log(`Logged in as ${c.data.handle} (${process.env.MASK_EMAIL || c.data.email || "unknown-email"}), account status: ${c.data.active}`); 35 + console.log(`Logged in as ${c.data.handle} (${process.env.MASK_EMAIL || c.data.email || "unknown-email"}), account status: ${c.data.active ? "active" : c.data.status}`); 36 36 37 37 const status = (await b.com.atproto.sync.getRepoStatus({ did: c.data.did! })).data; 38 38 console.log(`Repository status: ${status.status}, current rev: ${status.rev}`);
+2 -4
src/frontend.tsx
··· 8 8 import { createRoot, hydrateRoot } from "react-dom/client"; 9 9 import "./index.css"; 10 10 import { App } from "./App"; 11 - import { getLatestState } from "./lib/currentStateSync"; 11 + import { getLatestState, type State } from "./lib/currentStateSync"; 12 12 13 - type DebugState = { 14 - swapOutKrisWithSusie: boolean; 15 - }; 13 + type DebugState = State; 16 14 17 15 function mountDevStateWrench() { 18 16 if (document.getElementById("dev-state-wrench")) {
+1 -1
src/index.html
··· 7 7 <!-- <link rel="icon" type="image/svg+xml" href="./logo.svg" /> --> 8 8 <title>DARK WORLD</title> 9 9 <meta property="og:title" content="DARKWORLD (download)" /> 10 - <meta property="og:description" content="KRIS, YOU SERIOUSLY NEED DELTARUNE REFERENCES ON ATPROTO? " /> 10 + <meta property="og:description" content="KRIS, YOU SERIOUSLY NEED DELTARUNE REFERENCES ON ATPROTO?" /> 11 11 <meta property="og:locale" content="en_US" /> 12 12 <meta property="og:url" content="https://darkworld.download/" /> 13 13 </head>
+1 -1
src/index.template.html
··· 6 6 <meta name="robots" content="noindex, nofollow" /> 7 7 <title>DARK WORLD</title> 8 8 <meta property="og:title" content="DARKWORLD (download)" /> 9 - <meta property="og:description" content="KRIS, YOU SERIOUSLY NEED DELTARUNE REFERENCES ON ATPROTO? " /> 9 + <meta property="og:description" content="KRIS, YOU SERIOUSLY NEED DELTARUNE REFERENCES ON ATPROTO?" /> 10 10 <meta property="og:locale" content="en_US" /> 11 11 <meta property="og:url" content="https://darkworld.download/" /> 12 12 <!--app-head-->
+22
src/index.tsx
··· 25 25 return assetPath; 26 26 } 27 27 28 + function safeDistPath(pathname: string): string | null { 29 + const distPath = pathname.replace(/^\/\.dist\//, ""); 30 + if (!distPath || distPath.includes("..") || distPath.includes("\\")) { 31 + return null; 32 + } 33 + return distPath; 34 + } 35 + 28 36 function loadBuildManifest(): BuildManifest { 29 37 if (!isProduction) { 30 38 return { ··· 83 91 84 92 const prodOnlyRoutes = isProduction 85 93 ? { 94 + "/.dist/*": async (req: Request) => { 95 + const pathname = new URL(req.url).pathname; 96 + const relative = safeDistPath(pathname); 97 + if (!relative) { 98 + return new Response("Not Found", { status: 404 }); 99 + } 100 + 101 + const file = Bun.file(path.join(distDir, relative)); 102 + if (!(await file.exists())) { 103 + return new Response("Not Found", { status: 404 }); 104 + } 105 + 106 + return new Response(file); 107 + }, 86 108 "/*": async (req: Request) => { 87 109 const pathname = new URL(req.url).pathname; 88 110 const relative = safeRelativePath(pathname);
+102 -10
src/lib/currentStateSync.ts
··· 1 - type State = { 2 - swapOutKrisWithSusie: boolean; 1 + type TitleColor = "none" | "enby" | "trans"; 2 + 3 + export type State = { 4 + useSusieProphecy: boolean; 5 + titleColors: TitleColor; 6 + favoriteGames: string[]; 7 + favoriteArtists: string[]; 8 + favoriteAlbums: string[]; 9 + favoriteDeltaruneCharacters: string[]; 3 10 }; 4 11 5 12 declare global { ··· 9 16 } 10 17 11 18 const DEFAULT_STATE: State = { 12 - swapOutKrisWithSusie: false, 19 + useSusieProphecy: false, 20 + titleColors: "none", 21 + favoriteGames: [], 22 + favoriteArtists: [], 23 + favoriteAlbums: [], 24 + favoriteDeltaruneCharacters: [], 13 25 }; 14 26 15 27 const STATE_TTL_MS = 10_000; ··· 31 43 let didServiceCache: { did: string; endpoint: string; cachedAtMs: number } | null = null; 32 44 let lastSyncError = ""; 33 45 46 + function parseKnownTitleColors(input: unknown): Exclude<TitleColor, "none"> | null { 47 + if (input === "enby" || input === "trans") { 48 + return input; 49 + } 50 + 51 + return null; 52 + } 53 + 54 + function parseStringArray(input: unknown): string[] { 55 + if (!Array.isArray(input)) { 56 + return []; 57 + } 58 + 59 + return input.filter((entry): entry is string => typeof entry === "string"); 60 + } 61 + 34 62 function parseState(input: unknown): State | null { 35 63 if (!input || typeof input !== "object") { 36 64 return null; 37 65 } 38 66 39 - const value = input as { swapOutKrisWithSusie?: unknown }; 40 - if (typeof value.swapOutKrisWithSusie !== "boolean") { 67 + const value = input as { 68 + useSusieProphecy?: unknown; 69 + titleColors?: unknown; 70 + favoriteGames?: unknown; 71 + favoriteArtists?: unknown; 72 + favoriteAlbums?: unknown; 73 + favoriteDeltaruneCharacters?: unknown; 74 + swapOutKrisWithSusie?: unknown; 75 + site?: { susieProphecy?: unknown; titleColors?: unknown }; 76 + favorite?: { 77 + game?: unknown; 78 + artist?: unknown; 79 + album?: unknown; 80 + deltaruneCharacter?: unknown; 81 + }; 82 + }; 83 + 84 + const useSusieProphecy = 85 + typeof value.useSusieProphecy === "boolean" 86 + ? value.useSusieProphecy 87 + : typeof value.swapOutKrisWithSusie === "boolean" 88 + ? value.swapOutKrisWithSusie 89 + : typeof value.site?.susieProphecy === "boolean" 90 + ? value.site.susieProphecy 91 + : null; 92 + 93 + if (useSusieProphecy === null) { 41 94 return null; 42 95 } 43 96 44 - return { swapOutKrisWithSusie: value.swapOutKrisWithSusie }; 97 + const titleColorsFromSite = parseKnownTitleColors(value.site?.titleColors); 98 + 99 + let titleColors: TitleColor = "none"; 100 + if (value.titleColors === "none" || value.titleColors === "enby" || value.titleColors === "trans") { 101 + titleColors = value.titleColors; 102 + } else if (titleColorsFromSite) { 103 + titleColors = titleColorsFromSite; 104 + } 105 + 106 + return { 107 + useSusieProphecy, 108 + titleColors, 109 + favoriteGames: parseStringArray(value.favoriteGames ?? value.favorite?.game), 110 + favoriteArtists: parseStringArray(value.favoriteArtists ?? value.favorite?.artist), 111 + favoriteAlbums: parseStringArray(value.favoriteAlbums ?? value.favorite?.album), 112 + favoriteDeltaruneCharacters: parseStringArray( 113 + value.favoriteDeltaruneCharacters ?? value.favorite?.deltaruneCharacter, 114 + ), 115 + }; 45 116 } 46 117 47 118 function getServerState(): State { ··· 55 126 56 127 const record = input as { 57 128 swapOutKrisWithSusie?: unknown; 58 - site?: { susieProphecy?: unknown }; 129 + site?: { susieProphecy?: unknown; titleColors?: unknown }; 130 + favorite?: { 131 + game?: unknown; 132 + artist?: unknown; 133 + album?: unknown; 134 + deltaruneCharacter?: unknown; 135 + }; 59 136 }; 137 + const titleColors = parseKnownTitleColors(record.site?.titleColors) ?? "none"; 60 138 61 139 if (typeof record.swapOutKrisWithSusie === "boolean") { 62 - return { swapOutKrisWithSusie: record.swapOutKrisWithSusie }; 140 + return { 141 + useSusieProphecy: record.swapOutKrisWithSusie, 142 + titleColors, 143 + favoriteGames: parseStringArray(record.favorite?.game), 144 + favoriteArtists: parseStringArray(record.favorite?.artist), 145 + favoriteAlbums: parseStringArray(record.favorite?.album), 146 + favoriteDeltaruneCharacters: parseStringArray(record.favorite?.deltaruneCharacter), 147 + }; 63 148 } 64 149 65 150 if (typeof record.site?.susieProphecy === "boolean") { 66 - return { swapOutKrisWithSusie: record.site.susieProphecy }; 151 + return { 152 + useSusieProphecy: record.site.susieProphecy, 153 + titleColors, 154 + favoriteGames: parseStringArray(record.favorite?.game), 155 + favoriteArtists: parseStringArray(record.favorite?.artist), 156 + favoriteAlbums: parseStringArray(record.favorite?.album), 157 + favoriteDeltaruneCharacters: parseStringArray(record.favorite?.deltaruneCharacter), 158 + }; 67 159 } 68 160 69 161 return null; ··· 90 182 const body = (await response.json()) as { value?: unknown }; 91 183 const parsed = parseAtprotoStateRecord(body.value); 92 184 if (!parsed) { 93 - throw new Error("State record is missing `site.susieProphecy` or `swapOutKrisWithSusie`"); 185 + throw new Error("State record is missing `site.susieProphecy` or `useSusieProphecy`"); 94 186 } 95 187 96 188 return parsed;
+3 -3
src/lib/prophecy.ts
··· 52 52 image: "/assets/prophecy/deltarune.png", 53 53 } satisfies ProphecyPanelProps, 54 54 krisOrSusie: { 55 - text: state.swapOutKrisWithSusie 55 + text: state.useSusieProphecy 56 56 ? "THE SECOND HERO.\nTHE GIRL, WITH HOPE CROSSED ON HER HEART." 57 57 : `THE FIRST HERO.\nTHE CAGE, WITH HUMAN SOUL AND PARTS.`, 58 - image: state.swapOutKrisWithSusie 58 + image: state.useSusieProphecy 59 59 ? "/assets/prophecy/susie.png" 60 60 : "/assets/prophecy/kris.png", 61 - variant: state.swapOutKrisWithSusie 61 + variant: state.useSusieProphecy 62 62 ? ProphecyPanelVariant.SUSIE_DARKWORLD 63 63 : ProphecyPanelVariant.DEFAULT, 64 64 } satisfies ProphecyPanelProps,
+7 -1
src/runtime/generated/island-loaders.ts
··· 1 - export const islandLoaders: Record<string, () => Promise<Record<string, unknown>>> = { 1 + type IslandLoader = () => Promise<Record<string, unknown>>; 2 + 3 + const generatedIslandLoaders: Record<string, IslandLoader> = { 2 4 }; 5 + 6 + export function getGeneratedIslandLoaders(): Record<string, IslandLoader> { 7 + return generatedIslandLoaders; 8 + }
+18
src/runtime/island-loaders.ts
··· 1 + import { getGeneratedIslandLoaders } from "./generated/island-loaders"; 2 + 3 + export type IslandLoader = () => Promise<Record<string, unknown>>; 4 + export type IslandLoaders = Record<string, IslandLoader>; 5 + 6 + let cachedIslandLoaders: IslandLoaders | null = null; 7 + 8 + export function getIslandLoaders(): IslandLoaders { 9 + if (!cachedIslandLoaders) { 10 + cachedIslandLoaders = getGeneratedIslandLoaders(); 11 + } 12 + 13 + return cachedIslandLoaders; 14 + } 15 + 16 + export function getIslandLoader(moduleId: string): IslandLoader | undefined { 17 + return getIslandLoaders()[moduleId]; 18 + }
+2 -2
src/runtime/islands-client.tsx
··· 2 2 import { hydrateRoot } from "react-dom/client"; 3 3 import { decodeJsonProps, type JsonValue } from "./jsonSafe"; 4 4 import { islandAttrs } from "./server-islands"; 5 - import { islandLoaders } from "./generated/island-loaders"; 5 + import { getIslandLoader } from "./island-loaders"; 6 6 7 7 type ModuleRecord = Record<string, unknown>; 8 8 9 9 async function loadModule(moduleId: string): Promise<ModuleRecord | null> { 10 - const loader = islandLoaders[moduleId]; 10 + const loader = getIslandLoader(moduleId); 11 11 if (!loader) { 12 12 console.warn(`[islands] Missing loader for module: ${moduleId}`); 13 13 return null;
+2 -2
src/server/renderPage.tsx
··· 34 34 35 35 const scripts: string[] = []; 36 36 if (manifest.fullClient && manifest.fullClientScriptHref) { 37 - scripts.push(`<script type="module" src="${manifest.fullClientScriptHref}"></script>`); 37 + scripts.push(`<script type="module" src="${manifest.fullClientScriptHref}" async defer></script>`); 38 38 } else if (!manifest.fullClient && manifest.islandsScriptHref) { 39 - scripts.push(`<script type="module" src="${manifest.islandsScriptHref}"></script>`); 39 + scripts.push(`<script type="module" src="${manifest.islandsScriptHref}" defer></script>`); 40 40 } 41 41 scripts.unshift(stateScript); 42 42