My personal website!
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Init

Koehn d84d15a6

+1818
+34
biome.json
··· 1 + { 2 + "$schema": "https://biomejs.dev/schemas/2.4.10/schema.json", 3 + "vcs": { 4 + "enabled": false, 5 + "clientKind": "git", 6 + "useIgnoreFile": false 7 + }, 8 + "files": { 9 + "ignoreUnknown": false 10 + }, 11 + "formatter": { 12 + "enabled": true, 13 + "indentStyle": "tab" 14 + }, 15 + "linter": { 16 + "enabled": true, 17 + "rules": { 18 + "recommended": true 19 + } 20 + }, 21 + "javascript": { 22 + "formatter": { 23 + "quoteStyle": "double" 24 + } 25 + }, 26 + "assist": { 27 + "enabled": true, 28 + "actions": { 29 + "source": { 30 + "organizeImports": "on" 31 + } 32 + } 33 + } 34 + }
+14
environment.d.ts
··· 1 + // environment.d.ts 2 + declare global { 3 + namespace NodeJS { 4 + interface ProcessEnv { 5 + PORT: string; 6 + ATPROTO_DID: string; 7 + ATPROTO_PDS: string; 8 + ATPROTO_PASSWORD: string; 9 + } 10 + } 11 + } 12 + 13 + // Ensures the file is treated as a module, if needed 14 + export {};
+22
mise.toml
··· 1 + [tools] 2 + node = "latest" 3 + 4 + [env] 5 + _.path = ['{{config_root}}/node_modules/.bin'] 6 + 7 + [hooks] 8 + postinstall = 'npx corepack enable' 9 + 10 + [settings] 11 + experimental = true 12 + 13 + [tasks.pnpm-install] 14 + description = 'Installs dependencies with pnpm' 15 + run = 'pnpm install' 16 + sources = ['package.json', 'pnpm-lock.yaml', 'mise.toml'] 17 + outputs = ['node_modules/.pnpm/lock.yaml'] 18 + 19 + [tasks.dev] 20 + description = 'Calls your dev script in `package.json`' 21 + run = 'pnpm start' 22 + depends = ['pnpm-install']
+27
package.json
··· 1 + { 2 + "name": "www", 3 + "version": "0.0.1", 4 + "description": "", 5 + "main": "src/index.ts", 6 + "type": "module", 7 + "author": { 8 + "name": "Koehn", 9 + "email": "koehn@fastmail.com" 10 + }, 11 + "devDependencies": { 12 + "@biomejs/biome": "^2.4.8", 13 + "@types/express": "^5.0.6", 14 + "@types/sanitize-html": "^2.16.1", 15 + "tsx": "4.21.0" 16 + }, 17 + "dependencies": { 18 + "express": "^5.2.1", 19 + "front-matter": "^4.0.2", 20 + "marked": "17.0.6", 21 + "sanitize-html": "^2.17.2", 22 + "zod": "^4.3.6" 23 + }, 24 + "scripts": { 25 + "start": "tsx --watch --env-file=.env src/main.ts" 26 + } 27 + }
+1226
pnpm-lock.yaml
··· 1 + lockfileVersion: '9.0' 2 + 3 + settings: 4 + autoInstallPeers: true 5 + excludeLinksFromLockfile: false 6 + 7 + importers: 8 + 9 + .: 10 + dependencies: 11 + express: 12 + specifier: ^5.2.1 13 + version: 5.2.1 14 + front-matter: 15 + specifier: ^4.0.2 16 + version: 4.0.2 17 + marked: 18 + specifier: 17.0.6 19 + version: 17.0.6 20 + sanitize-html: 21 + specifier: ^2.17.2 22 + version: 2.17.2 23 + zod: 24 + specifier: ^4.3.6 25 + version: 4.3.6 26 + devDependencies: 27 + '@biomejs/biome': 28 + specifier: ^2.4.8 29 + version: 2.4.10 30 + '@types/express': 31 + specifier: ^5.0.6 32 + version: 5.0.6 33 + '@types/sanitize-html': 34 + specifier: ^2.16.1 35 + version: 2.16.1 36 + tsx: 37 + specifier: 4.21.0 38 + version: 4.21.0 39 + 40 + packages: 41 + 42 + '@biomejs/biome@2.4.10': 43 + resolution: {integrity: sha512-xxA3AphFQ1geij4JTHXv4EeSTda1IFn22ye9LdyVPoJU19fNVl0uzfEuhsfQ4Yue/0FaLs2/ccVi4UDiE7R30w==} 44 + engines: {node: '>=14.21.3'} 45 + hasBin: true 46 + 47 + '@biomejs/cli-darwin-arm64@2.4.10': 48 + resolution: {integrity: sha512-vuzzI1cWqDVzOMIkYyHbKqp+AkQq4K7k+UCXWpkYcY/HDn1UxdsbsfgtVpa40shem8Kax4TLDLlx8kMAecgqiw==} 49 + engines: {node: '>=14.21.3'} 50 + cpu: [arm64] 51 + os: [darwin] 52 + 53 + '@biomejs/cli-darwin-x64@2.4.10': 54 + resolution: {integrity: sha512-14fzASRo+BPotwp7nWULy2W5xeUyFnTaq1V13Etrrxkrih+ez/2QfgFm5Ehtf5vSjtgx/IJycMMpn5kPd5ZNaA==} 55 + engines: {node: '>=14.21.3'} 56 + cpu: [x64] 57 + os: [darwin] 58 + 59 + '@biomejs/cli-linux-arm64-musl@2.4.10': 60 + resolution: {integrity: sha512-WrJY6UuiSD/Dh+nwK2qOTu8kdMDlLV3dLMmychIghHPAysWFq1/DGC1pVZx8POE3ZkzKR3PUUnVrtZfMfaJjyQ==} 61 + engines: {node: '>=14.21.3'} 62 + cpu: [arm64] 63 + os: [linux] 64 + libc: [musl] 65 + 66 + '@biomejs/cli-linux-arm64@2.4.10': 67 + resolution: {integrity: sha512-7MH1CMW5uuxQ/s7FLST63qF8B3Hgu2HRdZ7tA1X1+mk+St4JOuIrqdhIBnnyqeyWJNI+Bww7Es5QZ0wIc1Cmkw==} 68 + engines: {node: '>=14.21.3'} 69 + cpu: [arm64] 70 + os: [linux] 71 + libc: [glibc] 72 + 73 + '@biomejs/cli-linux-x64-musl@2.4.10': 74 + resolution: {integrity: sha512-kDTi3pI6PBN6CiczsWYOyP2zk0IJI08EWEQyDMQWW221rPaaEz6FvjLhnU07KMzLv8q3qSuoB93ua6inSQ55Tw==} 75 + engines: {node: '>=14.21.3'} 76 + cpu: [x64] 77 + os: [linux] 78 + libc: [musl] 79 + 80 + '@biomejs/cli-linux-x64@2.4.10': 81 + resolution: {integrity: sha512-tZLvEEi2u9Xu1zAqRjTcpIDGVtldigVvzug2fTuPG0ME/g8/mXpRPcNgLB22bGn6FvLJpHHnqLnwliOu8xjYrg==} 82 + engines: {node: '>=14.21.3'} 83 + cpu: [x64] 84 + os: [linux] 85 + libc: [glibc] 86 + 87 + '@biomejs/cli-win32-arm64@2.4.10': 88 + resolution: {integrity: sha512-umwQU6qPzH+ISTf/eHyJ/QoQnJs3V9Vpjz2OjZXe9MVBZ7prgGafMy7yYeRGnlmDAn87AKTF3Q6weLoMGpeqdQ==} 89 + engines: {node: '>=14.21.3'} 90 + cpu: [arm64] 91 + os: [win32] 92 + 93 + '@biomejs/cli-win32-x64@2.4.10': 94 + resolution: {integrity: sha512-aW/JU5GuyH4uxMrNYpoC2kjaHlyJGLgIa3XkhPEZI0uKhZhJZU8BuEyJmvgzSPQNGozBwWjC972RaNdcJ9KyJg==} 95 + engines: {node: '>=14.21.3'} 96 + cpu: [x64] 97 + os: [win32] 98 + 99 + '@esbuild/aix-ppc64@0.27.4': 100 + resolution: {integrity: sha512-cQPwL2mp2nSmHHJlCyoXgHGhbEPMrEEU5xhkcy3Hs/O7nGZqEpZ2sUtLaL9MORLtDfRvVl2/3PAuEkYZH0Ty8Q==} 101 + engines: {node: '>=18'} 102 + cpu: [ppc64] 103 + os: [aix] 104 + 105 + '@esbuild/android-arm64@0.27.4': 106 + resolution: {integrity: sha512-gdLscB7v75wRfu7QSm/zg6Rx29VLdy9eTr2t44sfTW7CxwAtQghZ4ZnqHk3/ogz7xao0QAgrkradbBzcqFPasw==} 107 + engines: {node: '>=18'} 108 + cpu: [arm64] 109 + os: [android] 110 + 111 + '@esbuild/android-arm@0.27.4': 112 + resolution: {integrity: sha512-X9bUgvxiC8CHAGKYufLIHGXPJWnr0OCdR0anD2e21vdvgCI8lIfqFbnoeOz7lBjdrAGUhqLZLcQo6MLhTO2DKQ==} 113 + engines: {node: '>=18'} 114 + cpu: [arm] 115 + os: [android] 116 + 117 + '@esbuild/android-x64@0.27.4': 118 + resolution: {integrity: sha512-PzPFnBNVF292sfpfhiyiXCGSn9HZg5BcAz+ivBuSsl6Rk4ga1oEXAamhOXRFyMcjwr2DVtm40G65N3GLeH1Lvw==} 119 + engines: {node: '>=18'} 120 + cpu: [x64] 121 + os: [android] 122 + 123 + '@esbuild/darwin-arm64@0.27.4': 124 + resolution: {integrity: sha512-b7xaGIwdJlht8ZFCvMkpDN6uiSmnxxK56N2GDTMYPr2/gzvfdQN8rTfBsvVKmIVY/X7EM+/hJKEIbbHs9oA4tQ==} 125 + engines: {node: '>=18'} 126 + cpu: [arm64] 127 + os: [darwin] 128 + 129 + '@esbuild/darwin-x64@0.27.4': 130 + resolution: {integrity: sha512-sR+OiKLwd15nmCdqpXMnuJ9W2kpy0KigzqScqHI3Hqwr7IXxBp3Yva+yJwoqh7rE8V77tdoheRYataNKL4QrPw==} 131 + engines: {node: '>=18'} 132 + cpu: [x64] 133 + os: [darwin] 134 + 135 + '@esbuild/freebsd-arm64@0.27.4': 136 + resolution: {integrity: sha512-jnfpKe+p79tCnm4GVav68A7tUFeKQwQyLgESwEAUzyxk/TJr4QdGog9sqWNcUbr/bZt/O/HXouspuQDd9JxFSw==} 137 + engines: {node: '>=18'} 138 + cpu: [arm64] 139 + os: [freebsd] 140 + 141 + '@esbuild/freebsd-x64@0.27.4': 142 + resolution: {integrity: sha512-2kb4ceA/CpfUrIcTUl1wrP/9ad9Atrp5J94Lq69w7UwOMolPIGrfLSvAKJp0RTvkPPyn6CIWrNy13kyLikZRZQ==} 143 + engines: {node: '>=18'} 144 + cpu: [x64] 145 + os: [freebsd] 146 + 147 + '@esbuild/linux-arm64@0.27.4': 148 + resolution: {integrity: sha512-7nQOttdzVGth1iz57kxg9uCz57dxQLHWxopL6mYuYthohPKEK0vU0C3O21CcBK6KDlkYVcnDXY099HcCDXd9dA==} 149 + engines: {node: '>=18'} 150 + cpu: [arm64] 151 + os: [linux] 152 + 153 + '@esbuild/linux-arm@0.27.4': 154 + resolution: {integrity: sha512-aBYgcIxX/wd5n2ys0yESGeYMGF+pv6g0DhZr3G1ZG4jMfruU9Tl1i2Z+Wnj9/KjGz1lTLCcorqE2viePZqj4Eg==} 155 + engines: {node: '>=18'} 156 + cpu: [arm] 157 + os: [linux] 158 + 159 + '@esbuild/linux-ia32@0.27.4': 160 + resolution: {integrity: sha512-oPtixtAIzgvzYcKBQM/qZ3R+9TEUd1aNJQu0HhGyqtx6oS7qTpvjheIWBbes4+qu1bNlo2V4cbkISr8q6gRBFA==} 161 + engines: {node: '>=18'} 162 + cpu: [ia32] 163 + os: [linux] 164 + 165 + '@esbuild/linux-loong64@0.27.4': 166 + resolution: {integrity: sha512-8mL/vh8qeCoRcFH2nM8wm5uJP+ZcVYGGayMavi8GmRJjuI3g1v6Z7Ni0JJKAJW+m0EtUuARb6Lmp4hMjzCBWzA==} 167 + engines: {node: '>=18'} 168 + cpu: [loong64] 169 + os: [linux] 170 + 171 + '@esbuild/linux-mips64el@0.27.4': 172 + resolution: {integrity: sha512-1RdrWFFiiLIW7LQq9Q2NES+HiD4NyT8Itj9AUeCl0IVCA459WnPhREKgwrpaIfTOe+/2rdntisegiPWn/r/aAw==} 173 + engines: {node: '>=18'} 174 + cpu: [mips64el] 175 + os: [linux] 176 + 177 + '@esbuild/linux-ppc64@0.27.4': 178 + resolution: {integrity: sha512-tLCwNG47l3sd9lpfyx9LAGEGItCUeRCWeAx6x2Jmbav65nAwoPXfewtAdtbtit/pJFLUWOhpv0FpS6GQAmPrHA==} 179 + engines: {node: '>=18'} 180 + cpu: [ppc64] 181 + os: [linux] 182 + 183 + '@esbuild/linux-riscv64@0.27.4': 184 + resolution: {integrity: sha512-BnASypppbUWyqjd1KIpU4AUBiIhVr6YlHx/cnPgqEkNoVOhHg+YiSVxM1RLfiy4t9cAulbRGTNCKOcqHrEQLIw==} 185 + engines: {node: '>=18'} 186 + cpu: [riscv64] 187 + os: [linux] 188 + 189 + '@esbuild/linux-s390x@0.27.4': 190 + resolution: {integrity: sha512-+eUqgb/Z7vxVLezG8bVB9SfBie89gMueS+I0xYh2tJdw3vqA/0ImZJ2ROeWwVJN59ihBeZ7Tu92dF/5dy5FttA==} 191 + engines: {node: '>=18'} 192 + cpu: [s390x] 193 + os: [linux] 194 + 195 + '@esbuild/linux-x64@0.27.4': 196 + resolution: {integrity: sha512-S5qOXrKV8BQEzJPVxAwnryi2+Iq5pB40gTEIT69BQONqR7JH1EPIcQ/Uiv9mCnn05jff9umq/5nqzxlqTOg9NA==} 197 + engines: {node: '>=18'} 198 + cpu: [x64] 199 + os: [linux] 200 + 201 + '@esbuild/netbsd-arm64@0.27.4': 202 + resolution: {integrity: sha512-xHT8X4sb0GS8qTqiwzHqpY00C95DPAq7nAwX35Ie/s+LO9830hrMd3oX0ZMKLvy7vsonee73x0lmcdOVXFzd6Q==} 203 + engines: {node: '>=18'} 204 + cpu: [arm64] 205 + os: [netbsd] 206 + 207 + '@esbuild/netbsd-x64@0.27.4': 208 + resolution: {integrity: sha512-RugOvOdXfdyi5Tyv40kgQnI0byv66BFgAqjdgtAKqHoZTbTF2QqfQrFwa7cHEORJf6X2ht+l9ABLMP0dnKYsgg==} 209 + engines: {node: '>=18'} 210 + cpu: [x64] 211 + os: [netbsd] 212 + 213 + '@esbuild/openbsd-arm64@0.27.4': 214 + resolution: {integrity: sha512-2MyL3IAaTX+1/qP0O1SwskwcwCoOI4kV2IBX1xYnDDqthmq5ArrW94qSIKCAuRraMgPOmG0RDTA74mzYNQA9ow==} 215 + engines: {node: '>=18'} 216 + cpu: [arm64] 217 + os: [openbsd] 218 + 219 + '@esbuild/openbsd-x64@0.27.4': 220 + resolution: {integrity: sha512-u8fg/jQ5aQDfsnIV6+KwLOf1CmJnfu1ShpwqdwC0uA7ZPwFws55Ngc12vBdeUdnuWoQYx/SOQLGDcdlfXhYmXQ==} 221 + engines: {node: '>=18'} 222 + cpu: [x64] 223 + os: [openbsd] 224 + 225 + '@esbuild/openharmony-arm64@0.27.4': 226 + resolution: {integrity: sha512-JkTZrl6VbyO8lDQO3yv26nNr2RM2yZzNrNHEsj9bm6dOwwu9OYN28CjzZkH57bh4w0I2F7IodpQvUAEd1mbWXg==} 227 + engines: {node: '>=18'} 228 + cpu: [arm64] 229 + os: [openharmony] 230 + 231 + '@esbuild/sunos-x64@0.27.4': 232 + resolution: {integrity: sha512-/gOzgaewZJfeJTlsWhvUEmUG4tWEY2Spp5M20INYRg2ZKl9QPO3QEEgPeRtLjEWSW8FilRNacPOg8R1uaYkA6g==} 233 + engines: {node: '>=18'} 234 + cpu: [x64] 235 + os: [sunos] 236 + 237 + '@esbuild/win32-arm64@0.27.4': 238 + resolution: {integrity: sha512-Z9SExBg2y32smoDQdf1HRwHRt6vAHLXcxD2uGgO/v2jK7Y718Ix4ndsbNMU/+1Qiem9OiOdaqitioZwxivhXYg==} 239 + engines: {node: '>=18'} 240 + cpu: [arm64] 241 + os: [win32] 242 + 243 + '@esbuild/win32-ia32@0.27.4': 244 + resolution: {integrity: sha512-DAyGLS0Jz5G5iixEbMHi5KdiApqHBWMGzTtMiJ72ZOLhbu/bzxgAe8Ue8CTS3n3HbIUHQz/L51yMdGMeoxXNJw==} 245 + engines: {node: '>=18'} 246 + cpu: [ia32] 247 + os: [win32] 248 + 249 + '@esbuild/win32-x64@0.27.4': 250 + resolution: {integrity: sha512-+knoa0BDoeXgkNvvV1vvbZX4+hizelrkwmGJBdT17t8FNPwG2lKemmuMZlmaNQ3ws3DKKCxpb4zRZEIp3UxFCg==} 251 + engines: {node: '>=18'} 252 + cpu: [x64] 253 + os: [win32] 254 + 255 + '@types/body-parser@1.19.6': 256 + resolution: {integrity: sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==} 257 + 258 + '@types/connect@3.4.38': 259 + resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} 260 + 261 + '@types/express-serve-static-core@5.1.1': 262 + resolution: {integrity: sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A==} 263 + 264 + '@types/express@5.0.6': 265 + resolution: {integrity: sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==} 266 + 267 + '@types/http-errors@2.0.5': 268 + resolution: {integrity: sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==} 269 + 270 + '@types/node@25.5.0': 271 + resolution: {integrity: sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==} 272 + 273 + '@types/qs@6.15.0': 274 + resolution: {integrity: sha512-JawvT8iBVWpzTrz3EGw9BTQFg3BQNmwERdKE22vlTxawwtbyUSlMppvZYKLZzB5zgACXdXxbD3m1bXaMqP/9ow==} 275 + 276 + '@types/range-parser@1.2.7': 277 + resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} 278 + 279 + '@types/sanitize-html@2.16.1': 280 + resolution: {integrity: sha512-n9wjs8bCOTyN/ynwD8s/nTcTreIHB1vf31vhLMGqUPNHaweKC4/fAl4Dj+hUlCTKYgm4P3k83fmiFfzkZ6sgMA==} 281 + 282 + '@types/send@1.2.1': 283 + resolution: {integrity: sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==} 284 + 285 + '@types/serve-static@2.2.0': 286 + resolution: {integrity: sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==} 287 + 288 + accepts@2.0.0: 289 + resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} 290 + engines: {node: '>= 0.6'} 291 + 292 + argparse@1.0.10: 293 + resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} 294 + 295 + body-parser@2.2.2: 296 + resolution: {integrity: sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==} 297 + engines: {node: '>=18'} 298 + 299 + bytes@3.1.2: 300 + resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} 301 + engines: {node: '>= 0.8'} 302 + 303 + call-bind-apply-helpers@1.0.2: 304 + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} 305 + engines: {node: '>= 0.4'} 306 + 307 + call-bound@1.0.4: 308 + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} 309 + engines: {node: '>= 0.4'} 310 + 311 + content-disposition@1.0.1: 312 + resolution: {integrity: sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==} 313 + engines: {node: '>=18'} 314 + 315 + content-type@1.0.5: 316 + resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} 317 + engines: {node: '>= 0.6'} 318 + 319 + cookie-signature@1.2.2: 320 + resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==} 321 + engines: {node: '>=6.6.0'} 322 + 323 + cookie@0.7.2: 324 + resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} 325 + engines: {node: '>= 0.6'} 326 + 327 + debug@4.4.3: 328 + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} 329 + engines: {node: '>=6.0'} 330 + peerDependencies: 331 + supports-color: '*' 332 + peerDependenciesMeta: 333 + supports-color: 334 + optional: true 335 + 336 + deepmerge@4.3.1: 337 + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} 338 + engines: {node: '>=0.10.0'} 339 + 340 + depd@2.0.0: 341 + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} 342 + engines: {node: '>= 0.8'} 343 + 344 + dom-serializer@2.0.0: 345 + resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} 346 + 347 + domelementtype@2.3.0: 348 + resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} 349 + 350 + domhandler@5.0.3: 351 + resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} 352 + engines: {node: '>= 4'} 353 + 354 + domutils@3.2.2: 355 + resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} 356 + 357 + dunder-proto@1.0.1: 358 + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} 359 + engines: {node: '>= 0.4'} 360 + 361 + ee-first@1.1.1: 362 + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} 363 + 364 + encodeurl@2.0.0: 365 + resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} 366 + engines: {node: '>= 0.8'} 367 + 368 + entities@4.5.0: 369 + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} 370 + engines: {node: '>=0.12'} 371 + 372 + entities@7.0.1: 373 + resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==} 374 + engines: {node: '>=0.12'} 375 + 376 + es-define-property@1.0.1: 377 + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} 378 + engines: {node: '>= 0.4'} 379 + 380 + es-errors@1.3.0: 381 + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} 382 + engines: {node: '>= 0.4'} 383 + 384 + es-object-atoms@1.1.1: 385 + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} 386 + engines: {node: '>= 0.4'} 387 + 388 + esbuild@0.27.4: 389 + resolution: {integrity: sha512-Rq4vbHnYkK5fws5NF7MYTU68FPRE1ajX7heQ/8QXXWqNgqqJ/GkmmyxIzUnf2Sr/bakf8l54716CcMGHYhMrrQ==} 390 + engines: {node: '>=18'} 391 + hasBin: true 392 + 393 + escape-html@1.0.3: 394 + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} 395 + 396 + escape-string-regexp@4.0.0: 397 + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} 398 + engines: {node: '>=10'} 399 + 400 + esprima@4.0.1: 401 + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} 402 + engines: {node: '>=4'} 403 + hasBin: true 404 + 405 + etag@1.8.1: 406 + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} 407 + engines: {node: '>= 0.6'} 408 + 409 + express@5.2.1: 410 + resolution: {integrity: sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==} 411 + engines: {node: '>= 18'} 412 + 413 + finalhandler@2.1.1: 414 + resolution: {integrity: sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==} 415 + engines: {node: '>= 18.0.0'} 416 + 417 + forwarded@0.2.0: 418 + resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} 419 + engines: {node: '>= 0.6'} 420 + 421 + fresh@2.0.0: 422 + resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} 423 + engines: {node: '>= 0.8'} 424 + 425 + front-matter@4.0.2: 426 + resolution: {integrity: sha512-I8ZuJ/qG92NWX8i5x1Y8qyj3vizhXS31OxjKDu3LKP+7/qBgfIKValiZIEwoVoJKUHlhWtYrktkxV1XsX+pPlg==} 427 + 428 + fsevents@2.3.3: 429 + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} 430 + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} 431 + os: [darwin] 432 + 433 + function-bind@1.1.2: 434 + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} 435 + 436 + get-intrinsic@1.3.0: 437 + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} 438 + engines: {node: '>= 0.4'} 439 + 440 + get-proto@1.0.1: 441 + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} 442 + engines: {node: '>= 0.4'} 443 + 444 + get-tsconfig@4.13.7: 445 + resolution: {integrity: sha512-7tN6rFgBlMgpBML5j8typ92BKFi2sFQvIdpAqLA2beia5avZDrMs0FLZiM5etShWq5irVyGcGMEA1jcDaK7A/Q==} 446 + 447 + gopd@1.2.0: 448 + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} 449 + engines: {node: '>= 0.4'} 450 + 451 + has-symbols@1.1.0: 452 + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} 453 + engines: {node: '>= 0.4'} 454 + 455 + hasown@2.0.2: 456 + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} 457 + engines: {node: '>= 0.4'} 458 + 459 + htmlparser2@10.1.0: 460 + resolution: {integrity: sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==} 461 + 462 + http-errors@2.0.1: 463 + resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==} 464 + engines: {node: '>= 0.8'} 465 + 466 + iconv-lite@0.7.2: 467 + resolution: {integrity: sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==} 468 + engines: {node: '>=0.10.0'} 469 + 470 + inherits@2.0.4: 471 + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} 472 + 473 + ipaddr.js@1.9.1: 474 + resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} 475 + engines: {node: '>= 0.10'} 476 + 477 + is-plain-object@5.0.0: 478 + resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==} 479 + engines: {node: '>=0.10.0'} 480 + 481 + is-promise@4.0.0: 482 + resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} 483 + 484 + js-yaml@3.14.2: 485 + resolution: {integrity: sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==} 486 + hasBin: true 487 + 488 + marked@17.0.6: 489 + resolution: {integrity: sha512-gB0gkNafnonOw0obSTEGZTT86IuhILt2Wfx0mWH/1Au83kybTayroZ/V6nS25mN7u8ASy+5fMhgB3XPNrOZdmA==} 490 + engines: {node: '>= 20'} 491 + hasBin: true 492 + 493 + math-intrinsics@1.1.0: 494 + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} 495 + engines: {node: '>= 0.4'} 496 + 497 + media-typer@1.1.0: 498 + resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} 499 + engines: {node: '>= 0.8'} 500 + 501 + merge-descriptors@2.0.0: 502 + resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} 503 + engines: {node: '>=18'} 504 + 505 + mime-db@1.54.0: 506 + resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} 507 + engines: {node: '>= 0.6'} 508 + 509 + mime-types@3.0.2: 510 + resolution: {integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==} 511 + engines: {node: '>=18'} 512 + 513 + ms@2.1.3: 514 + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} 515 + 516 + nanoid@3.3.11: 517 + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} 518 + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} 519 + hasBin: true 520 + 521 + negotiator@1.0.0: 522 + resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} 523 + engines: {node: '>= 0.6'} 524 + 525 + object-inspect@1.13.4: 526 + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} 527 + engines: {node: '>= 0.4'} 528 + 529 + on-finished@2.4.1: 530 + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} 531 + engines: {node: '>= 0.8'} 532 + 533 + once@1.4.0: 534 + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} 535 + 536 + parse-srcset@1.0.2: 537 + resolution: {integrity: sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q==} 538 + 539 + parseurl@1.3.3: 540 + resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} 541 + engines: {node: '>= 0.8'} 542 + 543 + path-to-regexp@8.4.1: 544 + resolution: {integrity: sha512-fvU78fIjZ+SBM9YwCknCvKOUKkLVqtWDVctl0s7xIqfmfb38t2TT4ZU2gHm+Z8xGwgW+QWEU3oQSAzIbo89Ggw==} 545 + 546 + picocolors@1.1.1: 547 + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} 548 + 549 + postcss@8.5.8: 550 + resolution: {integrity: sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==} 551 + engines: {node: ^10 || ^12 || >=14} 552 + 553 + proxy-addr@2.0.7: 554 + resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} 555 + engines: {node: '>= 0.10'} 556 + 557 + qs@6.15.0: 558 + resolution: {integrity: sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==} 559 + engines: {node: '>=0.6'} 560 + 561 + range-parser@1.2.1: 562 + resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} 563 + engines: {node: '>= 0.6'} 564 + 565 + raw-body@3.0.2: 566 + resolution: {integrity: sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==} 567 + engines: {node: '>= 0.10'} 568 + 569 + resolve-pkg-maps@1.0.0: 570 + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} 571 + 572 + router@2.2.0: 573 + resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} 574 + engines: {node: '>= 18'} 575 + 576 + safer-buffer@2.1.2: 577 + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} 578 + 579 + sanitize-html@2.17.2: 580 + resolution: {integrity: sha512-EnffJUl46VE9uvZ0XeWzObHLurClLlT12gsOk1cHyP2Ol1P0BnBnsXmShlBmWVJM+dKieQI68R0tsPY5m/B+Jg==} 581 + 582 + send@1.2.1: 583 + resolution: {integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==} 584 + engines: {node: '>= 18'} 585 + 586 + serve-static@2.2.1: 587 + resolution: {integrity: sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==} 588 + engines: {node: '>= 18'} 589 + 590 + setprototypeof@1.2.0: 591 + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} 592 + 593 + side-channel-list@1.0.0: 594 + resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} 595 + engines: {node: '>= 0.4'} 596 + 597 + side-channel-map@1.0.1: 598 + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} 599 + engines: {node: '>= 0.4'} 600 + 601 + side-channel-weakmap@1.0.2: 602 + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} 603 + engines: {node: '>= 0.4'} 604 + 605 + side-channel@1.1.0: 606 + resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} 607 + engines: {node: '>= 0.4'} 608 + 609 + source-map-js@1.2.1: 610 + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} 611 + engines: {node: '>=0.10.0'} 612 + 613 + sprintf-js@1.0.3: 614 + resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} 615 + 616 + statuses@2.0.2: 617 + resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} 618 + engines: {node: '>= 0.8'} 619 + 620 + toidentifier@1.0.1: 621 + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} 622 + engines: {node: '>=0.6'} 623 + 624 + tsx@4.21.0: 625 + resolution: {integrity: sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==} 626 + engines: {node: '>=18.0.0'} 627 + hasBin: true 628 + 629 + type-is@2.0.1: 630 + resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==} 631 + engines: {node: '>= 0.6'} 632 + 633 + undici-types@7.18.2: 634 + resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==} 635 + 636 + unpipe@1.0.0: 637 + resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} 638 + engines: {node: '>= 0.8'} 639 + 640 + vary@1.1.2: 641 + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} 642 + engines: {node: '>= 0.8'} 643 + 644 + wrappy@1.0.2: 645 + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} 646 + 647 + zod@4.3.6: 648 + resolution: {integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==} 649 + 650 + snapshots: 651 + 652 + '@biomejs/biome@2.4.10': 653 + optionalDependencies: 654 + '@biomejs/cli-darwin-arm64': 2.4.10 655 + '@biomejs/cli-darwin-x64': 2.4.10 656 + '@biomejs/cli-linux-arm64': 2.4.10 657 + '@biomejs/cli-linux-arm64-musl': 2.4.10 658 + '@biomejs/cli-linux-x64': 2.4.10 659 + '@biomejs/cli-linux-x64-musl': 2.4.10 660 + '@biomejs/cli-win32-arm64': 2.4.10 661 + '@biomejs/cli-win32-x64': 2.4.10 662 + 663 + '@biomejs/cli-darwin-arm64@2.4.10': 664 + optional: true 665 + 666 + '@biomejs/cli-darwin-x64@2.4.10': 667 + optional: true 668 + 669 + '@biomejs/cli-linux-arm64-musl@2.4.10': 670 + optional: true 671 + 672 + '@biomejs/cli-linux-arm64@2.4.10': 673 + optional: true 674 + 675 + '@biomejs/cli-linux-x64-musl@2.4.10': 676 + optional: true 677 + 678 + '@biomejs/cli-linux-x64@2.4.10': 679 + optional: true 680 + 681 + '@biomejs/cli-win32-arm64@2.4.10': 682 + optional: true 683 + 684 + '@biomejs/cli-win32-x64@2.4.10': 685 + optional: true 686 + 687 + '@esbuild/aix-ppc64@0.27.4': 688 + optional: true 689 + 690 + '@esbuild/android-arm64@0.27.4': 691 + optional: true 692 + 693 + '@esbuild/android-arm@0.27.4': 694 + optional: true 695 + 696 + '@esbuild/android-x64@0.27.4': 697 + optional: true 698 + 699 + '@esbuild/darwin-arm64@0.27.4': 700 + optional: true 701 + 702 + '@esbuild/darwin-x64@0.27.4': 703 + optional: true 704 + 705 + '@esbuild/freebsd-arm64@0.27.4': 706 + optional: true 707 + 708 + '@esbuild/freebsd-x64@0.27.4': 709 + optional: true 710 + 711 + '@esbuild/linux-arm64@0.27.4': 712 + optional: true 713 + 714 + '@esbuild/linux-arm@0.27.4': 715 + optional: true 716 + 717 + '@esbuild/linux-ia32@0.27.4': 718 + optional: true 719 + 720 + '@esbuild/linux-loong64@0.27.4': 721 + optional: true 722 + 723 + '@esbuild/linux-mips64el@0.27.4': 724 + optional: true 725 + 726 + '@esbuild/linux-ppc64@0.27.4': 727 + optional: true 728 + 729 + '@esbuild/linux-riscv64@0.27.4': 730 + optional: true 731 + 732 + '@esbuild/linux-s390x@0.27.4': 733 + optional: true 734 + 735 + '@esbuild/linux-x64@0.27.4': 736 + optional: true 737 + 738 + '@esbuild/netbsd-arm64@0.27.4': 739 + optional: true 740 + 741 + '@esbuild/netbsd-x64@0.27.4': 742 + optional: true 743 + 744 + '@esbuild/openbsd-arm64@0.27.4': 745 + optional: true 746 + 747 + '@esbuild/openbsd-x64@0.27.4': 748 + optional: true 749 + 750 + '@esbuild/openharmony-arm64@0.27.4': 751 + optional: true 752 + 753 + '@esbuild/sunos-x64@0.27.4': 754 + optional: true 755 + 756 + '@esbuild/win32-arm64@0.27.4': 757 + optional: true 758 + 759 + '@esbuild/win32-ia32@0.27.4': 760 + optional: true 761 + 762 + '@esbuild/win32-x64@0.27.4': 763 + optional: true 764 + 765 + '@types/body-parser@1.19.6': 766 + dependencies: 767 + '@types/connect': 3.4.38 768 + '@types/node': 25.5.0 769 + 770 + '@types/connect@3.4.38': 771 + dependencies: 772 + '@types/node': 25.5.0 773 + 774 + '@types/express-serve-static-core@5.1.1': 775 + dependencies: 776 + '@types/node': 25.5.0 777 + '@types/qs': 6.15.0 778 + '@types/range-parser': 1.2.7 779 + '@types/send': 1.2.1 780 + 781 + '@types/express@5.0.6': 782 + dependencies: 783 + '@types/body-parser': 1.19.6 784 + '@types/express-serve-static-core': 5.1.1 785 + '@types/serve-static': 2.2.0 786 + 787 + '@types/http-errors@2.0.5': {} 788 + 789 + '@types/node@25.5.0': 790 + dependencies: 791 + undici-types: 7.18.2 792 + 793 + '@types/qs@6.15.0': {} 794 + 795 + '@types/range-parser@1.2.7': {} 796 + 797 + '@types/sanitize-html@2.16.1': 798 + dependencies: 799 + htmlparser2: 10.1.0 800 + 801 + '@types/send@1.2.1': 802 + dependencies: 803 + '@types/node': 25.5.0 804 + 805 + '@types/serve-static@2.2.0': 806 + dependencies: 807 + '@types/http-errors': 2.0.5 808 + '@types/node': 25.5.0 809 + 810 + accepts@2.0.0: 811 + dependencies: 812 + mime-types: 3.0.2 813 + negotiator: 1.0.0 814 + 815 + argparse@1.0.10: 816 + dependencies: 817 + sprintf-js: 1.0.3 818 + 819 + body-parser@2.2.2: 820 + dependencies: 821 + bytes: 3.1.2 822 + content-type: 1.0.5 823 + debug: 4.4.3 824 + http-errors: 2.0.1 825 + iconv-lite: 0.7.2 826 + on-finished: 2.4.1 827 + qs: 6.15.0 828 + raw-body: 3.0.2 829 + type-is: 2.0.1 830 + transitivePeerDependencies: 831 + - supports-color 832 + 833 + bytes@3.1.2: {} 834 + 835 + call-bind-apply-helpers@1.0.2: 836 + dependencies: 837 + es-errors: 1.3.0 838 + function-bind: 1.1.2 839 + 840 + call-bound@1.0.4: 841 + dependencies: 842 + call-bind-apply-helpers: 1.0.2 843 + get-intrinsic: 1.3.0 844 + 845 + content-disposition@1.0.1: {} 846 + 847 + content-type@1.0.5: {} 848 + 849 + cookie-signature@1.2.2: {} 850 + 851 + cookie@0.7.2: {} 852 + 853 + debug@4.4.3: 854 + dependencies: 855 + ms: 2.1.3 856 + 857 + deepmerge@4.3.1: {} 858 + 859 + depd@2.0.0: {} 860 + 861 + dom-serializer@2.0.0: 862 + dependencies: 863 + domelementtype: 2.3.0 864 + domhandler: 5.0.3 865 + entities: 4.5.0 866 + 867 + domelementtype@2.3.0: {} 868 + 869 + domhandler@5.0.3: 870 + dependencies: 871 + domelementtype: 2.3.0 872 + 873 + domutils@3.2.2: 874 + dependencies: 875 + dom-serializer: 2.0.0 876 + domelementtype: 2.3.0 877 + domhandler: 5.0.3 878 + 879 + dunder-proto@1.0.1: 880 + dependencies: 881 + call-bind-apply-helpers: 1.0.2 882 + es-errors: 1.3.0 883 + gopd: 1.2.0 884 + 885 + ee-first@1.1.1: {} 886 + 887 + encodeurl@2.0.0: {} 888 + 889 + entities@4.5.0: {} 890 + 891 + entities@7.0.1: {} 892 + 893 + es-define-property@1.0.1: {} 894 + 895 + es-errors@1.3.0: {} 896 + 897 + es-object-atoms@1.1.1: 898 + dependencies: 899 + es-errors: 1.3.0 900 + 901 + esbuild@0.27.4: 902 + optionalDependencies: 903 + '@esbuild/aix-ppc64': 0.27.4 904 + '@esbuild/android-arm': 0.27.4 905 + '@esbuild/android-arm64': 0.27.4 906 + '@esbuild/android-x64': 0.27.4 907 + '@esbuild/darwin-arm64': 0.27.4 908 + '@esbuild/darwin-x64': 0.27.4 909 + '@esbuild/freebsd-arm64': 0.27.4 910 + '@esbuild/freebsd-x64': 0.27.4 911 + '@esbuild/linux-arm': 0.27.4 912 + '@esbuild/linux-arm64': 0.27.4 913 + '@esbuild/linux-ia32': 0.27.4 914 + '@esbuild/linux-loong64': 0.27.4 915 + '@esbuild/linux-mips64el': 0.27.4 916 + '@esbuild/linux-ppc64': 0.27.4 917 + '@esbuild/linux-riscv64': 0.27.4 918 + '@esbuild/linux-s390x': 0.27.4 919 + '@esbuild/linux-x64': 0.27.4 920 + '@esbuild/netbsd-arm64': 0.27.4 921 + '@esbuild/netbsd-x64': 0.27.4 922 + '@esbuild/openbsd-arm64': 0.27.4 923 + '@esbuild/openbsd-x64': 0.27.4 924 + '@esbuild/openharmony-arm64': 0.27.4 925 + '@esbuild/sunos-x64': 0.27.4 926 + '@esbuild/win32-arm64': 0.27.4 927 + '@esbuild/win32-ia32': 0.27.4 928 + '@esbuild/win32-x64': 0.27.4 929 + 930 + escape-html@1.0.3: {} 931 + 932 + escape-string-regexp@4.0.0: {} 933 + 934 + esprima@4.0.1: {} 935 + 936 + etag@1.8.1: {} 937 + 938 + express@5.2.1: 939 + dependencies: 940 + accepts: 2.0.0 941 + body-parser: 2.2.2 942 + content-disposition: 1.0.1 943 + content-type: 1.0.5 944 + cookie: 0.7.2 945 + cookie-signature: 1.2.2 946 + debug: 4.4.3 947 + depd: 2.0.0 948 + encodeurl: 2.0.0 949 + escape-html: 1.0.3 950 + etag: 1.8.1 951 + finalhandler: 2.1.1 952 + fresh: 2.0.0 953 + http-errors: 2.0.1 954 + merge-descriptors: 2.0.0 955 + mime-types: 3.0.2 956 + on-finished: 2.4.1 957 + once: 1.4.0 958 + parseurl: 1.3.3 959 + proxy-addr: 2.0.7 960 + qs: 6.15.0 961 + range-parser: 1.2.1 962 + router: 2.2.0 963 + send: 1.2.1 964 + serve-static: 2.2.1 965 + statuses: 2.0.2 966 + type-is: 2.0.1 967 + vary: 1.1.2 968 + transitivePeerDependencies: 969 + - supports-color 970 + 971 + finalhandler@2.1.1: 972 + dependencies: 973 + debug: 4.4.3 974 + encodeurl: 2.0.0 975 + escape-html: 1.0.3 976 + on-finished: 2.4.1 977 + parseurl: 1.3.3 978 + statuses: 2.0.2 979 + transitivePeerDependencies: 980 + - supports-color 981 + 982 + forwarded@0.2.0: {} 983 + 984 + fresh@2.0.0: {} 985 + 986 + front-matter@4.0.2: 987 + dependencies: 988 + js-yaml: 3.14.2 989 + 990 + fsevents@2.3.3: 991 + optional: true 992 + 993 + function-bind@1.1.2: {} 994 + 995 + get-intrinsic@1.3.0: 996 + dependencies: 997 + call-bind-apply-helpers: 1.0.2 998 + es-define-property: 1.0.1 999 + es-errors: 1.3.0 1000 + es-object-atoms: 1.1.1 1001 + function-bind: 1.1.2 1002 + get-proto: 1.0.1 1003 + gopd: 1.2.0 1004 + has-symbols: 1.1.0 1005 + hasown: 2.0.2 1006 + math-intrinsics: 1.1.0 1007 + 1008 + get-proto@1.0.1: 1009 + dependencies: 1010 + dunder-proto: 1.0.1 1011 + es-object-atoms: 1.1.1 1012 + 1013 + get-tsconfig@4.13.7: 1014 + dependencies: 1015 + resolve-pkg-maps: 1.0.0 1016 + 1017 + gopd@1.2.0: {} 1018 + 1019 + has-symbols@1.1.0: {} 1020 + 1021 + hasown@2.0.2: 1022 + dependencies: 1023 + function-bind: 1.1.2 1024 + 1025 + htmlparser2@10.1.0: 1026 + dependencies: 1027 + domelementtype: 2.3.0 1028 + domhandler: 5.0.3 1029 + domutils: 3.2.2 1030 + entities: 7.0.1 1031 + 1032 + http-errors@2.0.1: 1033 + dependencies: 1034 + depd: 2.0.0 1035 + inherits: 2.0.4 1036 + setprototypeof: 1.2.0 1037 + statuses: 2.0.2 1038 + toidentifier: 1.0.1 1039 + 1040 + iconv-lite@0.7.2: 1041 + dependencies: 1042 + safer-buffer: 2.1.2 1043 + 1044 + inherits@2.0.4: {} 1045 + 1046 + ipaddr.js@1.9.1: {} 1047 + 1048 + is-plain-object@5.0.0: {} 1049 + 1050 + is-promise@4.0.0: {} 1051 + 1052 + js-yaml@3.14.2: 1053 + dependencies: 1054 + argparse: 1.0.10 1055 + esprima: 4.0.1 1056 + 1057 + marked@17.0.6: {} 1058 + 1059 + math-intrinsics@1.1.0: {} 1060 + 1061 + media-typer@1.1.0: {} 1062 + 1063 + merge-descriptors@2.0.0: {} 1064 + 1065 + mime-db@1.54.0: {} 1066 + 1067 + mime-types@3.0.2: 1068 + dependencies: 1069 + mime-db: 1.54.0 1070 + 1071 + ms@2.1.3: {} 1072 + 1073 + nanoid@3.3.11: {} 1074 + 1075 + negotiator@1.0.0: {} 1076 + 1077 + object-inspect@1.13.4: {} 1078 + 1079 + on-finished@2.4.1: 1080 + dependencies: 1081 + ee-first: 1.1.1 1082 + 1083 + once@1.4.0: 1084 + dependencies: 1085 + wrappy: 1.0.2 1086 + 1087 + parse-srcset@1.0.2: {} 1088 + 1089 + parseurl@1.3.3: {} 1090 + 1091 + path-to-regexp@8.4.1: {} 1092 + 1093 + picocolors@1.1.1: {} 1094 + 1095 + postcss@8.5.8: 1096 + dependencies: 1097 + nanoid: 3.3.11 1098 + picocolors: 1.1.1 1099 + source-map-js: 1.2.1 1100 + 1101 + proxy-addr@2.0.7: 1102 + dependencies: 1103 + forwarded: 0.2.0 1104 + ipaddr.js: 1.9.1 1105 + 1106 + qs@6.15.0: 1107 + dependencies: 1108 + side-channel: 1.1.0 1109 + 1110 + range-parser@1.2.1: {} 1111 + 1112 + raw-body@3.0.2: 1113 + dependencies: 1114 + bytes: 3.1.2 1115 + http-errors: 2.0.1 1116 + iconv-lite: 0.7.2 1117 + unpipe: 1.0.0 1118 + 1119 + resolve-pkg-maps@1.0.0: {} 1120 + 1121 + router@2.2.0: 1122 + dependencies: 1123 + debug: 4.4.3 1124 + depd: 2.0.0 1125 + is-promise: 4.0.0 1126 + parseurl: 1.3.3 1127 + path-to-regexp: 8.4.1 1128 + transitivePeerDependencies: 1129 + - supports-color 1130 + 1131 + safer-buffer@2.1.2: {} 1132 + 1133 + sanitize-html@2.17.2: 1134 + dependencies: 1135 + deepmerge: 4.3.1 1136 + escape-string-regexp: 4.0.0 1137 + htmlparser2: 10.1.0 1138 + is-plain-object: 5.0.0 1139 + parse-srcset: 1.0.2 1140 + postcss: 8.5.8 1141 + 1142 + send@1.2.1: 1143 + dependencies: 1144 + debug: 4.4.3 1145 + encodeurl: 2.0.0 1146 + escape-html: 1.0.3 1147 + etag: 1.8.1 1148 + fresh: 2.0.0 1149 + http-errors: 2.0.1 1150 + mime-types: 3.0.2 1151 + ms: 2.1.3 1152 + on-finished: 2.4.1 1153 + range-parser: 1.2.1 1154 + statuses: 2.0.2 1155 + transitivePeerDependencies: 1156 + - supports-color 1157 + 1158 + serve-static@2.2.1: 1159 + dependencies: 1160 + encodeurl: 2.0.0 1161 + escape-html: 1.0.3 1162 + parseurl: 1.3.3 1163 + send: 1.2.1 1164 + transitivePeerDependencies: 1165 + - supports-color 1166 + 1167 + setprototypeof@1.2.0: {} 1168 + 1169 + side-channel-list@1.0.0: 1170 + dependencies: 1171 + es-errors: 1.3.0 1172 + object-inspect: 1.13.4 1173 + 1174 + side-channel-map@1.0.1: 1175 + dependencies: 1176 + call-bound: 1.0.4 1177 + es-errors: 1.3.0 1178 + get-intrinsic: 1.3.0 1179 + object-inspect: 1.13.4 1180 + 1181 + side-channel-weakmap@1.0.2: 1182 + dependencies: 1183 + call-bound: 1.0.4 1184 + es-errors: 1.3.0 1185 + get-intrinsic: 1.3.0 1186 + object-inspect: 1.13.4 1187 + side-channel-map: 1.0.1 1188 + 1189 + side-channel@1.1.0: 1190 + dependencies: 1191 + es-errors: 1.3.0 1192 + object-inspect: 1.13.4 1193 + side-channel-list: 1.0.0 1194 + side-channel-map: 1.0.1 1195 + side-channel-weakmap: 1.0.2 1196 + 1197 + source-map-js@1.2.1: {} 1198 + 1199 + sprintf-js@1.0.3: {} 1200 + 1201 + statuses@2.0.2: {} 1202 + 1203 + toidentifier@1.0.1: {} 1204 + 1205 + tsx@4.21.0: 1206 + dependencies: 1207 + esbuild: 0.27.4 1208 + get-tsconfig: 4.13.7 1209 + optionalDependencies: 1210 + fsevents: 2.3.3 1211 + 1212 + type-is@2.0.1: 1213 + dependencies: 1214 + content-type: 1.0.5 1215 + media-typer: 1.1.0 1216 + mime-types: 3.0.2 1217 + 1218 + undici-types@7.18.2: {} 1219 + 1220 + unpipe@1.0.0: {} 1221 + 1222 + vary@1.1.2: {} 1223 + 1224 + wrappy@1.0.2: {} 1225 + 1226 + zod@4.3.6: {}
src/.DS_Store

