cdn.blueat.net is an alternative to the Bluesky CDN that's just as fast but powered by Cloudflare Workers with extra features. cdn.blueat.net
6
fork

Configure Feed

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

first commit

+1735
+6
.gitignore
··· 1 + node_modules/ 2 + .wrangler/ 3 + dist/ 4 + .DS_Store 5 + npm-debug.log* 6 +
+40
README.md
··· 1 + # cdn.blueat.net Worker (AT Protocol Image CDN) 2 + 3 + Cloudflare Worker that acts as a drop-in style replacement for Bluesky image CDN endpoints by proxying image blobs directly from a user's Personal Data Server (PDS). It resolves the PDS endpoint from the DID (supports `did:web:` and `did:plc:`) and then fetches the requested blob. 4 + 5 + ## Quick deploy (to your own Cloudflare account) 6 + 7 + 1. Install Node.js (LTS). 8 + 2. From this folder, run: 9 + - `npm install` 10 + - `npm run deploy` 11 + 3. If prompted, run `wrangler login` (Wrangler stores your auth token locally). 12 + 13 + The included `wrangler.toml` deploys with `workers_dev = true` (so it will be reachable under your Worker subdomain). To serve the real domain (`cdn.blueat.net`), update `wrangler.toml` with your `route` and `zone_id`. 14 + 15 + ## Configuration 16 + 17 + In `wrangler.toml`, you can change: 18 + 19 + - `PLC_DIRECTORY`: base URL used to resolve `did:plc:` to a PLC document. 20 + - `CACHE_MAX_AGE_SECONDS`: value used for the `Cache-Control: s-maxage=...` header. 21 + 22 + ## Local development 23 + 24 + - `npm run dev` 25 + 26 + ## Request format 27 + 28 + This worker expects paths like: 29 + 30 + `/img/{type}/plain/{did}/{cid}` 31 + 32 + Where `{type}` is typically `avatar` or `banner`. Example: 33 + 34 + `/img/avatar/plain/did:plc:exampleCid/ExampleImageCid` 35 + 36 + ## Notes 37 + 38 + - Results are cached via `caches.default` and also include `Cache-Control` for downstream caching. 39 + - If the requested `avatar`/`banner` CID is missing, the worker tries to find the current CID from the profile record and retries once. 40 +
+1505
package-lock.json
··· 1 + { 2 + "name": "cdn.blueat.net", 3 + "version": "1.0.0", 4 + "lockfileVersion": 3, 5 + "requires": true, 6 + "packages": { 7 + "": { 8 + "name": "cdn.blueat.net", 9 + "version": "1.0.0", 10 + "license": "ISC", 11 + "devDependencies": { 12 + "wrangler": "^4.76.0" 13 + } 14 + }, 15 + "node_modules/@cloudflare/kv-asset-handler": { 16 + "version": "0.4.2", 17 + "resolved": "https://registry.npmjs.org/@cloudflare/kv-asset-handler/-/kv-asset-handler-0.4.2.tgz", 18 + "integrity": "sha512-SIOD2DxrRRwQ+jgzlXCqoEFiKOFqaPjhnNTGKXSRLvp1HiOvapLaFG2kEr9dYQTYe8rKrd9uvDUzmAITeNyaHQ==", 19 + "dev": true, 20 + "license": "MIT OR Apache-2.0", 21 + "engines": { 22 + "node": ">=18.0.0" 23 + } 24 + }, 25 + "node_modules/@cloudflare/unenv-preset": { 26 + "version": "2.16.0", 27 + "resolved": "https://registry.npmjs.org/@cloudflare/unenv-preset/-/unenv-preset-2.16.0.tgz", 28 + "integrity": "sha512-8ovsRpwzPoEqPUzoErAYVv8l3FMZNeBVQfJTvtzP4AgLSRGZISRfuChFxHWUQd3n6cnrwkuTGxT+2cGo8EsyYg==", 29 + "dev": true, 30 + "license": "MIT OR Apache-2.0", 31 + "peerDependencies": { 32 + "unenv": "2.0.0-rc.24", 33 + "workerd": "1.20260301.1 || ~1.20260302.1 || ~1.20260303.1 || ~1.20260304.1 || >1.20260305.0 <2.0.0-0" 34 + }, 35 + "peerDependenciesMeta": { 36 + "workerd": { 37 + "optional": true 38 + } 39 + } 40 + }, 41 + "node_modules/@cloudflare/workerd-darwin-64": { 42 + "version": "1.20260317.1", 43 + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20260317.1.tgz", 44 + "integrity": "sha512-8hjh3sPMwY8M/zedq3/sXoA2Q4BedlGufn3KOOleIG+5a4ReQKLlUah140D7J6zlKmYZAFMJ4tWC7hCuI/s79g==", 45 + "cpu": [ 46 + "x64" 47 + ], 48 + "dev": true, 49 + "license": "Apache-2.0", 50 + "optional": true, 51 + "os": [ 52 + "darwin" 53 + ], 54 + "engines": { 55 + "node": ">=16" 56 + } 57 + }, 58 + "node_modules/@cloudflare/workerd-darwin-arm64": { 59 + "version": "1.20260317.1", 60 + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20260317.1.tgz", 61 + "integrity": "sha512-M/MnNyvO5HMgoIdr3QHjdCj2T1ki9gt0vIUnxYxBu9ISXS/jgtMl6chUVPJ7zHYBn9MyYr8ByeN6frjYxj0MGg==", 62 + "cpu": [ 63 + "arm64" 64 + ], 65 + "dev": true, 66 + "license": "Apache-2.0", 67 + "optional": true, 68 + "os": [ 69 + "darwin" 70 + ], 71 + "engines": { 72 + "node": ">=16" 73 + } 74 + }, 75 + "node_modules/@cloudflare/workerd-linux-64": { 76 + "version": "1.20260317.1", 77 + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20260317.1.tgz", 78 + "integrity": "sha512-1ltuEjkRcS3fsVF7CxsKlWiRmzq2ZqMfqDN0qUOgbUwkpXsLVJsXmoblaLf5OP00ELlcgF0QsN0p2xPEua4Uug==", 79 + "cpu": [ 80 + "x64" 81 + ], 82 + "dev": true, 83 + "license": "Apache-2.0", 84 + "optional": true, 85 + "os": [ 86 + "linux" 87 + ], 88 + "engines": { 89 + "node": ">=16" 90 + } 91 + }, 92 + "node_modules/@cloudflare/workerd-linux-arm64": { 93 + "version": "1.20260317.1", 94 + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20260317.1.tgz", 95 + "integrity": "sha512-3QrNnPF1xlaNwkHpasvRvAMidOvQs2NhXQmALJrEfpIJ/IDL2la8g499yXp3eqhG3hVMCB07XVY149GTs42Xtw==", 96 + "cpu": [ 97 + "arm64" 98 + ], 99 + "dev": true, 100 + "license": "Apache-2.0", 101 + "optional": true, 102 + "os": [ 103 + "linux" 104 + ], 105 + "engines": { 106 + "node": ">=16" 107 + } 108 + }, 109 + "node_modules/@cloudflare/workerd-windows-64": { 110 + "version": "1.20260317.1", 111 + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20260317.1.tgz", 112 + "integrity": "sha512-MfZTz+7LfuIpMGTa3RLXHX8Z/pnycZLItn94WRdHr8LPVet+C5/1Nzei399w/jr3+kzT4pDKk26JF/tlI5elpQ==", 113 + "cpu": [ 114 + "x64" 115 + ], 116 + "dev": true, 117 + "license": "Apache-2.0", 118 + "optional": true, 119 + "os": [ 120 + "win32" 121 + ], 122 + "engines": { 123 + "node": ">=16" 124 + } 125 + }, 126 + "node_modules/@cspotcode/source-map-support": { 127 + "version": "0.8.1", 128 + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", 129 + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", 130 + "dev": true, 131 + "license": "MIT", 132 + "dependencies": { 133 + "@jridgewell/trace-mapping": "0.3.9" 134 + }, 135 + "engines": { 136 + "node": ">=12" 137 + } 138 + }, 139 + "node_modules/@emnapi/runtime": { 140 + "version": "1.9.1", 141 + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.1.tgz", 142 + "integrity": "sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA==", 143 + "dev": true, 144 + "license": "MIT", 145 + "optional": true, 146 + "dependencies": { 147 + "tslib": "^2.4.0" 148 + } 149 + }, 150 + "node_modules/@esbuild/aix-ppc64": { 151 + "version": "0.27.3", 152 + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz", 153 + "integrity": "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==", 154 + "cpu": [ 155 + "ppc64" 156 + ], 157 + "dev": true, 158 + "license": "MIT", 159 + "optional": true, 160 + "os": [ 161 + "aix" 162 + ], 163 + "engines": { 164 + "node": ">=18" 165 + } 166 + }, 167 + "node_modules/@esbuild/android-arm": { 168 + "version": "0.27.3", 169 + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz", 170 + "integrity": "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==", 171 + "cpu": [ 172 + "arm" 173 + ], 174 + "dev": true, 175 + "license": "MIT", 176 + "optional": true, 177 + "os": [ 178 + "android" 179 + ], 180 + "engines": { 181 + "node": ">=18" 182 + } 183 + }, 184 + "node_modules/@esbuild/android-arm64": { 185 + "version": "0.27.3", 186 + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz", 187 + "integrity": "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==", 188 + "cpu": [ 189 + "arm64" 190 + ], 191 + "dev": true, 192 + "license": "MIT", 193 + "optional": true, 194 + "os": [ 195 + "android" 196 + ], 197 + "engines": { 198 + "node": ">=18" 199 + } 200 + }, 201 + "node_modules/@esbuild/android-x64": { 202 + "version": "0.27.3", 203 + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz", 204 + "integrity": "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==", 205 + "cpu": [ 206 + "x64" 207 + ], 208 + "dev": true, 209 + "license": "MIT", 210 + "optional": true, 211 + "os": [ 212 + "android" 213 + ], 214 + "engines": { 215 + "node": ">=18" 216 + } 217 + }, 218 + "node_modules/@esbuild/darwin-arm64": { 219 + "version": "0.27.3", 220 + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz", 221 + "integrity": "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==", 222 + "cpu": [ 223 + "arm64" 224 + ], 225 + "dev": true, 226 + "license": "MIT", 227 + "optional": true, 228 + "os": [ 229 + "darwin" 230 + ], 231 + "engines": { 232 + "node": ">=18" 233 + } 234 + }, 235 + "node_modules/@esbuild/darwin-x64": { 236 + "version": "0.27.3", 237 + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz", 238 + "integrity": "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==", 239 + "cpu": [ 240 + "x64" 241 + ], 242 + "dev": true, 243 + "license": "MIT", 244 + "optional": true, 245 + "os": [ 246 + "darwin" 247 + ], 248 + "engines": { 249 + "node": ">=18" 250 + } 251 + }, 252 + "node_modules/@esbuild/freebsd-arm64": { 253 + "version": "0.27.3", 254 + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz", 255 + "integrity": "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==", 256 + "cpu": [ 257 + "arm64" 258 + ], 259 + "dev": true, 260 + "license": "MIT", 261 + "optional": true, 262 + "os": [ 263 + "freebsd" 264 + ], 265 + "engines": { 266 + "node": ">=18" 267 + } 268 + }, 269 + "node_modules/@esbuild/freebsd-x64": { 270 + "version": "0.27.3", 271 + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz", 272 + "integrity": "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==", 273 + "cpu": [ 274 + "x64" 275 + ], 276 + "dev": true, 277 + "license": "MIT", 278 + "optional": true, 279 + "os": [ 280 + "freebsd" 281 + ], 282 + "engines": { 283 + "node": ">=18" 284 + } 285 + }, 286 + "node_modules/@esbuild/linux-arm": { 287 + "version": "0.27.3", 288 + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz", 289 + "integrity": "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==", 290 + "cpu": [ 291 + "arm" 292 + ], 293 + "dev": true, 294 + "license": "MIT", 295 + "optional": true, 296 + "os": [ 297 + "linux" 298 + ], 299 + "engines": { 300 + "node": ">=18" 301 + } 302 + }, 303 + "node_modules/@esbuild/linux-arm64": { 304 + "version": "0.27.3", 305 + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz", 306 + "integrity": "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==", 307 + "cpu": [ 308 + "arm64" 309 + ], 310 + "dev": true, 311 + "license": "MIT", 312 + "optional": true, 313 + "os": [ 314 + "linux" 315 + ], 316 + "engines": { 317 + "node": ">=18" 318 + } 319 + }, 320 + "node_modules/@esbuild/linux-ia32": { 321 + "version": "0.27.3", 322 + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz", 323 + "integrity": "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==", 324 + "cpu": [ 325 + "ia32" 326 + ], 327 + "dev": true, 328 + "license": "MIT", 329 + "optional": true, 330 + "os": [ 331 + "linux" 332 + ], 333 + "engines": { 334 + "node": ">=18" 335 + } 336 + }, 337 + "node_modules/@esbuild/linux-loong64": { 338 + "version": "0.27.3", 339 + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz", 340 + "integrity": "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==", 341 + "cpu": [ 342 + "loong64" 343 + ], 344 + "dev": true, 345 + "license": "MIT", 346 + "optional": true, 347 + "os": [ 348 + "linux" 349 + ], 350 + "engines": { 351 + "node": ">=18" 352 + } 353 + }, 354 + "node_modules/@esbuild/linux-mips64el": { 355 + "version": "0.27.3", 356 + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz", 357 + "integrity": "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==", 358 + "cpu": [ 359 + "mips64el" 360 + ], 361 + "dev": true, 362 + "license": "MIT", 363 + "optional": true, 364 + "os": [ 365 + "linux" 366 + ], 367 + "engines": { 368 + "node": ">=18" 369 + } 370 + }, 371 + "node_modules/@esbuild/linux-ppc64": { 372 + "version": "0.27.3", 373 + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz", 374 + "integrity": "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==", 375 + "cpu": [ 376 + "ppc64" 377 + ], 378 + "dev": true, 379 + "license": "MIT", 380 + "optional": true, 381 + "os": [ 382 + "linux" 383 + ], 384 + "engines": { 385 + "node": ">=18" 386 + } 387 + }, 388 + "node_modules/@esbuild/linux-riscv64": { 389 + "version": "0.27.3", 390 + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz", 391 + "integrity": "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==", 392 + "cpu": [ 393 + "riscv64" 394 + ], 395 + "dev": true, 396 + "license": "MIT", 397 + "optional": true, 398 + "os": [ 399 + "linux" 400 + ], 401 + "engines": { 402 + "node": ">=18" 403 + } 404 + }, 405 + "node_modules/@esbuild/linux-s390x": { 406 + "version": "0.27.3", 407 + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz", 408 + "integrity": "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==", 409 + "cpu": [ 410 + "s390x" 411 + ], 412 + "dev": true, 413 + "license": "MIT", 414 + "optional": true, 415 + "os": [ 416 + "linux" 417 + ], 418 + "engines": { 419 + "node": ">=18" 420 + } 421 + }, 422 + "node_modules/@esbuild/linux-x64": { 423 + "version": "0.27.3", 424 + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz", 425 + "integrity": "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==", 426 + "cpu": [ 427 + "x64" 428 + ], 429 + "dev": true, 430 + "license": "MIT", 431 + "optional": true, 432 + "os": [ 433 + "linux" 434 + ], 435 + "engines": { 436 + "node": ">=18" 437 + } 438 + }, 439 + "node_modules/@esbuild/netbsd-arm64": { 440 + "version": "0.27.3", 441 + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz", 442 + "integrity": "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==", 443 + "cpu": [ 444 + "arm64" 445 + ], 446 + "dev": true, 447 + "license": "MIT", 448 + "optional": true, 449 + "os": [ 450 + "netbsd" 451 + ], 452 + "engines": { 453 + "node": ">=18" 454 + } 455 + }, 456 + "node_modules/@esbuild/netbsd-x64": { 457 + "version": "0.27.3", 458 + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz", 459 + "integrity": "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==", 460 + "cpu": [ 461 + "x64" 462 + ], 463 + "dev": true, 464 + "license": "MIT", 465 + "optional": true, 466 + "os": [ 467 + "netbsd" 468 + ], 469 + "engines": { 470 + "node": ">=18" 471 + } 472 + }, 473 + "node_modules/@esbuild/openbsd-arm64": { 474 + "version": "0.27.3", 475 + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz", 476 + "integrity": "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==", 477 + "cpu": [ 478 + "arm64" 479 + ], 480 + "dev": true, 481 + "license": "MIT", 482 + "optional": true, 483 + "os": [ 484 + "openbsd" 485 + ], 486 + "engines": { 487 + "node": ">=18" 488 + } 489 + }, 490 + "node_modules/@esbuild/openbsd-x64": { 491 + "version": "0.27.3", 492 + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz", 493 + "integrity": "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==", 494 + "cpu": [ 495 + "x64" 496 + ], 497 + "dev": true, 498 + "license": "MIT", 499 + "optional": true, 500 + "os": [ 501 + "openbsd" 502 + ], 503 + "engines": { 504 + "node": ">=18" 505 + } 506 + }, 507 + "node_modules/@esbuild/openharmony-arm64": { 508 + "version": "0.27.3", 509 + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz", 510 + "integrity": "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==", 511 + "cpu": [ 512 + "arm64" 513 + ], 514 + "dev": true, 515 + "license": "MIT", 516 + "optional": true, 517 + "os": [ 518 + "openharmony" 519 + ], 520 + "engines": { 521 + "node": ">=18" 522 + } 523 + }, 524 + "node_modules/@esbuild/sunos-x64": { 525 + "version": "0.27.3", 526 + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz", 527 + "integrity": "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==", 528 + "cpu": [ 529 + "x64" 530 + ], 531 + "dev": true, 532 + "license": "MIT", 533 + "optional": true, 534 + "os": [ 535 + "sunos" 536 + ], 537 + "engines": { 538 + "node": ">=18" 539 + } 540 + }, 541 + "node_modules/@esbuild/win32-arm64": { 542 + "version": "0.27.3", 543 + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz", 544 + "integrity": "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==", 545 + "cpu": [ 546 + "arm64" 547 + ], 548 + "dev": true, 549 + "license": "MIT", 550 + "optional": true, 551 + "os": [ 552 + "win32" 553 + ], 554 + "engines": { 555 + "node": ">=18" 556 + } 557 + }, 558 + "node_modules/@esbuild/win32-ia32": { 559 + "version": "0.27.3", 560 + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz", 561 + "integrity": "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==", 562 + "cpu": [ 563 + "ia32" 564 + ], 565 + "dev": true, 566 + "license": "MIT", 567 + "optional": true, 568 + "os": [ 569 + "win32" 570 + ], 571 + "engines": { 572 + "node": ">=18" 573 + } 574 + }, 575 + "node_modules/@esbuild/win32-x64": { 576 + "version": "0.27.3", 577 + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz", 578 + "integrity": "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==", 579 + "cpu": [ 580 + "x64" 581 + ], 582 + "dev": true, 583 + "license": "MIT", 584 + "optional": true, 585 + "os": [ 586 + "win32" 587 + ], 588 + "engines": { 589 + "node": ">=18" 590 + } 591 + }, 592 + "node_modules/@img/colour": { 593 + "version": "1.1.0", 594 + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz", 595 + "integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==", 596 + "dev": true, 597 + "license": "MIT", 598 + "engines": { 599 + "node": ">=18" 600 + } 601 + }, 602 + "node_modules/@img/sharp-darwin-arm64": { 603 + "version": "0.34.5", 604 + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", 605 + "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", 606 + "cpu": [ 607 + "arm64" 608 + ], 609 + "dev": true, 610 + "license": "Apache-2.0", 611 + "optional": true, 612 + "os": [ 613 + "darwin" 614 + ], 615 + "engines": { 616 + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 617 + }, 618 + "funding": { 619 + "url": "https://opencollective.com/libvips" 620 + }, 621 + "optionalDependencies": { 622 + "@img/sharp-libvips-darwin-arm64": "1.2.4" 623 + } 624 + }, 625 + "node_modules/@img/sharp-darwin-x64": { 626 + "version": "0.34.5", 627 + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", 628 + "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", 629 + "cpu": [ 630 + "x64" 631 + ], 632 + "dev": true, 633 + "license": "Apache-2.0", 634 + "optional": true, 635 + "os": [ 636 + "darwin" 637 + ], 638 + "engines": { 639 + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 640 + }, 641 + "funding": { 642 + "url": "https://opencollective.com/libvips" 643 + }, 644 + "optionalDependencies": { 645 + "@img/sharp-libvips-darwin-x64": "1.2.4" 646 + } 647 + }, 648 + "node_modules/@img/sharp-libvips-darwin-arm64": { 649 + "version": "1.2.4", 650 + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", 651 + "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", 652 + "cpu": [ 653 + "arm64" 654 + ], 655 + "dev": true, 656 + "license": "LGPL-3.0-or-later", 657 + "optional": true, 658 + "os": [ 659 + "darwin" 660 + ], 661 + "funding": { 662 + "url": "https://opencollective.com/libvips" 663 + } 664 + }, 665 + "node_modules/@img/sharp-libvips-darwin-x64": { 666 + "version": "1.2.4", 667 + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", 668 + "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", 669 + "cpu": [ 670 + "x64" 671 + ], 672 + "dev": true, 673 + "license": "LGPL-3.0-or-later", 674 + "optional": true, 675 + "os": [ 676 + "darwin" 677 + ], 678 + "funding": { 679 + "url": "https://opencollective.com/libvips" 680 + } 681 + }, 682 + "node_modules/@img/sharp-libvips-linux-arm": { 683 + "version": "1.2.4", 684 + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", 685 + "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", 686 + "cpu": [ 687 + "arm" 688 + ], 689 + "dev": true, 690 + "license": "LGPL-3.0-or-later", 691 + "optional": true, 692 + "os": [ 693 + "linux" 694 + ], 695 + "funding": { 696 + "url": "https://opencollective.com/libvips" 697 + } 698 + }, 699 + "node_modules/@img/sharp-libvips-linux-arm64": { 700 + "version": "1.2.4", 701 + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", 702 + "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", 703 + "cpu": [ 704 + "arm64" 705 + ], 706 + "dev": true, 707 + "license": "LGPL-3.0-or-later", 708 + "optional": true, 709 + "os": [ 710 + "linux" 711 + ], 712 + "funding": { 713 + "url": "https://opencollective.com/libvips" 714 + } 715 + }, 716 + "node_modules/@img/sharp-libvips-linux-ppc64": { 717 + "version": "1.2.4", 718 + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", 719 + "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", 720 + "cpu": [ 721 + "ppc64" 722 + ], 723 + "dev": true, 724 + "license": "LGPL-3.0-or-later", 725 + "optional": true, 726 + "os": [ 727 + "linux" 728 + ], 729 + "funding": { 730 + "url": "https://opencollective.com/libvips" 731 + } 732 + }, 733 + "node_modules/@img/sharp-libvips-linux-riscv64": { 734 + "version": "1.2.4", 735 + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", 736 + "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", 737 + "cpu": [ 738 + "riscv64" 739 + ], 740 + "dev": true, 741 + "license": "LGPL-3.0-or-later", 742 + "optional": true, 743 + "os": [ 744 + "linux" 745 + ], 746 + "funding": { 747 + "url": "https://opencollective.com/libvips" 748 + } 749 + }, 750 + "node_modules/@img/sharp-libvips-linux-s390x": { 751 + "version": "1.2.4", 752 + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", 753 + "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", 754 + "cpu": [ 755 + "s390x" 756 + ], 757 + "dev": true, 758 + "license": "LGPL-3.0-or-later", 759 + "optional": true, 760 + "os": [ 761 + "linux" 762 + ], 763 + "funding": { 764 + "url": "https://opencollective.com/libvips" 765 + } 766 + }, 767 + "node_modules/@img/sharp-libvips-linux-x64": { 768 + "version": "1.2.4", 769 + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", 770 + "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", 771 + "cpu": [ 772 + "x64" 773 + ], 774 + "dev": true, 775 + "license": "LGPL-3.0-or-later", 776 + "optional": true, 777 + "os": [ 778 + "linux" 779 + ], 780 + "funding": { 781 + "url": "https://opencollective.com/libvips" 782 + } 783 + }, 784 + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { 785 + "version": "1.2.4", 786 + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", 787 + "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", 788 + "cpu": [ 789 + "arm64" 790 + ], 791 + "dev": true, 792 + "license": "LGPL-3.0-or-later", 793 + "optional": true, 794 + "os": [ 795 + "linux" 796 + ], 797 + "funding": { 798 + "url": "https://opencollective.com/libvips" 799 + } 800 + }, 801 + "node_modules/@img/sharp-libvips-linuxmusl-x64": { 802 + "version": "1.2.4", 803 + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", 804 + "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", 805 + "cpu": [ 806 + "x64" 807 + ], 808 + "dev": true, 809 + "license": "LGPL-3.0-or-later", 810 + "optional": true, 811 + "os": [ 812 + "linux" 813 + ], 814 + "funding": { 815 + "url": "https://opencollective.com/libvips" 816 + } 817 + }, 818 + "node_modules/@img/sharp-linux-arm": { 819 + "version": "0.34.5", 820 + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", 821 + "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", 822 + "cpu": [ 823 + "arm" 824 + ], 825 + "dev": true, 826 + "license": "Apache-2.0", 827 + "optional": true, 828 + "os": [ 829 + "linux" 830 + ], 831 + "engines": { 832 + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 833 + }, 834 + "funding": { 835 + "url": "https://opencollective.com/libvips" 836 + }, 837 + "optionalDependencies": { 838 + "@img/sharp-libvips-linux-arm": "1.2.4" 839 + } 840 + }, 841 + "node_modules/@img/sharp-linux-arm64": { 842 + "version": "0.34.5", 843 + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", 844 + "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", 845 + "cpu": [ 846 + "arm64" 847 + ], 848 + "dev": true, 849 + "license": "Apache-2.0", 850 + "optional": true, 851 + "os": [ 852 + "linux" 853 + ], 854 + "engines": { 855 + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 856 + }, 857 + "funding": { 858 + "url": "https://opencollective.com/libvips" 859 + }, 860 + "optionalDependencies": { 861 + "@img/sharp-libvips-linux-arm64": "1.2.4" 862 + } 863 + }, 864 + "node_modules/@img/sharp-linux-ppc64": { 865 + "version": "0.34.5", 866 + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", 867 + "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", 868 + "cpu": [ 869 + "ppc64" 870 + ], 871 + "dev": true, 872 + "license": "Apache-2.0", 873 + "optional": true, 874 + "os": [ 875 + "linux" 876 + ], 877 + "engines": { 878 + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 879 + }, 880 + "funding": { 881 + "url": "https://opencollective.com/libvips" 882 + }, 883 + "optionalDependencies": { 884 + "@img/sharp-libvips-linux-ppc64": "1.2.4" 885 + } 886 + }, 887 + "node_modules/@img/sharp-linux-riscv64": { 888 + "version": "0.34.5", 889 + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", 890 + "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", 891 + "cpu": [ 892 + "riscv64" 893 + ], 894 + "dev": true, 895 + "license": "Apache-2.0", 896 + "optional": true, 897 + "os": [ 898 + "linux" 899 + ], 900 + "engines": { 901 + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 902 + }, 903 + "funding": { 904 + "url": "https://opencollective.com/libvips" 905 + }, 906 + "optionalDependencies": { 907 + "@img/sharp-libvips-linux-riscv64": "1.2.4" 908 + } 909 + }, 910 + "node_modules/@img/sharp-linux-s390x": { 911 + "version": "0.34.5", 912 + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", 913 + "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", 914 + "cpu": [ 915 + "s390x" 916 + ], 917 + "dev": true, 918 + "license": "Apache-2.0", 919 + "optional": true, 920 + "os": [ 921 + "linux" 922 + ], 923 + "engines": { 924 + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 925 + }, 926 + "funding": { 927 + "url": "https://opencollective.com/libvips" 928 + }, 929 + "optionalDependencies": { 930 + "@img/sharp-libvips-linux-s390x": "1.2.4" 931 + } 932 + }, 933 + "node_modules/@img/sharp-linux-x64": { 934 + "version": "0.34.5", 935 + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", 936 + "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", 937 + "cpu": [ 938 + "x64" 939 + ], 940 + "dev": true, 941 + "license": "Apache-2.0", 942 + "optional": true, 943 + "os": [ 944 + "linux" 945 + ], 946 + "engines": { 947 + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 948 + }, 949 + "funding": { 950 + "url": "https://opencollective.com/libvips" 951 + }, 952 + "optionalDependencies": { 953 + "@img/sharp-libvips-linux-x64": "1.2.4" 954 + } 955 + }, 956 + "node_modules/@img/sharp-linuxmusl-arm64": { 957 + "version": "0.34.5", 958 + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", 959 + "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", 960 + "cpu": [ 961 + "arm64" 962 + ], 963 + "dev": true, 964 + "license": "Apache-2.0", 965 + "optional": true, 966 + "os": [ 967 + "linux" 968 + ], 969 + "engines": { 970 + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 971 + }, 972 + "funding": { 973 + "url": "https://opencollective.com/libvips" 974 + }, 975 + "optionalDependencies": { 976 + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" 977 + } 978 + }, 979 + "node_modules/@img/sharp-linuxmusl-x64": { 980 + "version": "0.34.5", 981 + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", 982 + "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", 983 + "cpu": [ 984 + "x64" 985 + ], 986 + "dev": true, 987 + "license": "Apache-2.0", 988 + "optional": true, 989 + "os": [ 990 + "linux" 991 + ], 992 + "engines": { 993 + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 994 + }, 995 + "funding": { 996 + "url": "https://opencollective.com/libvips" 997 + }, 998 + "optionalDependencies": { 999 + "@img/sharp-libvips-linuxmusl-x64": "1.2.4" 1000 + } 1001 + }, 1002 + "node_modules/@img/sharp-wasm32": { 1003 + "version": "0.34.5", 1004 + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", 1005 + "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", 1006 + "cpu": [ 1007 + "wasm32" 1008 + ], 1009 + "dev": true, 1010 + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", 1011 + "optional": true, 1012 + "dependencies": { 1013 + "@emnapi/runtime": "^1.7.0" 1014 + }, 1015 + "engines": { 1016 + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 1017 + }, 1018 + "funding": { 1019 + "url": "https://opencollective.com/libvips" 1020 + } 1021 + }, 1022 + "node_modules/@img/sharp-win32-arm64": { 1023 + "version": "0.34.5", 1024 + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", 1025 + "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", 1026 + "cpu": [ 1027 + "arm64" 1028 + ], 1029 + "dev": true, 1030 + "license": "Apache-2.0 AND LGPL-3.0-or-later", 1031 + "optional": true, 1032 + "os": [ 1033 + "win32" 1034 + ], 1035 + "engines": { 1036 + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 1037 + }, 1038 + "funding": { 1039 + "url": "https://opencollective.com/libvips" 1040 + } 1041 + }, 1042 + "node_modules/@img/sharp-win32-ia32": { 1043 + "version": "0.34.5", 1044 + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", 1045 + "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", 1046 + "cpu": [ 1047 + "ia32" 1048 + ], 1049 + "dev": true, 1050 + "license": "Apache-2.0 AND LGPL-3.0-or-later", 1051 + "optional": true, 1052 + "os": [ 1053 + "win32" 1054 + ], 1055 + "engines": { 1056 + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 1057 + }, 1058 + "funding": { 1059 + "url": "https://opencollective.com/libvips" 1060 + } 1061 + }, 1062 + "node_modules/@img/sharp-win32-x64": { 1063 + "version": "0.34.5", 1064 + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", 1065 + "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", 1066 + "cpu": [ 1067 + "x64" 1068 + ], 1069 + "dev": true, 1070 + "license": "Apache-2.0 AND LGPL-3.0-or-later", 1071 + "optional": true, 1072 + "os": [ 1073 + "win32" 1074 + ], 1075 + "engines": { 1076 + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 1077 + }, 1078 + "funding": { 1079 + "url": "https://opencollective.com/libvips" 1080 + } 1081 + }, 1082 + "node_modules/@jridgewell/resolve-uri": { 1083 + "version": "3.1.2", 1084 + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", 1085 + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", 1086 + "dev": true, 1087 + "license": "MIT", 1088 + "engines": { 1089 + "node": ">=6.0.0" 1090 + } 1091 + }, 1092 + "node_modules/@jridgewell/sourcemap-codec": { 1093 + "version": "1.5.5", 1094 + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", 1095 + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", 1096 + "dev": true, 1097 + "license": "MIT" 1098 + }, 1099 + "node_modules/@jridgewell/trace-mapping": { 1100 + "version": "0.3.9", 1101 + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", 1102 + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", 1103 + "dev": true, 1104 + "license": "MIT", 1105 + "dependencies": { 1106 + "@jridgewell/resolve-uri": "^3.0.3", 1107 + "@jridgewell/sourcemap-codec": "^1.4.10" 1108 + } 1109 + }, 1110 + "node_modules/@poppinss/colors": { 1111 + "version": "4.1.6", 1112 + "resolved": "https://registry.npmjs.org/@poppinss/colors/-/colors-4.1.6.tgz", 1113 + "integrity": "sha512-H9xkIdFswbS8n1d6vmRd8+c10t2Qe+rZITbbDHHkQixH5+2x1FDGmi/0K+WgWiqQFKPSlIYB7jlH6Kpfn6Fleg==", 1114 + "dev": true, 1115 + "license": "MIT", 1116 + "dependencies": { 1117 + "kleur": "^4.1.5" 1118 + } 1119 + }, 1120 + "node_modules/@poppinss/dumper": { 1121 + "version": "0.6.5", 1122 + "resolved": "https://registry.npmjs.org/@poppinss/dumper/-/dumper-0.6.5.tgz", 1123 + "integrity": "sha512-NBdYIb90J7LfOI32dOewKI1r7wnkiH6m920puQ3qHUeZkxNkQiFnXVWoE6YtFSv6QOiPPf7ys6i+HWWecDz7sw==", 1124 + "dev": true, 1125 + "license": "MIT", 1126 + "dependencies": { 1127 + "@poppinss/colors": "^4.1.5", 1128 + "@sindresorhus/is": "^7.0.2", 1129 + "supports-color": "^10.0.0" 1130 + } 1131 + }, 1132 + "node_modules/@poppinss/exception": { 1133 + "version": "1.2.3", 1134 + "resolved": "https://registry.npmjs.org/@poppinss/exception/-/exception-1.2.3.tgz", 1135 + "integrity": "sha512-dCED+QRChTVatE9ibtoaxc+WkdzOSjYTKi/+uacHWIsfodVfpsueo3+DKpgU5Px8qXjgmXkSvhXvSCz3fnP9lw==", 1136 + "dev": true, 1137 + "license": "MIT" 1138 + }, 1139 + "node_modules/@sindresorhus/is": { 1140 + "version": "7.2.0", 1141 + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-7.2.0.tgz", 1142 + "integrity": "sha512-P1Cz1dWaFfR4IR+U13mqqiGsLFf1KbayybWwdd2vfctdV6hDpUkgCY0nKOLLTMSoRd/jJNjtbqzf13K8DCCXQw==", 1143 + "dev": true, 1144 + "license": "MIT", 1145 + "engines": { 1146 + "node": ">=18" 1147 + }, 1148 + "funding": { 1149 + "url": "https://github.com/sindresorhus/is?sponsor=1" 1150 + } 1151 + }, 1152 + "node_modules/@speed-highlight/core": { 1153 + "version": "1.2.15", 1154 + "resolved": "https://registry.npmjs.org/@speed-highlight/core/-/core-1.2.15.tgz", 1155 + "integrity": "sha512-BMq1K3DsElxDWawkX6eLg9+CKJrTVGCBAWVuHXVUV2u0s2711qiChLSId6ikYPfxhdYocLNt3wWwSvDiTvFabw==", 1156 + "dev": true, 1157 + "license": "CC0-1.0" 1158 + }, 1159 + "node_modules/blake3-wasm": { 1160 + "version": "2.1.5", 1161 + "resolved": "https://registry.npmjs.org/blake3-wasm/-/blake3-wasm-2.1.5.tgz", 1162 + "integrity": "sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g==", 1163 + "dev": true, 1164 + "license": "MIT" 1165 + }, 1166 + "node_modules/cookie": { 1167 + "version": "1.1.1", 1168 + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", 1169 + "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", 1170 + "dev": true, 1171 + "license": "MIT", 1172 + "engines": { 1173 + "node": ">=18" 1174 + }, 1175 + "funding": { 1176 + "type": "opencollective", 1177 + "url": "https://opencollective.com/express" 1178 + } 1179 + }, 1180 + "node_modules/detect-libc": { 1181 + "version": "2.1.2", 1182 + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", 1183 + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", 1184 + "dev": true, 1185 + "license": "Apache-2.0", 1186 + "engines": { 1187 + "node": ">=8" 1188 + } 1189 + }, 1190 + "node_modules/error-stack-parser-es": { 1191 + "version": "1.0.5", 1192 + "resolved": "https://registry.npmjs.org/error-stack-parser-es/-/error-stack-parser-es-1.0.5.tgz", 1193 + "integrity": "sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA==", 1194 + "dev": true, 1195 + "license": "MIT", 1196 + "funding": { 1197 + "url": "https://github.com/sponsors/antfu" 1198 + } 1199 + }, 1200 + "node_modules/esbuild": { 1201 + "version": "0.27.3", 1202 + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz", 1203 + "integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==", 1204 + "dev": true, 1205 + "hasInstallScript": true, 1206 + "license": "MIT", 1207 + "bin": { 1208 + "esbuild": "bin/esbuild" 1209 + }, 1210 + "engines": { 1211 + "node": ">=18" 1212 + }, 1213 + "optionalDependencies": { 1214 + "@esbuild/aix-ppc64": "0.27.3", 1215 + "@esbuild/android-arm": "0.27.3", 1216 + "@esbuild/android-arm64": "0.27.3", 1217 + "@esbuild/android-x64": "0.27.3", 1218 + "@esbuild/darwin-arm64": "0.27.3", 1219 + "@esbuild/darwin-x64": "0.27.3", 1220 + "@esbuild/freebsd-arm64": "0.27.3", 1221 + "@esbuild/freebsd-x64": "0.27.3", 1222 + "@esbuild/linux-arm": "0.27.3", 1223 + "@esbuild/linux-arm64": "0.27.3", 1224 + "@esbuild/linux-ia32": "0.27.3", 1225 + "@esbuild/linux-loong64": "0.27.3", 1226 + "@esbuild/linux-mips64el": "0.27.3", 1227 + "@esbuild/linux-ppc64": "0.27.3", 1228 + "@esbuild/linux-riscv64": "0.27.3", 1229 + "@esbuild/linux-s390x": "0.27.3", 1230 + "@esbuild/linux-x64": "0.27.3", 1231 + "@esbuild/netbsd-arm64": "0.27.3", 1232 + "@esbuild/netbsd-x64": "0.27.3", 1233 + "@esbuild/openbsd-arm64": "0.27.3", 1234 + "@esbuild/openbsd-x64": "0.27.3", 1235 + "@esbuild/openharmony-arm64": "0.27.3", 1236 + "@esbuild/sunos-x64": "0.27.3", 1237 + "@esbuild/win32-arm64": "0.27.3", 1238 + "@esbuild/win32-ia32": "0.27.3", 1239 + "@esbuild/win32-x64": "0.27.3" 1240 + } 1241 + }, 1242 + "node_modules/fsevents": { 1243 + "version": "2.3.3", 1244 + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", 1245 + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", 1246 + "dev": true, 1247 + "hasInstallScript": true, 1248 + "license": "MIT", 1249 + "optional": true, 1250 + "os": [ 1251 + "darwin" 1252 + ], 1253 + "engines": { 1254 + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 1255 + } 1256 + }, 1257 + "node_modules/kleur": { 1258 + "version": "4.1.5", 1259 + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", 1260 + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", 1261 + "dev": true, 1262 + "license": "MIT", 1263 + "engines": { 1264 + "node": ">=6" 1265 + } 1266 + }, 1267 + "node_modules/miniflare": { 1268 + "version": "4.20260317.1", 1269 + "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-4.20260317.1.tgz", 1270 + "integrity": "sha512-A3csI1HXEIfqe3oscgpoRMHdYlkReQKPH/g5JE53vFSjoM6YIAOGAzyDNeYffwd9oQkPWDj9xER8+vpxei8klA==", 1271 + "dev": true, 1272 + "license": "MIT", 1273 + "dependencies": { 1274 + "@cspotcode/source-map-support": "0.8.1", 1275 + "sharp": "^0.34.5", 1276 + "undici": "7.24.4", 1277 + "workerd": "1.20260317.1", 1278 + "ws": "8.18.0", 1279 + "youch": "4.1.0-beta.10" 1280 + }, 1281 + "bin": { 1282 + "miniflare": "bootstrap.js" 1283 + }, 1284 + "engines": { 1285 + "node": ">=18.0.0" 1286 + } 1287 + }, 1288 + "node_modules/path-to-regexp": { 1289 + "version": "6.3.0", 1290 + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", 1291 + "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==", 1292 + "dev": true, 1293 + "license": "MIT" 1294 + }, 1295 + "node_modules/pathe": { 1296 + "version": "2.0.3", 1297 + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", 1298 + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", 1299 + "dev": true, 1300 + "license": "MIT" 1301 + }, 1302 + "node_modules/semver": { 1303 + "version": "7.7.4", 1304 + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", 1305 + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", 1306 + "dev": true, 1307 + "license": "ISC", 1308 + "bin": { 1309 + "semver": "bin/semver.js" 1310 + }, 1311 + "engines": { 1312 + "node": ">=10" 1313 + } 1314 + }, 1315 + "node_modules/sharp": { 1316 + "version": "0.34.5", 1317 + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", 1318 + "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", 1319 + "dev": true, 1320 + "hasInstallScript": true, 1321 + "license": "Apache-2.0", 1322 + "dependencies": { 1323 + "@img/colour": "^1.0.0", 1324 + "detect-libc": "^2.1.2", 1325 + "semver": "^7.7.3" 1326 + }, 1327 + "engines": { 1328 + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 1329 + }, 1330 + "funding": { 1331 + "url": "https://opencollective.com/libvips" 1332 + }, 1333 + "optionalDependencies": { 1334 + "@img/sharp-darwin-arm64": "0.34.5", 1335 + "@img/sharp-darwin-x64": "0.34.5", 1336 + "@img/sharp-libvips-darwin-arm64": "1.2.4", 1337 + "@img/sharp-libvips-darwin-x64": "1.2.4", 1338 + "@img/sharp-libvips-linux-arm": "1.2.4", 1339 + "@img/sharp-libvips-linux-arm64": "1.2.4", 1340 + "@img/sharp-libvips-linux-ppc64": "1.2.4", 1341 + "@img/sharp-libvips-linux-riscv64": "1.2.4", 1342 + "@img/sharp-libvips-linux-s390x": "1.2.4", 1343 + "@img/sharp-libvips-linux-x64": "1.2.4", 1344 + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", 1345 + "@img/sharp-libvips-linuxmusl-x64": "1.2.4", 1346 + "@img/sharp-linux-arm": "0.34.5", 1347 + "@img/sharp-linux-arm64": "0.34.5", 1348 + "@img/sharp-linux-ppc64": "0.34.5", 1349 + "@img/sharp-linux-riscv64": "0.34.5", 1350 + "@img/sharp-linux-s390x": "0.34.5", 1351 + "@img/sharp-linux-x64": "0.34.5", 1352 + "@img/sharp-linuxmusl-arm64": "0.34.5", 1353 + "@img/sharp-linuxmusl-x64": "0.34.5", 1354 + "@img/sharp-wasm32": "0.34.5", 1355 + "@img/sharp-win32-arm64": "0.34.5", 1356 + "@img/sharp-win32-ia32": "0.34.5", 1357 + "@img/sharp-win32-x64": "0.34.5" 1358 + } 1359 + }, 1360 + "node_modules/supports-color": { 1361 + "version": "10.2.2", 1362 + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-10.2.2.tgz", 1363 + "integrity": "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==", 1364 + "dev": true, 1365 + "license": "MIT", 1366 + "engines": { 1367 + "node": ">=18" 1368 + }, 1369 + "funding": { 1370 + "url": "https://github.com/chalk/supports-color?sponsor=1" 1371 + } 1372 + }, 1373 + "node_modules/tslib": { 1374 + "version": "2.8.1", 1375 + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", 1376 + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", 1377 + "dev": true, 1378 + "license": "0BSD", 1379 + "optional": true 1380 + }, 1381 + "node_modules/undici": { 1382 + "version": "7.24.4", 1383 + "resolved": "https://registry.npmjs.org/undici/-/undici-7.24.4.tgz", 1384 + "integrity": "sha512-BM/JzwwaRXxrLdElV2Uo6cTLEjhSb3WXboncJamZ15NgUURmvlXvxa6xkwIOILIjPNo9i8ku136ZvWV0Uly8+w==", 1385 + "dev": true, 1386 + "license": "MIT", 1387 + "engines": { 1388 + "node": ">=20.18.1" 1389 + } 1390 + }, 1391 + "node_modules/unenv": { 1392 + "version": "2.0.0-rc.24", 1393 + "resolved": "https://registry.npmjs.org/unenv/-/unenv-2.0.0-rc.24.tgz", 1394 + "integrity": "sha512-i7qRCmY42zmCwnYlh9H2SvLEypEFGye5iRmEMKjcGi7zk9UquigRjFtTLz0TYqr0ZGLZhaMHl/foy1bZR+Cwlw==", 1395 + "dev": true, 1396 + "license": "MIT", 1397 + "dependencies": { 1398 + "pathe": "^2.0.3" 1399 + } 1400 + }, 1401 + "node_modules/workerd": { 1402 + "version": "1.20260317.1", 1403 + "resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20260317.1.tgz", 1404 + "integrity": "sha512-ZuEq1OdrJBS+NV+L5HMYPCzVn49a2O60slQiiLpG44jqtlOo+S167fWC76kEXteXLLLydeuRrluRel7WdOUa4g==", 1405 + "dev": true, 1406 + "hasInstallScript": true, 1407 + "license": "Apache-2.0", 1408 + "bin": { 1409 + "workerd": "bin/workerd" 1410 + }, 1411 + "engines": { 1412 + "node": ">=16" 1413 + }, 1414 + "optionalDependencies": { 1415 + "@cloudflare/workerd-darwin-64": "1.20260317.1", 1416 + "@cloudflare/workerd-darwin-arm64": "1.20260317.1", 1417 + "@cloudflare/workerd-linux-64": "1.20260317.1", 1418 + "@cloudflare/workerd-linux-arm64": "1.20260317.1", 1419 + "@cloudflare/workerd-windows-64": "1.20260317.1" 1420 + } 1421 + }, 1422 + "node_modules/wrangler": { 1423 + "version": "4.76.0", 1424 + "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-4.76.0.tgz", 1425 + "integrity": "sha512-Wan+CU5a0tu4HIxGOrzjNbkmxCT27HUmzrMj6kc7aoAnjSLv50Ggcn2Ant7wNQrD6xW3g31phKupZJgTZ8wZfQ==", 1426 + "dev": true, 1427 + "license": "MIT OR Apache-2.0", 1428 + "dependencies": { 1429 + "@cloudflare/kv-asset-handler": "0.4.2", 1430 + "@cloudflare/unenv-preset": "2.16.0", 1431 + "blake3-wasm": "2.1.5", 1432 + "esbuild": "0.27.3", 1433 + "miniflare": "4.20260317.1", 1434 + "path-to-regexp": "6.3.0", 1435 + "unenv": "2.0.0-rc.24", 1436 + "workerd": "1.20260317.1" 1437 + }, 1438 + "bin": { 1439 + "wrangler": "bin/wrangler.js", 1440 + "wrangler2": "bin/wrangler.js" 1441 + }, 1442 + "engines": { 1443 + "node": ">=20.0.0" 1444 + }, 1445 + "optionalDependencies": { 1446 + "fsevents": "~2.3.2" 1447 + }, 1448 + "peerDependencies": { 1449 + "@cloudflare/workers-types": "^4.20260317.1" 1450 + }, 1451 + "peerDependenciesMeta": { 1452 + "@cloudflare/workers-types": { 1453 + "optional": true 1454 + } 1455 + } 1456 + }, 1457 + "node_modules/ws": { 1458 + "version": "8.18.0", 1459 + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", 1460 + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", 1461 + "dev": true, 1462 + "license": "MIT", 1463 + "engines": { 1464 + "node": ">=10.0.0" 1465 + }, 1466 + "peerDependencies": { 1467 + "bufferutil": "^4.0.1", 1468 + "utf-8-validate": ">=5.0.2" 1469 + }, 1470 + "peerDependenciesMeta": { 1471 + "bufferutil": { 1472 + "optional": true 1473 + }, 1474 + "utf-8-validate": { 1475 + "optional": true 1476 + } 1477 + } 1478 + }, 1479 + "node_modules/youch": { 1480 + "version": "4.1.0-beta.10", 1481 + "resolved": "https://registry.npmjs.org/youch/-/youch-4.1.0-beta.10.tgz", 1482 + "integrity": "sha512-rLfVLB4FgQneDr0dv1oddCVZmKjcJ6yX6mS4pU82Mq/Dt9a3cLZQ62pDBL4AUO+uVrCvtWz3ZFUL2HFAFJ/BXQ==", 1483 + "dev": true, 1484 + "license": "MIT", 1485 + "dependencies": { 1486 + "@poppinss/colors": "^4.1.5", 1487 + "@poppinss/dumper": "^0.6.4", 1488 + "@speed-highlight/core": "^1.2.7", 1489 + "cookie": "^1.0.2", 1490 + "youch-core": "^0.3.3" 1491 + } 1492 + }, 1493 + "node_modules/youch-core": { 1494 + "version": "0.3.3", 1495 + "resolved": "https://registry.npmjs.org/youch-core/-/youch-core-0.3.3.tgz", 1496 + "integrity": "sha512-ho7XuGjLaJ2hWHoK8yFnsUGy2Y5uDpqSTq1FkHLK4/oqKtyUU1AFbOOxY4IpC9f0fTLjwYbslUz0Po5BpD1wrA==", 1497 + "dev": true, 1498 + "license": "MIT", 1499 + "dependencies": { 1500 + "@poppinss/exception": "^1.2.2", 1501 + "error-stack-parser-es": "^1.0.5" 1502 + } 1503 + } 1504 + } 1505 + }
+21
package.json
··· 1 + { 2 + "name": "cdn.blueat.net", 3 + "version": "1.0.0", 4 + "description": "", 5 + "main": "worker.js", 6 + "scripts": { 7 + "dev": "wrangler dev", 8 + "deploy": "wrangler deploy", 9 + "publish": "wrangler publish" 10 + }, 11 + "repository": { 12 + "type": "git", 13 + "url": "git@tangled.org:danielmorrisey.com/cdn.blueat.net" 14 + }, 15 + "keywords": [], 16 + "author": "", 17 + "license": "ISC", 18 + "devDependencies": { 19 + "wrangler": "^4.76.0" 20 + } 21 + }
+145
worker.js
··· 1 + const PLC_DIRECTORY = "https://plc.directory"; 2 + 3 + const PATH_RE = /^\/img\/([^/]+)\/plain\/(did:[^/]+)\/([^@/]+)(?:@([^/]+))?$/; 4 + 5 + export default { 6 + async fetch(request, env, ctx) { 7 + const plcDirectory = env?.PLC_DIRECTORY ?? PLC_DIRECTORY; 8 + const cacheMaxAgeSeconds = 9 + env?.CACHE_MAX_AGE_SECONDS ?? 604800; 10 + const url = new URL(request.url); 11 + 12 + if (url.pathname === "/" || url.pathname === "") { 13 + return new Response(LANDING_HTML, { 14 + headers: { "Content-Type": "text/html;charset=UTF-8" }, 15 + }); 16 + } 17 + 18 + const match = url.pathname.match(PATH_RE); 19 + if (!match) return new Response("400: Invalid Path", { status: 400 }); 20 + const [_, type, did, cid] = match; 21 + 22 + const cache = caches.default; 23 + 24 + const cachedResponse = await cache.match(request); 25 + if (cachedResponse) return cachedResponse; 26 + 27 + const pdsUrl = await resolvePds(did, plcDirectory); 28 + if (!pdsUrl) { 29 + return new Response("404: PDS not found for this DID", { status: 404 }); 30 + } 31 + 32 + try { 33 + let blobRes = await fetchBlob(pdsUrl, did, cid); 34 + 35 + if (blobRes.status === 404 && (type === "avatar" || type === "banner")) { 36 + const originalCid = await findOriginalCidFromProfile(pdsUrl, did, type); 37 + if (originalCid && originalCid !== cid) { 38 + blobRes = await fetchBlob(pdsUrl, did, originalCid); 39 + } 40 + } 41 + 42 + if (!blobRes.ok) { 43 + return new Response("404: Asset not found on PDS.", { status: 404 }); 44 + } 45 + 46 + const finalRes = new Response(blobRes.body, blobRes); 47 + finalRes.headers.set( 48 + "Cache-Control", 49 + `public, s-maxage=${cacheMaxAgeSeconds}` 50 + ); 51 + finalRes.headers.set("X-Proxy-Source", "PDS-Direct"); 52 + finalRes.headers.set("Content-Disposition", "inline"); 53 + ctx.waitUntil(cache.put(request, finalRes.clone())); 54 + return finalRes; 55 + 56 + } catch (err) { 57 + return new Response(`502: PDS Error: ${err.message}`, { status: 502 }); 58 + } 59 + }, 60 + }; 61 + 62 + async function fetchBlob(pdsUrl, did, cid) { 63 + return fetch( 64 + `${pdsUrl}/xrpc/com.atproto.sync.getBlob?did=${encodeURIComponent(did)}&cid=${encodeURIComponent(cid)}` 65 + ); 66 + } 67 + 68 + async function resolvePds(did, plcDirectory) { 69 + const doc = did.startsWith("did:web:") 70 + ? await resolveDidWeb(did) 71 + : await resolveDidPlc(did, plcDirectory); 72 + if (!doc) return null; 73 + const pds = doc.service?.find( 74 + (s) => s.id === "#atproto_pds" || s.type === "AtprotoPersonalDataServer" 75 + ); 76 + return pds?.serviceEndpoint ?? null; 77 + } 78 + 79 + async function resolveDidPlc(did, plcDirectory) { 80 + const res = await fetch(`${plcDirectory}/${did}`); 81 + if (!res.ok) return null; 82 + return res.json(); 83 + } 84 + 85 + async function resolveDidWeb(did) { 86 + const identifier = did.slice("did:web:".length); 87 + const parts = identifier.split(":"); 88 + const host = decodeURIComponent(parts[0]); 89 + const didUrl = parts.length === 1 90 + ? `https://${host}/.well-known/did.json` 91 + : `https://${host}/${parts.slice(1).map(decodeURIComponent).join("/")}/did.json`; 92 + const res = await fetch(didUrl); 93 + if (!res.ok) return null; 94 + return res.json(); 95 + } 96 + 97 + async function findOriginalCidFromProfile(pdsUrl, did, type) { 98 + const res = await fetch( 99 + `${pdsUrl}/xrpc/com.atproto.repo.getRecord?repo=${encodeURIComponent(did)}&collection=app.bsky.actor.profile&rkey=self` 100 + ); 101 + if (!res.ok) return null; 102 + const data = await res.json(); 103 + return data.value?.[type]?.ref?.$link ?? null; 104 + } 105 + 106 + const LANDING_HTML = `<!DOCTYPE html> 107 + <html lang="en"> 108 + <head> 109 + <meta charset="UTF-8" /> 110 + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> 111 + 112 + <title>cdn.blueat.net — AT Protocol Image CDN</title> 113 + <meta name="description" content="A drop-in replacement for cdn.bsky.app, powered by Cloudflare Workers. Images are served directly from each user's Personal Data Server (PDS) via DID resolution — no Bluesky relay required." /> 114 + <meta name="keywords" content="Bluesky, AT Protocol, CDN, cdn.bsky.app alternative, PDS, image proxy, Cloudflare Workers, witchsky, atproto" /> 115 + <link rel="canonical" href="https://about.blueat.net/blueat-cdn" /> 116 + 117 + <meta property="og:type" content="website" /> 118 + <meta property="og:url" content="https://about.blueat.net/blueat-cdn" /> 119 + <meta property="og:title" content="cdn.blueat.net — AT Protocol Image CDN" /> 120 + <meta property="og:description" content="A drop-in replacement for cdn.bsky.app that serves images directly from users' PDSes via DID resolution, powered by Cloudflare Workers." /> 121 + <meta property="og:site_name" content="BlueAT.net" /> 122 + 123 + <meta name="twitter:card" content="summary" /> 124 + <meta name="twitter:title" content="cdn.blueat.net — AT Protocol Image CDN" /> 125 + <meta name="twitter:description" content="A drop-in replacement for cdn.bsky.app, served directly from each user's PDS via Cloudflare Workers." /> 126 + 127 + <script type="application/ld+json"> 128 + { 129 + "@context": "https://schema.org", 130 + "@type": "WebApplication", 131 + "name": "cdn.blueat.net", 132 + "url": "https://about.blueat.net/blueat-cdn", 133 + "description": "A drop-in replacement for cdn.bsky.app. Serves AT Protocol images directly from users' Personal Data Servers via DID resolution, powered by Cloudflare Workers.", 134 + "applicationCategory": "DeveloperApplication", 135 + "operatingSystem": "Any" 136 + } 137 + <\/script> 138 + 139 + <!-- Instant redirect — canonical page lives at BlueAT.net --> 140 + <meta http-equiv="refresh" content="0; url=https://about.blueat.net/blueat-cdn" /> 141 + </head> 142 + <body> 143 + <p>Redirecting to <a href="https://about.blueat.net/blueat-cdn">about.blueat.net/blueat-cdn</a>…</p> 144 + </body> 145 + </html>`;
+18
wrangler.toml
··· 1 + name = "cdn-blueat-worker" 2 + main = "worker.js" 3 + compatibility_date = "2024-11-01" 4 + workers_dev = true 5 + 6 + [vars] 7 + PLC_DIRECTORY = "https://plc.directory" 8 + CACHE_MAX_AGE_SECONDS = 604800 9 + 10 + # To serve the custom domain instead of the `workers.dev` subdomain: 11 + # - set `workers_dev = false` 12 + # - set `route` and `zone_id` 13 + # 14 + # Example: 15 + # workers_dev = false 16 + # route = "cdn.blueat.net/*" 17 + # zone_id = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" 18 +