A minimal starter for ATProto logins in Astro

Compare changes

Choose any two refs to compare.

+2
.env.template
···
+
PORT=4321
+
PUBLIC_URL=
+7
.prettierrc
···
+
{
+
"printWidth": 120,
+
"singleQuote": true,
+
"semi": false,
+
"tabWidth": 2,
+
"useTabs": false
+
}
+48 -17
README.md
···
-
# Astro Starter Kit: Minimal
+
# Astro ATProto OAuth Starter
+
+
A minimal [Astro](https://astro.build) starter template demonstrating OAuth authentication with AT Protocol (ATProto), the decentralized social networking protocol used by Bluesky and other services.
+
+
This starter includes:
+
- Complete OAuth authentication flow using `@atproto/oauth-client-node`
+
- Cookie-based session management
+
- Profile display after authentication
+
- Login/logout endpoints
+
- Tailwind CSS and DaisyUI styling
+
+
## ๐Ÿš€ Getting Started
+
+
1. **Install dependencies:**
+
```sh
+
npm install
+
```
-
```sh
-
npm create astro@latest -- --template minimal
-
```
+
2. **Configure environment variables:**
+
```sh
+
cp .env.template .env
+
```
+
Edit `.env` if you need to change the port (default: 4321) or set a public URL.
-
> ๐Ÿง‘โ€๐Ÿš€ **Seasoned astronaut?** Delete this file. Have fun!
+
3. **Start the development server:**
+
```sh
+
npm run dev
+
```
+
The app will be available at `http://localhost:4321`
-
## ๐Ÿš€ Project Structure
+
4. **Try logging in:**
+
Enter your AT Protocol handle (e.g., `alice.bsky.social`) to authenticate.
-
Inside of your Astro project, you'll see the following folders and files:
+
## ๐Ÿ“ Project Structure
```text
/
โ”œโ”€โ”€ public/
โ”œโ”€โ”€ src/
-
โ”‚ โ””โ”€โ”€ pages/
-
โ”‚ โ””โ”€โ”€ index.astro
+
โ”‚ โ”œโ”€โ”€ lib/
+
โ”‚ โ”‚ โ”œโ”€โ”€ context.ts # OAuth client singleton
+
โ”‚ โ”‚ โ”œโ”€โ”€ oauth.ts # OAuth client configuration
+
โ”‚ โ”‚ โ”œโ”€โ”€ session.ts # Session management
+
โ”‚ โ”‚ โ””โ”€โ”€ storage.ts # Cookie-based stores
+
โ”‚ โ”œโ”€โ”€ pages/
+
โ”‚ โ”‚ โ”œโ”€โ”€ api/
+
โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ login.ts # Login endpoint
+
โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ logout.ts # Logout endpoint
+
โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ oauth/
+
โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ callback.ts # OAuth callback handler
+
โ”‚ โ”‚ โ””โ”€โ”€ index.astro # Main page with login UI
+
โ”‚ โ””โ”€โ”€ styles.css
โ””โ”€โ”€ package.json
```
-
-
Astro looks for `.astro` or `.md` files in the `src/pages/` directory. Each page is exposed as a route based on its file name.
-
-
There's nothing special about `src/components/`, but that's where we like to put any Astro/React/Vue/Svelte/Preact components.
-
-
Any static assets, like images, can be placed in the `public/` directory.
## ๐Ÿงž Commands
···
| `npm run astro ...` | Run CLI commands like `astro add`, `astro check` |
| `npm run astro -- --help` | Get help using the Astro CLI |
-
## ๐Ÿ‘€ Want to learn more?
+
## ๐Ÿ“š Learn More
-
Feel free to check [our documentation](https://docs.astro.build) or jump into our [Discord server](https://astro.build/chat).
+
- [Astro Documentation](https://docs.astro.build)
+
- [AT Protocol Documentation](https://atproto.com)
+
- [@atproto/oauth-client-node](https://github.com/bluesky-social/atproto/tree/main/packages/oauth/oauth-client-node)
+
- [Bluesky](https://bsky.app)
+16 -4
astro.config.mjs
···
-
// @ts-check
-
import { defineConfig } from 'astro/config';
+
import { defineConfig } from "astro/config";
+
import tailwindcss from "@tailwindcss/vite";
+
import node from "@astrojs/node";
-
// https://astro.build/config
-
export default defineConfig({});
+
export default defineConfig({
+
output: "server",
+
adapter: node({
+
mode: "standalone",
+
}),
+
server: {
+
host: "127.0.0.1",
+
port: 4321,
+
},
+
vite: {
+
plugins: [tailwindcss()],
+
},
+
});
+1268 -62
package-lock.json
···
"name": "astro-atproto-starter",
"version": "0.0.1",
"dependencies": {
-
"astro": "^5.15.1"
+
"@astrojs/node": "^9.5.0",
+
"@atproto/api": "^0.17.4",
+
"@atproto/oauth-client-node": "^0.3.10",
+
"@tailwindcss/vite": "^4.1.16",
+
"astro": "^5.15.1",
+
"daisyui": "^5.3.9",
+
"dotenv": "^17.2.3",
+
"tailwindcss": "^4.1.16"
}
},
"node_modules/@astrojs/compiler": {
···
"vfile": "^6.0.3"
}
},
+
"node_modules/@astrojs/node": {
+
"version": "9.5.0",
+
"resolved": "https://registry.npmjs.org/@astrojs/node/-/node-9.5.0.tgz",
+
"integrity": "sha512-x1whLIatmCefaqJA8FjfI+P6FStF+bqmmrib0OUGM1M3cZhAXKLgPx6UF2AzQ3JgpXgCWYM24MHtraPvZhhyLQ==",
+
"license": "MIT",
+
"dependencies": {
+
"@astrojs/internal-helpers": "0.7.4",
+
"send": "^1.2.0",
+
"server-destroy": "^1.0.1"
+
},
+
"peerDependencies": {
+
"astro": "^5.14.3"
+
}
+
},
"node_modules/@astrojs/prism": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/@astrojs/prism/-/prism-3.3.0.tgz",
···
},
"engines": {
"node": "18.20.8 || ^20.3.0 || >=22.0.0"
+
}
+
},
+
"node_modules/@atproto-labs/did-resolver": {
+
"version": "0.2.2",
+
"resolved": "https://registry.npmjs.org/@atproto-labs/did-resolver/-/did-resolver-0.2.2.tgz",
+
"integrity": "sha512-ca2B7xR43tVoQ8XxBvha58DXwIH8cIyKQl6lpOKGkPUrJuFoO4iCLlDiSDi2Ueh+yE1rMDPP/qveHdajgDX3WQ==",
+
"license": "MIT",
+
"dependencies": {
+
"@atproto-labs/fetch": "0.2.3",
+
"@atproto-labs/pipe": "0.1.1",
+
"@atproto-labs/simple-store": "0.3.0",
+
"@atproto-labs/simple-store-memory": "0.1.4",
+
"@atproto/did": "0.2.1",
+
"zod": "^3.23.8"
+
}
+
},
+
"node_modules/@atproto-labs/fetch": {
+
"version": "0.2.3",
+
"resolved": "https://registry.npmjs.org/@atproto-labs/fetch/-/fetch-0.2.3.tgz",
+
"integrity": "sha512-NZtbJOCbxKUFRFKMpamT38PUQMY0hX0p7TG5AEYOPhZKZEP7dHZ1K2s1aB8MdVH0qxmqX7nQleNrrvLf09Zfdw==",
+
"license": "MIT",
+
"dependencies": {
+
"@atproto-labs/pipe": "0.1.1"
+
}
+
},
+
"node_modules/@atproto-labs/fetch-node": {
+
"version": "0.2.0",
+
"resolved": "https://registry.npmjs.org/@atproto-labs/fetch-node/-/fetch-node-0.2.0.tgz",
+
"integrity": "sha512-Krq09nH/aeoiU2s9xdHA0FjTEFWG9B5FFenipv1iRixCcPc7V3DhTNDawxG9gI8Ny0k4dBVS9WTRN/IDzBx86Q==",
+
"license": "MIT",
+
"dependencies": {
+
"@atproto-labs/fetch": "0.2.3",
+
"@atproto-labs/pipe": "0.1.1",
+
"ipaddr.js": "^2.1.0",
+
"undici": "^6.14.1"
+
},
+
"engines": {
+
"node": ">=18.7.0"
+
}
+
},
+
"node_modules/@atproto-labs/handle-resolver": {
+
"version": "0.3.2",
+
"resolved": "https://registry.npmjs.org/@atproto-labs/handle-resolver/-/handle-resolver-0.3.2.tgz",
+
"integrity": "sha512-KIerCzh3qb+zZoqWbIvTlvBY0XPq0r56kwViaJY/LTe/3oPO2JaqlYKS/F4dByWBhHK6YoUOJ0sWrh6PMJl40A==",
+
"license": "MIT",
+
"dependencies": {
+
"@atproto-labs/simple-store": "0.3.0",
+
"@atproto-labs/simple-store-memory": "0.1.4",
+
"@atproto/did": "0.2.1",
+
"zod": "^3.23.8"
+
}
+
},
+
"node_modules/@atproto-labs/handle-resolver-node": {
+
"version": "0.1.21",
+
"resolved": "https://registry.npmjs.org/@atproto-labs/handle-resolver-node/-/handle-resolver-node-0.1.21.tgz",
+
"integrity": "sha512-fuJy5Px5pGF3lJX/ATdurbT8tbmaFWtf+PPxAQDFy7ot2no3t+iaAgymhyxYymrssOuWs6BwOP8tyF3VrfdwtQ==",
+
"license": "MIT",
+
"dependencies": {
+
"@atproto-labs/fetch-node": "0.2.0",
+
"@atproto-labs/handle-resolver": "0.3.2",
+
"@atproto/did": "0.2.1"
+
},
+
"engines": {
+
"node": ">=18.7.0"
+
}
+
},
+
"node_modules/@atproto-labs/identity-resolver": {
+
"version": "0.3.2",
+
"resolved": "https://registry.npmjs.org/@atproto-labs/identity-resolver/-/identity-resolver-0.3.2.tgz",
+
"integrity": "sha512-MYxO9pe0WsFyi5HFdKAwqIqHfiF2kBPoVhAIuH/4PYHzGr799ED47xLhNMxR3ZUYrJm5+TQzWXypGZ0Btw1Ffw==",
+
"license": "MIT",
+
"dependencies": {
+
"@atproto-labs/did-resolver": "0.2.2",
+
"@atproto-labs/handle-resolver": "0.3.2"
+
}
+
},
+
"node_modules/@atproto-labs/pipe": {
+
"version": "0.1.1",
+
"resolved": "https://registry.npmjs.org/@atproto-labs/pipe/-/pipe-0.1.1.tgz",
+
"integrity": "sha512-hdNw2oUs2B6BN1lp+32pF7cp8EMKuIN5Qok2Vvv/aOpG/3tNSJ9YkvfI0k6Zd188LeDDYRUpYpxcoFIcGH/FNg==",
+
"license": "MIT"
+
},
+
"node_modules/@atproto-labs/simple-store": {
+
"version": "0.3.0",
+
"resolved": "https://registry.npmjs.org/@atproto-labs/simple-store/-/simple-store-0.3.0.tgz",
+
"integrity": "sha512-nOb6ONKBRJHRlukW1sVawUkBqReLlLx6hT35VS3imaNPwiXDxLnTK7lxw3Lrl9k5yugSBDQAkZAq3MPTEFSUBQ==",
+
"license": "MIT"
+
},
+
"node_modules/@atproto-labs/simple-store-memory": {
+
"version": "0.1.4",
+
"resolved": "https://registry.npmjs.org/@atproto-labs/simple-store-memory/-/simple-store-memory-0.1.4.tgz",
+
"integrity": "sha512-3mKY4dP8I7yKPFj9VKpYyCRzGJOi5CEpOLPlRhoJyLmgs3J4RzDrjn323Oakjz2Aj2JzRU/AIvWRAZVhpYNJHw==",
+
"license": "MIT",
+
"dependencies": {
+
"@atproto-labs/simple-store": "0.3.0",
+
"lru-cache": "^10.2.0"
+
}
+
},
+
"node_modules/@atproto/api": {
+
"version": "0.17.6",
+
"resolved": "https://registry.npmjs.org/@atproto/api/-/api-0.17.6.tgz",
+
"integrity": "sha512-0iYCD8+LOsHjHjwJcqGPfJN/h4b+IpU3GjOV0TSLk0XdCaxpHBKNu3wgCJVst4DhVjXcgsr2qQoRZ3Jja2LupA==",
+
"license": "MIT",
+
"dependencies": {
+
"@atproto/common-web": "^0.4.3",
+
"@atproto/lexicon": "^0.5.1",
+
"@atproto/syntax": "^0.4.1",
+
"@atproto/xrpc": "^0.7.5",
+
"await-lock": "^2.2.2",
+
"multiformats": "^9.9.0",
+
"tlds": "^1.234.0",
+
"zod": "^3.23.8"
+
}
+
},
+
"node_modules/@atproto/common-web": {
+
"version": "0.4.3",
+
"resolved": "https://registry.npmjs.org/@atproto/common-web/-/common-web-0.4.3.tgz",
+
"integrity": "sha512-nRDINmSe4VycJzPo6fP/hEltBcULFxt9Kw7fQk6405FyAWZiTluYHlXOnU7GkQfeUK44OENG1qFTBcmCJ7e8pg==",
+
"license": "MIT",
+
"dependencies": {
+
"graphemer": "^1.4.0",
+
"multiformats": "^9.9.0",
+
"uint8arrays": "3.0.0",
+
"zod": "^3.23.8"
+
}
+
},
+
"node_modules/@atproto/did": {
+
"version": "0.2.1",
+
"resolved": "https://registry.npmjs.org/@atproto/did/-/did-0.2.1.tgz",
+
"integrity": "sha512-1i5BTU2GnBaaeYWhxUOnuEKFVq9euT5+dQPFabHpa927BlJ54PmLGyBBaOI7/NbLmN5HWwBa18SBkMpg3jGZRA==",
+
"license": "MIT",
+
"dependencies": {
+
"zod": "^3.23.8"
+
}
+
},
+
"node_modules/@atproto/jwk": {
+
"version": "0.6.0",
+
"resolved": "https://registry.npmjs.org/@atproto/jwk/-/jwk-0.6.0.tgz",
+
"integrity": "sha512-bDoJPvt7TrQVi/rBfBrSSpGykhtIriKxeYCYQTiPRKFfyRhbgpElF0wPXADjIswnbzZdOwbY63az4E/CFVT3Tw==",
+
"license": "MIT",
+
"dependencies": {
+
"multiformats": "^9.9.0",
+
"zod": "^3.23.8"
+
}
+
},
+
"node_modules/@atproto/jwk-jose": {
+
"version": "0.1.11",
+
"resolved": "https://registry.npmjs.org/@atproto/jwk-jose/-/jwk-jose-0.1.11.tgz",
+
"integrity": "sha512-i4Fnr2sTBYmMmHXl7NJh8GrCH+tDQEVWrcDMDnV5DjJfkgT17wIqvojIw9SNbSL4Uf0OtfEv6AgG0A+mgh8b5Q==",
+
"license": "MIT",
+
"dependencies": {
+
"@atproto/jwk": "0.6.0",
+
"jose": "^5.2.0"
+
}
+
},
+
"node_modules/@atproto/jwk-webcrypto": {
+
"version": "0.2.0",
+
"resolved": "https://registry.npmjs.org/@atproto/jwk-webcrypto/-/jwk-webcrypto-0.2.0.tgz",
+
"integrity": "sha512-UmgRrrEAkWvxwhlwe30UmDOdTEFidlIzBC7C3cCbeJMcBN1x8B3KH+crXrsTqfWQBG58mXgt8wgSK3Kxs2LhFg==",
+
"license": "MIT",
+
"dependencies": {
+
"@atproto/jwk": "0.6.0",
+
"@atproto/jwk-jose": "0.1.11",
+
"zod": "^3.23.8"
+
}
+
},
+
"node_modules/@atproto/lexicon": {
+
"version": "0.5.1",
+
"resolved": "https://registry.npmjs.org/@atproto/lexicon/-/lexicon-0.5.1.tgz",
+
"integrity": "sha512-y8AEtYmfgVl4fqFxqXAeGvhesiGkxiy3CWoJIfsFDDdTlZUC8DFnZrYhcqkIop3OlCkkljvpSJi1hbeC1tbi8A==",
+
"license": "MIT",
+
"dependencies": {
+
"@atproto/common-web": "^0.4.3",
+
"@atproto/syntax": "^0.4.1",
+
"iso-datestring-validator": "^2.2.2",
+
"multiformats": "^9.9.0",
+
"zod": "^3.23.8"
+
}
+
},
+
"node_modules/@atproto/oauth-client": {
+
"version": "0.5.8",
+
"resolved": "https://registry.npmjs.org/@atproto/oauth-client/-/oauth-client-0.5.8.tgz",
+
"integrity": "sha512-7YEym6d97+Dd73qGdkQTXi5La8xvCQxwRUDzzlR/NVAARa9a4YP7MCmqBJVeP2anT0By+DSAPyPDLTsxcjIcCg==",
+
"license": "MIT",
+
"dependencies": {
+
"@atproto-labs/did-resolver": "0.2.2",
+
"@atproto-labs/fetch": "0.2.3",
+
"@atproto-labs/handle-resolver": "0.3.2",
+
"@atproto-labs/identity-resolver": "0.3.2",
+
"@atproto-labs/simple-store": "0.3.0",
+
"@atproto-labs/simple-store-memory": "0.1.4",
+
"@atproto/did": "0.2.1",
+
"@atproto/jwk": "0.6.0",
+
"@atproto/oauth-types": "0.5.0",
+
"@atproto/xrpc": "0.7.5",
+
"core-js": "^3",
+
"multiformats": "^9.9.0",
+
"zod": "^3.23.8"
+
}
+
},
+
"node_modules/@atproto/oauth-client-node": {
+
"version": "0.3.10",
+
"resolved": "https://registry.npmjs.org/@atproto/oauth-client-node/-/oauth-client-node-0.3.10.tgz",
+
"integrity": "sha512-6khKlJqu1Ed5rt3rzcTD5hymB6JUjKdOHWYXwiphw4inkAIo6GxLCighI4eGOqZorYk2j8ueeTNB6KsgH0kcRw==",
+
"license": "MIT",
+
"dependencies": {
+
"@atproto-labs/did-resolver": "0.2.2",
+
"@atproto-labs/handle-resolver-node": "0.1.21",
+
"@atproto-labs/simple-store": "0.3.0",
+
"@atproto/did": "0.2.1",
+
"@atproto/jwk": "0.6.0",
+
"@atproto/jwk-jose": "0.1.11",
+
"@atproto/jwk-webcrypto": "0.2.0",
+
"@atproto/oauth-client": "0.5.8",
+
"@atproto/oauth-types": "0.5.0"
+
},
+
"engines": {
+
"node": ">=18.7.0"
+
}
+
},
+
"node_modules/@atproto/oauth-types": {
+
"version": "0.5.0",
+
"resolved": "https://registry.npmjs.org/@atproto/oauth-types/-/oauth-types-0.5.0.tgz",
+
"integrity": "sha512-33xz7HcXhbl+XRqbIMVu3GE02iK1nKe2oMWENASsfZEYbCz2b9ZOarOFuwi7g4LKqpGowGp0iRKsQHFcq4SDaQ==",
+
"license": "MIT",
+
"dependencies": {
+
"@atproto/did": "0.2.1",
+
"@atproto/jwk": "0.6.0",
+
"zod": "^3.23.8"
+
}
+
},
+
"node_modules/@atproto/syntax": {
+
"version": "0.4.1",
+
"resolved": "https://registry.npmjs.org/@atproto/syntax/-/syntax-0.4.1.tgz",
+
"integrity": "sha512-CJdImtLAiFO+0z3BWTtxwk6aY5w4t8orHTMVJgkf++QRJWTxPbIFko/0hrkADB7n2EruDxDSeAgfUGehpH6ngw==",
+
"license": "MIT"
+
},
+
"node_modules/@atproto/xrpc": {
+
"version": "0.7.5",
+
"resolved": "https://registry.npmjs.org/@atproto/xrpc/-/xrpc-0.7.5.tgz",
+
"integrity": "sha512-MUYNn5d2hv8yVegRL0ccHvTHAVj5JSnW07bkbiaz96UH45lvYNRVwt44z+yYVnb0/mvBzyD3/ZQ55TRGt7fHkA==",
+
"license": "MIT",
+
"dependencies": {
+
"@atproto/lexicon": "^0.5.1",
+
"zod": "^3.23.8"
}
},
"node_modules/@babel/helper-string-parser": {
···
"url": "https://opencollective.com/libvips"
}
},
+
"node_modules/@jridgewell/gen-mapping": {
+
"version": "0.3.13",
+
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
+
"integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
+
"license": "MIT",
+
"dependencies": {
+
"@jridgewell/sourcemap-codec": "^1.5.0",
+
"@jridgewell/trace-mapping": "^0.3.24"
+
}
+
},
+
"node_modules/@jridgewell/remapping": {
+
"version": "2.3.5",
+
"resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
+
"integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
+
"license": "MIT",
+
"dependencies": {
+
"@jridgewell/gen-mapping": "^0.3.5",
+
"@jridgewell/trace-mapping": "^0.3.24"
+
}
+
},
+
"node_modules/@jridgewell/resolve-uri": {
+
"version": "3.1.2",
+
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+
"license": "MIT",
+
"engines": {
+
"node": ">=6.0.0"
+
}
+
},
"node_modules/@jridgewell/sourcemap-codec": {
"version": "1.5.5",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
"license": "MIT"
+
},
+
"node_modules/@jridgewell/trace-mapping": {
+
"version": "0.3.31",
+
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
+
"integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
+
"license": "MIT",
+
"dependencies": {
+
"@jridgewell/resolve-uri": "^3.1.0",
+
"@jridgewell/sourcemap-codec": "^1.4.14"
+
}
},
"node_modules/@oslojs/encoding": {
"version": "1.1.0",
···
},
"node_modules/@shikijs/core": {
-
"version": "3.13.0",
-
"resolved": "https://registry.npmjs.org/@shikijs/core/-/core-3.13.0.tgz",
-
"integrity": "sha512-3P8rGsg2Eh2qIHekwuQjzWhKI4jV97PhvYjYUzGqjvJfqdQPz+nMlfWahU24GZAyW1FxFI1sYjyhfh5CoLmIUA==",
+
"version": "3.14.0",
+
"resolved": "https://registry.npmjs.org/@shikijs/core/-/core-3.14.0.tgz",
+
"integrity": "sha512-qRSeuP5vlYHCNUIrpEBQFO7vSkR7jn7Kv+5X3FO/zBKVDGQbcnlScD3XhkrHi/R8Ltz0kEjvFR9Szp/XMRbFMw==",
"license": "MIT",
"dependencies": {
-
"@shikijs/types": "3.13.0",
+
"@shikijs/types": "3.14.0",
"@shikijs/vscode-textmate": "^10.0.2",
"@types/hast": "^3.0.4",
"hast-util-to-html": "^9.0.5"
},
"node_modules/@shikijs/engine-javascript": {
-
"version": "3.13.0",
-
"resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-3.13.0.tgz",
-
"integrity": "sha512-Ty7xv32XCp8u0eQt8rItpMs6rU9Ki6LJ1dQOW3V/56PKDcpvfHPnYFbsx5FFUP2Yim34m/UkazidamMNVR4vKg==",
+
"version": "3.14.0",
+
"resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-3.14.0.tgz",
+
"integrity": "sha512-3v1kAXI2TsWQuwv86cREH/+FK9Pjw3dorVEykzQDhwrZj0lwsHYlfyARaKmn6vr5Gasf8aeVpb8JkzeWspxOLQ==",
"license": "MIT",
"dependencies": {
-
"@shikijs/types": "3.13.0",
+
"@shikijs/types": "3.14.0",
"@shikijs/vscode-textmate": "^10.0.2",
"oniguruma-to-es": "^4.3.3"
},
"node_modules/@shikijs/engine-oniguruma": {
-
"version": "3.13.0",
-
"resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-3.13.0.tgz",
-
"integrity": "sha512-O42rBGr4UDSlhT2ZFMxqM7QzIU+IcpoTMzb3W7AlziI1ZF7R8eS2M0yt5Ry35nnnTX/LTLXFPUjRFCIW+Operg==",
+
"version": "3.14.0",
+
"resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-3.14.0.tgz",
+
"integrity": "sha512-TNcYTYMbJyy+ZjzWtt0bG5y4YyMIWC2nyePz+CFMWqm+HnZZyy9SWMgo8Z6KBJVIZnx8XUXS8U2afO6Y0g1Oug==",
"license": "MIT",
"dependencies": {
-
"@shikijs/types": "3.13.0",
+
"@shikijs/types": "3.14.0",
"@shikijs/vscode-textmate": "^10.0.2"
},
"node_modules/@shikijs/langs": {
-
"version": "3.13.0",
-
"resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-3.13.0.tgz",
-
"integrity": "sha512-672c3WAETDYHwrRP0yLy3W1QYB89Hbpj+pO4KhxK6FzIrDI2FoEXNiNCut6BQmEApYLfuYfpgOZaqbY+E9b8wQ==",
+
"version": "3.14.0",
+
"resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-3.14.0.tgz",
+
"integrity": "sha512-DIB2EQY7yPX1/ZH7lMcwrK5pl+ZkP/xoSpUzg9YC8R+evRCCiSQ7yyrvEyBsMnfZq4eBzLzBlugMyTAf13+pzg==",
"license": "MIT",
"dependencies": {
-
"@shikijs/types": "3.13.0"
+
"@shikijs/types": "3.14.0"
},
"node_modules/@shikijs/themes": {
-
"version": "3.13.0",
-
"resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-3.13.0.tgz",
-
"integrity": "sha512-Vxw1Nm1/Od8jyA7QuAenaV78BG2nSr3/gCGdBkLpfLscddCkzkL36Q5b67SrLLfvAJTOUzW39x4FHVCFriPVgg==",
+
"version": "3.14.0",
+
"resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-3.14.0.tgz",
+
"integrity": "sha512-fAo/OnfWckNmv4uBoUu6dSlkcBc+SA1xzj5oUSaz5z3KqHtEbUypg/9xxgJARtM6+7RVm0Q6Xnty41xA1ma1IA==",
"license": "MIT",
"dependencies": {
-
"@shikijs/types": "3.13.0"
+
"@shikijs/types": "3.14.0"
},
"node_modules/@shikijs/types": {
-
"version": "3.13.0",
-
"resolved": "https://registry.npmjs.org/@shikijs/types/-/types-3.13.0.tgz",
-
"integrity": "sha512-oM9P+NCFri/mmQ8LoFGVfVyemm5Hi27330zuOBp0annwJdKH1kOLndw3zCtAVDehPLg9fKqoEx3Ht/wNZxolfw==",
+
"version": "3.14.0",
+
"resolved": "https://registry.npmjs.org/@shikijs/types/-/types-3.14.0.tgz",
+
"integrity": "sha512-bQGgC6vrY8U/9ObG1Z/vTro+uclbjjD/uG58RvfxKZVD5p9Yc1ka3tVyEFy7BNJLzxuWyHH5NWynP9zZZS59eQ==",
"license": "MIT",
"dependencies": {
"@shikijs/vscode-textmate": "^10.0.2",
···
"tslib": "^2.8.0"
},
+
"node_modules/@tailwindcss/node": {
+
"version": "4.1.16",
+
"resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.16.tgz",
+
"integrity": "sha512-BX5iaSsloNuvKNHRN3k2RcCuTEgASTo77mofW0vmeHkfrDWaoFAFvNHpEgtu0eqyypcyiBkDWzSMxJhp3AUVcw==",
+
"license": "MIT",
+
"dependencies": {
+
"@jridgewell/remapping": "^2.3.4",
+
"enhanced-resolve": "^5.18.3",
+
"jiti": "^2.6.1",
+
"lightningcss": "1.30.2",
+
"magic-string": "^0.30.19",
+
"source-map-js": "^1.2.1",
+
"tailwindcss": "4.1.16"
+
}
+
},
+
"node_modules/@tailwindcss/oxide": {
+
"version": "4.1.16",
+
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.16.tgz",
+
"integrity": "sha512-2OSv52FRuhdlgyOQqgtQHuCgXnS8nFSYRp2tJ+4WZXKgTxqPy7SMSls8c3mPT5pkZ17SBToGM5LHEJBO7miEdg==",
+
"license": "MIT",
+
"engines": {
+
"node": ">= 10"
+
},
+
"optionalDependencies": {
+
"@tailwindcss/oxide-android-arm64": "4.1.16",
+
"@tailwindcss/oxide-darwin-arm64": "4.1.16",
+
"@tailwindcss/oxide-darwin-x64": "4.1.16",
+
"@tailwindcss/oxide-freebsd-x64": "4.1.16",
+
"@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.16",
+
"@tailwindcss/oxide-linux-arm64-gnu": "4.1.16",
+
"@tailwindcss/oxide-linux-arm64-musl": "4.1.16",
+
"@tailwindcss/oxide-linux-x64-gnu": "4.1.16",
+
"@tailwindcss/oxide-linux-x64-musl": "4.1.16",
+
"@tailwindcss/oxide-wasm32-wasi": "4.1.16",
+
"@tailwindcss/oxide-win32-arm64-msvc": "4.1.16",
+
"@tailwindcss/oxide-win32-x64-msvc": "4.1.16"
+
}
+
},
+
"node_modules/@tailwindcss/oxide-android-arm64": {
+
"version": "4.1.16",
+
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.16.tgz",
+
"integrity": "sha512-8+ctzkjHgwDJ5caq9IqRSgsP70xhdhJvm+oueS/yhD5ixLhqTw9fSL1OurzMUhBwE5zK26FXLCz2f/RtkISqHA==",
+
"cpu": [
+
"arm64"
+
],
+
"license": "MIT",
+
"optional": true,
+
"os": [
+
"android"
+
],
+
"engines": {
+
"node": ">= 10"
+
}
+
},
+
"node_modules/@tailwindcss/oxide-darwin-arm64": {
+
"version": "4.1.16",
+
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.16.tgz",
+
"integrity": "sha512-C3oZy5042v2FOALBZtY0JTDnGNdS6w7DxL/odvSny17ORUnaRKhyTse8xYi3yKGyfnTUOdavRCdmc8QqJYwFKA==",
+
"cpu": [
+
"arm64"
+
],
+
"license": "MIT",
+
"optional": true,
+
"os": [
+
"darwin"
+
],
+
"engines": {
+
"node": ">= 10"
+
}
+
},
+
"node_modules/@tailwindcss/oxide-darwin-x64": {
+
"version": "4.1.16",
+
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.16.tgz",
+
"integrity": "sha512-vjrl/1Ub9+JwU6BP0emgipGjowzYZMjbWCDqwA2Z4vCa+HBSpP4v6U2ddejcHsolsYxwL5r4bPNoamlV0xDdLg==",
+
"cpu": [
+
"x64"
+
],
+
"license": "MIT",
+
"optional": true,
+
"os": [
+
"darwin"
+
],
+
"engines": {
+
"node": ">= 10"
+
}
+
},
+
"node_modules/@tailwindcss/oxide-freebsd-x64": {
+
"version": "4.1.16",
+
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.16.tgz",
+
"integrity": "sha512-TSMpPYpQLm+aR1wW5rKuUuEruc/oOX3C7H0BTnPDn7W/eMw8W+MRMpiypKMkXZfwH8wqPIRKppuZoedTtNj2tg==",
+
"cpu": [
+
"x64"
+
],
+
"license": "MIT",
+
"optional": true,
+
"os": [
+
"freebsd"
+
],
+
"engines": {
+
"node": ">= 10"
+
}
+
},
+
"node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": {
+
"version": "4.1.16",
+
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.16.tgz",
+
"integrity": "sha512-p0GGfRg/w0sdsFKBjMYvvKIiKy/LNWLWgV/plR4lUgrsxFAoQBFrXkZ4C0w8IOXfslB9vHK/JGASWD2IefIpvw==",
+
"cpu": [
+
"arm"
+
],
+
"license": "MIT",
+
"optional": true,
+
"os": [
+
"linux"
+
],
+
"engines": {
+
"node": ">= 10"
+
}
+
},
+
"node_modules/@tailwindcss/oxide-linux-arm64-gnu": {
+
"version": "4.1.16",
+
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.16.tgz",
+
"integrity": "sha512-DoixyMmTNO19rwRPdqviTrG1rYzpxgyYJl8RgQvdAQUzxC1ToLRqtNJpU/ATURSKgIg6uerPw2feW0aS8SNr/w==",
+
"cpu": [
+
"arm64"
+
],
+
"license": "MIT",
+
"optional": true,
+
"os": [
+
"linux"
+
],
+
"engines": {
+
"node": ">= 10"
+
}
+
},
+
"node_modules/@tailwindcss/oxide-linux-arm64-musl": {
+
"version": "4.1.16",
+
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.16.tgz",
+
"integrity": "sha512-H81UXMa9hJhWhaAUca6bU2wm5RRFpuHImrwXBUvPbYb+3jo32I9VIwpOX6hms0fPmA6f2pGVlybO6qU8pF4fzQ==",
+
"cpu": [
+
"arm64"
+
],
+
"license": "MIT",
+
"optional": true,
+
"os": [
+
"linux"
+
],
+
"engines": {
+
"node": ">= 10"
+
}
+
},
+
"node_modules/@tailwindcss/oxide-linux-x64-gnu": {
+
"version": "4.1.16",
+
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.16.tgz",
+
"integrity": "sha512-ZGHQxDtFC2/ruo7t99Qo2TTIvOERULPl5l0K1g0oK6b5PGqjYMga+FcY1wIUnrUxY56h28FxybtDEla+ICOyew==",
+
"cpu": [
+
"x64"
+
],
+
"license": "MIT",
+
"optional": true,
+
"os": [
+
"linux"
+
],
+
"engines": {
+
"node": ">= 10"
+
}
+
},
+
"node_modules/@tailwindcss/oxide-linux-x64-musl": {
+
"version": "4.1.16",
+
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.16.tgz",
+
"integrity": "sha512-Oi1tAaa0rcKf1Og9MzKeINZzMLPbhxvm7rno5/zuP1WYmpiG0bEHq4AcRUiG2165/WUzvxkW4XDYCscZWbTLZw==",
+
"cpu": [
+
"x64"
+
],
+
"license": "MIT",
+
"optional": true,
+
"os": [
+
"linux"
+
],
+
"engines": {
+
"node": ">= 10"
+
}
+
},
+
"node_modules/@tailwindcss/oxide-wasm32-wasi": {
+
"version": "4.1.16",
+
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.16.tgz",
+
"integrity": "sha512-B01u/b8LteGRwucIBmCQ07FVXLzImWESAIMcUU6nvFt/tYsQ6IHz8DmZ5KtvmwxD+iTYBtM1xwoGXswnlu9v0Q==",
+
"bundleDependencies": [
+
"@napi-rs/wasm-runtime",
+
"@emnapi/core",
+
"@emnapi/runtime",
+
"@tybys/wasm-util",
+
"@emnapi/wasi-threads",
+
"tslib"
+
],
+
"cpu": [
+
"wasm32"
+
],
+
"license": "MIT",
+
"optional": true,
+
"dependencies": {
+
"@emnapi/core": "^1.5.0",
+
"@emnapi/runtime": "^1.5.0",
+
"@emnapi/wasi-threads": "^1.1.0",
+
"@napi-rs/wasm-runtime": "^1.0.7",
+
"@tybys/wasm-util": "^0.10.1",
+
"tslib": "^2.4.0"
+
},
+
"engines": {
+
"node": ">=14.0.0"
+
}
+
},
+
"node_modules/@tailwindcss/oxide-win32-arm64-msvc": {
+
"version": "4.1.16",
+
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.16.tgz",
+
"integrity": "sha512-zX+Q8sSkGj6HKRTMJXuPvOcP8XfYON24zJBRPlszcH1Np7xuHXhWn8qfFjIujVzvH3BHU+16jBXwgpl20i+v9A==",
+
"cpu": [
+
"arm64"
+
],
+
"license": "MIT",
+
"optional": true,
+
"os": [
+
"win32"
+
],
+
"engines": {
+
"node": ">= 10"
+
}
+
},
+
"node_modules/@tailwindcss/oxide-win32-x64-msvc": {
+
"version": "4.1.16",
+
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.16.tgz",
+
"integrity": "sha512-m5dDFJUEejbFqP+UXVstd4W/wnxA4F61q8SoL+mqTypId2T2ZpuxosNSgowiCnLp2+Z+rivdU0AqpfgiD7yCBg==",
+
"cpu": [
+
"x64"
+
],
+
"license": "MIT",
+
"optional": true,
+
"os": [
+
"win32"
+
],
+
"engines": {
+
"node": ">= 10"
+
}
+
},
+
"node_modules/@tailwindcss/vite": {
+
"version": "4.1.16",
+
"resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.16.tgz",
+
"integrity": "sha512-bbguNBcDxsRmi9nnlWJxhfDWamY3lmcyACHcdO1crxfzuLpOhHLLtEIN/nCbbAtj5rchUgQD17QVAKi1f7IsKg==",
+
"license": "MIT",
+
"dependencies": {
+
"@tailwindcss/node": "4.1.16",
+
"@tailwindcss/oxide": "4.1.16",
+
"tailwindcss": "4.1.16"
+
},
+
"peerDependencies": {
+
"vite": "^5.2.0 || ^6 || ^7"
+
}
+
},
"node_modules/@types/debug": {
"version": "4.1.12",
"resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz",
···
},
"node_modules/@types/node": {
-
"version": "24.9.1",
-
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.9.1.tgz",
-
"integrity": "sha512-QoiaXANRkSXK6p0Duvt56W208du4P9Uye9hWLWgGMDTEoKPhuenzNcC4vGUmrNkiOKTlIrBoyNQYNpSwfEZXSg==",
+
"version": "24.9.2",
+
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.9.2.tgz",
+
"integrity": "sha512-uWN8YqxXxqFMX2RqGOrumsKeti4LlmIMIyV0lgut4jx7KQBcBiW6vkDtIBvHnHIquwNfJhk8v2OtmO8zXWHfPA==",
"license": "MIT",
"dependencies": {
"undici-types": "~7.16.0"
···
},
"node_modules/astro": {
-
"version": "5.15.1",
-
"resolved": "https://registry.npmjs.org/astro/-/astro-5.15.1.tgz",
-
"integrity": "sha512-VM679M1qxOjGo6q3vKYDNDddkALGgMopG93IwbEXd3Buc2xVLuuPj4HNziNugSbPQx5S6UReMp5uzw10EJN81A==",
+
"version": "5.15.2",
+
"resolved": "https://registry.npmjs.org/astro/-/astro-5.15.2.tgz",
+
"integrity": "sha512-xQQ+PiYJ7WpUJrHJpAb52TQAUCFmSR8lAtQr3tFfSIZoTQiEMFx3HITJ01t3eDUpHjja8J6JcYqgAhr9xygKQg==",
"license": "MIT",
"dependencies": {
"@astrojs/compiler": "^2.12.2",
···
"unist-util-visit": "^5.0.0",
"unstorage": "^1.17.0",
"vfile": "^6.0.3",
-
"vite": "^6.3.6",
+
"vite": "^6.4.1",
"vitefu": "^1.1.1",
"xxhash-wasm": "^1.1.0",
"yargs-parser": "^21.1.1",
···
"sharp": "^0.34.0"
},
+
"node_modules/astro/node_modules/vite": {
+
"version": "6.4.1",
+
"resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz",
+
"integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==",
+
"license": "MIT",
+
"dependencies": {
+
"esbuild": "^0.25.0",
+
"fdir": "^6.4.4",
+
"picomatch": "^4.0.2",
+
"postcss": "^8.5.3",
+
"rollup": "^4.34.9",
+
"tinyglobby": "^0.2.13"
+
},
+
"bin": {
+
"vite": "bin/vite.js"
+
},
+
"engines": {
+
"node": "^18.0.0 || ^20.0.0 || >=22.0.0"
+
},
+
"funding": {
+
"url": "https://github.com/vitejs/vite?sponsor=1"
+
},
+
"optionalDependencies": {
+
"fsevents": "~2.3.3"
+
},
+
"peerDependencies": {
+
"@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0",
+
"jiti": ">=1.21.0",
+
"less": "*",
+
"lightningcss": "^1.21.0",
+
"sass": "*",
+
"sass-embedded": "*",
+
"stylus": "*",
+
"sugarss": "*",
+
"terser": "^5.16.0",
+
"tsx": "^4.8.1",
+
"yaml": "^2.4.2"
+
},
+
"peerDependenciesMeta": {
+
"@types/node": {
+
"optional": true
+
},
+
"jiti": {
+
"optional": true
+
},
+
"less": {
+
"optional": true
+
},
+
"lightningcss": {
+
"optional": true
+
},
+
"sass": {
+
"optional": true
+
},
+
"sass-embedded": {
+
"optional": true
+
},
+
"stylus": {
+
"optional": true
+
},
+
"sugarss": {
+
"optional": true
+
},
+
"terser": {
+
"optional": true
+
},
+
"tsx": {
+
"optional": true
+
},
+
"yaml": {
+
"optional": true
+
}
+
}
+
},
+
"node_modules/await-lock": {
+
"version": "2.2.2",
+
"resolved": "https://registry.npmjs.org/await-lock/-/await-lock-2.2.2.tgz",
+
"integrity": "sha512-aDczADvlvTGajTDjcjpJMqRkOF6Qdz3YbPZm/PyW6tKPkx2hlYBzxMhEywM/tU72HrVZjgl5VCdRuMlA7pZ8Gw==",
+
"license": "MIT"
+
},
"node_modules/axobject-query": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz",
···
"integrity": "sha512-+W7VmiVINB+ywl1HGXJXmrqkOhpKrIiVZV6tQuV54ZyQC7MMuBt81Vc336GMLoHBq5hV/F9eXgt5Mnx0Rha5Fg==",
"license": "MIT"
},
+
"node_modules/core-js": {
+
"version": "3.46.0",
+
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.46.0.tgz",
+
"integrity": "sha512-vDMm9B0xnqqZ8uSBpZ8sNtRtOdmfShrvT6h2TuQGLs0Is+cR0DYbj/KWP6ALVNbWPpqA/qPLoOuppJN07humpA==",
+
"hasInstallScript": true,
+
"license": "MIT",
+
"funding": {
+
"type": "opencollective",
+
"url": "https://opencollective.com/core-js"
+
}
+
},
"node_modules/crossws": {
"version": "0.3.5",
"resolved": "https://registry.npmjs.org/crossws/-/crossws-0.3.5.tgz",
···
"node": ">=4"
},
+
"node_modules/daisyui": {
+
"version": "5.3.10",
+
"resolved": "https://registry.npmjs.org/daisyui/-/daisyui-5.3.10.tgz",
+
"integrity": "sha512-vmjyPmm0hvFhA95KB6uiGmWakziB2pBv6CUcs5Ka/3iMBMn9S+C3SZYx9G9l2JrgTZ1EFn61F/HrPcwaUm2kLQ==",
+
"license": "MIT",
+
"funding": {
+
"url": "https://github.com/saadeghi/daisyui?sponsor=1"
+
}
+
},
"node_modules/debug": {
"version": "4.4.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
···
"integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==",
"license": "MIT"
},
+
"node_modules/depd": {
+
"version": "2.0.0",
+
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
+
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
+
"license": "MIT",
+
"engines": {
+
"node": ">= 0.8"
+
}
+
},
"node_modules/dequal": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
···
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
"integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
"license": "Apache-2.0",
-
"optional": true,
"engines": {
"node": ">=8"
···
"integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==",
"license": "MIT"
},
+
"node_modules/dotenv": {
+
"version": "17.2.3",
+
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.3.tgz",
+
"integrity": "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==",
+
"license": "BSD-2-Clause",
+
"engines": {
+
"node": ">=12"
+
},
+
"funding": {
+
"url": "https://dotenvx.com"
+
}
+
},
"node_modules/dset": {
"version": "3.1.4",
"resolved": "https://registry.npmjs.org/dset/-/dset-3.1.4.tgz",
···
"node": ">=4"
},
+
"node_modules/ee-first": {
+
"version": "1.1.1",
+
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
+
"license": "MIT"
+
},
"node_modules/emoji-regex": {
"version": "10.6.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz",
"integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==",
"license": "MIT"
},
+
"node_modules/encodeurl": {
+
"version": "2.0.0",
+
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
+
"integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
+
"license": "MIT",
+
"engines": {
+
"node": ">= 0.8"
+
}
+
},
+
"node_modules/enhanced-resolve": {
+
"version": "5.18.3",
+
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz",
+
"integrity": "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==",
+
"license": "MIT",
+
"dependencies": {
+
"graceful-fs": "^4.2.4",
+
"tapable": "^2.2.0"
+
},
+
"engines": {
+
"node": ">=10.13.0"
+
}
+
},
"node_modules/entities": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz",
···
"@esbuild/win32-x64": "0.25.11"
},
+
"node_modules/escape-html": {
+
"version": "1.0.3",
+
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
+
"license": "MIT"
+
},
"node_modules/escape-string-regexp": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz",
···
"@types/estree": "^1.0.0"
},
+
"node_modules/etag": {
+
"version": "1.8.1",
+
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+
"integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
+
"license": "MIT",
+
"engines": {
+
"node": ">= 0.6"
+
}
+
},
"node_modules/eventemitter3": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz",
···
"unicode-trie": "^2.0.0"
},
+
"node_modules/fresh": {
+
"version": "2.0.0",
+
"resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz",
+
"integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==",
+
"license": "MIT",
+
"engines": {
+
"node": ">= 0.8"
+
}
+
},
"node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
···
"resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-2.0.0.tgz",
"integrity": "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==",
"license": "ISC"
+
},
+
"node_modules/graceful-fs": {
+
"version": "4.2.11",
+
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
+
"license": "ISC"
+
},
+
"node_modules/graphemer": {
+
"version": "1.4.0",
+
"resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
+
"integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
+
"license": "MIT"
},
"node_modules/h3": {
"version": "1.15.4",
···
"integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==",
"license": "BSD-2-Clause"
},
+
"node_modules/http-errors": {
+
"version": "2.0.0",
+
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
+
"integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
+
"license": "MIT",
+
"dependencies": {
+
"depd": "2.0.0",
+
"inherits": "2.0.4",
+
"setprototypeof": "1.2.0",
+
"statuses": "2.0.1",
+
"toidentifier": "1.0.1"
+
},
+
"engines": {
+
"node": ">= 0.8"
+
}
+
},
+
"node_modules/http-errors/node_modules/statuses": {
+
"version": "2.0.1",
+
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
+
"integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
+
"license": "MIT",
+
"engines": {
+
"node": ">= 0.8"
+
}
+
},
"node_modules/import-meta-resolve": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz",
···
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
+
}
+
},
+
"node_modules/inherits": {
+
"version": "2.0.4",
+
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+
"license": "ISC"
+
},
+
"node_modules/ipaddr.js": {
+
"version": "2.2.0",
+
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz",
+
"integrity": "sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==",
+
"license": "MIT",
+
"engines": {
+
"node": ">= 10"
},
"node_modules/iron-webcrypto": {
···
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
+
}
+
},
+
"node_modules/iso-datestring-validator": {
+
"version": "2.2.2",
+
"resolved": "https://registry.npmjs.org/iso-datestring-validator/-/iso-datestring-validator-2.2.2.tgz",
+
"integrity": "sha512-yLEMkBbLZTlVQqOnQ4FiMujR6T4DEcCb1xizmvXS+OxuhwcbtynoosRzdMA69zZCShCNAbi+gJ71FxZBBXx1SA==",
+
"license": "MIT"
+
},
+
"node_modules/jiti": {
+
"version": "2.6.1",
+
"resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz",
+
"integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==",
+
"license": "MIT",
+
"bin": {
+
"jiti": "lib/jiti-cli.mjs"
+
}
+
},
+
"node_modules/jose": {
+
"version": "5.10.0",
+
"resolved": "https://registry.npmjs.org/jose/-/jose-5.10.0.tgz",
+
"integrity": "sha512-s+3Al/p9g32Iq+oqXxkW//7jk2Vig6FF1CFqzVXoTUXt2qz89YWbL+OwS17NFYEvxC35n0FKeGO2LGYSxeM2Gg==",
+
"license": "MIT",
+
"funding": {
+
"url": "https://github.com/sponsors/panva"
},
"node_modules/js-yaml": {
···
"node": ">=6"
},
+
"node_modules/lightningcss": {
+
"version": "1.30.2",
+
"resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.2.tgz",
+
"integrity": "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==",
+
"license": "MPL-2.0",
+
"dependencies": {
+
"detect-libc": "^2.0.3"
+
},
+
"engines": {
+
"node": ">= 12.0.0"
+
},
+
"funding": {
+
"type": "opencollective",
+
"url": "https://opencollective.com/parcel"
+
},
+
"optionalDependencies": {
+
"lightningcss-android-arm64": "1.30.2",
+
"lightningcss-darwin-arm64": "1.30.2",
+
"lightningcss-darwin-x64": "1.30.2",
+
"lightningcss-freebsd-x64": "1.30.2",
+
"lightningcss-linux-arm-gnueabihf": "1.30.2",
+
"lightningcss-linux-arm64-gnu": "1.30.2",
+
"lightningcss-linux-arm64-musl": "1.30.2",
+
"lightningcss-linux-x64-gnu": "1.30.2",
+
"lightningcss-linux-x64-musl": "1.30.2",
+
"lightningcss-win32-arm64-msvc": "1.30.2",
+
"lightningcss-win32-x64-msvc": "1.30.2"
+
}
+
},
+
"node_modules/lightningcss-android-arm64": {
+
"version": "1.30.2",
+
"resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.30.2.tgz",
+
"integrity": "sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==",
+
"cpu": [
+
"arm64"
+
],
+
"license": "MPL-2.0",
+
"optional": true,
+
"os": [
+
"android"
+
],
+
"engines": {
+
"node": ">= 12.0.0"
+
},
+
"funding": {
+
"type": "opencollective",
+
"url": "https://opencollective.com/parcel"
+
}
+
},
+
"node_modules/lightningcss-darwin-arm64": {
+
"version": "1.30.2",
+
"resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.2.tgz",
+
"integrity": "sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA==",
+
"cpu": [
+
"arm64"
+
],
+
"license": "MPL-2.0",
+
"optional": true,
+
"os": [
+
"darwin"
+
],
+
"engines": {
+
"node": ">= 12.0.0"
+
},
+
"funding": {
+
"type": "opencollective",
+
"url": "https://opencollective.com/parcel"
+
}
+
},
+
"node_modules/lightningcss-darwin-x64": {
+
"version": "1.30.2",
+
"resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.2.tgz",
+
"integrity": "sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ==",
+
"cpu": [
+
"x64"
+
],
+
"license": "MPL-2.0",
+
"optional": true,
+
"os": [
+
"darwin"
+
],
+
"engines": {
+
"node": ">= 12.0.0"
+
},
+
"funding": {
+
"type": "opencollective",
+
"url": "https://opencollective.com/parcel"
+
}
+
},
+
"node_modules/lightningcss-freebsd-x64": {
+
"version": "1.30.2",
+
"resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.2.tgz",
+
"integrity": "sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA==",
+
"cpu": [
+
"x64"
+
],
+
"license": "MPL-2.0",
+
"optional": true,
+
"os": [
+
"freebsd"
+
],
+
"engines": {
+
"node": ">= 12.0.0"
+
},
+
"funding": {
+
"type": "opencollective",
+
"url": "https://opencollective.com/parcel"
+
}
+
},
+
"node_modules/lightningcss-linux-arm-gnueabihf": {
+
"version": "1.30.2",
+
"resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.2.tgz",
+
"integrity": "sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA==",
+
"cpu": [
+
"arm"
+
],
+
"license": "MPL-2.0",
+
"optional": true,
+
"os": [
+
"linux"
+
],
+
"engines": {
+
"node": ">= 12.0.0"
+
},
+
"funding": {
+
"type": "opencollective",
+
"url": "https://opencollective.com/parcel"
+
}
+
},
+
"node_modules/lightningcss-linux-arm64-gnu": {
+
"version": "1.30.2",
+
"resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.2.tgz",
+
"integrity": "sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A==",
+
"cpu": [
+
"arm64"
+
],
+
"license": "MPL-2.0",
+
"optional": true,
+
"os": [
+
"linux"
+
],
+
"engines": {
+
"node": ">= 12.0.0"
+
},
+
"funding": {
+
"type": "opencollective",
+
"url": "https://opencollective.com/parcel"
+
}
+
},
+
"node_modules/lightningcss-linux-arm64-musl": {
+
"version": "1.30.2",
+
"resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.2.tgz",
+
"integrity": "sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==",
+
"cpu": [
+
"arm64"
+
],
+
"license": "MPL-2.0",
+
"optional": true,
+
"os": [
+
"linux"
+
],
+
"engines": {
+
"node": ">= 12.0.0"
+
},
+
"funding": {
+
"type": "opencollective",
+
"url": "https://opencollective.com/parcel"
+
}
+
},
+
"node_modules/lightningcss-linux-x64-gnu": {
+
"version": "1.30.2",
+
"resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.2.tgz",
+
"integrity": "sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==",
+
"cpu": [
+
"x64"
+
],
+
"license": "MPL-2.0",
+
"optional": true,
+
"os": [
+
"linux"
+
],
+
"engines": {
+
"node": ">= 12.0.0"
+
},
+
"funding": {
+
"type": "opencollective",
+
"url": "https://opencollective.com/parcel"
+
}
+
},
+
"node_modules/lightningcss-linux-x64-musl": {
+
"version": "1.30.2",
+
"resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.2.tgz",
+
"integrity": "sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==",
+
"cpu": [
+
"x64"
+
],
+
"license": "MPL-2.0",
+
"optional": true,
+
"os": [
+
"linux"
+
],
+
"engines": {
+
"node": ">= 12.0.0"
+
},
+
"funding": {
+
"type": "opencollective",
+
"url": "https://opencollective.com/parcel"
+
}
+
},
+
"node_modules/lightningcss-win32-arm64-msvc": {
+
"version": "1.30.2",
+
"resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.2.tgz",
+
"integrity": "sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==",
+
"cpu": [
+
"arm64"
+
],
+
"license": "MPL-2.0",
+
"optional": true,
+
"os": [
+
"win32"
+
],
+
"engines": {
+
"node": ">= 12.0.0"
+
},
+
"funding": {
+
"type": "opencollective",
+
"url": "https://opencollective.com/parcel"
+
}
+
},
+
"node_modules/lightningcss-win32-x64-msvc": {
+
"version": "1.30.2",
+
"resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.2.tgz",
+
"integrity": "sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw==",
+
"cpu": [
+
"x64"
+
],
+
"license": "MPL-2.0",
+
"optional": true,
+
"os": [
+
"win32"
+
],
+
"engines": {
+
"node": ">= 12.0.0"
+
},
+
"funding": {
+
"type": "opencollective",
+
"url": "https://opencollective.com/parcel"
+
}
+
},
"node_modules/longest-streak": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz",
···
],
"license": "MIT"
},
+
"node_modules/mime-db": {
+
"version": "1.54.0",
+
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
+
"integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
+
"license": "MIT",
+
"engines": {
+
"node": ">= 0.6"
+
}
+
},
+
"node_modules/mime-types": {
+
"version": "3.0.1",
+
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz",
+
"integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==",
+
"license": "MIT",
+
"dependencies": {
+
"mime-db": "^1.54.0"
+
},
+
"engines": {
+
"node": ">= 0.6"
+
}
+
},
"node_modules/mrmime": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz",
···
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"license": "MIT"
+
},
+
"node_modules/multiformats": {
+
"version": "9.9.0",
+
"resolved": "https://registry.npmjs.org/multiformats/-/multiformats-9.9.0.tgz",
+
"integrity": "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg==",
+
"license": "(Apache-2.0 AND MIT)"
},
"node_modules/nanoid": {
"version": "3.3.11",
···
},
"node_modules/ofetch": {
-
"version": "1.4.1",
-
"resolved": "https://registry.npmjs.org/ofetch/-/ofetch-1.4.1.tgz",
-
"integrity": "sha512-QZj2DfGplQAr2oj9KzceK9Hwz6Whxazmn85yYeVuS3u9XTMOGMRx0kO95MQ+vLsj/S/NwBDMMLU5hpxvI6Tklw==",
+
"version": "1.5.0",
+
"resolved": "https://registry.npmjs.org/ofetch/-/ofetch-1.5.0.tgz",
+
"integrity": "sha512-A7llJ7eZyziA5xq9//3ZurA8OhFqtS99K5/V1sLBJ5j137CM/OAjlbA/TEJXBuOWwOfLqih+oH5U3ran4za1FQ==",
"license": "MIT",
"dependencies": {
-
"destr": "^2.0.3",
-
"node-fetch-native": "^1.6.4",
-
"ufo": "^1.5.4"
+
"destr": "^2.0.5",
+
"node-fetch-native": "^1.6.7",
+
"ufo": "^1.6.1"
},
"node_modules/ohash": {
···
"integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==",
"license": "MIT"
},
+
"node_modules/on-finished": {
+
"version": "2.4.1",
+
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
+
"integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
+
"license": "MIT",
+
"dependencies": {
+
"ee-first": "1.1.1"
+
},
+
"engines": {
+
"node": ">= 0.8"
+
}
+
},
"node_modules/oniguruma-parser": {
"version": "0.12.1",
"resolved": "https://registry.npmjs.org/oniguruma-parser/-/oniguruma-parser-0.12.1.tgz",
···
"resolved": "https://registry.npmjs.org/radix3/-/radix3-1.1.2.tgz",
"integrity": "sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==",
"license": "MIT"
+
},
+
"node_modules/range-parser": {
+
"version": "1.2.1",
+
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
+
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
+
"license": "MIT",
+
"engines": {
+
"node": ">= 0.6"
+
}
},
"node_modules/readdirp": {
"version": "4.1.2",
···
"node": ">=10"
},
+
"node_modules/send": {
+
"version": "1.2.0",
+
"resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz",
+
"integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==",
+
"license": "MIT",
+
"dependencies": {
+
"debug": "^4.3.5",
+
"encodeurl": "^2.0.0",
+
"escape-html": "^1.0.3",
+
"etag": "^1.8.1",
+
"fresh": "^2.0.0",
+
"http-errors": "^2.0.0",
+
"mime-types": "^3.0.1",
+
"ms": "^2.1.3",
+
"on-finished": "^2.4.1",
+
"range-parser": "^1.2.1",
+
"statuses": "^2.0.1"
+
},
+
"engines": {
+
"node": ">= 18"
+
}
+
},
+
"node_modules/server-destroy": {
+
"version": "1.0.1",
+
"resolved": "https://registry.npmjs.org/server-destroy/-/server-destroy-1.0.1.tgz",
+
"integrity": "sha512-rb+9B5YBIEzYcD6x2VKidaa+cqYBJQKnU4oe4E3ANwRRN56yk/ua1YCJT1n21NTS8w6CcOclAKNP3PhdCXKYtQ==",
+
"license": "ISC"
+
},
+
"node_modules/setprototypeof": {
+
"version": "1.2.0",
+
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
+
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
+
"license": "ISC"
+
},
"node_modules/sharp": {
"version": "0.34.4",
"resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.4.tgz",
···
},
"node_modules/shiki": {
-
"version": "3.13.0",
-
"resolved": "https://registry.npmjs.org/shiki/-/shiki-3.13.0.tgz",
-
"integrity": "sha512-aZW4l8Og16CokuCLf8CF8kq+KK2yOygapU5m3+hoGw0Mdosc6fPitjM+ujYarppj5ZIKGyPDPP1vqmQhr+5/0g==",
+
"version": "3.14.0",
+
"resolved": "https://registry.npmjs.org/shiki/-/shiki-3.14.0.tgz",
+
"integrity": "sha512-J0yvpLI7LSig3Z3acIuDLouV5UCKQqu8qOArwMx+/yPVC3WRMgrP67beaG8F+j4xfEWE0eVC4GeBCIXeOPra1g==",
"license": "MIT",
"dependencies": {
-
"@shikijs/core": "3.13.0",
-
"@shikijs/engine-javascript": "3.13.0",
-
"@shikijs/engine-oniguruma": "3.13.0",
-
"@shikijs/langs": "3.13.0",
-
"@shikijs/themes": "3.13.0",
-
"@shikijs/types": "3.13.0",
+
"@shikijs/core": "3.14.0",
+
"@shikijs/engine-javascript": "3.14.0",
+
"@shikijs/engine-oniguruma": "3.14.0",
+
"@shikijs/langs": "3.14.0",
+
"@shikijs/themes": "3.14.0",
+
"@shikijs/types": "3.14.0",
"@shikijs/vscode-textmate": "^10.0.2",
"@types/hast": "^3.0.4"
···
"url": "https://github.com/sponsors/wooorm"
},
+
"node_modules/statuses": {
+
"version": "2.0.2",
+
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
+
"integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
+
"license": "MIT",
+
"engines": {
+
"node": ">= 0.8"
+
}
+
},
"node_modules/string-width": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
···
"url": "https://github.com/chalk/strip-ansi?sponsor=1"
},
+
"node_modules/tailwindcss": {
+
"version": "4.1.16",
+
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.16.tgz",
+
"integrity": "sha512-pONL5awpaQX4LN5eiv7moSiSPd/DLDzKVRJz8Q9PgzmAdd1R4307GQS2ZpfiN7ZmekdQrfhZZiSE5jkLR4WNaA==",
+
"license": "MIT"
+
},
+
"node_modules/tapable": {
+
"version": "2.3.0",
+
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz",
+
"integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==",
+
"license": "MIT",
+
"engines": {
+
"node": ">=6"
+
},
+
"funding": {
+
"type": "opencollective",
+
"url": "https://opencollective.com/webpack"
+
}
+
},
"node_modules/tiny-inflate": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz",
···
"url": "https://github.com/sponsors/SuperchupuDev"
},
+
"node_modules/tlds": {
+
"version": "1.261.0",
+
"resolved": "https://registry.npmjs.org/tlds/-/tlds-1.261.0.tgz",
+
"integrity": "sha512-QXqwfEl9ddlGBaRFXIvNKK6OhipSiLXuRuLJX5DErz0o0Q0rYxulWLdFryTkV5PkdZct5iMInwYEGe/eR++1AA==",
+
"license": "MIT",
+
"bin": {
+
"tlds": "bin.js"
+
}
+
},
+
"node_modules/toidentifier": {
+
"version": "1.0.1",
+
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
+
"integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
+
"license": "MIT",
+
"engines": {
+
"node": ">=0.6"
+
}
+
},
"node_modules/trim-lines": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz",
···
"integrity": "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==",
"license": "MIT"
},
+
"node_modules/uint8arrays": {
+
"version": "3.0.0",
+
"resolved": "https://registry.npmjs.org/uint8arrays/-/uint8arrays-3.0.0.tgz",
+
"integrity": "sha512-HRCx0q6O9Bfbp+HHSfQQKD7wU70+lydKVt4EghkdOvlK/NlrF90z+eXV34mUd48rNvVJXwkrMSPpCATkct8fJA==",
+
"license": "MIT",
+
"dependencies": {
+
"multiformats": "^9.4.2"
+
}
+
},
"node_modules/ultrahtml": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/ultrahtml/-/ultrahtml-1.6.0.tgz",
···
"resolved": "https://registry.npmjs.org/uncrypto/-/uncrypto-0.1.3.tgz",
"integrity": "sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==",
"license": "MIT"
+
},
+
"node_modules/undici": {
+
"version": "6.22.0",
+
"resolved": "https://registry.npmjs.org/undici/-/undici-6.22.0.tgz",
+
"integrity": "sha512-hU/10obOIu62MGYjdskASR3CUAiYaFTtC9Pa6vHyf//mAipSvSQg6od2CnJswq7fvzNS3zJhxoRkgNVaHurWKw==",
+
"license": "MIT",
+
"engines": {
+
"node": ">=18.17"
+
}
},
"node_modules/undici-types": {
"version": "7.16.0",
···
},
"node_modules/vite": {
-
"version": "6.4.1",
-
"resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz",
-
"integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==",
+
"version": "7.1.12",
+
"resolved": "https://registry.npmjs.org/vite/-/vite-7.1.12.tgz",
+
"integrity": "sha512-ZWyE8YXEXqJrrSLvYgrRP7p62OziLW7xI5HYGWFzOvupfAlrLvURSzv/FyGyy0eidogEM3ujU+kUG1zuHgb6Ug==",
"license": "MIT",
+
"peer": true,
"dependencies": {
"esbuild": "^0.25.0",
-
"fdir": "^6.4.4",
-
"picomatch": "^4.0.2",
-
"postcss": "^8.5.3",
-
"rollup": "^4.34.9",
-
"tinyglobby": "^0.2.13"
+
"fdir": "^6.5.0",
+
"picomatch": "^4.0.3",
+
"postcss": "^8.5.6",
+
"rollup": "^4.43.0",
+
"tinyglobby": "^0.2.15"
},
"bin": {
"vite": "bin/vite.js"
},
"engines": {
-
"node": "^18.0.0 || ^20.0.0 || >=22.0.0"
+
"node": "^20.19.0 || >=22.12.0"
},
"funding": {
"url": "https://github.com/vitejs/vite?sponsor=1"
···
"fsevents": "~2.3.3"
},
"peerDependencies": {
-
"@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0",
+
"@types/node": "^20.19.0 || >=22.12.0",
"jiti": ">=1.21.0",
-
"less": "*",
+
"less": "^4.0.0",
"lightningcss": "^1.21.0",
-
"sass": "*",
-
"sass-embedded": "*",
-
"stylus": "*",
-
"sugarss": "*",
+
"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"
+9 -2
package.json
···
"astro": "astro"
},
"dependencies": {
-
"astro": "^5.15.1"
+
"@astrojs/node": "^9.5.0",
+
"@atproto/api": "^0.17.4",
+
"@atproto/oauth-client-node": "^0.3.10",
+
"@tailwindcss/vite": "^4.1.16",
+
"astro": "^5.15.1",
+
"daisyui": "^5.3.9",
+
"dotenv": "^17.2.3",
+
"tailwindcss": "^4.1.16"
}
-
}
+
}
+7
src/lib/context.ts
···
+
import type { AstroCookies } from 'astro'
+
import { createOAuthClient } from './oauth'
+
+
// Create a request-scoped OAuth client with cookie-based storage
+
export function getOAuthClient(cookies: AstroCookies) {
+
return createOAuthClient(cookies)
+
}
+4
src/lib/env.ts
···
+
export const env = {
+
PORT: Number(process.env.PORT) || 4321,
+
PUBLIC_URL: process.env.PUBLIC_URL || undefined,
+
}
+22
src/lib/oauth.ts
···
+
import type { AstroCookies } from 'astro'
+
import {
+
atprotoLoopbackClientMetadata,
+
NodeOAuthClient,
+
} from "@atproto/oauth-client-node";
+
import { env } from "./env";
+
import { CookieSessionStore, CookieStateStore } from "./storage";
+
+
export function createOAuthClient(cookies: AstroCookies) {
+
const clientMetadata = atprotoLoopbackClientMetadata(
+
`http://localhost?${new URLSearchParams([
+
["redirect_uri", `http://127.0.0.1:${env.PORT}/api/oauth/callback`],
+
["scope", `atproto transition:generic`],
+
])}`,
+
);
+
+
return new NodeOAuthClient({
+
clientMetadata,
+
stateStore: new CookieStateStore(cookies),
+
sessionStore: new CookieSessionStore(cookies),
+
});
+
}
+61
src/lib/session.ts
···
+
import type { AstroCookies } from 'astro'
+
+
export type SessionData = { did?: string }
+
+
const COOKIE_NAME = 'sid'
+
+
// Simple cookie-based session using Web Crypto API
+
export class Session {
+
private data: SessionData = {}
+
private cookies: AstroCookies
+
+
constructor(cookies: AstroCookies, data: SessionData = {}) {
+
this.cookies = cookies
+
this.data = data
+
}
+
+
get did() {
+
return this.data.did
+
}
+
+
set did(value: string | undefined) {
+
this.data.did = value
+
}
+
+
async save() {
+
const jsonData = JSON.stringify(this.data)
+
// For simplicity, we'll just base64 encode the data
+
// In production, you'd want proper encryption
+
const encoded = btoa(jsonData)
+
+
this.cookies.set(COOKIE_NAME, encoded, {
+
httpOnly: true,
+
secure: false,
+
sameSite: 'lax',
+
path: '/',
+
maxAge: 60 * 60 * 24 * 30, // 30 days
+
})
+
}
+
+
destroy() {
+
this.data = {}
+
this.cookies.delete(COOKIE_NAME, { path: '/' })
+
}
+
}
+
+
export function getSession(cookies: AstroCookies): Session {
+
const cookie = cookies.get(COOKIE_NAME)
+
+
if (!cookie?.value) {
+
return new Session(cookies)
+
}
+
+
try {
+
const decoded = atob(cookie.value)
+
const data = JSON.parse(decoded) as SessionData
+
return new Session(cookies, data)
+
} catch (err) {
+
console.warn('Failed to decode session:', err)
+
return new Session(cookies)
+
}
+
}
+82
src/lib/storage.ts
···
+
import type { AstroCookies } from 'astro'
+
import type {
+
NodeSavedSession,
+
NodeSavedSessionStore,
+
NodeSavedState,
+
NodeSavedStateStore,
+
} from '@atproto/oauth-client-node'
+
+
// Cookie-based storage for OAuth state and sessions
+
// All data is serialized into cookies for stateless operation
+
+
export class CookieStateStore implements NodeSavedStateStore {
+
constructor(private cookies: AstroCookies) {}
+
+
async get(key: string): Promise<NodeSavedState | undefined> {
+
const cookieName = `oauth_state_${key}`
+
const cookie = this.cookies.get(cookieName)
+
if (!cookie?.value) return undefined
+
+
try {
+
const decoded = atob(cookie.value)
+
return JSON.parse(decoded) as NodeSavedState
+
} catch (err) {
+
console.warn('Failed to decode OAuth state:', err)
+
return undefined
+
}
+
}
+
+
async set(key: string, val: NodeSavedState) {
+
const cookieName = `oauth_state_${key}`
+
const encoded = btoa(JSON.stringify(val))
+
+
this.cookies.set(cookieName, encoded, {
+
httpOnly: true,
+
secure: false,
+
sameSite: 'lax',
+
path: '/',
+
maxAge: 60 * 10, // 10 minutes (OAuth flow timeout)
+
})
+
}
+
+
async del(key: string) {
+
const cookieName = `oauth_state_${key}`
+
this.cookies.delete(cookieName, { path: '/' })
+
}
+
}
+
+
export class CookieSessionStore implements NodeSavedSessionStore {
+
constructor(private cookies: AstroCookies) {}
+
+
async get(key: string): Promise<NodeSavedSession | undefined> {
+
const cookieName = `oauth_session_${key}`
+
const cookie = this.cookies.get(cookieName)
+
if (!cookie?.value) return undefined
+
+
try {
+
const decoded = atob(cookie.value)
+
return JSON.parse(decoded) as NodeSavedSession
+
} catch (err) {
+
console.warn('Failed to decode OAuth session:', err)
+
return undefined
+
}
+
}
+
+
async set(key: string, val: NodeSavedSession) {
+
const cookieName = `oauth_session_${key}`
+
const encoded = btoa(JSON.stringify(val))
+
+
this.cookies.set(cookieName, encoded, {
+
httpOnly: true,
+
secure: false,
+
sameSite: 'lax',
+
path: '/',
+
maxAge: 60 * 60 * 24 * 30, // 30 days
+
})
+
}
+
+
async del(key: string) {
+
const cookieName = `oauth_session_${key}`
+
this.cookies.delete(cookieName, { path: '/' })
+
}
+
}
+24
src/pages/api/login.ts
···
+
import type { APIRoute } from 'astro'
+
import { getOAuthClient } from '../../lib/context'
+
+
export const POST: APIRoute = async ({ request, cookies, redirect }) => {
+
try {
+
const oauthClient = getOAuthClient(cookies)
+
const formData = await request.formData()
+
const handle = formData.get('handle')
+
+
if (!handle || typeof handle !== 'string') {
+
return new Response('Invalid handle', { status: 400 })
+
}
+
+
const url = await oauthClient.authorize(handle, {
+
scope: 'atproto transition:generic',
+
})
+
+
return redirect(url.toString())
+
} catch (err) {
+
console.error('OAuth authorize failed:', err)
+
const error = err instanceof Error ? err.message : 'unexpected error'
+
return new Response(`Login failed: ${error}`, { status: 500 })
+
}
+
}
+26
src/pages/api/logout.ts
···
+
import type { APIRoute } from 'astro'
+
import { getOAuthClient } from '../../lib/context'
+
import { getSession } from '../../lib/session'
+
+
export const POST: APIRoute = async (context) => {
+
try {
+
const oauthClient = getOAuthClient(context.cookies)
+
const session = getSession(context.cookies)
+
+
if (session.did) {
+
try {
+
const oauthSession = await oauthClient.restore(session.did)
+
if (oauthSession) await oauthSession.signOut()
+
} catch (err) {
+
console.warn('Failed to revoke credentials:', err)
+
}
+
}
+
+
session.destroy()
+
+
return context.redirect('/')
+
} catch (err) {
+
console.error('Logout failed:', err)
+
return new Response('Logout failed', { status: 500 })
+
}
+
}
+31
src/pages/api/oauth/callback.ts
···
+
import type { APIRoute } from 'astro'
+
import { getOAuthClient } from '../../../lib/context'
+
import { getSession } from '../../../lib/session'
+
+
export const GET: APIRoute = async (context) => {
+
try {
+
const oauthClient = getOAuthClient(context.cookies)
+
const url = new URL(context.request.url)
+
const params = new URLSearchParams(url.search)
+
+
const session = getSession(context.cookies)
+
+
if (session.did) {
+
try {
+
const oauthSession = await oauthClient.restore(session.did)
+
if (oauthSession) await oauthSession.signOut()
+
} catch (err) {
+
console.warn('OAuth restore failed during callback:', err)
+
}
+
}
+
+
const oauth = await oauthClient.callback(params)
+
session.did = oauth.session.did
+
await session.save()
+
+
return context.redirect('/')
+
} catch (err) {
+
console.error('OAuth callback failed:', err)
+
return context.redirect('/?error=login_failed')
+
}
+
}
+97 -11
src/pages/index.astro
···
---
+
import '../styles.css'
+
import { getSession } from '../lib/session'
+
import { getOAuthClient } from '../lib/context'
+
import { Agent } from '@atproto/api'
+
const session = getSession(Astro.cookies)
+
const oauthClient = getOAuthClient(Astro.cookies)
+
+
let agent: Agent | null = null
+
let profile: any = null
+
+
if (session.did) {
+
try {
+
const oauthSession = await oauthClient.restore(session.did)
+
if (oauthSession) {
+
agent = new Agent(oauthSession)
+
+
try {
+
const profileResponse = await agent.app.bsky.actor.getProfile({
+
actor: agent.assertDid,
+
})
+
profile = profileResponse.data
+
} catch (err) {
+
console.warn('Failed to fetch profile:', err)
+
}
+
}
+
} catch (err) {
+
console.warn('OAuth restore failed:', err)
+
session.destroy()
+
}
+
}
+
+
const displayName = profile?.displayName || agent?.assertDid || 'User'
+
const handle = profile?.handle || agent?.assertDid || ''
+
const avatar = profile?.avatar
+
const description = profile?.description
---
-
<html lang="en">
-
<head>
-
<meta charset="utf-8" />
-
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
-
<meta name="viewport" content="width=device-width" />
-
<meta name="generator" content={Astro.generator} />
-
<title>Astro</title>
-
</head>
-
<body>
-
<h1>Astro</h1>
-
</body>
+
<html lang="en" data-theme="dracula">
+
<head>
+
<meta charset="utf-8" />
+
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
+
<meta name="viewport" content="width=device-width" />
+
<meta name="generator" content={Astro.generator} />
+
<title>ATProto Login</title>
+
</head>
+
<body>
+
<div class="min-h-screen flex items-center justify-center">
+
<div class="card w-96 bg-base-200 shadow-xl p-8">
+
<div class="card-body">
+
{
+
agent ? (
+
<>
+
<h2 class="card-title justify-center mb-4">Welcome!</h2>
+
<div class="space-y-4">
+
<div class="flex flex-col items-center text-center">
+
{avatar && (
+
<div class="avatar mb-4">
+
<div class="w-24 rounded-full">
+
<img src={avatar} alt={displayName} />
+
</div>
+
</div>
+
)}
+
<p class="text-lg font-semibold">{displayName}</p>
+
<p class="text-sm opacity-70">{handle}</p>
+
{description && (
+
<p class="text-sm mt-2 opacity-80">{description}</p>
+
)}
+
</div>
+
<form method="POST" action="/api/logout" class="w-full">
+
<button type="submit" class="btn btn-error w-full">
+
Logout
+
</button>
+
</form>
+
</div>
+
</>
+
) : (
+
<>
+
<h2 class="card-title justify-center mb-6">ATProto Login</h2>
+
<form method="POST" action="/api/login">
+
<div class="join join-vertical w-full">
+
<input
+
type="text"
+
placeholder="Enter your handle (e.g. alice.bsky.social)"
+
class="input input-bordered join-item w-full"
+
name="handle"
+
required
+
/>
+
<button type="submit" class="btn btn-primary join-item w-full">
+
Login
+
</button>
+
</div>
+
</form>
+
</>
+
)
+
}
+
</div>
+
</div>
+
</div>
+
</body>
</html>
+7
src/styles.css
···
+
@import "tailwindcss";
+
+
@plugin "daisyui" {
+
themes:
+
dracula --default,
+
dracula --prefersdark;
+
}
+4
tailwind.config.mjs
···
+
/** @type {import('tailwindcss').Config} */
+
export default {
+
content: ["./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}"],
+
};