This is a binary file and will not be displayed.

+15
src/components/banner.ts
··· 1 + import { Banner } from "../utils/structs"; 2 + 3 + export function banner(bannerStruct: unknown, options?: { home: boolean }) { 4 + try { 5 + let validatedData = Banner.parse(bannerStruct); 6 + 7 + if (options?.home !== false) { 8 + validatedData = [{ link: "/", title: "home" }, ...validatedData]; 9 + } 10 + 11 + return ` <nav class="banner">${validatedData.map((i) => `[ <a href="${i.link}">${i.title}</a> ]`).join(" · ")}</nav>`; 12 + } catch (error) { 13 + console.error("Validation failed:", error); 14 + } 15 + }
+14
src/components/footer.ts
··· 1 + // import { execSync } from "node:child_process"; 2 + import { version } from "../../package.json"; 3 + 4 + export async function footer() { 5 + // const longCommit = execSync("git rev-parse HEAD").toString().trim(); 6 + // const shortCommit = execSync("git rev-parse --short HEAD").toString().trim(); 7 + // <a href="https://tangled.org/fromkoehn.com/www/commit/${longCommit}">${shortCommit}</a> 8 + 9 + return ` 10 + <footer role="contentinfo"> 11 + © 2026 <a href="mailto:hello@fromkoehn.com" rel="me">Koehn Humphries</a> · <a href="/public/key.txt">signature</a> · v${version} 12 + </footer> 13 + `; 14 + }
+18
src/components/head.ts
··· 1 + export function head(options?: { title?: string; stylesheet?: string }) { 2 + const stylesheet = options?.stylesheet || "index"; 3 + const title = options?.title || "From, Koehn."; 4 + 5 + return ` 6 + <head> 7 + <meta charset="utf-8" /> 8 + <title>${title}</title> 9 + <link rel="stylesheet" href="/public/${stylesheet}.css" /> 10 + <meta name="viewport" content="width=device-width, initial-scale=1" /> 11 + <meta name="fediverse:creator" content="@koehn@famichiki.jp"> 12 + <link rel="me" href="https://famichiki.jp/@koehn" /> 13 + <link rel="alternate" type="application/atom+xml" title="From, Koehn" href="/feed.xml"> 14 + <link rel="human-json" href="/.well-known/human.json"> 15 + <link rel="icon" type="image/x-icon" href="/public/favicon.ico"> 16 + </head> 17 + `; 18 + }
+62
src/main.ts
··· 1 + import fs from "node:fs/promises"; 2 + import path from "node:path"; 3 + import express from "express"; 4 + import type { z } from "zod"; 5 + import { banner } from "./components/banner"; 6 + import { footer } from "./components/footer"; 7 + import { head } from "./components/head"; 8 + import type { Post } from "./utils/structs"; 9 + 10 + const app = express(); 11 + const port = process.env.PORT || 8080; 12 + 13 + app.use("/public", express.static(path.join(path.resolve(), "src/public"))); 14 + app.get("/", async (_req, res) => { 15 + const content: z.infer<typeof Post>[] = JSON.parse( 16 + await fs.readFile(path.join(path.resolve(), "data", "db.json"), "utf-8"), 17 + ); 18 + 19 + res.send(` 20 + <!DOCTYPE html> 21 + <html lang="en-US"> 22 + ${head()} 23 + <body> 24 + <main> 25 + <h1>From, <i class="me">Koehn</i></h1> 26 + ${( 27 + await Promise.all( 28 + content 29 + .filter((p) => !p.flags?.includes("private")) 30 + .sort((a, b) => +b.published - +a.published) 31 + .map(async (post) => { 32 + let decoration: string = ""; 33 + 34 + if (!post.flags.includes("source:local")) { 35 + const flag = post.flags.find((f) => 36 + f.startsWith("source:"), 37 + ); 38 + decoration = ` <span class="banner">[ ${flag?.split(":")[1]} ]</span>`; 39 + } 40 + 41 + return `<p role="article"><time class="mute" datetime="${post.published}">${ 42 + new Date(post.published).toISOString().split("T")[0] 43 + }</time>${decoration} <a href="${post.slug}">${post.title}</a></p>`; 44 + }), 45 + ) 46 + ).join(" ")} 47 + </main> 48 + ${banner( 49 + [ 50 + { title: "about", link: "infer" }, 51 + { title: "time machine", link: "infer" }, 52 + ], 53 + { home: false }, 54 + )} 55 + ${await footer()} 56 + </html> 57 + `); 58 + }); 59 + 60 + app.listen(port, () => { 61 + console.log(`[INFO] Started listening on: ${port}`); 62 + });
src/public/.DS_Store

