a simple pds frontend for listing accounts and generating invite codes
1
fork

Configure Feed

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

first commit

Winter b5997f3d

+1080
.DS_Store

This is a binary file and will not be displayed.

+4
.env.example
··· 1 + PDS_URL=https://pds.madoka.systems 2 + PDS_ADMIN_PASSWORD= 3 + INVITE_RATE_LIMIT=3 4 + INVITE_RATE_WINDOW=86400
+4
.gitignore
··· 1 + .env 2 + node_modules/ 3 + build/ 4 + .svelte-kit/
+1
AGENTS.md
··· 1 + CLAUDE.md
+36
CLAUDE.md
··· 1 + # CLAUDE.md 2 + 3 + This file provides guidance to Claude Code (claude.ai/code) and other agents when working with code in this repository. 4 + 5 + ## Commands 6 + 7 + ```bash 8 + bun run dev # dev server 9 + bun run build # SSR build via adapter-node → build/ 10 + bun run preview # preview production build locally 11 + 12 + bun install # install deps (uses bun.lock, not package-lock) 13 + ``` 14 + 15 + there are no tests and no lint script. build success is the primary correctness signal. 16 + 17 + ## Architecture 18 + 19 + SvelteKit app with `@sveltejs/adapter-node`, running under Bun in production. deployed via Docker with env vars injected at runtime. 20 + 21 + **data flow:** 22 + 23 + - `+page.server.ts` — SSR only. calls `com.atproto.sync.listRepos` (unauthenticated, max 1000), then `Promise.allSettled` over all active DIDs doing parallel `describeRepo` + `getRecord` fetches. avatar URLs use `https://blobs.blue/{did}/avatar@webp` (no CID parsing needed). 24 + - `api/invite/+server.ts` — POST endpoint. in-memory IP rate limit (`Map<string, number[]>`), then raw `fetch` to the PDS using Basic auth (`admin:{PDS_ADMIN_PASSWORD}`). rate limiting is disabled in `dev` mode. 25 + - `+page.svelte` — client component. invite code generation, clipboard copy, error display. pdsmoover link at bottom. 26 + 27 + **env vars** (all read via `$env/dynamic/private` — resolved at runtime, never baked into bundles): 28 + 29 + - `PDS_URL` — required for all PDS calls 30 + - `PDS_ADMIN_PASSWORD` — required for invite code generation 31 + - `INVITE_RATE_LIMIT` — codes per IP per window (default 3) 32 + - `INVITE_RATE_WINDOW` — window in seconds (default 86400) 33 + 34 + **key constraint:** `$lib/server/` is enforced server-only by SvelteKit. never use `process.env` directly for secrets — use `$env/dynamic/private`. never import from `$lib/server/` in `+page.svelte` or any client-accessible module. 35 + 36 + **styling:** ported from `../site/` — Host Grotesk variable font, oklch color vars (`--color-bg`, `--color-surface`, `--color-fg`), dark mode via `prefers-color-scheme`. pds.ls button icon uses CSS `mask-image` over a vectorized SVG (`static/pdsls-icon.svg`, generated with potrace from the upstream PNG).
+14
Dockerfile
··· 1 + FROM oven/bun:1 AS build 2 + WORKDIR /app 3 + COPY package.json bun.lock ./ 4 + RUN bun install --frozen-lockfile 5 + COPY . . 6 + RUN bun run build 7 + 8 + FROM oven/bun:1-slim 9 + WORKDIR /app 10 + COPY --from=build /app/build ./build 11 + COPY --from=build /app/node_modules ./node_modules 12 + EXPOSE 4269 13 + ENV HOST=0.0.0.0 PORT=4269 14 + CMD ["bun", "./build/index.js"]
+38
README.md
··· 1 + # Disclaimer!!! 2 + 3 + this project is entirely vibecoded by sonnet 4.6 (except for the readme i made that myself), if you use this project i cannot guarentee that you will not suffer your pds admin password leaking, though i've attempted to manually make sure that it won't/can't get leaked. 4 + 5 + this is a project specifically for my own selfish desires and i was too lazy to make it myself, _you have been warned._ 6 + 7 + --- 8 + 9 + # tea party 10 + 11 + this is a basic svelte frontend for a pds which displays repos/accounts on a specific pds and has invite code generation (for a semi-open pds). this is mostly server side rendered. 12 + 13 + ## hosting 14 + 15 + **_did you read the [disclaimer?](#Disclaimer!!!)_** 16 + 17 + dependancies are: 18 + 19 + - `bun` 20 + 21 + copy `.env.example` to `.env` and edit as needed 22 + 23 + ### dev 24 + 25 + ```bash 26 + bun install 27 + bun run dev 28 + ``` 29 + 30 + ### prod 31 + 32 + use the `Dockerfile` or: 33 + 34 + ```bash 35 + bun install 36 + bun run build 37 + bun ./build/index.ts 38 + ```
+277
bun.lock
··· 1 + { 2 + "lockfileVersion": 1, 3 + "configVersion": 1, 4 + "workspaces": { 5 + "": { 6 + "name": "tea-party", 7 + "dependencies": { 8 + "@atcute/atproto": "latest", 9 + "@atcute/client": "latest", 10 + "@sveltejs/adapter-node": "latest", 11 + "@sveltejs/kit": "latest", 12 + "svelte": "latest", 13 + "vite": "latest", 14 + }, 15 + "devDependencies": { 16 + "@sveltejs/vite-plugin-svelte": "latest", 17 + "svelte-check": "latest", 18 + "typescript": "latest", 19 + }, 20 + }, 21 + }, 22 + "packages": { 23 + "@atcute/atproto": ["@atcute/atproto@3.1.10", "", { "dependencies": { "@atcute/lexicons": "^1.2.6" } }, "sha512-+GKZpOc0PJcdWMQEkTfg/rSNDAAHxmAUGBl60g2az15etqJn5WaUPNGFE2sB7hKpwi5Ue2h/L0OacINcE/JDDQ=="], 24 + 25 + "@atcute/client": ["@atcute/client@4.2.1", "", { "dependencies": { "@atcute/identity": "^1.1.3", "@atcute/lexicons": "^1.2.6" } }, "sha512-ZBFM2pW075JtgGFu5g7HHZBecrClhlcNH8GVP9Zz1aViWR+cjjBsTpeE63rJs+FCOHFYlirUyo5L8SGZ4kMINw=="], 26 + 27 + "@atcute/identity": ["@atcute/identity@1.1.3", "", { "dependencies": { "@atcute/lexicons": "^1.2.4", "@badrap/valita": "^0.4.6" } }, "sha512-oIqPoI8TwWeQxvcLmFEZLdN2XdWcaLVtlm8pNk0E72As9HNzzD9pwKPrLr3rmTLRIoULPPFmq9iFNsTeCIU9ng=="], 28 + 29 + "@atcute/lexicons": ["@atcute/lexicons@1.2.9", "", { "dependencies": { "@atcute/uint8array": "^1.1.1", "@atcute/util-text": "^1.1.1", "@standard-schema/spec": "^1.1.0", "esm-env": "^1.2.2" } }, "sha512-/RRHm2Cw9o8Mcsrq0eo8fjS9okKYLGfuFwrQ0YoP/6sdSDsXshaTLJsvLlcUcaDaSJ1YFOuHIo3zr2Om2F/16g=="], 30 + 31 + "@atcute/uint8array": ["@atcute/uint8array@1.1.1", "", {}, "sha512-3LsC8XB8TKe9q/5hOA5sFuzGaIFdJZJNewC5OKa3o/eU6+K7JR6see9Zy2JbQERNVnRl11EzbNov1efgLMAs4g=="], 32 + 33 + "@atcute/util-text": ["@atcute/util-text@1.1.1", "", { "dependencies": { "unicode-segmenter": "^0.14.5" } }, "sha512-JH0SxzUQJAmbOBTYyhxQbkkI6M33YpjlVLEcbP5GYt43xgFArzV0FJVmEpvIj0kjsmphHB45b6IitdvxPdec9w=="], 34 + 35 + "@badrap/valita": ["@badrap/valita@0.4.6", "", {}, "sha512-4kdqcjyxo/8RQ8ayjms47HCWZIF5981oE5nIenbfThKDxWXtEHKipAOWlflpPJzZx9y/JWYQkp18Awr7VuepFg=="], 36 + 37 + "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.3", "", { "os": "aix", "cpu": "ppc64" }, "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg=="], 38 + 39 + "@esbuild/android-arm": ["@esbuild/android-arm@0.27.3", "", { "os": "android", "cpu": "arm" }, "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA=="], 40 + 41 + "@esbuild/android-arm64": ["@esbuild/android-arm64@0.27.3", "", { "os": "android", "cpu": "arm64" }, "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg=="], 42 + 43 + "@esbuild/android-x64": ["@esbuild/android-x64@0.27.3", "", { "os": "android", "cpu": "x64" }, "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ=="], 44 + 45 + "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.27.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg=="], 46 + 47 + "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.27.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg=="], 48 + 49 + "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.27.3", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w=="], 50 + 51 + "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.27.3", "", { "os": "freebsd", "cpu": "x64" }, "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA=="], 52 + 53 + "@esbuild/linux-arm": ["@esbuild/linux-arm@0.27.3", "", { "os": "linux", "cpu": "arm" }, "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw=="], 54 + 55 + "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.27.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg=="], 56 + 57 + "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.27.3", "", { "os": "linux", "cpu": "ia32" }, "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg=="], 58 + 59 + "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.27.3", "", { "os": "linux", "cpu": "none" }, "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA=="], 60 + 61 + "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.27.3", "", { "os": "linux", "cpu": "none" }, "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw=="], 62 + 63 + "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.27.3", "", { "os": "linux", "cpu": "ppc64" }, "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA=="], 64 + 65 + "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.27.3", "", { "os": "linux", "cpu": "none" }, "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ=="], 66 + 67 + "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.27.3", "", { "os": "linux", "cpu": "s390x" }, "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw=="], 68 + 69 + "@esbuild/linux-x64": ["@esbuild/linux-x64@0.27.3", "", { "os": "linux", "cpu": "x64" }, "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA=="], 70 + 71 + "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.27.3", "", { "os": "none", "cpu": "arm64" }, "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA=="], 72 + 73 + "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.27.3", "", { "os": "none", "cpu": "x64" }, "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA=="], 74 + 75 + "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.27.3", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw=="], 76 + 77 + "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.27.3", "", { "os": "openbsd", "cpu": "x64" }, "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ=="], 78 + 79 + "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.27.3", "", { "os": "none", "cpu": "arm64" }, "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g=="], 80 + 81 + "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.27.3", "", { "os": "sunos", "cpu": "x64" }, "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA=="], 82 + 83 + "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.27.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA=="], 84 + 85 + "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.27.3", "", { "os": "win32", "cpu": "ia32" }, "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q=="], 86 + 87 + "@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.3", "", { "os": "win32", "cpu": "x64" }, "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA=="], 88 + 89 + "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="], 90 + 91 + "@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="], 92 + 93 + "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="], 94 + 95 + "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="], 96 + 97 + "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], 98 + 99 + "@polka/url": ["@polka/url@1.0.0-next.29", "", {}, "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww=="], 100 + 101 + "@rollup/plugin-commonjs": ["@rollup/plugin-commonjs@29.0.2", "", { "dependencies": { "@rollup/pluginutils": "^5.0.1", "commondir": "^1.0.1", "estree-walker": "^2.0.2", "fdir": "^6.2.0", "is-reference": "1.2.1", "magic-string": "^0.30.3", "picomatch": "^4.0.2" }, "peerDependencies": { "rollup": "^2.68.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-S/ggWH1LU7jTyi9DxZOKyxpVd4hF/OZ0JrEbeLjXk/DFXwRny0tjD2c992zOUYQobLrVkRVMDdmHP16HKP7GRg=="], 102 + 103 + "@rollup/plugin-json": ["@rollup/plugin-json@6.1.0", "", { "dependencies": { "@rollup/pluginutils": "^5.1.0" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA=="], 104 + 105 + "@rollup/plugin-node-resolve": ["@rollup/plugin-node-resolve@16.0.3", "", { "dependencies": { "@rollup/pluginutils": "^5.0.1", "@types/resolve": "1.20.2", "deepmerge": "^4.2.2", "is-module": "^1.0.0", "resolve": "^1.22.1" }, "peerDependencies": { "rollup": "^2.78.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-lUYM3UBGuM93CnMPG1YocWu7X802BrNF3jW2zny5gQyLQgRFJhV1Sq0Zi74+dh/6NBx1DxFC4b4GXg9wUCG5Qg=="], 106 + 107 + "@rollup/pluginutils": ["@rollup/pluginutils@5.3.0", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^2.0.2", "picomatch": "^4.0.2" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q=="], 108 + 109 + "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.59.0", "", { "os": "android", "cpu": "arm" }, "sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg=="], 110 + 111 + "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.59.0", "", { "os": "android", "cpu": "arm64" }, "sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q=="], 112 + 113 + "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.59.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg=="], 114 + 115 + "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.59.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w=="], 116 + 117 + "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.59.0", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA=="], 118 + 119 + "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.59.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg=="], 120 + 121 + "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.59.0", "", { "os": "linux", "cpu": "arm" }, "sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw=="], 122 + 123 + "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.59.0", "", { "os": "linux", "cpu": "arm" }, "sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA=="], 124 + 125 + "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.59.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA=="], 126 + 127 + "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.59.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA=="], 128 + 129 + "@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.59.0", "", { "os": "linux", "cpu": "none" }, "sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg=="], 130 + 131 + "@rollup/rollup-linux-loong64-musl": ["@rollup/rollup-linux-loong64-musl@4.59.0", "", { "os": "linux", "cpu": "none" }, "sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q=="], 132 + 133 + "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.59.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA=="], 134 + 135 + "@rollup/rollup-linux-ppc64-musl": ["@rollup/rollup-linux-ppc64-musl@4.59.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA=="], 136 + 137 + "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.59.0", "", { "os": "linux", "cpu": "none" }, "sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg=="], 138 + 139 + "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.59.0", "", { "os": "linux", "cpu": "none" }, "sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg=="], 140 + 141 + "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.59.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w=="], 142 + 143 + "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.59.0", "", { "os": "linux", "cpu": "x64" }, "sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg=="], 144 + 145 + "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.59.0", "", { "os": "linux", "cpu": "x64" }, "sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg=="], 146 + 147 + "@rollup/rollup-openbsd-x64": ["@rollup/rollup-openbsd-x64@4.59.0", "", { "os": "openbsd", "cpu": "x64" }, "sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ=="], 148 + 149 + "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.59.0", "", { "os": "none", "cpu": "arm64" }, "sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA=="], 150 + 151 + "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.59.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A=="], 152 + 153 + "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.59.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA=="], 154 + 155 + "@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.59.0", "", { "os": "win32", "cpu": "x64" }, "sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA=="], 156 + 157 + "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.59.0", "", { "os": "win32", "cpu": "x64" }, "sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA=="], 158 + 159 + "@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], 160 + 161 + "@sveltejs/acorn-typescript": ["@sveltejs/acorn-typescript@1.0.9", "", { "peerDependencies": { "acorn": "^8.9.0" } }, "sha512-lVJX6qEgs/4DOcRTpo56tmKzVPtoWAaVbL4hfO7t7NVwl9AAXzQR6cihesW1BmNMPl+bK6dreu2sOKBP2Q9CIA=="], 162 + 163 + "@sveltejs/adapter-node": ["@sveltejs/adapter-node@5.5.4", "", { "dependencies": { "@rollup/plugin-commonjs": "^29.0.0", "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "^16.0.0", "rollup": "^4.59.0" }, "peerDependencies": { "@sveltejs/kit": "^2.4.0" } }, "sha512-45X92CXW+2J8ZUzPv3eLlKWEzINKiiGeFWTjyER4ZN4sGgNoaoeSkCY/QYNxHpPXy71QPsctwccBo9jJs0ySPQ=="], 164 + 165 + "@sveltejs/kit": ["@sveltejs/kit@2.53.4", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "@sveltejs/acorn-typescript": "^1.0.5", "@types/cookie": "^0.6.0", "acorn": "^8.14.1", "cookie": "^0.6.0", "devalue": "^5.6.3", "esm-env": "^1.2.2", "kleur": "^4.1.5", "magic-string": "^0.30.5", "mrmime": "^2.0.0", "set-cookie-parser": "^3.0.0", "sirv": "^3.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.0.0", "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0 || ^7.0.0", "svelte": "^4.0.0 || ^5.0.0-next.0", "typescript": "^5.3.3", "vite": "^5.0.3 || ^6.0.0 || ^7.0.0-beta.0 || ^8.0.0" }, "optionalPeers": ["@opentelemetry/api", "typescript"], "bin": { "svelte-kit": "svelte-kit.js" } }, "sha512-iAIPEahFgDJJyvz8g0jP08KvqnM6JvdW8YfsygZ+pMeMvyM2zssWMltcsotETvjSZ82G3VlitgDtBIvpQSZrTA=="], 166 + 167 + "@sveltejs/vite-plugin-svelte": ["@sveltejs/vite-plugin-svelte@6.2.4", "", { "dependencies": { "@sveltejs/vite-plugin-svelte-inspector": "^5.0.0", "deepmerge": "^4.3.1", "magic-string": "^0.30.21", "obug": "^2.1.0", "vitefu": "^1.1.1" }, "peerDependencies": { "svelte": "^5.0.0", "vite": "^6.3.0 || ^7.0.0" } }, "sha512-ou/d51QSdTyN26D7h6dSpusAKaZkAiGM55/AKYi+9AGZw7q85hElbjK3kEyzXHhLSnRISHOYzVge6x0jRZ7DXA=="], 168 + 169 + "@sveltejs/vite-plugin-svelte-inspector": ["@sveltejs/vite-plugin-svelte-inspector@5.0.2", "", { "dependencies": { "obug": "^2.1.0" }, "peerDependencies": { "@sveltejs/vite-plugin-svelte": "^6.0.0-next.0", "svelte": "^5.0.0", "vite": "^6.3.0 || ^7.0.0" } }, "sha512-TZzRTcEtZffICSAoZGkPSl6Etsj2torOVrx6Uw0KpXxrec9Gg6jFWQ60Q3+LmNGfZSxHRCZL7vXVZIWmuV50Ig=="], 170 + 171 + "@types/cookie": ["@types/cookie@0.6.0", "", {}, "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA=="], 172 + 173 + "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], 174 + 175 + "@types/resolve": ["@types/resolve@1.20.2", "", {}, "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q=="], 176 + 177 + "@types/trusted-types": ["@types/trusted-types@2.0.7", "", {}, "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw=="], 178 + 179 + "acorn": ["acorn@8.16.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw=="], 180 + 181 + "aria-query": ["aria-query@5.3.1", "", {}, "sha512-Z/ZeOgVl7bcSYZ/u/rh0fOpvEpq//LZmdbkXyc7syVzjPAhfOa9ebsdTSjEBDU4vs5nC98Kfduj1uFo0qyET3g=="], 182 + 183 + "axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="], 184 + 185 + "chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="], 186 + 187 + "clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="], 188 + 189 + "commondir": ["commondir@1.0.1", "", {}, "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg=="], 190 + 191 + "cookie": ["cookie@0.6.0", "", {}, "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw=="], 192 + 193 + "deepmerge": ["deepmerge@4.3.1", "", {}, "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="], 194 + 195 + "devalue": ["devalue@5.6.3", "", {}, "sha512-nc7XjUU/2Lb+SvEFVGcWLiKkzfw8+qHI7zn8WYXKkLMgfGSHbgCEaR6bJpev8Cm6Rmrb19Gfd/tZvGqx9is3wg=="], 196 + 197 + "esbuild": ["esbuild@0.27.3", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.3", "@esbuild/android-arm": "0.27.3", "@esbuild/android-arm64": "0.27.3", "@esbuild/android-x64": "0.27.3", "@esbuild/darwin-arm64": "0.27.3", "@esbuild/darwin-x64": "0.27.3", "@esbuild/freebsd-arm64": "0.27.3", "@esbuild/freebsd-x64": "0.27.3", "@esbuild/linux-arm": "0.27.3", "@esbuild/linux-arm64": "0.27.3", "@esbuild/linux-ia32": "0.27.3", "@esbuild/linux-loong64": "0.27.3", "@esbuild/linux-mips64el": "0.27.3", "@esbuild/linux-ppc64": "0.27.3", "@esbuild/linux-riscv64": "0.27.3", "@esbuild/linux-s390x": "0.27.3", "@esbuild/linux-x64": "0.27.3", "@esbuild/netbsd-arm64": "0.27.3", "@esbuild/netbsd-x64": "0.27.3", "@esbuild/openbsd-arm64": "0.27.3", "@esbuild/openbsd-x64": "0.27.3", "@esbuild/openharmony-arm64": "0.27.3", "@esbuild/sunos-x64": "0.27.3", "@esbuild/win32-arm64": "0.27.3", "@esbuild/win32-ia32": "0.27.3", "@esbuild/win32-x64": "0.27.3" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg=="], 198 + 199 + "esm-env": ["esm-env@1.2.2", "", {}, "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA=="], 200 + 201 + "esrap": ["esrap@2.2.3", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" } }, "sha512-8fOS+GIGCQZl/ZIlhl59htOlms6U8NvX6ZYgYHpRU/b6tVSh3uHkOHZikl3D4cMbYM0JlpBe+p/BkZEi8J9XIQ=="], 202 + 203 + "estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], 204 + 205 + "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], 206 + 207 + "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], 208 + 209 + "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], 210 + 211 + "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], 212 + 213 + "is-core-module": ["is-core-module@2.16.1", "", { "dependencies": { "hasown": "^2.0.2" } }, "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w=="], 214 + 215 + "is-module": ["is-module@1.0.0", "", {}, "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g=="], 216 + 217 + "is-reference": ["is-reference@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.6" } }, "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw=="], 218 + 219 + "kleur": ["kleur@4.1.5", "", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="], 220 + 221 + "locate-character": ["locate-character@3.0.0", "", {}, "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA=="], 222 + 223 + "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="], 224 + 225 + "mri": ["mri@1.2.0", "", {}, "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA=="], 226 + 227 + "mrmime": ["mrmime@2.0.1", "", {}, "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ=="], 228 + 229 + "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], 230 + 231 + "obug": ["obug@2.1.1", "", {}, "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ=="], 232 + 233 + "path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="], 234 + 235 + "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], 236 + 237 + "picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], 238 + 239 + "postcss": ["postcss@8.5.8", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg=="], 240 + 241 + "readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="], 242 + 243 + "resolve": ["resolve@1.22.11", "", { "dependencies": { "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ=="], 244 + 245 + "rollup": ["rollup@4.59.0", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.59.0", "@rollup/rollup-android-arm64": "4.59.0", "@rollup/rollup-darwin-arm64": "4.59.0", "@rollup/rollup-darwin-x64": "4.59.0", "@rollup/rollup-freebsd-arm64": "4.59.0", "@rollup/rollup-freebsd-x64": "4.59.0", "@rollup/rollup-linux-arm-gnueabihf": "4.59.0", "@rollup/rollup-linux-arm-musleabihf": "4.59.0", "@rollup/rollup-linux-arm64-gnu": "4.59.0", "@rollup/rollup-linux-arm64-musl": "4.59.0", "@rollup/rollup-linux-loong64-gnu": "4.59.0", "@rollup/rollup-linux-loong64-musl": "4.59.0", "@rollup/rollup-linux-ppc64-gnu": "4.59.0", "@rollup/rollup-linux-ppc64-musl": "4.59.0", "@rollup/rollup-linux-riscv64-gnu": "4.59.0", "@rollup/rollup-linux-riscv64-musl": "4.59.0", "@rollup/rollup-linux-s390x-gnu": "4.59.0", "@rollup/rollup-linux-x64-gnu": "4.59.0", "@rollup/rollup-linux-x64-musl": "4.59.0", "@rollup/rollup-openbsd-x64": "4.59.0", "@rollup/rollup-openharmony-arm64": "4.59.0", "@rollup/rollup-win32-arm64-msvc": "4.59.0", "@rollup/rollup-win32-ia32-msvc": "4.59.0", "@rollup/rollup-win32-x64-gnu": "4.59.0", "@rollup/rollup-win32-x64-msvc": "4.59.0", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg=="], 246 + 247 + "sade": ["sade@1.8.1", "", { "dependencies": { "mri": "^1.1.0" } }, "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A=="], 248 + 249 + "set-cookie-parser": ["set-cookie-parser@3.0.1", "", {}, "sha512-n7Z7dXZhJbwuAHhNzkTti6Aw9QDDjZtm3JTpTGATIdNzdQz5GuFs22w90BcvF4INfnrL5xrX3oGsuqO5Dx3A1Q=="], 250 + 251 + "sirv": ["sirv@3.0.2", "", { "dependencies": { "@polka/url": "^1.0.0-next.24", "mrmime": "^2.0.0", "totalist": "^3.0.0" } }, "sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g=="], 252 + 253 + "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], 254 + 255 + "supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="], 256 + 257 + "svelte": ["svelte@5.53.9", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "@jridgewell/sourcemap-codec": "^1.5.0", "@sveltejs/acorn-typescript": "^1.0.5", "@types/estree": "^1.0.5", "@types/trusted-types": "^2.0.7", "acorn": "^8.12.1", "aria-query": "5.3.1", "axobject-query": "^4.1.0", "clsx": "^2.1.1", "devalue": "^5.6.3", "esm-env": "^1.2.1", "esrap": "^2.2.2", "is-reference": "^3.0.3", "locate-character": "^3.0.0", "magic-string": "^0.30.11", "zimmerframe": "^1.1.2" } }, "sha512-MwDfWsN8qZzeP0jlQsWF4k/4B3csb3IbzCRggF+L/QqY7T8bbKvnChEo1cPZztF51HJQhilDbevWYl2LvXbquA=="], 258 + 259 + "svelte-check": ["svelte-check@4.4.5", "", { "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", "chokidar": "^4.0.1", "fdir": "^6.2.0", "picocolors": "^1.0.0", "sade": "^1.7.4" }, "peerDependencies": { "svelte": "^4.0.0 || ^5.0.0-next.0", "typescript": ">=5.0.0" }, "bin": { "svelte-check": "bin/svelte-check" } }, "sha512-1bSwIRCvvmSHrlK52fOlZmVtUZgil43jNL/2H18pRpa+eQjzGt6e3zayxhp1S7GajPFKNM/2PMCG+DZFHlG9fw=="], 260 + 261 + "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], 262 + 263 + "totalist": ["totalist@3.0.1", "", {}, "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ=="], 264 + 265 + "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], 266 + 267 + "unicode-segmenter": ["unicode-segmenter@0.14.5", "", {}, "sha512-jHGmj2LUuqDcX3hqY12Ql+uhUTn8huuxNZGq7GvtF6bSybzH3aFgedYu/KTzQStEgt1Ra2F3HxadNXsNjb3m3g=="], 268 + 269 + "vite": ["vite@7.3.1", "", { "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA=="], 270 + 271 + "vitefu": ["vitefu@1.1.2", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-beta.0" }, "optionalPeers": ["vite"] }, "sha512-zpKATdUbzbsycPFBN71nS2uzBUQiVnFoOrr2rvqv34S1lcAgMKKkjWleLGeiJlZ8lwCXvtWaRn7R3ZC16SYRuw=="], 272 + 273 + "zimmerframe": ["zimmerframe@1.1.4", "", {}, "sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ=="], 274 + 275 + "@rollup/plugin-commonjs/is-reference": ["is-reference@1.2.1", "", { "dependencies": { "@types/estree": "*" } }, "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ=="], 276 + } 277 + }
+24
package.json
··· 1 + { 2 + "name": "tea-party", 3 + "version": "0.0.1", 4 + "private": true, 5 + "scripts": { 6 + "dev": "vite dev", 7 + "build": "vite build", 8 + "preview": "vite preview" 9 + }, 10 + "dependencies": { 11 + "@atcute/atproto": "latest", 12 + "@atcute/client": "latest", 13 + "@sveltejs/adapter-node": "latest", 14 + "@sveltejs/kit": "latest", 15 + "svelte": "latest", 16 + "vite": "latest" 17 + }, 18 + "devDependencies": { 19 + "@sveltejs/vite-plugin-svelte": "latest", 20 + "typescript": "latest", 21 + "svelte-check": "latest" 22 + }, 23 + "type": "module" 24 + }
+136
src/app.css
··· 1 + *, 2 + *::before, 3 + *::after { 4 + box-sizing: border-box; 5 + } 6 + 7 + *:not(dialog) { 8 + margin: 0; 9 + } 10 + 11 + @media (prefers-reduced-motion: no-preference) { 12 + html { 13 + interpolate-size: allow-keywords; 14 + } 15 + } 16 + 17 + body { 18 + line-height: 1.5; 19 + -webkit-font-smoothing: antialiased; 20 + } 21 + 22 + img, 23 + picture, 24 + video, 25 + canvas, 26 + svg { 27 + display: block; 28 + max-width: 100%; 29 + } 30 + 31 + input, 32 + button, 33 + textarea, 34 + select { 35 + font: inherit; 36 + } 37 + 38 + p, 39 + h1, 40 + h2, 41 + h3, 42 + h4, 43 + h5, 44 + h6 { 45 + overflow-wrap: break-word; 46 + } 47 + 48 + p { 49 + text-wrap: pretty; 50 + } 51 + 52 + h1, 53 + h2, 54 + h3, 55 + h4, 56 + h5, 57 + h6 { 58 + text-wrap: balance; 59 + } 60 + 61 + @font-face { 62 + font-family: "Host Grotesk"; 63 + font-optical-sizing: auto; 64 + font-weight: 300 800; 65 + font-style: normal; 66 + font-display: swap; 67 + src: url("/fonts/HostGrotesk-VariableFont.woff2") format("woff2-variations"); 68 + } 69 + 70 + :root { 71 + font-size: 16px; 72 + text-rendering: optimizeLegibility; 73 + -webkit-font-smoothing: antialiased; 74 + -moz-osx-font-smoothing: grayscale; 75 + 76 + @media (width >= 600px) { 77 + font-size: 18px; 78 + } 79 + 80 + font-family: "Host Grotesk", sans-serif; 81 + 82 + --color-bg: oklch(95% 0 0); 83 + --color-surface: oklch(100% 0 0); 84 + --color-fg: oklch(0% 0 0); 85 + } 86 + 87 + @media (prefers-color-scheme: dark) { 88 + :root { 89 + --color-bg: oklch(20% 0 0); 90 + --color-surface: oklch(0% 0 0); 91 + --color-fg: oklch(100% 0 0); 92 + } 93 + } 94 + 95 + :root { 96 + background-color: var(--color-bg); 97 + color: var(--color-fg); 98 + } 99 + 100 + a { 101 + color: inherit; 102 + text-decoration: none; 103 + font-weight: 700; 104 + padding: 0.05rem 0.2rem; 105 + margin-inline: -0.2rem; 106 + } 107 + 108 + a:hover, 109 + a:active { 110 + color: var(--color-surface); 111 + background-color: var(--color-fg); 112 + } 113 + 114 + a:focus { 115 + outline: 2px solid var(--color-fg); 116 + outline-offset: 2px; 117 + } 118 + 119 + h1 { 120 + font-size: 2rem; 121 + padding-bottom: 0.25rem; 122 + } 123 + 124 + h2 { 125 + font-size: 1.5rem; 126 + padding-bottom: 0.2rem; 127 + } 128 + 129 + h3 { 130 + font-size: 1.25rem; 131 + padding-bottom: 0.1rem; 132 + } 133 + 134 + h4 { 135 + font-size: 1.125rem; 136 + }
+12
src/app.d.ts
··· 1 + /// <reference types="@atcute/atproto" /> 2 + 3 + declare global { 4 + namespace App { 5 + // interface Error {} 6 + // interface Locals {} 7 + // interface PageData {} 8 + // interface Platform {} 9 + } 10 + } 11 + 12 + export {};
+12
src/app.html
··· 1 + <!doctype html> 2 + <html lang="en"> 3 + <head> 4 + <meta charset="utf-8" /> 5 + <link rel="icon" href="%sveltekit.assets%/favicon.png" /> 6 + <meta name="viewport" content="width=device-width, initial-scale=1" /> 7 + %sveltekit.head% 8 + </head> 9 + <body data-sveltekit-preload-data="hover"> 10 + <div style="display: contents">%sveltekit.body%</div> 11 + </body> 12 + </html>
+8
src/lib/server/pds.ts
··· 1 + import { Client, simpleFetchHandler } from '@atcute/client'; 2 + import { env } from '$env/dynamic/private'; 3 + 4 + const PDS_URL = env.PDS_URL ?? 'https://pds.madoka.systems'; 5 + 6 + export const publicRpc = new Client({ 7 + handler: simpleFetchHandler({ service: PDS_URL }), 8 + });
+70
src/routes/+page.server.ts
··· 1 + import type { PageServerLoad } from './$types'; 2 + import { publicRpc } from '$lib/server/pds'; 3 + 4 + const DID_RE = /^did:[a-z]+:[a-zA-Z0-9._:%-]+$/; 5 + 6 + export interface Account { 7 + did: string; 8 + handle: string; 9 + displayName: string | null; 10 + avatarUrl: string; 11 + } 12 + 13 + export const load: PageServerLoad = async () => { 14 + const listResult = await publicRpc.get('com.atproto.sync.listRepos', { 15 + params: { limit: 1000 }, 16 + }); 17 + 18 + if (listResult.data.cursor) { 19 + console.warn('[listRepos] response truncated at 1000 — cursor present, some accounts not shown'); 20 + } 21 + 22 + const active = listResult.data.repos.filter((r) => r.active !== false); 23 + 24 + const results = await Promise.allSettled( 25 + active.map(async (repo) => { 26 + if (!DID_RE.test(repo.did)) { 27 + throw new Error(`invalid DID: ${repo.did}`); 28 + } 29 + 30 + const [describeResult, profileResult] = await Promise.allSettled([ 31 + publicRpc.get('com.atproto.repo.describeRepo', { 32 + params: { repo: repo.did }, 33 + }), 34 + publicRpc.get('com.atproto.repo.getRecord', { 35 + params: { 36 + repo: repo.did, 37 + collection: 'app.bsky.actor.profile', 38 + rkey: 'self', 39 + }, 40 + }), 41 + ]); 42 + 43 + const handle = 44 + describeResult.status === 'fulfilled' 45 + ? describeResult.value.data.handle 46 + : repo.did; 47 + 48 + let displayName: string | null = null; 49 + if (profileResult.status === 'fulfilled') { 50 + const value = profileResult.value.data.value as Record<string, unknown>; 51 + if (typeof value.displayName === 'string' && value.displayName.trim()) { 52 + displayName = value.displayName.trim().slice(0, 640); 53 + } 54 + } 55 + 56 + return { 57 + did: repo.did, 58 + handle, 59 + displayName, 60 + avatarUrl: `https://blobs.blue/${repo.did}/avatar@webp`, 61 + } satisfies Account; 62 + }) 63 + ); 64 + 65 + const accounts: Account[] = results 66 + .filter((r) => r.status === 'fulfilled') 67 + .map((r) => (r as PromiseFulfilledResult<Account>).value); 68 + 69 + return { accounts }; 70 + };
+320
src/routes/+page.svelte
··· 1 + <script lang="ts"> 2 + import '../app.css'; 3 + import type { PageData } from './$types'; 4 + 5 + export let data: PageData; 6 + 7 + let inviteState: 'idle' | 'loading' | 'done' | 'error' = 'idle'; 8 + let inviteCode = ''; 9 + let inviteError = ''; 10 + let copied = false; 11 + 12 + async function generateInvite() { 13 + inviteState = 'loading'; 14 + inviteError = ''; 15 + try { 16 + const res = await fetch('/api/invite', { method: 'POST' }); 17 + const body = await res.json().catch(() => ({})); 18 + if (res.ok) { 19 + inviteCode = body.code; 20 + inviteState = 'done'; 21 + copied = false; 22 + navigator.clipboard.writeText(body.code).then(() => { 23 + copied = true; 24 + }).catch(() => {}); 25 + } else if (res.status === 429) { 26 + const retryAfter = res.headers.get('Retry-After'); 27 + inviteError = retryAfter 28 + ? `rate limited — try again in ${Math.ceil(parseInt(retryAfter) / 3600)}h` 29 + : 'rate limited'; 30 + inviteState = 'error'; 31 + } else { 32 + inviteError = body.error ?? `server error (${res.status})`; 33 + inviteState = 'error'; 34 + } 35 + } catch { 36 + inviteError = 'network error — are you online?'; 37 + inviteState = 'error'; 38 + } 39 + } 40 + </script> 41 + 42 + <svelte:head> 43 + <title>pds.madoka.systems</title> 44 + </svelte:head> 45 + 46 + <main> 47 + <div class="container"> 48 + <header> 49 + <h1>pds.madoka.systems</h1> 50 + <p class="subtitle">{data.accounts.length} accounts</p> 51 + </header> 52 + 53 + <hr class="dashed" /> 54 + 55 + <ul class="accounts"> 56 + {#each data.accounts as account (account.did)} 57 + <li class="account"> 58 + <img 59 + class="avatar" 60 + src={account.avatarUrl} 61 + alt="" 62 + width="48" 63 + height="48" 64 + loading="lazy" 65 + onerror={(e) => ((e.currentTarget as HTMLImageElement).style.visibility = 'hidden')} 66 + /> 67 + <div class="account-info"> 68 + {#if account.displayName} 69 + <span class="display-name">{account.displayName}</span> 70 + {/if} 71 + <div class="handle-row"> 72 + <a 73 + href="https://bsky.app/profile/{account.handle}" 74 + target="_blank" 75 + rel="noopener noreferrer" 76 + class="handle">@{account.handle}</a 77 + > 78 + <span class="did">{account.did}</span> 79 + </div> 80 + </div> 81 + <div class="account-actions"> 82 + <a 83 + href="https://bsky.app/profile/{account.did}" 84 + target="_blank" 85 + rel="noopener noreferrer" 86 + class="action-btn" 87 + title="Open in Bluesky" 88 + > 89 + <svg width="16" height="16" viewBox="0 0 360 320" fill="currentColor" aria-hidden="true"> 90 + <path d="M180 142c-16.3-31.7-60.7-90.8-102-120C38.5-5.9 23.4-1 13.5 3.4 2.1 8.6 0 26.2 0 36.5c0 10.4 5.7 84.8 9.4 97.2 12.2 41 55.7 55 95.7 50.5-58.7 8.6-110.8 30-53.2 105.8C131.6 380.1 143.8 295.3 180 256c36.2 39.3 48.4 124.1 128.1 33.9 57.6-75.8 5.5-97.2-53.2-105.8 40 4.4 83.5-9.5 95.7-50.5 3.7-12.4 9.4-86.8 9.4-97.2 0-10.3-2.1-27.9-13.5-33.1C336.6-1 321.5-5.9 282 22c-41.3 29.2-85.7 88.3-102 120Z"/> 91 + </svg> 92 + </a> 93 + <a 94 + href="https://pds.ls/{account.did}" 95 + target="_blank" 96 + rel="noopener noreferrer" 97 + class="action-btn" 98 + title="Open in pds.ls" 99 + > 100 + <span class="pdsls-icon" aria-hidden="true"></span> 101 + </a> 102 + </div> 103 + </li> 104 + {/each} 105 + </ul> 106 + 107 + <hr class="dashed" /> 108 + 109 + <div class="footer-actions"> 110 + {#if inviteState === 'idle' || inviteState === 'loading'} 111 + <button 112 + class="primary-btn" 113 + onclick={generateInvite} 114 + disabled={inviteState === 'loading'} 115 + > 116 + {inviteState === 'loading' ? 'generating...' : 'generate invite code'} 117 + </button> 118 + {:else if inviteState === 'done'} 119 + <div class="invite-result"> 120 + <code class="invite-code">{inviteCode}</code> 121 + <span class="copied-hint">{copied ? 'copied!' : 'click to copy'}</span> 122 + <button 123 + class="primary-btn" 124 + onclick={generateInvite} 125 + >generate another</button> 126 + </div> 127 + {:else if inviteState === 'error'} 128 + <button 129 + class="primary-btn" 130 + onclick={generateInvite} 131 + >generate invite code</button> 132 + <span class="invite-error">{inviteError}</span> 133 + {/if} 134 + <a 135 + href="https://pdsmoover.com/moover" 136 + target="_blank" 137 + rel="noopener noreferrer" 138 + class="moover-link" 139 + >migrate</a> 140 + </div> 141 + </div> 142 + </main> 143 + 144 + <style> 145 + main { 146 + padding: 1rem; 147 + } 148 + 149 + .container { 150 + max-width: 600px; 151 + margin-inline: auto; 152 + margin-block: 0.5rem; 153 + border: 2px solid var(--color-fg); 154 + background-color: var(--color-surface); 155 + } 156 + 157 + header { 158 + padding: 1.25rem 1.5rem 1rem; 159 + } 160 + 161 + .subtitle { 162 + color: oklch(from var(--color-fg) l c h / 0.6); 163 + font-size: 0.9rem; 164 + } 165 + 166 + .dashed { 167 + border: none; 168 + border-top: 2px dashed oklch(from var(--color-fg) l c h / 0.3); 169 + margin: 0; 170 + } 171 + 172 + .accounts { 173 + list-style: none; 174 + padding: 0; 175 + } 176 + 177 + .account { 178 + display: flex; 179 + align-items: center; 180 + gap: 0.875rem; 181 + padding: 0.75rem 1rem 0.75rem 1.5rem; 182 + border-bottom: 1px solid oklch(from var(--color-fg) l c h / 0.1); 183 + } 184 + 185 + .account:last-child { 186 + border-bottom: none; 187 + } 188 + 189 + .avatar { 190 + width: 48px; 191 + height: 48px; 192 + border-radius: 50%; 193 + object-fit: cover; 194 + flex-shrink: 0; 195 + background-color: oklch(from var(--color-fg) l c h / 0.1); 196 + } 197 + 198 + .account-info { 199 + display: flex; 200 + flex-direction: column; 201 + min-width: 0; 202 + flex: 1; 203 + } 204 + 205 + .display-name { 206 + font-weight: 600; 207 + white-space: nowrap; 208 + overflow: hidden; 209 + text-overflow: ellipsis; 210 + } 211 + 212 + .handle-row { 213 + display: flex; 214 + align-items: baseline; 215 + gap: 0.5rem; 216 + min-width: 0; 217 + } 218 + 219 + .handle { 220 + font-size: 0.875rem; 221 + white-space: nowrap; 222 + flex-shrink: 0; 223 + } 224 + 225 + .did { 226 + font-size: 0.75rem; 227 + color: oklch(from var(--color-fg) l c h / 0.45); 228 + white-space: nowrap; 229 + overflow: hidden; 230 + text-overflow: ellipsis; 231 + min-width: 0; 232 + } 233 + 234 + .account-actions { 235 + display: flex; 236 + gap: 0.25rem; 237 + flex-shrink: 0; 238 + margin-left: auto; 239 + } 240 + 241 + .action-btn { 242 + display: flex; 243 + align-items: center; 244 + justify-content: center; 245 + width: 2rem; 246 + height: 2rem; 247 + padding: 0; 248 + margin: 0; 249 + font-weight: normal; 250 + } 251 + 252 + .pdsls-icon { 253 + display: block; 254 + width: 22px; 255 + height: 22px; 256 + background-color: currentColor; 257 + mask-image: url('/pdsls-icon.svg'); 258 + mask-size: 160%; 259 + mask-repeat: no-repeat; 260 + mask-position: center; 261 + } 262 + 263 + .footer-actions { 264 + padding: 1.25rem 1.5rem; 265 + display: flex; 266 + flex-wrap: wrap; 267 + align-items: center; 268 + gap: 0.75rem; 269 + } 270 + 271 + .primary-btn { 272 + cursor: pointer; 273 + background: var(--color-fg); 274 + color: var(--color-surface); 275 + border: 2px solid var(--color-fg); 276 + padding: 0.4rem 1rem; 277 + font-weight: 700; 278 + font-size: 1rem; 279 + align-self: flex-start; 280 + } 281 + 282 + .primary-btn:hover:not(:disabled) { 283 + background: var(--color-surface); 284 + color: var(--color-fg); 285 + } 286 + 287 + .primary-btn:disabled { 288 + opacity: 0.6; 289 + cursor: not-allowed; 290 + } 291 + 292 + .invite-result { 293 + display: flex; 294 + align-items: center; 295 + flex-wrap: wrap; 296 + gap: 0.75rem; 297 + } 298 + 299 + .invite-code { 300 + font-family: monospace; 301 + font-size: 0.9rem; 302 + background: oklch(from var(--color-fg) l c h / 0.08); 303 + padding: 0.2rem 0.5rem; 304 + user-select: all; 305 + } 306 + 307 + .copied-hint { 308 + font-size: 0.8rem; 309 + color: oklch(from var(--color-fg) l c h / 0.5); 310 + } 311 + 312 + .invite-error { 313 + font-size: 0.875rem; 314 + color: oklch(55% 0.2 25); 315 + } 316 + 317 + .moover-link { 318 + margin-left: auto; 319 + } 320 + </style>
+79
src/routes/api/invite/+server.ts
··· 1 + import type { RequestHandler } from './$types'; 2 + import { json } from '@sveltejs/kit'; 3 + import { dev } from '$app/environment'; 4 + import { env } from '$env/dynamic/private'; 5 + 6 + const RATE_LIMIT = parseInt(env.INVITE_RATE_LIMIT ?? '3', 10) || 3; 7 + const RATE_WINDOW = (parseInt(env.INVITE_RATE_WINDOW ?? '86400', 10) || 86400) * 1000; 8 + 9 + const ipTimestamps = new Map<string, number[]>(); 10 + 11 + function getIp(request: Request): string { 12 + const ip = request.headers.get('x-forwarded-for')?.split(',')[0]?.trim(); 13 + if (!ip) console.warn('[invite] no x-forwarded-for header — rate limiting under "unknown"'); 14 + return ip ?? 'unknown'; 15 + } 16 + 17 + export const POST: RequestHandler = async ({ request }) => { 18 + const pdsUrl = env.PDS_URL; 19 + const adminPassword = env.PDS_ADMIN_PASSWORD; 20 + 21 + if (!pdsUrl || !adminPassword) { 22 + return json({ error: 'server not configured (missing PDS_URL or PDS_ADMIN_PASSWORD)' }, { status: 503 }); 23 + } 24 + 25 + const ip = getIp(request); 26 + const now = Date.now(); 27 + 28 + const timestamps = (ipTimestamps.get(ip) ?? []).filter( 29 + (t) => now - t < RATE_WINDOW 30 + ); 31 + 32 + if (!dev && timestamps.length >= RATE_LIMIT) { 33 + const oldest = Math.min(...timestamps); 34 + const retryAfter = Math.ceil((oldest + RATE_WINDOW - now) / 1000); 35 + return new Response(JSON.stringify({ error: 'rate limited' }), { 36 + status: 429, 37 + headers: { 38 + 'Content-Type': 'application/json', 39 + 'Retry-After': String(retryAfter), 40 + }, 41 + }); 42 + } 43 + 44 + try { 45 + const credentials = Buffer.from(`admin:${adminPassword}`).toString('base64'); 46 + const res = await fetch( 47 + new URL('/xrpc/com.atproto.server.createInviteCode', pdsUrl), 48 + { 49 + method: 'POST', 50 + headers: { 51 + 'Content-Type': 'application/json', 52 + Authorization: `Basic ${credentials}`, 53 + }, 54 + body: JSON.stringify({ useCount: 1 }), 55 + } 56 + ); 57 + 58 + const body = await res.json(); 59 + 60 + if (!res.ok) { 61 + console.error('[invite] PDS error:', res.status, body); 62 + return json({ error: body.message ?? `PDS returned ${res.status}` }, { status: 502 }); 63 + } 64 + 65 + if (typeof body.code !== 'string') { 66 + console.error('[invite] unexpected response:', body); 67 + return json({ error: `unexpected response: ${JSON.stringify(body)}` }, { status: 502 }); 68 + } 69 + 70 + timestamps.push(now); 71 + ipTimestamps.set(ip, timestamps); 72 + 73 + return json({ code: body.code }); 74 + } catch (err) { 75 + console.error('[invite] fetch error:', err); 76 + const msg = err instanceof Error ? err.message : String(err); 77 + return json({ error: msg }, { status: 502 }); 78 + } 79 + };
static/fonts/HostGrotesk-VariableFont.woff2

This is a binary file and will not be displayed.

static/pdsls-icon.png

This is a binary file and will not be displayed.

+29
static/pdsls-icon.svg
··· 1 + <?xml version="1.0" standalone="no"?> 2 + <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN" 3 + "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"> 4 + <svg version="1.0" xmlns="http://www.w3.org/2000/svg" 5 + width="922.000000pt" height="922.000000pt" viewBox="0 0 922.000000 922.000000" 6 + preserveAspectRatio="xMidYMid meet"> 7 + <metadata> 8 + Created by potrace 1.16, written by Peter Selinger 2001-2019 9 + </metadata> 10 + <g transform="translate(0.000000,922.000000) scale(0.100000,-0.100000)" 11 + fill="#000000" stroke="none"> 12 + <path d="M4945 6949 c-311 -47 -545 -315 -545 -623 0 -143 28 -236 115 -381 13 + l18 -31 -210 -334 -210 -335 -94 3 c-113 5 -195 -9 -290 -48 -206 -83 -347 14 + -259 -389 -484 -26 -138 12 -324 91 -447 l38 -58 -212 -340 -212 -339 -101 6 15 + c-238 12 -443 -90 -573 -284 -74 -111 -104 -210 -104 -349 0 -92 4 -123 26 16 + -190 68 -207 215 -354 422 -422 98 -32 243 -37 343 -10 180 48 333 170 410 17 + 327 54 109 72 183 72 290 0 131 -36 249 -111 370 l-22 35 209 335 209 336 144 18 + -1 144 -1 204 -324 c113 -179 207 -330 210 -337 3 -7 -12 -39 -32 -72 -62 19 + -101 -87 -181 -92 -306 -4 -83 -1 -126 11 -175 75 -296 322 -492 621 -493 177 20 + -1 321 59 451 188 127 126 188 272 187 450 -1 240 -136 462 -343 563 -124 61 21 + -192 75 -325 70 l-111 -5 -212 339 -212 340 25 32 c65 85 115 244 115 366 0 22 + 122 -49 278 -114 363 l-26 34 211 339 212 339 144 -1 143 -1 211 -337 211 23 + -338 -20 -27 c-129 -175 -153 -452 -55 -645 86 -170 213 -281 385 -337 92 -30 24 + 280 -33 366 -6 113 35 184 79 272 167 96 96 146 182 175 307 23 97 17 252 -14 25 + 343 -51 149 -182 302 -315 368 -115 57 -149 64 -299 66 l-138 1 -185 295 26 + c-102 162 -197 314 -211 337 l-25 43 20 27 c36 51 82 153 99 216 19 75 22 222 27 + 4 297 -52 220 -246 419 -461 474 -78 19 -185 26 -255 15z"/> 28 + </g> 29 + </svg>
+10
svelte.config.js
··· 1 + import adapter from '@sveltejs/adapter-node'; 2 + 3 + /** @type {import('@sveltejs/kit').Config} */ 4 + const config = { 5 + kit: { 6 + adapter: adapter(), 7 + }, 8 + }; 9 + 10 + export default config;
+6
vite.config.ts
··· 1 + import { sveltekit } from '@sveltejs/kit/vite'; 2 + import { defineConfig } from 'vite'; 3 + 4 + export default defineConfig({ 5 + plugins: [sveltekit()], 6 + });