tangled
alpha
login
or
join now
kris.darkworld.download
/
darkworld
1
fork
atom
The code for darkworld.download
darkworld.download
1
fork
atom
overview
issues
pulls
pipelines
feat: dangerous atproto sync, part 2
kris.darkworld.download
5 days ago
ccb365a2
af5612e7
verified
This commit was signed with the committer's
known signature
.
kris.darkworld.download
SSH Key Fingerprint:
SHA256:4iiUkypaBzJPnEeVlazWCFBrJncWXreVhtJPB4DlswE=
+241
-39
14 changed files
expand all
collapse all
unified
split
build.ts
lexicons
download.darkworld.site.getState.json
download.darkworld.state.json
scripts
publish-lexicons.ts
src
frontend.tsx
index.html
index.template.html
index.tsx
lib
currentStateSync.ts
prophecy.ts
runtime
generated
island-loaders.ts
island-loaders.ts
islands-client.tsx
server
renderPage.tsx
+9
-2
build.ts
···
13
13
};
14
14
15
15
const outdir = path.join(process.cwd(), "dist");
16
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
95
-
"export const islandLoaders: Record<string, () => Promise<Record<string, unknown>>> = {",
96
96
+
"type IslandLoader = () => Promise<Record<string, unknown>>;",
97
97
+
"",
98
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
105
+
"export function getGeneratedIslandLoaders(): Record<string, IslandLoader> {",
106
106
+
"\treturn generatedIslandLoaders;",
107
107
+
"}",
108
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
109
-
return `/${relative}`;
116
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
1
+
{
2
2
+
"id": "download.darkworld.site.getState",
3
3
+
"defs": {
4
4
+
"main": {
5
5
+
"type": "query",
6
6
+
"description": "Return the normalized website state used by darkworld.download clients.",
7
7
+
"output": {
8
8
+
"encoding": "application/json",
9
9
+
"schema": {
10
10
+
"type": "ref",
11
11
+
"ref": "#output"
12
12
+
}
13
13
+
}
14
14
+
},
15
15
+
"output": {
16
16
+
"type": "object",
17
17
+
"required": [
18
18
+
"useSusieProphecy",
19
19
+
"titleColors",
20
20
+
"favoriteGames",
21
21
+
"favoriteArtists",
22
22
+
"favoriteAlbums",
23
23
+
"favoriteDeltaruneCharacters"
24
24
+
],
25
25
+
"properties": {
26
26
+
"useSusieProphecy": {
27
27
+
"type": "boolean",
28
28
+
"description": "Whether to show Susie instead of Kris in prophecy content."
29
29
+
},
30
30
+
"titleColors": {
31
31
+
"type": "string",
32
32
+
"description": "Named title color mode for the site.",
33
33
+
"knownValues": [
34
34
+
"none",
35
35
+
"enby",
36
36
+
"trans"
37
37
+
]
38
38
+
},
39
39
+
"favoriteGames": {
40
40
+
"type": "array",
41
41
+
"items": {
42
42
+
"type": "string"
43
43
+
}
44
44
+
},
45
45
+
"favoriteArtists": {
46
46
+
"type": "array",
47
47
+
"items": {
48
48
+
"type": "string"
49
49
+
}
50
50
+
},
51
51
+
"favoriteAlbums": {
52
52
+
"type": "array",
53
53
+
"items": {
54
54
+
"type": "string"
55
55
+
}
56
56
+
},
57
57
+
"favoriteDeltaruneCharacters": {
58
58
+
"type": "array",
59
59
+
"items": {
60
60
+
"type": "string"
61
61
+
}
62
62
+
}
63
63
+
}
64
64
+
}
65
65
+
},
66
66
+
"lexicon": 1
67
67
+
}
+4
-12
lexicons/download.darkworld.state.json
···
39
39
"properties": {
40
40
"titleColors": {
41
41
"description": "TBD",
42
42
-
"refs": [
43
43
-
"#titleColorsEnby",
44
44
-
"#titleColorsTrans"
42
42
+
"knownValues": [
43
43
+
"enby",
44
44
+
"trans"
45
45
],
46
46
-
"type": "union"
46
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
88
-
},
89
89
-
"titleColorsEnby": {
90
90
-
"type": "object",
91
91
-
"properties": {}
92
92
-
},
93
93
-
"titleColorsTrans": {
94
94
-
"type": "object",
95
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
35
-
console.log(`Logged in as ${c.data.handle} (${process.env.MASK_EMAIL || c.data.email || "unknown-email"}), account status: ${c.data.active}`);
35
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
11
-
import { getLatestState } from "./lib/currentStateSync";
11
11
+
import { getLatestState, type State } from "./lib/currentStateSync";
12
12
13
13
-
type DebugState = {
14
14
-
swapOutKrisWithSusie: boolean;
15
15
-
};
13
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
10
-
<meta property="og:description" content="KRIS, YOU SERIOUSLY NEED DELTARUNE REFERENCES ON ATPROTO? " />
10
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
9
-
<meta property="og:description" content="KRIS, YOU SERIOUSLY NEED DELTARUNE REFERENCES ON ATPROTO? " />
9
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
28
+
function safeDistPath(pathname: string): string | null {
29
29
+
const distPath = pathname.replace(/^\/\.dist\//, "");
30
30
+
if (!distPath || distPath.includes("..") || distPath.includes("\\")) {
31
31
+
return null;
32
32
+
}
33
33
+
return distPath;
34
34
+
}
35
35
+
28
36
function loadBuildManifest(): BuildManifest {
29
37
if (!isProduction) {
30
38
return {
···
83
91
84
92
const prodOnlyRoutes = isProduction
85
93
? {
94
94
+
"/.dist/*": async (req: Request) => {
95
95
+
const pathname = new URL(req.url).pathname;
96
96
+
const relative = safeDistPath(pathname);
97
97
+
if (!relative) {
98
98
+
return new Response("Not Found", { status: 404 });
99
99
+
}
100
100
+
101
101
+
const file = Bun.file(path.join(distDir, relative));
102
102
+
if (!(await file.exists())) {
103
103
+
return new Response("Not Found", { status: 404 });
104
104
+
}
105
105
+
106
106
+
return new Response(file);
107
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
1
-
type State = {
2
2
-
swapOutKrisWithSusie: boolean;
1
1
+
type TitleColor = "none" | "enby" | "trans";
2
2
+
3
3
+
export type State = {
4
4
+
useSusieProphecy: boolean;
5
5
+
titleColors: TitleColor;
6
6
+
favoriteGames: string[];
7
7
+
favoriteArtists: string[];
8
8
+
favoriteAlbums: string[];
9
9
+
favoriteDeltaruneCharacters: string[];
3
10
};
4
11
5
12
declare global {
···
9
16
}
10
17
11
18
const DEFAULT_STATE: State = {
12
12
-
swapOutKrisWithSusie: false,
19
19
+
useSusieProphecy: false,
20
20
+
titleColors: "none",
21
21
+
favoriteGames: [],
22
22
+
favoriteArtists: [],
23
23
+
favoriteAlbums: [],
24
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
46
+
function parseKnownTitleColors(input: unknown): Exclude<TitleColor, "none"> | null {
47
47
+
if (input === "enby" || input === "trans") {
48
48
+
return input;
49
49
+
}
50
50
+
51
51
+
return null;
52
52
+
}
53
53
+
54
54
+
function parseStringArray(input: unknown): string[] {
55
55
+
if (!Array.isArray(input)) {
56
56
+
return [];
57
57
+
}
58
58
+
59
59
+
return input.filter((entry): entry is string => typeof entry === "string");
60
60
+
}
61
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
39
-
const value = input as { swapOutKrisWithSusie?: unknown };
40
40
-
if (typeof value.swapOutKrisWithSusie !== "boolean") {
67
67
+
const value = input as {
68
68
+
useSusieProphecy?: unknown;
69
69
+
titleColors?: unknown;
70
70
+
favoriteGames?: unknown;
71
71
+
favoriteArtists?: unknown;
72
72
+
favoriteAlbums?: unknown;
73
73
+
favoriteDeltaruneCharacters?: unknown;
74
74
+
swapOutKrisWithSusie?: unknown;
75
75
+
site?: { susieProphecy?: unknown; titleColors?: unknown };
76
76
+
favorite?: {
77
77
+
game?: unknown;
78
78
+
artist?: unknown;
79
79
+
album?: unknown;
80
80
+
deltaruneCharacter?: unknown;
81
81
+
};
82
82
+
};
83
83
+
84
84
+
const useSusieProphecy =
85
85
+
typeof value.useSusieProphecy === "boolean"
86
86
+
? value.useSusieProphecy
87
87
+
: typeof value.swapOutKrisWithSusie === "boolean"
88
88
+
? value.swapOutKrisWithSusie
89
89
+
: typeof value.site?.susieProphecy === "boolean"
90
90
+
? value.site.susieProphecy
91
91
+
: null;
92
92
+
93
93
+
if (useSusieProphecy === null) {
41
94
return null;
42
95
}
43
96
44
44
-
return { swapOutKrisWithSusie: value.swapOutKrisWithSusie };
97
97
+
const titleColorsFromSite = parseKnownTitleColors(value.site?.titleColors);
98
98
+
99
99
+
let titleColors: TitleColor = "none";
100
100
+
if (value.titleColors === "none" || value.titleColors === "enby" || value.titleColors === "trans") {
101
101
+
titleColors = value.titleColors;
102
102
+
} else if (titleColorsFromSite) {
103
103
+
titleColors = titleColorsFromSite;
104
104
+
}
105
105
+
106
106
+
return {
107
107
+
useSusieProphecy,
108
108
+
titleColors,
109
109
+
favoriteGames: parseStringArray(value.favoriteGames ?? value.favorite?.game),
110
110
+
favoriteArtists: parseStringArray(value.favoriteArtists ?? value.favorite?.artist),
111
111
+
favoriteAlbums: parseStringArray(value.favoriteAlbums ?? value.favorite?.album),
112
112
+
favoriteDeltaruneCharacters: parseStringArray(
113
113
+
value.favoriteDeltaruneCharacters ?? value.favorite?.deltaruneCharacter,
114
114
+
),
115
115
+
};
45
116
}
46
117
47
118
function getServerState(): State {
···
55
126
56
127
const record = input as {
57
128
swapOutKrisWithSusie?: unknown;
58
58
-
site?: { susieProphecy?: unknown };
129
129
+
site?: { susieProphecy?: unknown; titleColors?: unknown };
130
130
+
favorite?: {
131
131
+
game?: unknown;
132
132
+
artist?: unknown;
133
133
+
album?: unknown;
134
134
+
deltaruneCharacter?: unknown;
135
135
+
};
59
136
};
137
137
+
const titleColors = parseKnownTitleColors(record.site?.titleColors) ?? "none";
60
138
61
139
if (typeof record.swapOutKrisWithSusie === "boolean") {
62
62
-
return { swapOutKrisWithSusie: record.swapOutKrisWithSusie };
140
140
+
return {
141
141
+
useSusieProphecy: record.swapOutKrisWithSusie,
142
142
+
titleColors,
143
143
+
favoriteGames: parseStringArray(record.favorite?.game),
144
144
+
favoriteArtists: parseStringArray(record.favorite?.artist),
145
145
+
favoriteAlbums: parseStringArray(record.favorite?.album),
146
146
+
favoriteDeltaruneCharacters: parseStringArray(record.favorite?.deltaruneCharacter),
147
147
+
};
63
148
}
64
149
65
150
if (typeof record.site?.susieProphecy === "boolean") {
66
66
-
return { swapOutKrisWithSusie: record.site.susieProphecy };
151
151
+
return {
152
152
+
useSusieProphecy: record.site.susieProphecy,
153
153
+
titleColors,
154
154
+
favoriteGames: parseStringArray(record.favorite?.game),
155
155
+
favoriteArtists: parseStringArray(record.favorite?.artist),
156
156
+
favoriteAlbums: parseStringArray(record.favorite?.album),
157
157
+
favoriteDeltaruneCharacters: parseStringArray(record.favorite?.deltaruneCharacter),
158
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
93
-
throw new Error("State record is missing `site.susieProphecy` or `swapOutKrisWithSusie`");
185
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
55
-
text: state.swapOutKrisWithSusie
55
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
58
-
image: state.swapOutKrisWithSusie
58
58
+
image: state.useSusieProphecy
59
59
? "/assets/prophecy/susie.png"
60
60
: "/assets/prophecy/kris.png",
61
61
-
variant: state.swapOutKrisWithSusie
61
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
1
-
export const islandLoaders: Record<string, () => Promise<Record<string, unknown>>> = {
1
1
+
type IslandLoader = () => Promise<Record<string, unknown>>;
2
2
+
3
3
+
const generatedIslandLoaders: Record<string, IslandLoader> = {
2
4
};
5
5
+
6
6
+
export function getGeneratedIslandLoaders(): Record<string, IslandLoader> {
7
7
+
return generatedIslandLoaders;
8
8
+
}
+18
src/runtime/island-loaders.ts
···
1
1
+
import { getGeneratedIslandLoaders } from "./generated/island-loaders";
2
2
+
3
3
+
export type IslandLoader = () => Promise<Record<string, unknown>>;
4
4
+
export type IslandLoaders = Record<string, IslandLoader>;
5
5
+
6
6
+
let cachedIslandLoaders: IslandLoaders | null = null;
7
7
+
8
8
+
export function getIslandLoaders(): IslandLoaders {
9
9
+
if (!cachedIslandLoaders) {
10
10
+
cachedIslandLoaders = getGeneratedIslandLoaders();
11
11
+
}
12
12
+
13
13
+
return cachedIslandLoaders;
14
14
+
}
15
15
+
16
16
+
export function getIslandLoader(moduleId: string): IslandLoader | undefined {
17
17
+
return getIslandLoaders()[moduleId];
18
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
5
-
import { islandLoaders } from "./generated/island-loaders";
5
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
10
-
const loader = islandLoaders[moduleId];
10
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
37
-
scripts.push(`<script type="module" src="${manifest.fullClientScriptHref}"></script>`);
37
37
+
scripts.push(`<script type="module" src="${manifest.fullClientScriptHref}" async defer></script>`);
38
38
} else if (!manifest.fullClient && manifest.islandsScriptHref) {
39
39
-
scripts.push(`<script type="module" src="${manifest.islandsScriptHref}"></script>`);
39
39
+
scripts.push(`<script type="module" src="${manifest.islandsScriptHref}" defer></script>`);
40
40
}
41
41
scripts.unshift(stateScript);
42
42