this repo has no description
1
fork

Configure Feed

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

Finalize auth flow

modamo-gh bdb25b06 519cc172

+675 -28
+2
.gitignore
··· 39 39 # typescript 40 40 *.tsbuildinfo 41 41 next-env.d.ts 42 + 43 + app.db*
+16 -3
app/oauth/callback/route.ts
··· 5 5 6 6 export async function GET(request: NextRequest) { 7 7 try { 8 - const params = request.nextUrl.searchParams; 9 - const client = await getOAuthClient(); 10 - const {session} = await client. 8 + const params = request.nextUrl.searchParams; 9 + const client = await getOAuthClient(); 10 + 11 + console.log(params) 12 + const { session } = await client.callback(params); 13 + const response = NextResponse.redirect(new URL("/", PUBLIC_URL)); 14 + 15 + response.cookies.set("did", session.did, { 16 + httpOnly: true, 17 + maxAge: 60 * 60 * 24 * 7, 18 + path: "/", 19 + sameSite: "lax", 20 + secure: process.env.NODE_ENV === "production" 21 + }); 22 + 23 + return response; 11 24 } catch (error) { 12 25 console.error("OAuth callback error:", error); 13 26
+19 -11
app/page.tsx
··· 1 1 import LoginForm from "@/components/LoginForm"; 2 + import { getSession } from "@/lib/auth/session"; 3 + import { redirect } from "next/navigation"; 2 4 3 - const Home = () => { 4 - return ( 5 - <main className="bg-amber-100 grid grid-cols-3 h-screen p-4 w-screen"> 6 - <section className="flex col-span-1 items-center justify-center "> 7 - <h1 className="text-emerald-900 text-4xl">bambü</h1> 8 - </section> 9 - <section className="bg-emerald-900 col-span-2 flex items-center justify-center rounded-2xl"> 10 - <LoginForm /> 11 - </section> 12 - </main> 13 - ); 5 + const Home = async () => { 6 + const session = await getSession(); 7 + 8 + if (session) { 9 + return ( 10 + <main className="bg-amber-100 grid grid-cols-3 h-screen p-4 w-screen"> 11 + <section className="flex col-span-1 items-center justify-center "> 12 + <h1 className="text-emerald-900 text-4xl">bambü</h1> 13 + </section> 14 + <section className="bg-emerald-900 col-span-2 flex items-center justify-center rounded-2xl"> 15 + <LoginForm /> 16 + </section> 17 + </main> 18 + ); 19 + } 20 + 21 + redirect("/library"); 14 22 }; 15 23 16 24 export default Home;
+109 -12
lib/auth/client.ts
··· 1 1 import { 2 2 buildAtprotoLoopbackClientMetadata, 3 - NodeOAuthClient 3 + JoseKey, 4 + Keyset, 5 + NodeOAuthClient, 6 + NodeSavedSession, 7 + NodeSavedState 4 8 } from "@atproto/oauth-client-node"; 9 + import { getDB } from "../db"; 5 10 11 + declare global { 12 + var _oauthClient: NodeOAuthClient | undefined 13 + } 14 + 15 + const PRIVATE_KEY = process.env.PRIVATE_KEY; 6 16 const PUBLIC_URL = process.env.PUBLIC_URL; 7 17 8 18 export const SCOPE = "atproto"; 9 19 10 - let client: NodeOAuthClient | null = null; 11 - 12 20 const getClientMetadata = () => { 13 21 if (PUBLIC_URL) { 14 - } else { 15 - return buildAtprotoLoopbackClientMetadata({ 16 - redirect_uris: ["http://127.0.0.1:3000/oauth/callback"], 17 - scope: SCOPE 18 - }); 22 + return { 23 + client_id: `${PUBLIC_URL}/oauth-client-metadata.json`, 24 + client_name: "bambü", 25 + client_uri: PUBLIC_URL, 26 + dpop_bound_access_tokens: true, 27 + grant_types: ["authorization_code", "refresh_token"], 28 + jwks_uri: `${PUBLIC_URL}/.well-known/jwks.json`, 29 + redirect_uris: [`${PUBLIC_URL}/oauth/callback`], 30 + response_types: ["code"], 31 + scope: SCOPE, 32 + token_endpoint_auth_method: "private_key_jwt" as const, 33 + token_endpoint_auth_signing_alg: "ES256" as const 34 + }; 35 + } 36 + 37 + return buildAtprotoLoopbackClientMetadata({ 38 + redirect_uris: ["http://127.0.0.1:3000/oauth/callback"], 39 + scope: SCOPE 40 + }); 41 + }; 42 + 43 + const getKeyset = async () => { 44 + if (PUBLIC_URL && PRIVATE_KEY) { 45 + return new Keyset([await JoseKey.fromJWK(JSON.parse(PRIVATE_KEY))]); 19 46 } 47 + 48 + return undefined; 20 49 }; 50 + 21 51 export const getOAuthClient = async () => { 22 - if (client) { 23 - return client; 52 + if(globalThis._oauthClient){ 53 + return globalThis._oauthClient 24 54 } 25 55 26 - client = new NodeOAuthClient({ 27 - clientMetadata: getClientMetadata() 56 + globalThis._oauthClient = new NodeOAuthClient({ 57 + clientMetadata: getClientMetadata(), 58 + keyset: await getKeyset(), 59 + sessionStore: { 60 + del: async (key: string) => { 61 + const db = getDB(); 62 + 63 + await db 64 + ?.deleteFrom("auth_session") 65 + .where("key", "=", key) 66 + .execute(); 67 + }, 68 + get: async (key: string) => { 69 + const db = getDB(); 70 + const row = await db 71 + ?.selectFrom("auth_session") 72 + .select("value") 73 + .where("key", "=", key) 74 + .executeTakeFirst(); 75 + 76 + return row ? JSON.parse(row.value) : undefined; 77 + }, 78 + set: async (key: string, value: NodeSavedSession) => { 79 + const db = getDB(); 80 + const valueJSON = JSON.stringify(value); 81 + 82 + await db 83 + ?.insertInto("auth_session") 84 + .values({ key, value: valueJSON }) 85 + .onConflict((oc) => 86 + oc.column("key").doUpdateSet({ value: valueJSON }) 87 + ) 88 + .execute(); 89 + } 90 + }, 91 + stateStore: { 92 + del: async (key: string) => { 93 + const db = getDB(); 94 + 95 + await db 96 + ?.deleteFrom("auth_state") 97 + .where("key", "=", key) 98 + .execute(); 99 + }, 100 + get: async (key: string) => { 101 + const db = getDB(); 102 + const row = await db 103 + ?.selectFrom("auth_state") 104 + .select("value") 105 + .where("key", "=", key) 106 + .executeTakeFirst(); 107 + 108 + return row ? JSON.parse(row.value) : undefined; 109 + }, 110 + set: async (key: string, value: NodeSavedState) => { 111 + const db = getDB(); 112 + const valueJSON = JSON.stringify(value); 113 + 114 + await db 115 + ?.insertInto("auth_state") 116 + .values({ key, value: valueJSON }) 117 + .onConflict((oc) => 118 + oc.column("key").doUpdateSet({ value: valueJSON }) 119 + ) 120 + .execute(); 121 + } 122 + } 28 123 }); 124 + 125 + return globalThis._oauthClient; 29 126 };
+24
lib/auth/session.ts
··· 1 + import { cookies } from "next/headers"; 2 + import { getOAuthClient } from "./client"; 3 + 4 + export const getDID = async () => { 5 + const cookieStore = await cookies(); 6 + 7 + return cookieStore.get("did")?.value ?? null; 8 + }; 9 + 10 + export const getSession = async () => { 11 + const did = await getDID(); 12 + 13 + if (!did) { 14 + return null; 15 + } 16 + 17 + try { 18 + const client = await getOAuthClient(); 19 + 20 + return await client.restore(did); 21 + } catch { 22 + return null; 23 + } 24 + };
+47
lib/db/index.ts
··· 1 + import Database from "better-sqlite3"; 2 + import { Kysely, SqliteDialect } from "kysely"; 3 + 4 + const DATABASE_PATH = process.env.DATABASE_PATH || "app.db"; 5 + 6 + let _db: Kysely<DatabaseSchema> | null = null; 7 + 8 + export const getDB = () => { 9 + if (!_db) { 10 + const sqlite = new Database(DATABASE_PATH); 11 + 12 + sqlite.pragma("journal_mode = WAL"); 13 + 14 + sqlite.exec(` 15 + CREATE TABLE IF NOT EXISTS auth_state ( 16 + key TEXT PRIMARY KEY, 17 + value TEXT NOT NULL 18 + ); 19 + 20 + CREATE TABLE IF NOT EXISTS auth_session ( 21 + key TEXT PRIMARY KEY, 22 + value TEXT NOT NULL 23 + ); 24 + `); 25 + 26 + _db = new Kysely<DatabaseSchema>({ 27 + dialect: new SqliteDialect({ database: sqlite }) 28 + }); 29 + } 30 + 31 + return _db; 32 + }; 33 + 34 + interface AuthSessionTable { 35 + key: string; 36 + value: string; 37 + } 38 + 39 + interface AuthStateTable { 40 + key: string; 41 + value: string; 42 + } 43 + 44 + export interface DatabaseSchema { 45 + auth_session: AuthSessionTable; 46 + auth_state: AuthStateTable; 47 + }
+455 -2
package-lock.json
··· 9 9 "version": "0.1.0", 10 10 "dependencies": { 11 11 "@atproto/oauth-client-node": "^0.3.16", 12 + "better-sqlite3": "^12.6.2", 13 + "kysely": "^0.28.11", 12 14 "next": "16.1.6", 13 15 "react": "19.2.3", 14 16 "react-dom": "19.2.3" 15 17 }, 16 18 "devDependencies": { 17 19 "@tailwindcss/postcss": "^4", 20 + "@types/better-sqlite3": "^7.6.13", 18 21 "@types/node": "^20", 19 22 "@types/react": "^19", 20 23 "@types/react-dom": "^19", ··· 1875 1878 "tslib": "^2.4.0" 1876 1879 } 1877 1880 }, 1881 + "node_modules/@types/better-sqlite3": { 1882 + "version": "7.6.13", 1883 + "resolved": "https://registry.npmjs.org/@types/better-sqlite3/-/better-sqlite3-7.6.13.tgz", 1884 + "integrity": "sha512-NMv9ASNARoKksWtsq/SHakpYAYnhBrQgGD8zkLYk/jaK8jUGn08CfEdTRgYhMypUQAfzSP8W6gNLe0q19/t4VA==", 1885 + "dev": true, 1886 + "license": "MIT", 1887 + "dependencies": { 1888 + "@types/node": "*" 1889 + } 1890 + }, 1878 1891 "node_modules/@types/estree": { 1879 1892 "version": "1.0.8", 1880 1893 "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", ··· 2757 2770 "dev": true, 2758 2771 "license": "MIT" 2759 2772 }, 2773 + "node_modules/base64-js": { 2774 + "version": "1.5.1", 2775 + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", 2776 + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", 2777 + "funding": [ 2778 + { 2779 + "type": "github", 2780 + "url": "https://github.com/sponsors/feross" 2781 + }, 2782 + { 2783 + "type": "patreon", 2784 + "url": "https://www.patreon.com/feross" 2785 + }, 2786 + { 2787 + "type": "consulting", 2788 + "url": "https://feross.org/support" 2789 + } 2790 + ], 2791 + "license": "MIT" 2792 + }, 2760 2793 "node_modules/baseline-browser-mapping": { 2761 2794 "version": "2.9.19", 2762 2795 "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.19.tgz", ··· 2766 2799 "baseline-browser-mapping": "dist/cli.js" 2767 2800 } 2768 2801 }, 2802 + "node_modules/better-sqlite3": { 2803 + "version": "12.6.2", 2804 + "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-12.6.2.tgz", 2805 + "integrity": "sha512-8VYKM3MjCa9WcaSAI3hzwhmyHVlH8tiGFwf0RlTsZPWJ1I5MkzjiudCo4KC4DxOaL/53A5B1sI/IbldNFDbsKA==", 2806 + "hasInstallScript": true, 2807 + "license": "MIT", 2808 + "dependencies": { 2809 + "bindings": "^1.5.0", 2810 + "prebuild-install": "^7.1.1" 2811 + }, 2812 + "engines": { 2813 + "node": "20.x || 22.x || 23.x || 24.x || 25.x" 2814 + } 2815 + }, 2816 + "node_modules/bindings": { 2817 + "version": "1.5.0", 2818 + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", 2819 + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", 2820 + "license": "MIT", 2821 + "dependencies": { 2822 + "file-uri-to-path": "1.0.0" 2823 + } 2824 + }, 2825 + "node_modules/bl": { 2826 + "version": "4.1.0", 2827 + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", 2828 + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", 2829 + "license": "MIT", 2830 + "dependencies": { 2831 + "buffer": "^5.5.0", 2832 + "inherits": "^2.0.4", 2833 + "readable-stream": "^3.4.0" 2834 + } 2835 + }, 2769 2836 "node_modules/brace-expansion": { 2770 2837 "version": "1.1.12", 2771 2838 "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", ··· 2824 2891 "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" 2825 2892 } 2826 2893 }, 2894 + "node_modules/buffer": { 2895 + "version": "5.7.1", 2896 + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", 2897 + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", 2898 + "funding": [ 2899 + { 2900 + "type": "github", 2901 + "url": "https://github.com/sponsors/feross" 2902 + }, 2903 + { 2904 + "type": "patreon", 2905 + "url": "https://www.patreon.com/feross" 2906 + }, 2907 + { 2908 + "type": "consulting", 2909 + "url": "https://feross.org/support" 2910 + } 2911 + ], 2912 + "license": "MIT", 2913 + "dependencies": { 2914 + "base64-js": "^1.3.1", 2915 + "ieee754": "^1.1.13" 2916 + } 2917 + }, 2827 2918 "node_modules/call-bind": { 2828 2919 "version": "1.0.8", 2829 2920 "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", ··· 2920 3011 "funding": { 2921 3012 "url": "https://github.com/chalk/chalk?sponsor=1" 2922 3013 } 3014 + }, 3015 + "node_modules/chownr": { 3016 + "version": "1.1.4", 3017 + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", 3018 + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", 3019 + "license": "ISC" 2923 3020 }, 2924 3021 "node_modules/client-only": { 2925 3022 "version": "0.0.1", ··· 3073 3170 } 3074 3171 } 3075 3172 }, 3173 + "node_modules/decompress-response": { 3174 + "version": "6.0.0", 3175 + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", 3176 + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", 3177 + "license": "MIT", 3178 + "dependencies": { 3179 + "mimic-response": "^3.1.0" 3180 + }, 3181 + "engines": { 3182 + "node": ">=10" 3183 + }, 3184 + "funding": { 3185 + "url": "https://github.com/sponsors/sindresorhus" 3186 + } 3187 + }, 3188 + "node_modules/deep-extend": { 3189 + "version": "0.6.0", 3190 + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", 3191 + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", 3192 + "license": "MIT", 3193 + "engines": { 3194 + "node": ">=4.0.0" 3195 + } 3196 + }, 3076 3197 "node_modules/deep-is": { 3077 3198 "version": "0.1.4", 3078 3199 "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", ··· 3120 3241 "version": "2.1.2", 3121 3242 "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", 3122 3243 "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", 3123 - "devOptional": true, 3124 3244 "license": "Apache-2.0", 3125 3245 "engines": { 3126 3246 "node": ">=8" ··· 3167 3287 "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", 3168 3288 "dev": true, 3169 3289 "license": "MIT" 3290 + }, 3291 + "node_modules/end-of-stream": { 3292 + "version": "1.4.5", 3293 + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", 3294 + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", 3295 + "license": "MIT", 3296 + "dependencies": { 3297 + "once": "^1.4.0" 3298 + } 3170 3299 }, 3171 3300 "node_modules/enhanced-resolve": { 3172 3301 "version": "5.18.4", ··· 3806 3935 "node": ">=0.10.0" 3807 3936 } 3808 3937 }, 3938 + "node_modules/expand-template": { 3939 + "version": "2.0.3", 3940 + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", 3941 + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", 3942 + "license": "(MIT OR WTFPL)", 3943 + "engines": { 3944 + "node": ">=6" 3945 + } 3946 + }, 3809 3947 "node_modules/fast-deep-equal": { 3810 3948 "version": "3.1.3", 3811 3949 "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", ··· 3880 4018 "node": ">=16.0.0" 3881 4019 } 3882 4020 }, 4021 + "node_modules/file-uri-to-path": { 4022 + "version": "1.0.0", 4023 + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", 4024 + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", 4025 + "license": "MIT" 4026 + }, 3883 4027 "node_modules/fill-range": { 3884 4028 "version": "7.1.1", 3885 4029 "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", ··· 3946 4090 "funding": { 3947 4091 "url": "https://github.com/sponsors/ljharb" 3948 4092 } 4093 + }, 4094 + "node_modules/fs-constants": { 4095 + "version": "1.0.0", 4096 + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", 4097 + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", 4098 + "license": "MIT" 3949 4099 }, 3950 4100 "node_modules/function-bind": { 3951 4101 "version": "1.1.2", ··· 4078 4228 "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" 4079 4229 } 4080 4230 }, 4231 + "node_modules/github-from-package": { 4232 + "version": "0.0.0", 4233 + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", 4234 + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", 4235 + "license": "MIT" 4236 + }, 4081 4237 "node_modules/glob-parent": { 4082 4238 "version": "6.0.2", 4083 4239 "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", ··· 4252 4408 "hermes-estree": "0.25.1" 4253 4409 } 4254 4410 }, 4411 + "node_modules/ieee754": { 4412 + "version": "1.2.1", 4413 + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", 4414 + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", 4415 + "funding": [ 4416 + { 4417 + "type": "github", 4418 + "url": "https://github.com/sponsors/feross" 4419 + }, 4420 + { 4421 + "type": "patreon", 4422 + "url": "https://www.patreon.com/feross" 4423 + }, 4424 + { 4425 + "type": "consulting", 4426 + "url": "https://feross.org/support" 4427 + } 4428 + ], 4429 + "license": "BSD-3-Clause" 4430 + }, 4255 4431 "node_modules/ignore": { 4256 4432 "version": "5.3.2", 4257 4433 "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", ··· 4288 4464 "engines": { 4289 4465 "node": ">=0.8.19" 4290 4466 } 4467 + }, 4468 + "node_modules/inherits": { 4469 + "version": "2.0.4", 4470 + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 4471 + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 4472 + "license": "ISC" 4473 + }, 4474 + "node_modules/ini": { 4475 + "version": "1.3.8", 4476 + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", 4477 + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", 4478 + "license": "ISC" 4291 4479 }, 4292 4480 "node_modules/internal-slot": { 4293 4481 "version": "1.1.0", ··· 4878 5066 "json-buffer": "3.0.1" 4879 5067 } 4880 5068 }, 5069 + "node_modules/kysely": { 5070 + "version": "0.28.11", 5071 + "resolved": "https://registry.npmjs.org/kysely/-/kysely-0.28.11.tgz", 5072 + "integrity": "sha512-zpGIFg0HuoC893rIjYX1BETkVWdDnzTzF5e0kWXJFg5lE0k1/LfNWBejrcnOFu8Q2Rfq/hTDTU7XLUM8QOrpzg==", 5073 + "license": "MIT", 5074 + "engines": { 5075 + "node": ">=20.0.0" 5076 + } 5077 + }, 4881 5078 "node_modules/language-subtag-registry": { 4882 5079 "version": "0.3.23", 4883 5080 "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", ··· 5263 5460 "node": ">=8.6" 5264 5461 } 5265 5462 }, 5463 + "node_modules/mimic-response": { 5464 + "version": "3.1.0", 5465 + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", 5466 + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", 5467 + "license": "MIT", 5468 + "engines": { 5469 + "node": ">=10" 5470 + }, 5471 + "funding": { 5472 + "url": "https://github.com/sponsors/sindresorhus" 5473 + } 5474 + }, 5266 5475 "node_modules/minimatch": { 5267 5476 "version": "3.1.2", 5268 5477 "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", ··· 5280 5489 "version": "1.2.8", 5281 5490 "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", 5282 5491 "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", 5283 - "dev": true, 5284 5492 "license": "MIT", 5285 5493 "funding": { 5286 5494 "url": "https://github.com/sponsors/ljharb" 5287 5495 } 5288 5496 }, 5497 + "node_modules/mkdirp-classic": { 5498 + "version": "0.5.3", 5499 + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", 5500 + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", 5501 + "license": "MIT" 5502 + }, 5289 5503 "node_modules/ms": { 5290 5504 "version": "2.1.3", 5291 5505 "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", ··· 5316 5530 "engines": { 5317 5531 "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" 5318 5532 } 5533 + }, 5534 + "node_modules/napi-build-utils": { 5535 + "version": "2.0.0", 5536 + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", 5537 + "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", 5538 + "license": "MIT" 5319 5539 }, 5320 5540 "node_modules/napi-postinstall": { 5321 5541 "version": "0.3.4", ··· 5421 5641 "node": "^10 || ^12 || >=14" 5422 5642 } 5423 5643 }, 5644 + "node_modules/node-abi": { 5645 + "version": "3.87.0", 5646 + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.87.0.tgz", 5647 + "integrity": "sha512-+CGM1L1CgmtheLcBuleyYOn7NWPVu0s0EJH2C4puxgEZb9h8QpR9G2dBfZJOAUhi7VQxuBPMd0hiISWcTyiYyQ==", 5648 + "license": "MIT", 5649 + "dependencies": { 5650 + "semver": "^7.3.5" 5651 + }, 5652 + "engines": { 5653 + "node": ">=10" 5654 + } 5655 + }, 5656 + "node_modules/node-abi/node_modules/semver": { 5657 + "version": "7.7.3", 5658 + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", 5659 + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", 5660 + "license": "ISC", 5661 + "bin": { 5662 + "semver": "bin/semver.js" 5663 + }, 5664 + "engines": { 5665 + "node": ">=10" 5666 + } 5667 + }, 5424 5668 "node_modules/node-releases": { 5425 5669 "version": "2.0.27", 5426 5670 "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", ··· 5549 5793 }, 5550 5794 "funding": { 5551 5795 "url": "https://github.com/sponsors/ljharb" 5796 + } 5797 + }, 5798 + "node_modules/once": { 5799 + "version": "1.4.0", 5800 + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 5801 + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", 5802 + "license": "ISC", 5803 + "dependencies": { 5804 + "wrappy": "1" 5552 5805 } 5553 5806 }, 5554 5807 "node_modules/optionator": { ··· 5717 5970 "node": "^10 || ^12 || >=14" 5718 5971 } 5719 5972 }, 5973 + "node_modules/prebuild-install": { 5974 + "version": "7.1.3", 5975 + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", 5976 + "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", 5977 + "license": "MIT", 5978 + "dependencies": { 5979 + "detect-libc": "^2.0.0", 5980 + "expand-template": "^2.0.3", 5981 + "github-from-package": "0.0.0", 5982 + "minimist": "^1.2.3", 5983 + "mkdirp-classic": "^0.5.3", 5984 + "napi-build-utils": "^2.0.0", 5985 + "node-abi": "^3.3.0", 5986 + "pump": "^3.0.0", 5987 + "rc": "^1.2.7", 5988 + "simple-get": "^4.0.0", 5989 + "tar-fs": "^2.0.0", 5990 + "tunnel-agent": "^0.6.0" 5991 + }, 5992 + "bin": { 5993 + "prebuild-install": "bin.js" 5994 + }, 5995 + "engines": { 5996 + "node": ">=10" 5997 + } 5998 + }, 5720 5999 "node_modules/prelude-ls": { 5721 6000 "version": "1.2.1", 5722 6001 "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", ··· 5739 6018 "react-is": "^16.13.1" 5740 6019 } 5741 6020 }, 6021 + "node_modules/pump": { 6022 + "version": "3.0.3", 6023 + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", 6024 + "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", 6025 + "license": "MIT", 6026 + "dependencies": { 6027 + "end-of-stream": "^1.1.0", 6028 + "once": "^1.3.1" 6029 + } 6030 + }, 5742 6031 "node_modules/punycode": { 5743 6032 "version": "2.3.1", 5744 6033 "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", ··· 5770 6059 ], 5771 6060 "license": "MIT" 5772 6061 }, 6062 + "node_modules/rc": { 6063 + "version": "1.2.8", 6064 + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", 6065 + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", 6066 + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", 6067 + "dependencies": { 6068 + "deep-extend": "^0.6.0", 6069 + "ini": "~1.3.0", 6070 + "minimist": "^1.2.0", 6071 + "strip-json-comments": "~2.0.1" 6072 + }, 6073 + "bin": { 6074 + "rc": "cli.js" 6075 + } 6076 + }, 6077 + "node_modules/rc/node_modules/strip-json-comments": { 6078 + "version": "2.0.1", 6079 + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", 6080 + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", 6081 + "license": "MIT", 6082 + "engines": { 6083 + "node": ">=0.10.0" 6084 + } 6085 + }, 5773 6086 "node_modules/react": { 5774 6087 "version": "19.2.3", 5775 6088 "resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz", ··· 5797 6110 "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", 5798 6111 "dev": true, 5799 6112 "license": "MIT" 6113 + }, 6114 + "node_modules/readable-stream": { 6115 + "version": "3.6.2", 6116 + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", 6117 + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", 6118 + "license": "MIT", 6119 + "dependencies": { 6120 + "inherits": "^2.0.3", 6121 + "string_decoder": "^1.1.1", 6122 + "util-deprecate": "^1.0.1" 6123 + }, 6124 + "engines": { 6125 + "node": ">= 6" 6126 + } 5800 6127 }, 5801 6128 "node_modules/reflect.getprototypeof": { 5802 6129 "version": "1.0.10", ··· 5938 6265 "url": "https://github.com/sponsors/ljharb" 5939 6266 } 5940 6267 }, 6268 + "node_modules/safe-buffer": { 6269 + "version": "5.2.1", 6270 + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 6271 + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 6272 + "funding": [ 6273 + { 6274 + "type": "github", 6275 + "url": "https://github.com/sponsors/feross" 6276 + }, 6277 + { 6278 + "type": "patreon", 6279 + "url": "https://www.patreon.com/feross" 6280 + }, 6281 + { 6282 + "type": "consulting", 6283 + "url": "https://feross.org/support" 6284 + } 6285 + ], 6286 + "license": "MIT" 6287 + }, 5941 6288 "node_modules/safe-push-apply": { 5942 6289 "version": "1.0.0", 5943 6290 "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", ··· 6195 6542 "url": "https://github.com/sponsors/ljharb" 6196 6543 } 6197 6544 }, 6545 + "node_modules/simple-concat": { 6546 + "version": "1.0.1", 6547 + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", 6548 + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", 6549 + "funding": [ 6550 + { 6551 + "type": "github", 6552 + "url": "https://github.com/sponsors/feross" 6553 + }, 6554 + { 6555 + "type": "patreon", 6556 + "url": "https://www.patreon.com/feross" 6557 + }, 6558 + { 6559 + "type": "consulting", 6560 + "url": "https://feross.org/support" 6561 + } 6562 + ], 6563 + "license": "MIT" 6564 + }, 6565 + "node_modules/simple-get": { 6566 + "version": "4.0.1", 6567 + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", 6568 + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", 6569 + "funding": [ 6570 + { 6571 + "type": "github", 6572 + "url": "https://github.com/sponsors/feross" 6573 + }, 6574 + { 6575 + "type": "patreon", 6576 + "url": "https://www.patreon.com/feross" 6577 + }, 6578 + { 6579 + "type": "consulting", 6580 + "url": "https://feross.org/support" 6581 + } 6582 + ], 6583 + "license": "MIT", 6584 + "dependencies": { 6585 + "decompress-response": "^6.0.0", 6586 + "once": "^1.3.1", 6587 + "simple-concat": "^1.0.0" 6588 + } 6589 + }, 6198 6590 "node_modules/source-map-js": { 6199 6591 "version": "1.2.1", 6200 6592 "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", ··· 6225 6617 "node": ">= 0.4" 6226 6618 } 6227 6619 }, 6620 + "node_modules/string_decoder": { 6621 + "version": "1.3.0", 6622 + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", 6623 + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", 6624 + "license": "MIT", 6625 + "dependencies": { 6626 + "safe-buffer": "~5.2.0" 6627 + } 6628 + }, 6228 6629 "node_modules/string.prototype.includes": { 6229 6630 "version": "2.0.1", 6230 6631 "resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz", ··· 6431 6832 "url": "https://opencollective.com/webpack" 6432 6833 } 6433 6834 }, 6835 + "node_modules/tar-fs": { 6836 + "version": "2.1.4", 6837 + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz", 6838 + "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==", 6839 + "license": "MIT", 6840 + "dependencies": { 6841 + "chownr": "^1.1.1", 6842 + "mkdirp-classic": "^0.5.2", 6843 + "pump": "^3.0.0", 6844 + "tar-stream": "^2.1.4" 6845 + } 6846 + }, 6847 + "node_modules/tar-stream": { 6848 + "version": "2.2.0", 6849 + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", 6850 + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", 6851 + "license": "MIT", 6852 + "dependencies": { 6853 + "bl": "^4.0.3", 6854 + "end-of-stream": "^1.4.1", 6855 + "fs-constants": "^1.0.0", 6856 + "inherits": "^2.0.3", 6857 + "readable-stream": "^3.1.1" 6858 + }, 6859 + "engines": { 6860 + "node": ">=6" 6861 + } 6862 + }, 6434 6863 "node_modules/tinyglobby": { 6435 6864 "version": "0.2.15", 6436 6865 "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", ··· 6536 6965 "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", 6537 6966 "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", 6538 6967 "license": "0BSD" 6968 + }, 6969 + "node_modules/tunnel-agent": { 6970 + "version": "0.6.0", 6971 + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", 6972 + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", 6973 + "license": "Apache-2.0", 6974 + "dependencies": { 6975 + "safe-buffer": "^5.0.1" 6976 + }, 6977 + "engines": { 6978 + "node": "*" 6979 + } 6539 6980 }, 6540 6981 "node_modules/type-check": { 6541 6982 "version": "0.4.0", ··· 6792 7233 "punycode": "^2.1.0" 6793 7234 } 6794 7235 }, 7236 + "node_modules/util-deprecate": { 7237 + "version": "1.0.2", 7238 + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 7239 + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", 7240 + "license": "MIT" 7241 + }, 6795 7242 "node_modules/which": { 6796 7243 "version": "2.0.2", 6797 7244 "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", ··· 6906 7353 "engines": { 6907 7354 "node": ">=0.10.0" 6908 7355 } 7356 + }, 7357 + "node_modules/wrappy": { 7358 + "version": "1.0.2", 7359 + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 7360 + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", 7361 + "license": "ISC" 6909 7362 }, 6910 7363 "node_modules/yallist": { 6911 7364 "version": "3.1.1",
+3
package.json
··· 10 10 }, 11 11 "dependencies": { 12 12 "@atproto/oauth-client-node": "^0.3.16", 13 + "better-sqlite3": "^12.6.2", 14 + "kysely": "^0.28.11", 13 15 "next": "16.1.6", 14 16 "react": "19.2.3", 15 17 "react-dom": "19.2.3" 16 18 }, 17 19 "devDependencies": { 18 20 "@tailwindcss/postcss": "^4", 21 + "@types/better-sqlite3": "^7.6.13", 19 22 "@types/node": "^20", 20 23 "@types/react": "^19", 21 24 "@types/react-dom": "^19",