This is a binary file and will not be displayed.

src/public/favicon.ico

This is a binary file and will not be displayed.

+122
src/public/index.css
··· 1 + @font-face { 2 + font-family: "Aman"; 3 + src: url("/public/fonts/Aman-Variable[wght,opsz,slnt].woff2") 4 + format("woff2-variations"); 5 + font-style: normal; 6 + } 7 + 8 + @font-face { 9 + font-family: "Apoc JP"; 10 + src: url("/public/fonts/ApocJPRevelations-Regular.otf") format("opentype"); 11 + font-style: normal; 12 + } 13 + 14 + :root { 15 + color-scheme: light dark; 16 + --background-color: light-dark( 17 + oklch(0.9889 0.0053 17.25), 18 + oklch(0.1957 0.0062 337.89) 19 + ); 20 + 21 + --secondary-background-color: light-dark( 22 + oklch(0.26 0.02 94.4), 23 + oklch(0.7382 0.0075 88.66) 24 + ); 25 + 26 + --text-color: light-dark(oklch(0.16 0.012 260), oklch(0.93 0.01 20)); 27 + 28 + --accent-color: light-dark(oklch(0.68 0.16 360), oklch(0.72 0.17 360)); 29 + 30 + --complementary-color: oklch(0.75 0.17 165); 31 + 32 + --abbr-color: light-dark(oklch(0.68 0.12 210), oklch(0.72 0.12 210)); 33 + } 34 + 35 + @media screen and (max-width: 500px) { 36 + body { 37 + max-width: 20rem; 38 + font-size: 1.2em; 39 + } 40 + } 41 + 42 + body { 43 + text-align: left; 44 + max-width: 40rem; 45 + margin: 0 auto; 46 + font-family: "Aman", "Apoc JP", ui-serif; 47 + font-variation-settings: 48 + "wght" 400, 49 + "opsz" 9; 50 + background-color: var(--background-color); 51 + color: var(--text-color); 52 + font-size: 1.5em; 53 + text-decoration-thickness: 0.14em; 54 + text-underline-offset: 0.18em; 55 + } 56 + 57 + .me { 58 + color: var(--accent-color); 59 + } 60 + 61 + .mute { 62 + opacity: 70%; 63 + } 64 + 65 + a { 66 + color: var(--text-color); 67 + text-decoration-color: var(--accent-color); 68 + } 69 + 70 + a:hover, 71 + a:focus { 72 + color: var(--accent-color); 73 + font-variation-settings: 74 + "wght" 400, 75 + "opsz" 9, 76 + "slnt" -4.4; 77 + } 78 + 79 + .bar { 80 + border-left: 3px solid var(--secondary-background-color); 81 + margin: 1.5em 10px; 82 + padding: 0.5em 10px; 83 + } 84 + 85 + h1, 86 + h2, 87 + h3 { 88 + font-variation-settings: 89 + "wght" 450, 90 + "opsz" 24, 91 + "slnt" -4.4; 92 + } 93 + 94 + a[href^="https://"] { 95 + text-decoration-color: var(--complementary-color); 96 + text-decoration-style: dashed; 97 + } 98 + 99 + a[href^="https://"]:hover { 100 + color: var(--complementary-color); 101 + text-decoration-style: solid; 102 + } 103 + 104 + abbr[title] { 105 + text-decoration: underline dotted var(--abbr-color); 106 + cursor: help; 107 + } 108 + 109 + .banner { 110 + padding: 10px 0; 111 + position: relative; 112 + width: 100%; 113 + color: var(--secondary-background-color); 114 + } 115 + 116 + footer { 117 + bottom: 0; 118 + } 119 + 120 + header { 121 + top: 0; 122 + }
+40
src/utils/mergePosts.ts
··· 1 + import fs from "node:fs"; 2 + import path from "node:path"; 3 + import type { z } from "zod"; 4 + import { standardSite } from "./sources/standard.site"; 5 + import type { Collection, Post } from "./structs"; 6 + import { localPosts } from "./sources/local"; 7 + 8 + const proposedIndexes: z.output<typeof Post>[] = []; 9 + 10 + function push(index: z.output<typeof Collection>) { 11 + console.log(`[INFO] Collecting posts from ${index.collection}...`); 12 + 13 + index.data.forEach((p) => { 14 + proposedIndexes.push(p); 15 + console.log(`[INFO] Pushed ${p.id}`); 16 + }); 17 + } 18 + 19 + push(await standardSite()); 20 + push(await localPosts()); 21 + 22 + export async function merge() { 23 + try { 24 + const sorted = proposedIndexes.toSorted( 25 + (a, b) => 26 + new Date(b.published).getTime() - new Date(a.published).getTime(), 27 + ); 28 + 29 + await fs.promises.writeFile( 30 + path.join(path.resolve(), "data", "db.json"), 31 + JSON.stringify(sorted), 32 + { flag: "w+" }, 33 + ); 34 + console.log("[INFO] Wrote to database!"); 35 + } catch (err) { 36 + console.error(`[ERROR] Failed to write to file: ${err}`); 37 + } 38 + } 39 + 40 + await merge();
+41
src/utils/sources/local.ts
··· 1 + import fs from "node:fs"; 2 + import path from "node:path"; 3 + import sanitizeHtml from "sanitize-html"; 4 + import fm from "front-matter"; 5 + import { marked } from "marked"; 6 + import type { z } from "zod"; 7 + import { Post } from "../structs"; 8 + 9 + export async function localPosts() { 10 + const finalDocuments: z.output<typeof Post>[] = []; 11 + 12 + for (const file of await fs.promises.readdir( 13 + path.join(path.resolve(), "data", "posts"), 14 + )) { 15 + const post = await fs.promises.readFile( 16 + path.join(path.resolve(), "data", "posts", file), 17 + "utf-8", 18 + ); 19 + 20 + const { attributes, body } = fm(post); 21 + 22 + const finalAttribs = attributes as Partial<z.infer<typeof Post>>; 23 + 24 + const stage = { 25 + title: finalAttribs.title, 26 + description: finalAttribs.description, 27 + published: new Date(finalAttribs.published ?? Date.now()).toISOString(), 28 + tags: finalAttribs.tags, 29 + id: finalAttribs.id, 30 + flags: [`source:local`, ...(finalAttribs.flags ?? [])], 31 + slug: `/posts/${file.replace(".md", "")}`, 32 + content: sanitizeHtml(await marked.parse(body)), 33 + }; 34 + 35 + const finalDoc = Post.parse(stage); 36 + 37 + finalDocuments.push(finalDoc); 38 + } 39 + 40 + return { collection: "local", data: finalDocuments }; 41 + }
+98
src/utils/sources/standard.site.ts
··· 1 + import fs from "node:fs"; 2 + import path from "node:path"; 3 + import type { z } from "zod"; 4 + import { 5 + ATProtoResponse, 6 + type LocalPublicationEntry, 7 + type Post, 8 + StandardDocument, 9 + StandardPublication, 10 + } from "../structs"; 11 + 12 + export async function standardSite() { 13 + const records = await fetch( 14 + `${process.env.ATPROTO_PDS}/xrpc/com.atproto.repo.listRecords?repo=${process.env.ATPROTO_DID}&collection=site.standard.document`, 15 + ); 16 + 17 + const publications = await fetch( 18 + `${process.env.ATPROTO_PDS}/xrpc/com.atproto.repo.listRecords?repo=${process.env.ATPROTO_DID}&collection=site.standard.publication`, 19 + ); 20 + 21 + const finalDocuments: z.output<typeof Post>[] = []; 22 + const knownPublications: z.output<typeof LocalPublicationEntry>[] = []; 23 + 24 + if (publications.ok) { 25 + const data = ATProtoResponse.parse(await publications.json()); 26 + 27 + data.records.forEach(async (r) => { 28 + const pub = StandardPublication.parse(r.value); 29 + 30 + knownPublications.push({ uri: r.uri, ...pub }); 31 + 32 + if ( 33 + !(await fs.promises 34 + .access( 35 + path.join( 36 + path.resolve(), 37 + "data", 38 + "artifacts", 39 + `${r.uri.split("/").at(-1)}.json`, 40 + ), 41 + ) 42 + .then(() => true) 43 + .catch(() => false)) 44 + ) { 45 + try { 46 + await fs.promises.writeFile( 47 + path.join( 48 + path.resolve(), 49 + "data", 50 + "artifacts", 51 + `${r.uri.split("/").at(-1)}.json`, 52 + ), 53 + JSON.stringify(pub), 54 + { flag: "w+" }, 55 + ); 56 + console.log( 57 + `[INFO] Created artifact: "${pub.name}" as ${r.uri.split("/").at(-1)}`, 58 + ); 59 + } catch (err) { 60 + console.error(`[ERROR] Failed to write to file: ${err}`); 61 + } 62 + } 63 + }); 64 + } 65 + 66 + if (records.ok) { 67 + const data = ATProtoResponse.parse(await records.json()); 68 + 69 + data.records.forEach(async (r) => { 70 + const doc = StandardDocument.parse(r.value); 71 + 72 + if (doc.content.$type === "pub.leaflet.content") { 73 + const pub = knownPublications.find((p) => p.uri === doc.site); 74 + 75 + const stage = { 76 + title: doc.title, 77 + description: doc.description, 78 + published: doc.publishedAt, 79 + tags: doc.tags, 80 + id: `${doc.path.replace("/", "")}`, 81 + flags: [ 82 + `source:leaflet`, 83 + `publication:${doc.site.split("/").at(-1)}`, 84 + ], 85 + slug: `${pub?.url}${doc.path}`, 86 + }; 87 + 88 + finalDocuments.push(stage); 89 + } else { 90 + console.warn( 91 + `[WARN] Type of "${doc.title}" is not leaflet (${doc.content.$type}), skipping`, 92 + ); 93 + } 94 + }); 95 + } 96 + 97 + return { collection: "site.standard", data: finalDocuments }; 98 + }
+68
src/utils/structs.ts
··· 1 + import { z } from "zod"; 2 + 3 + export const Post = z.object({ 4 + title: z.string(), 5 + description: z.string().optional(), 6 + published: z.iso.datetime().transform((str) => new Date(str)), 7 + flags: z.array(z.string()), 8 + tags: z.array(z.string()).optional(), 9 + slug: z.string().optional(), 10 + content: z.string().optional(), 11 + id: z.string(), 12 + }); 13 + 14 + export const Collection = z.object({ 15 + collection: z.string(), 16 + data: z.array(Post), 17 + }); 18 + 19 + export const Record = z.object({ 20 + uri: z.string(), 21 + cid: z.string(), 22 + value: z.unknown(), 23 + }); 24 + 25 + export const StandardDocument = z.object({ 26 + title: z.string(), 27 + description: z.string().optional(), 28 + publishedAt: z.iso 29 + .datetime({ offset: true }) 30 + .transform((str) => new Date(str)), 31 + site: z.string(), 32 + path: z.string(), 33 + tags: z.array(z.string()).optional(), 34 + content: z.object({ $type: z.string() }), 35 + $type: z.string(), 36 + }); 37 + 38 + export const StandardPublication = z.object({ 39 + url: z.string(), 40 + name: z.string(), 41 + $type: z.string(), 42 + }); 43 + 44 + export const LocalPublicationEntry = z.object({ 45 + uri: z.string(), 46 + url: z.string(), 47 + name: z.string(), 48 + $type: z.string(), 49 + }); 50 + 51 + export const ATProtoResponse = z.object({ 52 + records: z.array(Record), 53 + cursor: z.string(), 54 + }); 55 + 56 + export const Banner = z.array( 57 + z 58 + .object({ 59 + link: z.string(), 60 + title: z.string(), 61 + type: z.literal("variable").optional(), 62 + }) 63 + .transform((item) => 64 + item.link === "infer" 65 + ? { ...item, link: `/${item.title.toLowerCase().replace(/\s+/g, "-")}` } 66 + : item, 67 + ), 68 + );
+5
tsconfig.json
··· 1 + { 2 + "compilerOptions": { 3 + "strict": true 4 + } 5 + }
+1
well-known/atproto-did
··· 1 + did:plc:ex55cz2xbuihypjbwowctvaw
+11
well-known/human.json
··· 1 + { 2 + "$schema": "https://codeberg.org/robida/human.json/raw/branch/main/schema/0.1.1.json", 3 + "version": "0.1.1", 4 + "url": "https://fromkoehn.com", 5 + "vouches": [ 6 + { 7 + "url": "https://neatnik.net", 8 + "vouched_at": "2026-03-11" 9 + } 10 + ] 11 + }