Unfollow tool for Bluesky

Merge pull request #2 from mary-ext/mary/atcute-oauth

Switch to atcute's OAuth client

authored by Juliet and committed by GitHub fdecf843 b46524e8

+42 -237
package-lock.json
··· 11 11 "dependencies": { 12 12 "@atcute/bluesky": "^1.0.7", 13 13 "@atcute/client": "^2.0.2", 14 - "@atproto/oauth-client-browser": "^0.2.0", 14 + "@atcute/oauth-browser-client": "^1.0.1", 15 15 "solid-js": "^1.8.11" 16 16 }, 17 17 "devDependencies": { 18 18 "@tailwindcss/forms": "^0.5.7", 19 + "@types/node": "^22.7.5", 19 20 "autoprefixer": "^10.4.19", 20 21 "postcss": "^8.4.39", 21 22 "prettier": "^3.3.3", ··· 64 65 } 65 66 }, 66 67 "node_modules/@atcute/client": { 67 - "version": "2.0.2", 68 - "resolved": "https://registry.npmjs.org/@atcute/client/-/client-2.0.2.tgz", 69 - "integrity": "sha512-bTGoo8cs0B0gXLwI8YYaFayjDIlo8V1wxgng3HkA+jyz22wCh8aL0/4F9Tt4GDKXGCgf+/6IHg1M6uGOX8JVSw==", 68 + "version": "2.0.3", 69 + "resolved": "https://registry.npmjs.org/@atcute/client/-/client-2.0.3.tgz", 70 + "integrity": "sha512-j9GryA5l+4F0BTQWa6/1XmsuSPSq+bqNCY3mrHUGD592hMqUZxgpYDLgRWL+719V287AW/56AwvFYlbjlENp7A==", 70 71 "license": "MIT" 71 72 }, 72 - "node_modules/@atproto-labs/did-resolver": { 73 - "version": "0.1.4", 74 - "resolved": "https://registry.npmjs.org/@atproto-labs/did-resolver/-/did-resolver-0.1.4.tgz", 75 - "integrity": "sha512-5d+LHScS2ueYsFRjMOC3c1EwM2ui1yBVbBA0yY3MH7aydbljm5D28scsOVuymIhHwPFwcGvZbMON4PVSfpBbbQ==", 73 + "node_modules/@atcute/oauth-browser-client": { 74 + "version": "1.0.1", 75 + "resolved": "https://registry.npmjs.org/@atcute/oauth-browser-client/-/oauth-browser-client-1.0.1.tgz", 76 + "integrity": "sha512-vMHf2P+R4tv4qG5cB5YHgOhZf3HNkZhV1DIWXBsRgbfBqoIRWSVTPt2NOunOk0aHtY1NvzU0RTr/j6OnKps/WA==", 76 77 "license": "MIT", 77 78 "dependencies": { 78 - "@atproto-labs/fetch": "0.1.1", 79 - "@atproto-labs/pipe": "0.1.0", 80 - "@atproto-labs/simple-store": "0.1.1", 81 - "@atproto-labs/simple-store-memory": "0.1.1", 82 - "@atproto/did": "0.1.2", 83 - "zod": "^3.23.8" 79 + "@atcute/client": "^2.0.3", 80 + "nanoid": "^5.0.7" 84 81 } 85 82 }, 86 - "node_modules/@atproto-labs/fetch": { 87 - "version": "0.1.1", 88 - "resolved": "https://registry.npmjs.org/@atproto-labs/fetch/-/fetch-0.1.1.tgz", 89 - "integrity": "sha512-X1zO1MDoJzEurbWXMAe1H8EZ995Xam/aXdxhGVrXmOMyPDuvBa1oxwh/kQNZRCKcMQUbiwkk+Jfq6ZkTuvGbww==", 83 + "node_modules/@atcute/oauth-browser-client/node_modules/nanoid": { 84 + "version": "5.0.7", 85 + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.0.7.tgz", 86 + "integrity": "sha512-oLxFY2gd2IqnjcYyOXD8XGCftpGtZP2AbHbOkthDkvRywH5ayNtPVy9YlOPcHckXzbLTCHpkb7FB+yuxKV13pQ==", 87 + "funding": [ 88 + { 89 + "type": "github", 90 + "url": "https://github.com/sponsors/ai" 91 + } 92 + ], 90 93 "license": "MIT", 91 - "dependencies": { 92 - "@atproto-labs/pipe": "0.1.0" 94 + "bin": { 95 + "nanoid": "bin/nanoid.js" 93 96 }, 94 - "optionalDependencies": { 95 - "zod": "^3.23.8" 96 - } 97 - }, 98 - "node_modules/@atproto-labs/handle-resolver": { 99 - "version": "0.1.3", 100 - "resolved": "https://registry.npmjs.org/@atproto-labs/handle-resolver/-/handle-resolver-0.1.3.tgz", 101 - "integrity": "sha512-pUn8uqQNqMpecQjO0UWmdKhKX1NnXdLBXHRgID2g4kmhpz3hkbbec+h34uSk6wLfZnwPFaVQnGdEkyMq/tNToQ==", 102 - "license": "MIT", 103 - "dependencies": { 104 - "@atproto-labs/simple-store": "0.1.1", 105 - "@atproto-labs/simple-store-memory": "0.1.1", 106 - "@atproto/did": "0.1.2", 107 - "zod": "^3.23.8" 108 - } 109 - }, 110 - "node_modules/@atproto-labs/identity-resolver": { 111 - "version": "0.1.4", 112 - "resolved": "https://registry.npmjs.org/@atproto-labs/identity-resolver/-/identity-resolver-0.1.4.tgz", 113 - "integrity": "sha512-uaRJsYFCRZQcw0c7S+RuziSVm5qHcK3N6uqsXz8NzYoJAE55Ah4DkQMj0rfapZmb0jyBau04RC/WoI4PSim+Aw==", 114 - "license": "MIT", 115 - "dependencies": { 116 - "@atproto-labs/did-resolver": "0.1.4", 117 - "@atproto-labs/handle-resolver": "0.1.3", 118 - "@atproto/syntax": "0.3.0" 119 - } 120 - }, 121 - "node_modules/@atproto-labs/pipe": { 122 - "version": "0.1.0", 123 - "resolved": "https://registry.npmjs.org/@atproto-labs/pipe/-/pipe-0.1.0.tgz", 124 - "integrity": "sha512-ghOqHFyJlQVFPESzlVHjKroP0tPzbmG5Jms0dNI9yLDEfL8xp4OFPWLX4f6T8mRq69wWs4nIDM3sSsFbFqLa1w==", 125 - "license": "MIT" 126 - }, 127 - "node_modules/@atproto-labs/simple-store": { 128 - "version": "0.1.1", 129 - "resolved": "https://registry.npmjs.org/@atproto-labs/simple-store/-/simple-store-0.1.1.tgz", 130 - "integrity": "sha512-WKILW2b3QbAYKh+w5U2x6p5FqqLl0nAeLwGeDY+KjX01K4Dq3vQTR9b/qNp0jZm48CabPQVrqCv0PPU9LgRRRg==", 131 - "license": "MIT" 132 - }, 133 - "node_modules/@atproto-labs/simple-store-memory": { 134 - "version": "0.1.1", 135 - "resolved": "https://registry.npmjs.org/@atproto-labs/simple-store-memory/-/simple-store-memory-0.1.1.tgz", 136 - "integrity": "sha512-PCRqhnZ8NBNBvLku53O56T0lsVOtclfIrQU/rwLCc4+p45/SBPrRYNBi6YFq5rxZbK6Njos9MCmILV/KLQxrWA==", 137 - "license": "MIT", 138 - "dependencies": { 139 - "@atproto-labs/simple-store": "0.1.1", 140 - "lru-cache": "^10.2.0" 141 - } 142 - }, 143 - "node_modules/@atproto/common-web": { 144 - "version": "0.3.1", 145 - "resolved": "https://registry.npmjs.org/@atproto/common-web/-/common-web-0.3.1.tgz", 146 - "integrity": "sha512-N7wiTnus5vAr+lT//0y8m/FaHHLJ9LpGuEwkwDAeV3LCiPif4m/FS8x/QOYrx1PdZQwKso95RAPzCGWQBH5j6Q==", 147 - "license": "MIT", 148 - "dependencies": { 149 - "graphemer": "^1.4.0", 150 - "multiformats": "^9.9.0", 151 - "uint8arrays": "3.0.0", 152 - "zod": "^3.23.8" 153 - } 154 - }, 155 - "node_modules/@atproto/did": { 156 - "version": "0.1.2", 157 - "resolved": "https://registry.npmjs.org/@atproto/did/-/did-0.1.2.tgz", 158 - "integrity": "sha512-gmY1SyAuqfmsFbIXkUIScfnULqn39FoUNz4oE0fUuMu9in6PEyoxlmD2lAo7Q3KMy3X/hvTn2u5f8W/2KuDg1w==", 159 - "license": "MIT", 160 - "dependencies": { 161 - "zod": "^3.23.8" 162 - } 163 - }, 164 - "node_modules/@atproto/jwk": { 165 - "version": "0.1.1", 166 - "resolved": "https://registry.npmjs.org/@atproto/jwk/-/jwk-0.1.1.tgz", 167 - "integrity": "sha512-6h/bj1APUk7QcV9t/oA6+9DB5NZx9SZru9x+/pV5oHFI9Xz4ZuM5+dq1PfsJV54pZyqdnZ6W6M717cxoC7q7og==", 168 - "license": "MIT", 169 - "dependencies": { 170 - "multiformats": "^9.9.0", 171 - "zod": "^3.23.8" 172 - } 173 - }, 174 - "node_modules/@atproto/jwk-jose": { 175 - "version": "0.1.2", 176 - "resolved": "https://registry.npmjs.org/@atproto/jwk-jose/-/jwk-jose-0.1.2.tgz", 177 - "integrity": "sha512-lDwc/6lLn2aZ/JpyyggyjLFsJPMntrVzryyGUx5aNpuTS8SIuc4Ky0REhxqfLopQXJJZCuRRjagHG3uP05/moQ==", 178 - "license": "MIT", 179 - "dependencies": { 180 - "@atproto/jwk": "0.1.1", 181 - "jose": "^5.2.0" 182 - } 183 - }, 184 - "node_modules/@atproto/jwk-webcrypto": { 185 - "version": "0.1.2", 186 - "resolved": "https://registry.npmjs.org/@atproto/jwk-webcrypto/-/jwk-webcrypto-0.1.2.tgz", 187 - "integrity": "sha512-vTBUbUZXh0GI+6KJiPGukmI4BQEHFAij8fJJ4WnReF/hefAs3ISZtrWZHGBebz+q2EcExYlnhhlmxvDzV7veGw==", 188 - "license": "MIT", 189 - "dependencies": { 190 - "@atproto/jwk": "0.1.1", 191 - "@atproto/jwk-jose": "0.1.2" 192 - } 193 - }, 194 - "node_modules/@atproto/lexicon": { 195 - "version": "0.4.2", 196 - "resolved": "https://registry.npmjs.org/@atproto/lexicon/-/lexicon-0.4.2.tgz", 197 - "integrity": "sha512-CXoOkhcdF3XVUnR2oNgCs2ljWfo/8zUjxL5RIhJW/UNLp/FSl+KpF8Jm5fbk8Y/XXVPGRAsv9OYfxyU/14N/pw==", 198 - "license": "MIT", 199 - "dependencies": { 200 - "@atproto/common-web": "^0.3.1", 201 - "@atproto/syntax": "^0.3.0", 202 - "iso-datestring-validator": "^2.2.2", 203 - "multiformats": "^9.9.0", 204 - "zod": "^3.23.8" 205 - } 206 - }, 207 - "node_modules/@atproto/oauth-client": { 208 - "version": "0.2.2", 209 - "resolved": "https://registry.npmjs.org/@atproto/oauth-client/-/oauth-client-0.2.2.tgz", 210 - "integrity": "sha512-hYL7Hx2h52zeC1WZeFjV9FFfqt8PZnURKofz0VJVeiPqB9wySJ46MgFVxsZ3t08c03WSDugujEz2JlhtQpS7Zg==", 211 - "license": "MIT", 212 - "dependencies": { 213 - "@atproto-labs/did-resolver": "0.1.4", 214 - "@atproto-labs/fetch": "0.1.1", 215 - "@atproto-labs/handle-resolver": "0.1.3", 216 - "@atproto-labs/identity-resolver": "0.1.4", 217 - "@atproto-labs/simple-store": "0.1.1", 218 - "@atproto-labs/simple-store-memory": "0.1.1", 219 - "@atproto/did": "0.1.2", 220 - "@atproto/jwk": "0.1.1", 221 - "@atproto/oauth-types": "0.1.5", 222 - "@atproto/xrpc": "0.6.3", 223 - "multiformats": "^9.9.0", 224 - "zod": "^3.23.8" 225 - } 226 - }, 227 - "node_modules/@atproto/oauth-client-browser": { 228 - "version": "0.2.2", 229 - "resolved": "https://registry.npmjs.org/@atproto/oauth-client-browser/-/oauth-client-browser-0.2.2.tgz", 230 - "integrity": "sha512-Y36LTqNMq2iqWrdGJljD5yqWDy9CbhHElxI5syiPCcgLCm9/UU8Hu5QTgCrESDzAyV5viYWDweeqAmLrpVrpQg==", 231 - "license": "MIT", 232 - "dependencies": { 233 - "@atproto-labs/did-resolver": "0.1.4", 234 - "@atproto-labs/handle-resolver": "0.1.3", 235 - "@atproto-labs/simple-store": "0.1.1", 236 - "@atproto/did": "0.1.2", 237 - "@atproto/jwk": "0.1.1", 238 - "@atproto/jwk-webcrypto": "0.1.2", 239 - "@atproto/oauth-client": "0.2.2", 240 - "@atproto/oauth-types": "0.1.5" 241 - } 242 - }, 243 - "node_modules/@atproto/oauth-types": { 244 - "version": "0.1.5", 245 - "resolved": "https://registry.npmjs.org/@atproto/oauth-types/-/oauth-types-0.1.5.tgz", 246 - "integrity": "sha512-vNab/6BYUQCfmfhGc3G61EcatQxvh2d41FDWqR8CAYsblNXO6nOEVXn7cXdQUkb3K49LU0vy5Jf1+wFNcpY3IQ==", 247 - "license": "MIT", 248 - "dependencies": { 249 - "@atproto/jwk": "0.1.1", 250 - "zod": "^3.23.8" 251 - } 252 - }, 253 - "node_modules/@atproto/syntax": { 254 - "version": "0.3.0", 255 - "resolved": "https://registry.npmjs.org/@atproto/syntax/-/syntax-0.3.0.tgz", 256 - "integrity": "sha512-Weq0ZBxffGHDXHl9U7BQc2BFJi/e23AL+k+i5+D9hUq/bzT4yjGsrCejkjq0xt82xXDjmhhvQSZ0LqxyZ5woxA==", 257 - "license": "MIT" 258 - }, 259 - "node_modules/@atproto/xrpc": { 260 - "version": "0.6.3", 261 - "resolved": "https://registry.npmjs.org/@atproto/xrpc/-/xrpc-0.6.3.tgz", 262 - "integrity": "sha512-S3tRvOdA9amPkKLll3rc4vphlDitLrkN5TwWh5Tu/jzk7mnobVVE3akYgICV9XCNHKjWM+IAPxFFI2qi+VW6nQ==", 263 - "license": "MIT", 264 - "dependencies": { 265 - "@atproto/lexicon": "^0.4.2", 266 - "zod": "^3.23.8" 97 + "engines": { 98 + "node": "^18 || >=20" 267 99 } 268 100 }, 269 101 "node_modules/@babel/code-frame": { ··· 1661 1493 "dev": true, 1662 1494 "license": "MIT" 1663 1495 }, 1496 + "node_modules/@types/node": { 1497 + "version": "22.7.5", 1498 + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.5.tgz", 1499 + "integrity": "sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==", 1500 + "dev": true, 1501 + "license": "MIT", 1502 + "dependencies": { 1503 + "undici-types": "~6.19.2" 1504 + } 1505 + }, 1664 1506 "node_modules/agent-base": { 1665 1507 "version": "7.1.1", 1666 1508 "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", ··· 2416 2258 "node": ">=4" 2417 2259 } 2418 2260 }, 2419 - "node_modules/graphemer": { 2420 - "version": "1.4.0", 2421 - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", 2422 - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", 2423 - "license": "MIT" 2424 - }, 2425 2261 "node_modules/has-flag": { 2426 2262 "version": "3.0.0", 2427 2263 "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", ··· 2605 2441 "dev": true, 2606 2442 "license": "ISC" 2607 2443 }, 2608 - "node_modules/iso-datestring-validator": { 2609 - "version": "2.2.2", 2610 - "resolved": "https://registry.npmjs.org/iso-datestring-validator/-/iso-datestring-validator-2.2.2.tgz", 2611 - "integrity": "sha512-yLEMkBbLZTlVQqOnQ4FiMujR6T4DEcCb1xizmvXS+OxuhwcbtynoosRzdMA69zZCShCNAbi+gJ71FxZBBXx1SA==", 2612 - "license": "MIT" 2613 - }, 2614 2444 "node_modules/jackspeak": { 2615 2445 "version": "3.4.3", 2616 2446 "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", ··· 2739 2569 "jiti": "bin/jiti.js" 2740 2570 } 2741 2571 }, 2742 - "node_modules/jose": { 2743 - "version": "5.9.3", 2744 - "resolved": "https://registry.npmjs.org/jose/-/jose-5.9.3.tgz", 2745 - "integrity": "sha512-egLIoYSpcd+QUF+UHgobt5YzI2Pkw/H39ou9suW687MY6PmCwPmkNV/4TNjn1p2tX5xO3j0d0sq5hiYE24bSlg==", 2746 - "license": "MIT", 2747 - "funding": { 2748 - "url": "https://github.com/sponsors/panva" 2749 - } 2750 - }, 2751 2572 "node_modules/js-tokens": { 2752 2573 "version": "4.0.0", 2753 2574 "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", ··· 2843 2664 "version": "10.4.3", 2844 2665 "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", 2845 2666 "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", 2667 + "dev": true, 2846 2668 "license": "ISC" 2847 2669 }, 2848 2670 "node_modules/merge-anything": { ··· 2950 2772 "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", 2951 2773 "dev": true, 2952 2774 "license": "MIT" 2953 - }, 2954 - "node_modules/multiformats": { 2955 - "version": "9.9.0", 2956 - "resolved": "https://registry.npmjs.org/multiformats/-/multiformats-9.9.0.tgz", 2957 - "integrity": "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg==", 2958 - "license": "(Apache-2.0 AND MIT)" 2959 2775 }, 2960 2776 "node_modules/mz": { 2961 2777 "version": "2.7.0", ··· 4040 3856 "node": ">=14.17" 4041 3857 } 4042 3858 }, 4043 - "node_modules/uint8arrays": { 4044 - "version": "3.0.0", 4045 - "resolved": "https://registry.npmjs.org/uint8arrays/-/uint8arrays-3.0.0.tgz", 4046 - "integrity": "sha512-HRCx0q6O9Bfbp+HHSfQQKD7wU70+lydKVt4EghkdOvlK/NlrF90z+eXV34mUd48rNvVJXwkrMSPpCATkct8fJA==", 4047 - "license": "MIT", 4048 - "dependencies": { 4049 - "multiformats": "^9.4.2" 4050 - } 3859 + "node_modules/undici-types": { 3860 + "version": "6.19.8", 3861 + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", 3862 + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", 3863 + "dev": true, 3864 + "license": "MIT" 4051 3865 }, 4052 3866 "node_modules/update-browserslist-db": { 4053 3867 "version": "1.1.1", ··· 4458 4272 }, 4459 4273 "engines": { 4460 4274 "node": ">= 14" 4461 - } 4462 - }, 4463 - "node_modules/zod": { 4464 - "version": "3.23.8", 4465 - "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", 4466 - "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", 4467 - "license": "MIT", 4468 - "funding": { 4469 - "url": "https://github.com/sponsors/colinhacks" 4470 4275 } 4471 4276 } 4472 4277 }
+2 -1
package.json
··· 12 12 "license": "0BSD", 13 13 "devDependencies": { 14 14 "@tailwindcss/forms": "^0.5.7", 15 + "@types/node": "^22.7.5", 15 16 "autoprefixer": "^10.4.19", 16 17 "postcss": "^8.4.39", 17 18 "prettier": "^3.3.3", ··· 25 26 "dependencies": { 26 27 "@atcute/bluesky": "^1.0.7", 27 28 "@atcute/client": "^2.0.2", 28 - "@atproto/oauth-client-browser": "^0.2.0", 29 + "@atcute/oauth-browser-client": "^1.0.1", 29 30 "solid-js": "^1.8.11" 30 31 } 31 32 }
+72 -34
src/App.tsx
··· 1 1 import { 2 + createEffect, 2 3 createSignal, 3 - onMount, 4 4 For, 5 + onMount, 5 6 Show, 6 7 type Component, 7 - createEffect, 8 8 } from "solid-js"; 9 9 import { createStore } from "solid-js/store"; 10 10 11 - import { 12 - BrowserOAuthClient, 13 - OAuthSession, 14 - } from "@atproto/oauth-client-browser"; 15 - import "@atcute/bluesky/lexicons"; 16 11 import { XRPC } from "@atcute/client"; 17 12 import { 18 13 AppBskyGraphFollow, 19 - ComAtprotoRepoListRecords, 14 + At, 15 + Brand, 20 16 ComAtprotoRepoApplyWrites, 21 - Brand, 17 + ComAtprotoRepoListRecords, 22 18 } from "@atcute/client/lexicons"; 19 + import { 20 + configureOAuth, 21 + createAuthorizationUrl, 22 + finalizeAuthorization, 23 + getSession, 24 + OAuthUserAgent, 25 + resolveFromIdentity, 26 + type Session, 27 + } from "@atcute/oauth-browser-client"; 28 + 29 + configureOAuth({ 30 + metadata: { 31 + client_id: import.meta.env.VITE_OAUTH_CLIENT_ID, 32 + redirect_uri: import.meta.env.VITE_OAUTH_REDIRECT_URL, 33 + }, 34 + }); 23 35 24 36 enum RepoStatus { 25 37 BLOCKEDBY = 1 << 0, ··· 44 56 const [followRecords, setFollowRecords] = createStore<FollowRecord[]>([]); 45 57 const [loginState, setLoginState] = createSignal(false); 46 58 let rpc: XRPC; 47 - let session: OAuthSession; 59 + let agent: OAuthUserAgent; 48 60 49 61 const resolveDid = async (did: string) => { 50 62 const res = await fetch( ··· 66 78 const [loginInput, setLoginInput] = createSignal(""); 67 79 const [handle, setHandle] = createSignal(""); 68 80 const [notice, setNotice] = createSignal(""); 69 - let client: BrowserOAuthClient; 70 81 71 82 onMount(async () => { 72 83 setNotice("Loading..."); 73 - client = await BrowserOAuthClient.load({ 74 - clientId: "https://cleanfollow-bsky.pages.dev/client-metadata.json", 75 - handleResolver: "https://boletus.us-west.host.bsky.network", 76 - }); 77 84 78 - client.addEventListener("deleted", () => { 79 - setLoginState(false); 80 - }); 81 - const result = await client.init().catch(() => {}); 85 + const init = async (): Promise<Session | undefined> => { 86 + const params = new URLSearchParams(location.hash.slice(1)); 87 + 88 + if (params.has("state") && (params.has("code") || params.has("error"))) { 89 + history.replaceState(null, "", location.pathname + location.search); 82 90 83 - if (result) { 84 - session = result.session; 85 - rpc = new XRPC({ 86 - handler: { handle: session.fetchHandler.bind(session) }, 87 - }); 91 + const session = await finalizeAuthorization(params); 92 + const did = session.info.sub; 93 + 94 + localStorage.setItem("lastSignedIn", did); 95 + return session; 96 + } else { 97 + const lastSignedIn = localStorage.getItem("lastSignedIn"); 98 + 99 + if (lastSignedIn) { 100 + try { 101 + const session = await getSession(lastSignedIn as At.DID); 102 + return session; 103 + } catch (err) { 104 + localStorage.removeItem("lastSignedIn"); 105 + throw err; 106 + } 107 + } 108 + } 109 + }; 110 + 111 + const session = await init().catch(() => {}); 112 + 113 + if (session) { 114 + agent = new OAuthUserAgent(session); 115 + rpc = new XRPC({ handler: agent }); 116 + 88 117 setLoginState(true); 89 - setHandle(await resolveDid(session.did)); 118 + setHandle(await resolveDid(agent.sub)); 90 119 } 120 + 91 121 setNotice(""); 92 122 }); 93 123 94 124 const loginBsky = async (handle: string) => { 95 - setNotice("Redirecting..."); 96 125 try { 97 - await client.signIn(handle, { 98 - scope: "atproto transition:generic", 99 - signal: new AbortController().signal, 126 + setNotice(`Resolving your identity...`); 127 + const resolved = await resolveFromIdentity(handle); 128 + 129 + setNotice(`Contacting your data server...`); 130 + const authUrl = await createAuthorizationUrl({ 131 + scope: import.meta.env.VITE_OAUTH_SCOPE, 132 + ...resolved, 100 133 }); 134 + 135 + setNotice(`Redirecting...`); 136 + await new Promise((resolve) => setTimeout(resolve, 250)); 137 + 138 + location.assign(authUrl); 101 139 } catch (err) { 102 - setNotice("Error during OAuth redirection"); 140 + setNotice("Error during OAuth login"); 103 141 } 104 142 }; 105 143 106 144 const logoutBsky = async () => { 107 - if (session.sub) await client.revoke(session.sub); 145 + await agent.signOut(); 108 146 }; 109 147 110 148 return ( ··· 157 195 const fetchPage = async (cursor?: string) => { 158 196 return await rpc.get("com.atproto.repo.listRecords", { 159 197 params: { 160 - repo: session.did, 198 + repo: agent.sub, 161 199 collection: "app.bsky.graph.follow", 162 200 limit: PAGE_LIMIT, 163 201 cursor: cursor, ··· 203 241 viewer.blocking || viewer.blockingByList ? 204 242 RepoStatus.BLOCKEDBY | RepoStatus.BLOCKING 205 243 : RepoStatus.BLOCKEDBY; 206 - } else if (res.data.did.includes(session.did)) { 244 + } else if (res.data.did.includes(agent.sub)) { 207 245 status = RepoStatus.YOURSELF; 208 246 } else if (viewer.blocking || viewer.blockingByList) { 209 247 status = RepoStatus.BLOCKING; ··· 260 298 for (let i = 0; i < writes.length; i += BATCHSIZE) { 261 299 await rpc.call("com.atproto.repo.applyWrites", { 262 300 data: { 263 - repo: session.did, 301 + repo: agent.sub, 264 302 writes: writes.slice(i, i + BATCHSIZE), 265 303 }, 266 304 });
+14
src/vite-env.d.ts
··· 1 + /// <reference types="vite/client" /> 2 + /// <reference types="@atcute/bluesky/lexicons" /> 3 + 4 + interface ImportMetaEnv { 5 + readonly VITE_DEV_SERVER_PORT?: string; 6 + readonly VITE_CLIENT_URI: string; 7 + readonly VITE_OAUTH_CLIENT_ID: string; 8 + readonly VITE_OAUTH_REDIRECT_URL: string; 9 + readonly VITE_OAUTH_SCOPE: string; 10 + } 11 + 12 + interface ImportMeta { 13 + readonly env: ImportMetaEnv; 14 + }
+26
tsconfig.app.json
··· 1 + { 2 + "compilerOptions": { 3 + "target": "ESNext", 4 + "useDefineForClassFields": true, 5 + "module": "ESNext", 6 + "lib": ["ESNext", "DOM", "DOM.Iterable"], 7 + "types": [], 8 + "skipLibCheck": true, 9 + 10 + /* Bundler mode */ 11 + "moduleResolution": "bundler", 12 + "allowImportingTsExtensions": true, 13 + "isolatedModules": true, 14 + "moduleDetection": "force", 15 + "noEmit": true, 16 + "jsx": "preserve", 17 + "jsxImportSource": "solid-js", 18 + 19 + /* Linting */ 20 + "strict": true, 21 + "noUnusedLocals": true, 22 + "noUnusedParameters": true, 23 + "noFallthroughCasesInSwitch": true 24 + }, 25 + "include": ["src"] 26 + }
+6 -16
tsconfig.json
··· 1 1 { 2 - "compilerOptions": { 3 - "strict": true, 4 - "target": "ESNext", 5 - "module": "nodenext", 6 - "moduleResolution": "nodenext", 7 - "allowSyntheticDefaultImports": true, 8 - "esModuleInterop": true, 9 - "jsx": "preserve", 10 - "jsxImportSource": "solid-js", 11 - "types": [ 12 - "vite/client" 13 - ], 14 - "noEmit": true, 15 - "isolatedModules": true 16 - } 17 - } 2 + "files": [], 3 + "references": [ 4 + { "path": "./tsconfig.app.json" }, 5 + { "path": "./tsconfig.node.json" } 6 + ] 7 + }
+23
tsconfig.node.json
··· 1 + { 2 + "compilerOptions": { 3 + "target": "ESNext", 4 + "lib": ["ESNext"], 5 + "types": ["node"], 6 + "module": "ESNext", 7 + "skipLibCheck": true, 8 + 9 + /* Bundler mode */ 10 + "moduleResolution": "bundler", 11 + "allowImportingTsExtensions": true, 12 + "isolatedModules": true, 13 + "moduleDetection": "force", 14 + "noEmit": true, 15 + 16 + /* Linting */ 17 + "strict": true, 18 + "noUnusedLocals": true, 19 + "noUnusedParameters": true, 20 + "noFallthroughCasesInSwitch": true 21 + }, 22 + "include": ["vite.config.ts"] 23 + }
+35 -2
vite.config.ts
··· 2 2 import solidPlugin from "vite-plugin-solid"; 3 3 // import devtools from 'solid-devtools/vite'; 4 4 5 + import metadata from "./public/client-metadata.json"; 6 + 7 + const SERVER_HOST = "127.0.0.1"; 8 + const SERVER_PORT = 13213; 9 + 5 10 export default defineConfig({ 6 11 plugins: [ 7 12 /* ··· 10 15 */ 11 16 // devtools(), 12 17 solidPlugin(), 18 + 19 + // Injects OAuth-related variables 20 + { 21 + name: "oauth", 22 + config(_conf, { command }) { 23 + if (command === "build") { 24 + process.env.VITE_OAUTH_CLIENT_ID = metadata.client_id; 25 + process.env.VITE_OAUTH_REDIRECT_URL = metadata.redirect_uris[0]; 26 + } else { 27 + const redirectUri = ((): string => { 28 + const url = new URL(metadata.redirect_uris[0]); 29 + return `http://${SERVER_HOST}:${SERVER_PORT}${url.pathname}`; 30 + })(); 31 + 32 + const clientId = 33 + `http://localhost` + 34 + `?redirect_uri=${encodeURIComponent(redirectUri)}` + 35 + `&scope=${encodeURIComponent(metadata.scope)}`; 36 + 37 + process.env.VITE_DEV_SERVER_PORT = "" + SERVER_PORT; 38 + process.env.VITE_OAUTH_CLIENT_ID = clientId; 39 + process.env.VITE_OAUTH_REDIRECT_URL = redirectUri; 40 + } 41 + 42 + process.env.VITE_CLIENT_URI = metadata.client_uri; 43 + process.env.VITE_OAUTH_SCOPE = metadata.scope; 44 + }, 45 + }, 13 46 ], 14 47 server: { 15 - host: "127.0.0.1", 16 - port: 3000, 48 + host: SERVER_HOST, 49 + port: SERVER_PORT, 17 50 }, 18 51 build: { 19 52 target: "esnext",