Demo using Slices Network GraphQL Relay API to make a teal.fm client

🎷

+24
.gitignore
··· 1 + # Logs 2 + logs 3 + *.log 4 + npm-debug.log* 5 + yarn-debug.log* 6 + yarn-error.log* 7 + pnpm-debug.log* 8 + lerna-debug.log* 9 + 10 + node_modules 11 + dist 12 + dist-ssr 13 + *.local 14 + 15 + # Editor directories and files 16 + .vscode/* 17 + !.vscode/extensions.json 18 + .idea 19 + .DS_Store 20 + *.suo 21 + *.ntvs* 22 + *.njsproj 23 + *.sln 24 + *.sw?
+131
README.md
··· 1 + # Slices Teal Relay Demo 2 + 3 + A teal.fm timeline built with React, Relay, and Tailwind CSS. Displays music 4 + plays from the Slices GraphQL API. 5 + 6 + ## Features 7 + 8 + - Real-time music scrobble feed 9 + - User profiles with listening history 10 + - Album artwork from MusicBrainz 11 + - Infinite scroll pagination 12 + - Dark mode UI inspired by Last.fm 13 + - Powered by Relay for efficient GraphQL data management 14 + 15 + ## Tech Stack 16 + 17 + - **React** - UI framework 18 + - **Relay** - GraphQL client with automatic cache management 19 + - **React Router** - Client-side routing 20 + - **Tailwind CSS** - Styling 21 + - **Vite** - Build tool 22 + - **TypeScript** - Type safety 23 + 24 + ## Prerequisites 25 + 26 + - Node.js 18+ or Deno 27 + - npm or pnpm 28 + 29 + ## Getting Started 30 + 31 + 1. **Install dependencies** 32 + 33 + ```bash 34 + npm install 35 + ``` 36 + 37 + 2. **Fetch the GraphQL schema** 38 + 39 + ```bash 40 + npm run schema 41 + ``` 42 + 43 + 3. **Generate Relay types** 44 + 45 + ```bash 46 + npx relay-compiler 47 + ``` 48 + 49 + 4. **Start the development server** 50 + 51 + ```bash 52 + npm run dev 53 + ``` 54 + 55 + The app will be available at `http://localhost:5173` 56 + 57 + ## Development 58 + 59 + ### Updating the GraphQL Schema 60 + 61 + The project connects to the Slices API. To update the schema: 62 + 63 + ```bash 64 + npm run schema 65 + npx relay-compiler 66 + ``` 67 + 68 + ### Project Structure 69 + 70 + ``` 71 + src/ 72 + ├── App.tsx # Main feed component 73 + ├── Profile.tsx # User profile page 74 + ├── TrackItem.tsx # Individual track card component 75 + ├── LoadingFallback.tsx # Loading skeleton 76 + ├── useAlbumArt.ts # Hook to fetch album art from MusicBrainz 77 + └── __generated__/ # Relay-generated TypeScript types 78 + ``` 79 + 80 + ### Working with Relay 81 + 82 + This project uses Relay's modern API with: 83 + 84 + - **`useLazyLoadQuery`** - Load data on component mount 85 + - **`usePaginationFragment`** - Infinite scroll pagination with automatic cache 86 + updates 87 + - **`useFragment`** - Fragment composition for data colocation 88 + - **`@connection`** directive - Automatic list merging for pagination 89 + 90 + After modifying any GraphQL queries or fragments, run: 91 + 92 + ```bash 93 + npx relay-compiler 94 + ``` 95 + 96 + ### Environment 97 + 98 + The app connects to: 99 + 100 + - **Production API**: `https://api.slices.network/graphql` 101 + - **Slice**: 102 + `at://did:plc:fpruhuo22xkm5o7ttr2ktxdo/network.slices.slice/3m257yljpbg2a` 103 + 104 + ## Scripts 105 + 106 + - `npm run dev` - Start development server 107 + - `npm run build` - Build for production 108 + - `npm run preview` - Preview production build 109 + - `npm run lint` - Run ESLint 110 + - `npm run schema` - Fetch GraphQL schema from production API 111 + 112 + ## Features in Detail 113 + 114 + ### Album Artwork 115 + 116 + The app fetches album artwork from MusicBrainz using the `releaseMbId` field. 117 + The fetch happens asynchronously per track and displays when available. 118 + 119 + ### Infinite Scroll 120 + 121 + Both the main feed and profile pages use Relay's `usePaginationFragment` for 122 + efficient infinite scrolling with automatic cache management. 123 + 124 + ### Routing 125 + 126 + - `/` - Main feed 127 + - `/profile/:handle` - User profile page 128 + 129 + ## License 130 + 131 + MIT
+2030
deno.lock
··· 1 + { 2 + "version": "5", 3 + "specifiers": { 4 + "npm:@eslint/js@^9.36.0": "9.36.0", 5 + "npm:@tailwindcss/postcss@^4.1.14": "4.1.14", 6 + "npm:@types/node@^24.6.0": "24.6.2", 7 + "npm:@types/react-dom@^19.1.9": "19.2.0_@types+react@19.2.0", 8 + "npm:@types/react-relay@^18.2.1": "18.2.1", 9 + "npm:@types/react@^19.1.16": "19.2.0", 10 + "npm:@types/relay-runtime@^19.0.3": "19.0.3", 11 + "npm:@vitejs/plugin-react@^5.0.4": "5.0.4_vite@7.1.8__@types+node@24.6.2__picomatch@4.0.3_@babel+core@7.28.4_@types+node@24.6.2", 12 + "npm:autoprefixer@^10.4.21": "10.4.21_postcss@8.5.6", 13 + "npm:babel-plugin-relay@^20.1.1": "20.1.1", 14 + "npm:eslint-plugin-react-hooks@^5.2.0": "5.2.0_eslint@9.36.0", 15 + "npm:eslint-plugin-react-refresh@~0.4.22": "0.4.23_eslint@9.36.0", 16 + "npm:eslint@^9.36.0": "9.36.0", 17 + "npm:globals@^16.4.0": "16.4.0", 18 + "npm:graphql@^16.11.0": "16.11.0", 19 + "npm:postcss@^8.5.6": "8.5.6", 20 + "npm:react-dom@^19.1.1": "19.2.0_react@19.2.0", 21 + "npm:react-relay@^20.1.1": "20.1.1_react@19.2.0", 22 + "npm:react-router-dom@^7.9.3": "7.9.3_react@19.2.0_react-dom@19.2.0__react@19.2.0", 23 + "npm:react@^19.1.1": "19.2.0", 24 + "npm:relay-compiler@^20.1.1": "20.1.1", 25 + "npm:relay-runtime@^20.1.1": "20.1.1", 26 + "npm:tailwindcss@^4.1.14": "4.1.14", 27 + "npm:typescript-eslint@^8.45.0": "8.45.0_eslint@9.36.0_typescript@5.9.3_@typescript-eslint+parser@8.45.0__eslint@9.36.0__typescript@5.9.3", 28 + "npm:typescript@~5.9.3": "5.9.3", 29 + "npm:vite@^7.1.7": "7.1.8_@types+node@24.6.2_picomatch@4.0.3" 30 + }, 31 + "npm": { 32 + "@alloc/quick-lru@5.2.0": { 33 + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==" 34 + }, 35 + "@babel/code-frame@7.27.1": { 36 + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", 37 + "dependencies": [ 38 + "@babel/helper-validator-identifier", 39 + "js-tokens", 40 + "picocolors" 41 + ] 42 + }, 43 + "@babel/compat-data@7.28.4": { 44 + "integrity": "sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw==" 45 + }, 46 + "@babel/core@7.28.4": { 47 + "integrity": "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==", 48 + "dependencies": [ 49 + "@babel/code-frame", 50 + "@babel/generator", 51 + "@babel/helper-compilation-targets", 52 + "@babel/helper-module-transforms", 53 + "@babel/helpers", 54 + "@babel/parser", 55 + "@babel/template", 56 + "@babel/traverse", 57 + "@babel/types", 58 + "@jridgewell/remapping", 59 + "convert-source-map", 60 + "debug", 61 + "gensync", 62 + "json5", 63 + "semver@6.3.1" 64 + ] 65 + }, 66 + "@babel/generator@7.28.3": { 67 + "integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==", 68 + "dependencies": [ 69 + "@babel/parser", 70 + "@babel/types", 71 + "@jridgewell/gen-mapping", 72 + "@jridgewell/trace-mapping", 73 + "jsesc" 74 + ] 75 + }, 76 + "@babel/helper-compilation-targets@7.27.2": { 77 + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", 78 + "dependencies": [ 79 + "@babel/compat-data", 80 + "@babel/helper-validator-option", 81 + "browserslist", 82 + "lru-cache", 83 + "semver@6.3.1" 84 + ] 85 + }, 86 + "@babel/helper-globals@7.28.0": { 87 + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==" 88 + }, 89 + "@babel/helper-module-imports@7.27.1": { 90 + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", 91 + "dependencies": [ 92 + "@babel/traverse", 93 + "@babel/types" 94 + ] 95 + }, 96 + "@babel/helper-module-transforms@7.28.3_@babel+core@7.28.4": { 97 + "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", 98 + "dependencies": [ 99 + "@babel/core", 100 + "@babel/helper-module-imports", 101 + "@babel/helper-validator-identifier", 102 + "@babel/traverse" 103 + ] 104 + }, 105 + "@babel/helper-plugin-utils@7.27.1": { 106 + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==" 107 + }, 108 + "@babel/helper-string-parser@7.27.1": { 109 + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==" 110 + }, 111 + "@babel/helper-validator-identifier@7.27.1": { 112 + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==" 113 + }, 114 + "@babel/helper-validator-option@7.27.1": { 115 + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==" 116 + }, 117 + "@babel/helpers@7.28.4": { 118 + "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", 119 + "dependencies": [ 120 + "@babel/template", 121 + "@babel/types" 122 + ] 123 + }, 124 + "@babel/parser@7.28.4": { 125 + "integrity": "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==", 126 + "dependencies": [ 127 + "@babel/types" 128 + ], 129 + "bin": true 130 + }, 131 + "@babel/plugin-transform-react-jsx-self@7.27.1_@babel+core@7.28.4": { 132 + "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", 133 + "dependencies": [ 134 + "@babel/core", 135 + "@babel/helper-plugin-utils" 136 + ] 137 + }, 138 + "@babel/plugin-transform-react-jsx-source@7.27.1_@babel+core@7.28.4": { 139 + "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", 140 + "dependencies": [ 141 + "@babel/core", 142 + "@babel/helper-plugin-utils" 143 + ] 144 + }, 145 + "@babel/runtime@7.28.4": { 146 + "integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==" 147 + }, 148 + "@babel/template@7.27.2": { 149 + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", 150 + "dependencies": [ 151 + "@babel/code-frame", 152 + "@babel/parser", 153 + "@babel/types" 154 + ] 155 + }, 156 + "@babel/traverse@7.28.4": { 157 + "integrity": "sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==", 158 + "dependencies": [ 159 + "@babel/code-frame", 160 + "@babel/generator", 161 + "@babel/helper-globals", 162 + "@babel/parser", 163 + "@babel/template", 164 + "@babel/types", 165 + "debug" 166 + ] 167 + }, 168 + "@babel/types@7.28.4": { 169 + "integrity": "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==", 170 + "dependencies": [ 171 + "@babel/helper-string-parser", 172 + "@babel/helper-validator-identifier" 173 + ] 174 + }, 175 + "@emnapi/core@1.5.0": { 176 + "integrity": "sha512-sbP8GzB1WDzacS8fgNPpHlp6C9VZe+SJP3F90W9rLemaQj2PzIuTEl1qDOYQf58YIpyjViI24y9aPWCjEzY2cg==", 177 + "dependencies": [ 178 + "@emnapi/wasi-threads", 179 + "tslib" 180 + ] 181 + }, 182 + "@emnapi/runtime@1.5.0": { 183 + "integrity": "sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ==", 184 + "dependencies": [ 185 + "tslib" 186 + ] 187 + }, 188 + "@emnapi/wasi-threads@1.1.0": { 189 + "integrity": "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==", 190 + "dependencies": [ 191 + "tslib" 192 + ] 193 + }, 194 + "@esbuild/aix-ppc64@0.25.10": { 195 + "integrity": "sha512-0NFWnA+7l41irNuaSVlLfgNT12caWJVLzp5eAVhZ0z1qpxbockccEt3s+149rE64VUI3Ml2zt8Nv5JVc4QXTsw==", 196 + "os": ["aix"], 197 + "cpu": ["ppc64"] 198 + }, 199 + "@esbuild/android-arm64@0.25.10": { 200 + "integrity": "sha512-LSQa7eDahypv/VO6WKohZGPSJDq5OVOo3UoFR1E4t4Gj1W7zEQMUhI+lo81H+DtB+kP+tDgBp+M4oNCwp6kffg==", 201 + "os": ["android"], 202 + "cpu": ["arm64"] 203 + }, 204 + "@esbuild/android-arm@0.25.10": { 205 + "integrity": "sha512-dQAxF1dW1C3zpeCDc5KqIYuZ1tgAdRXNoZP7vkBIRtKZPYe2xVr/d3SkirklCHudW1B45tGiUlz2pUWDfbDD4w==", 206 + "os": ["android"], 207 + "cpu": ["arm"] 208 + }, 209 + "@esbuild/android-x64@0.25.10": { 210 + "integrity": "sha512-MiC9CWdPrfhibcXwr39p9ha1x0lZJ9KaVfvzA0Wxwz9ETX4v5CHfF09bx935nHlhi+MxhA63dKRRQLiVgSUtEg==", 211 + "os": ["android"], 212 + "cpu": ["x64"] 213 + }, 214 + "@esbuild/darwin-arm64@0.25.10": { 215 + "integrity": "sha512-JC74bdXcQEpW9KkV326WpZZjLguSZ3DfS8wrrvPMHgQOIEIG/sPXEN/V8IssoJhbefLRcRqw6RQH2NnpdprtMA==", 216 + "os": ["darwin"], 217 + "cpu": ["arm64"] 218 + }, 219 + "@esbuild/darwin-x64@0.25.10": { 220 + "integrity": "sha512-tguWg1olF6DGqzws97pKZ8G2L7Ig1vjDmGTwcTuYHbuU6TTjJe5FXbgs5C1BBzHbJ2bo1m3WkQDbWO2PvamRcg==", 221 + "os": ["darwin"], 222 + "cpu": ["x64"] 223 + }, 224 + "@esbuild/freebsd-arm64@0.25.10": { 225 + "integrity": "sha512-3ZioSQSg1HT2N05YxeJWYR+Libe3bREVSdWhEEgExWaDtyFbbXWb49QgPvFH8u03vUPX10JhJPcz7s9t9+boWg==", 226 + "os": ["freebsd"], 227 + "cpu": ["arm64"] 228 + }, 229 + "@esbuild/freebsd-x64@0.25.10": { 230 + "integrity": "sha512-LLgJfHJk014Aa4anGDbh8bmI5Lk+QidDmGzuC2D+vP7mv/GeSN+H39zOf7pN5N8p059FcOfs2bVlrRr4SK9WxA==", 231 + "os": ["freebsd"], 232 + "cpu": ["x64"] 233 + }, 234 + "@esbuild/linux-arm64@0.25.10": { 235 + "integrity": "sha512-5luJWN6YKBsawd5f9i4+c+geYiVEw20FVW5x0v1kEMWNq8UctFjDiMATBxLvmmHA4bf7F6hTRaJgtghFr9iziQ==", 236 + "os": ["linux"], 237 + "cpu": ["arm64"] 238 + }, 239 + "@esbuild/linux-arm@0.25.10": { 240 + "integrity": "sha512-oR31GtBTFYCqEBALI9r6WxoU/ZofZl962pouZRTEYECvNF/dtXKku8YXcJkhgK/beU+zedXfIzHijSRapJY3vg==", 241 + "os": ["linux"], 242 + "cpu": ["arm"] 243 + }, 244 + "@esbuild/linux-ia32@0.25.10": { 245 + "integrity": "sha512-NrSCx2Kim3EnnWgS4Txn0QGt0Xipoumb6z6sUtl5bOEZIVKhzfyp/Lyw4C1DIYvzeW/5mWYPBFJU3a/8Yr75DQ==", 246 + "os": ["linux"], 247 + "cpu": ["ia32"] 248 + }, 249 + "@esbuild/linux-loong64@0.25.10": { 250 + "integrity": "sha512-xoSphrd4AZda8+rUDDfD9J6FUMjrkTz8itpTITM4/xgerAZZcFW7Dv+sun7333IfKxGG8gAq+3NbfEMJfiY+Eg==", 251 + "os": ["linux"], 252 + "cpu": ["loong64"] 253 + }, 254 + "@esbuild/linux-mips64el@0.25.10": { 255 + "integrity": "sha512-ab6eiuCwoMmYDyTnyptoKkVS3k8fy/1Uvq7Dj5czXI6DF2GqD2ToInBI0SHOp5/X1BdZ26RKc5+qjQNGRBelRA==", 256 + "os": ["linux"], 257 + "cpu": ["mips64el"] 258 + }, 259 + "@esbuild/linux-ppc64@0.25.10": { 260 + "integrity": "sha512-NLinzzOgZQsGpsTkEbdJTCanwA5/wozN9dSgEl12haXJBzMTpssebuXR42bthOF3z7zXFWH1AmvWunUCkBE4EA==", 261 + "os": ["linux"], 262 + "cpu": ["ppc64"] 263 + }, 264 + "@esbuild/linux-riscv64@0.25.10": { 265 + "integrity": "sha512-FE557XdZDrtX8NMIeA8LBJX3dC2M8VGXwfrQWU7LB5SLOajfJIxmSdyL/gU1m64Zs9CBKvm4UAuBp5aJ8OgnrA==", 266 + "os": ["linux"], 267 + "cpu": ["riscv64"] 268 + }, 269 + "@esbuild/linux-s390x@0.25.10": { 270 + "integrity": "sha512-3BBSbgzuB9ajLoVZk0mGu+EHlBwkusRmeNYdqmznmMc9zGASFjSsxgkNsqmXugpPk00gJ0JNKh/97nxmjctdew==", 271 + "os": ["linux"], 272 + "cpu": ["s390x"] 273 + }, 274 + "@esbuild/linux-x64@0.25.10": { 275 + "integrity": "sha512-QSX81KhFoZGwenVyPoberggdW1nrQZSvfVDAIUXr3WqLRZGZqWk/P4T8p2SP+de2Sr5HPcvjhcJzEiulKgnxtA==", 276 + "os": ["linux"], 277 + "cpu": ["x64"] 278 + }, 279 + "@esbuild/netbsd-arm64@0.25.10": { 280 + "integrity": "sha512-AKQM3gfYfSW8XRk8DdMCzaLUFB15dTrZfnX8WXQoOUpUBQ+NaAFCP1kPS/ykbbGYz7rxn0WS48/81l9hFl3u4A==", 281 + "os": ["netbsd"], 282 + "cpu": ["arm64"] 283 + }, 284 + "@esbuild/netbsd-x64@0.25.10": { 285 + "integrity": "sha512-7RTytDPGU6fek/hWuN9qQpeGPBZFfB4zZgcz2VK2Z5VpdUxEI8JKYsg3JfO0n/Z1E/6l05n0unDCNc4HnhQGig==", 286 + "os": ["netbsd"], 287 + "cpu": ["x64"] 288 + }, 289 + "@esbuild/openbsd-arm64@0.25.10": { 290 + "integrity": "sha512-5Se0VM9Wtq797YFn+dLimf2Zx6McttsH2olUBsDml+lm0GOCRVebRWUvDtkY4BWYv/3NgzS8b/UM3jQNh5hYyw==", 291 + "os": ["openbsd"], 292 + "cpu": ["arm64"] 293 + }, 294 + "@esbuild/openbsd-x64@0.25.10": { 295 + "integrity": "sha512-XkA4frq1TLj4bEMB+2HnI0+4RnjbuGZfet2gs/LNs5Hc7D89ZQBHQ0gL2ND6Lzu1+QVkjp3x1gIcPKzRNP8bXw==", 296 + "os": ["openbsd"], 297 + "cpu": ["x64"] 298 + }, 299 + "@esbuild/openharmony-arm64@0.25.10": { 300 + "integrity": "sha512-AVTSBhTX8Y/Fz6OmIVBip9tJzZEUcY8WLh7I59+upa5/GPhh2/aM6bvOMQySspnCCHvFi79kMtdJS1w0DXAeag==", 301 + "os": ["openharmony"], 302 + "cpu": ["arm64"] 303 + }, 304 + "@esbuild/sunos-x64@0.25.10": { 305 + "integrity": "sha512-fswk3XT0Uf2pGJmOpDB7yknqhVkJQkAQOcW/ccVOtfx05LkbWOaRAtn5SaqXypeKQra1QaEa841PgrSL9ubSPQ==", 306 + "os": ["sunos"], 307 + "cpu": ["x64"] 308 + }, 309 + "@esbuild/win32-arm64@0.25.10": { 310 + "integrity": "sha512-ah+9b59KDTSfpaCg6VdJoOQvKjI33nTaQr4UluQwW7aEwZQsbMCfTmfEO4VyewOxx4RaDT/xCy9ra2GPWmO7Kw==", 311 + "os": ["win32"], 312 + "cpu": ["arm64"] 313 + }, 314 + "@esbuild/win32-ia32@0.25.10": { 315 + "integrity": "sha512-QHPDbKkrGO8/cz9LKVnJU22HOi4pxZnZhhA2HYHez5Pz4JeffhDjf85E57Oyco163GnzNCVkZK0b/n4Y0UHcSw==", 316 + "os": ["win32"], 317 + "cpu": ["ia32"] 318 + }, 319 + "@esbuild/win32-x64@0.25.10": { 320 + "integrity": "sha512-9KpxSVFCu0iK1owoez6aC/s/EdUQLDN3adTxGCqxMVhrPDj6bt5dbrHDXUuq+Bs2vATFBBrQS5vdQ/Ed2P+nbw==", 321 + "os": ["win32"], 322 + "cpu": ["x64"] 323 + }, 324 + "@eslint-community/eslint-utils@4.9.0_eslint@9.36.0": { 325 + "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", 326 + "dependencies": [ 327 + "eslint", 328 + "eslint-visitor-keys@3.4.3" 329 + ] 330 + }, 331 + "@eslint-community/regexpp@4.12.1": { 332 + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==" 333 + }, 334 + "@eslint/config-array@0.21.0": { 335 + "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", 336 + "dependencies": [ 337 + "@eslint/object-schema", 338 + "debug", 339 + "minimatch@3.1.2" 340 + ] 341 + }, 342 + "@eslint/config-helpers@0.3.1": { 343 + "integrity": "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==" 344 + }, 345 + "@eslint/core@0.15.2": { 346 + "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==", 347 + "dependencies": [ 348 + "@types/json-schema" 349 + ] 350 + }, 351 + "@eslint/eslintrc@3.3.1": { 352 + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", 353 + "dependencies": [ 354 + "ajv", 355 + "debug", 356 + "espree", 357 + "globals@14.0.0", 358 + "ignore@5.3.2", 359 + "import-fresh@3.3.1", 360 + "js-yaml@4.1.0", 361 + "minimatch@3.1.2", 362 + "strip-json-comments" 363 + ] 364 + }, 365 + "@eslint/js@9.36.0": { 366 + "integrity": "sha512-uhCbYtYynH30iZErszX78U+nR3pJU3RHGQ57NXy5QupD4SBVwDeU8TNBy+MjMngc1UyIW9noKqsRqfjQTBU2dw==" 367 + }, 368 + "@eslint/object-schema@2.1.6": { 369 + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==" 370 + }, 371 + "@eslint/plugin-kit@0.3.5": { 372 + "integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==", 373 + "dependencies": [ 374 + "@eslint/core", 375 + "levn" 376 + ] 377 + }, 378 + "@humanfs/core@0.19.1": { 379 + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==" 380 + }, 381 + "@humanfs/node@0.16.7": { 382 + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", 383 + "dependencies": [ 384 + "@humanfs/core", 385 + "@humanwhocodes/retry" 386 + ] 387 + }, 388 + "@humanwhocodes/module-importer@1.0.1": { 389 + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==" 390 + }, 391 + "@humanwhocodes/retry@0.4.3": { 392 + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==" 393 + }, 394 + "@isaacs/fs-minipass@4.0.1": { 395 + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", 396 + "dependencies": [ 397 + "minipass" 398 + ] 399 + }, 400 + "@jridgewell/gen-mapping@0.3.13": { 401 + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", 402 + "dependencies": [ 403 + "@jridgewell/sourcemap-codec", 404 + "@jridgewell/trace-mapping" 405 + ] 406 + }, 407 + "@jridgewell/remapping@2.3.5": { 408 + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", 409 + "dependencies": [ 410 + "@jridgewell/gen-mapping", 411 + "@jridgewell/trace-mapping" 412 + ] 413 + }, 414 + "@jridgewell/resolve-uri@3.1.2": { 415 + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==" 416 + }, 417 + "@jridgewell/sourcemap-codec@1.5.5": { 418 + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==" 419 + }, 420 + "@jridgewell/trace-mapping@0.3.31": { 421 + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", 422 + "dependencies": [ 423 + "@jridgewell/resolve-uri", 424 + "@jridgewell/sourcemap-codec" 425 + ] 426 + }, 427 + "@napi-rs/wasm-runtime@1.0.5": { 428 + "integrity": "sha512-TBr9Cf9onSAS2LQ2+QHx6XcC6h9+RIzJgbqG3++9TUZSH204AwEy5jg3BTQ0VATsyoGj4ee49tN/y6rvaOOtcg==", 429 + "dependencies": [ 430 + "@emnapi/core", 431 + "@emnapi/runtime", 432 + "@tybys/wasm-util" 433 + ] 434 + }, 435 + "@nodelib/fs.scandir@2.1.5": { 436 + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", 437 + "dependencies": [ 438 + "@nodelib/fs.stat", 439 + "run-parallel" 440 + ] 441 + }, 442 + "@nodelib/fs.stat@2.0.5": { 443 + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==" 444 + }, 445 + "@nodelib/fs.walk@1.2.8": { 446 + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", 447 + "dependencies": [ 448 + "@nodelib/fs.scandir", 449 + "fastq" 450 + ] 451 + }, 452 + "@rolldown/pluginutils@1.0.0-beta.38": { 453 + "integrity": "sha512-N/ICGKleNhA5nc9XXQG/kkKHJ7S55u0x0XUJbbkmdCnFuoRkM1Il12q9q0eX19+M7KKUEPw/daUPIRnxhcxAIw==" 454 + }, 455 + "@rollup/rollup-android-arm-eabi@4.52.3": { 456 + "integrity": "sha512-h6cqHGZ6VdnwliFG1NXvMPTy/9PS3h8oLh7ImwR+kl+oYnQizgjxsONmmPSb2C66RksfkfIxEVtDSEcJiO0tqw==", 457 + "os": ["android"], 458 + "cpu": ["arm"] 459 + }, 460 + "@rollup/rollup-android-arm64@4.52.3": { 461 + "integrity": "sha512-wd+u7SLT/u6knklV/ifG7gr5Qy4GUbH2hMWcDauPFJzmCZUAJ8L2bTkVXC2niOIxp8lk3iH/QX8kSrUxVZrOVw==", 462 + "os": ["android"], 463 + "cpu": ["arm64"] 464 + }, 465 + "@rollup/rollup-darwin-arm64@4.52.3": { 466 + "integrity": "sha512-lj9ViATR1SsqycwFkJCtYfQTheBdvlWJqzqxwc9f2qrcVrQaF/gCuBRTiTolkRWS6KvNxSk4KHZWG7tDktLgjg==", 467 + "os": ["darwin"], 468 + "cpu": ["arm64"] 469 + }, 470 + "@rollup/rollup-darwin-x64@4.52.3": { 471 + "integrity": "sha512-+Dyo7O1KUmIsbzx1l+4V4tvEVnVQqMOIYtrxK7ncLSknl1xnMHLgn7gddJVrYPNZfEB8CIi3hK8gq8bDhb3h5A==", 472 + "os": ["darwin"], 473 + "cpu": ["x64"] 474 + }, 475 + "@rollup/rollup-freebsd-arm64@4.52.3": { 476 + "integrity": "sha512-u9Xg2FavYbD30g3DSfNhxgNrxhi6xVG4Y6i9Ur1C7xUuGDW3banRbXj+qgnIrwRN4KeJ396jchwy9bCIzbyBEQ==", 477 + "os": ["freebsd"], 478 + "cpu": ["arm64"] 479 + }, 480 + "@rollup/rollup-freebsd-x64@4.52.3": { 481 + "integrity": "sha512-5M8kyi/OX96wtD5qJR89a/3x5x8x5inXBZO04JWhkQb2JWavOWfjgkdvUqibGJeNNaz1/Z1PPza5/tAPXICI6A==", 482 + "os": ["freebsd"], 483 + "cpu": ["x64"] 484 + }, 485 + "@rollup/rollup-linux-arm-gnueabihf@4.52.3": { 486 + "integrity": "sha512-IoerZJ4l1wRMopEHRKOO16e04iXRDyZFZnNZKrWeNquh5d6bucjezgd+OxG03mOMTnS1x7hilzb3uURPkJ0OfA==", 487 + "os": ["linux"], 488 + "cpu": ["arm"] 489 + }, 490 + "@rollup/rollup-linux-arm-musleabihf@4.52.3": { 491 + "integrity": "sha512-ZYdtqgHTDfvrJHSh3W22TvjWxwOgc3ThK/XjgcNGP2DIwFIPeAPNsQxrJO5XqleSlgDux2VAoWQ5iJrtaC1TbA==", 492 + "os": ["linux"], 493 + "cpu": ["arm"] 494 + }, 495 + "@rollup/rollup-linux-arm64-gnu@4.52.3": { 496 + "integrity": "sha512-NcViG7A0YtuFDA6xWSgmFb6iPFzHlf5vcqb2p0lGEbT+gjrEEz8nC/EeDHvx6mnGXnGCC1SeVV+8u+smj0CeGQ==", 497 + "os": ["linux"], 498 + "cpu": ["arm64"] 499 + }, 500 + "@rollup/rollup-linux-arm64-musl@4.52.3": { 501 + "integrity": "sha512-d3pY7LWno6SYNXRm6Ebsq0DJGoiLXTb83AIPCXl9fmtIQs/rXoS8SJxxUNtFbJ5MiOvs+7y34np77+9l4nfFMw==", 502 + "os": ["linux"], 503 + "cpu": ["arm64"] 504 + }, 505 + "@rollup/rollup-linux-loong64-gnu@4.52.3": { 506 + "integrity": "sha512-3y5GA0JkBuirLqmjwAKwB0keDlI6JfGYduMlJD/Rl7fvb4Ni8iKdQs1eiunMZJhwDWdCvrcqXRY++VEBbvk6Eg==", 507 + "os": ["linux"], 508 + "cpu": ["loong64"] 509 + }, 510 + "@rollup/rollup-linux-ppc64-gnu@4.52.3": { 511 + "integrity": "sha512-AUUH65a0p3Q0Yfm5oD2KVgzTKgwPyp9DSXc3UA7DtxhEb/WSPfbG4wqXeSN62OG5gSo18em4xv6dbfcUGXcagw==", 512 + "os": ["linux"], 513 + "cpu": ["ppc64"] 514 + }, 515 + "@rollup/rollup-linux-riscv64-gnu@4.52.3": { 516 + "integrity": "sha512-1makPhFFVBqZE+XFg3Dkq+IkQ7JvmUrwwqaYBL2CE+ZpxPaqkGaiWFEWVGyvTwZace6WLJHwjVh/+CXbKDGPmg==", 517 + "os": ["linux"], 518 + "cpu": ["riscv64"] 519 + }, 520 + "@rollup/rollup-linux-riscv64-musl@4.52.3": { 521 + "integrity": "sha512-OOFJa28dxfl8kLOPMUOQBCO6z3X2SAfzIE276fwT52uXDWUS178KWq0pL7d6p1kz7pkzA0yQwtqL0dEPoVcRWg==", 522 + "os": ["linux"], 523 + "cpu": ["riscv64"] 524 + }, 525 + "@rollup/rollup-linux-s390x-gnu@4.52.3": { 526 + "integrity": "sha512-jMdsML2VI5l+V7cKfZx3ak+SLlJ8fKvLJ0Eoa4b9/vCUrzXKgoKxvHqvJ/mkWhFiyp88nCkM5S2v6nIwRtPcgg==", 527 + "os": ["linux"], 528 + "cpu": ["s390x"] 529 + }, 530 + "@rollup/rollup-linux-x64-gnu@4.52.3": { 531 + "integrity": "sha512-tPgGd6bY2M2LJTA1uGq8fkSPK8ZLYjDjY+ZLK9WHncCnfIz29LIXIqUgzCR0hIefzy6Hpbe8Th5WOSwTM8E7LA==", 532 + "os": ["linux"], 533 + "cpu": ["x64"] 534 + }, 535 + "@rollup/rollup-linux-x64-musl@4.52.3": { 536 + "integrity": "sha512-BCFkJjgk+WFzP+tcSMXq77ymAPIxsX9lFJWs+2JzuZTLtksJ2o5hvgTdIcZ5+oKzUDMwI0PfWzRBYAydAHF2Mw==", 537 + "os": ["linux"], 538 + "cpu": ["x64"] 539 + }, 540 + "@rollup/rollup-openharmony-arm64@4.52.3": { 541 + "integrity": "sha512-KTD/EqjZF3yvRaWUJdD1cW+IQBk4fbQaHYJUmP8N4XoKFZilVL8cobFSTDnjTtxWJQ3JYaMgF4nObY/+nYkumA==", 542 + "os": ["openharmony"], 543 + "cpu": ["arm64"] 544 + }, 545 + "@rollup/rollup-win32-arm64-msvc@4.52.3": { 546 + "integrity": "sha512-+zteHZdoUYLkyYKObGHieibUFLbttX2r+58l27XZauq0tcWYYuKUwY2wjeCN9oK1Um2YgH2ibd6cnX/wFD7DuA==", 547 + "os": ["win32"], 548 + "cpu": ["arm64"] 549 + }, 550 + "@rollup/rollup-win32-ia32-msvc@4.52.3": { 551 + "integrity": "sha512-of1iHkTQSo3kr6dTIRX6t81uj/c/b15HXVsPcEElN5sS859qHrOepM5p9G41Hah+CTqSh2r8Bm56dL2z9UQQ7g==", 552 + "os": ["win32"], 553 + "cpu": ["ia32"] 554 + }, 555 + "@rollup/rollup-win32-x64-gnu@4.52.3": { 556 + "integrity": "sha512-s0hybmlHb56mWVZQj8ra9048/WZTPLILKxcvcq+8awSZmyiSUZjjem1AhU3Tf4ZKpYhK4mg36HtHDOe8QJS5PQ==", 557 + "os": ["win32"], 558 + "cpu": ["x64"] 559 + }, 560 + "@rollup/rollup-win32-x64-msvc@4.52.3": { 561 + "integrity": "sha512-zGIbEVVXVtauFgl3MRwGWEN36P5ZGenHRMgNw88X5wEhEBpq0XrMEZwOn07+ICrwM17XO5xfMZqh0OldCH5VTA==", 562 + "os": ["win32"], 563 + "cpu": ["x64"] 564 + }, 565 + "@tailwindcss/node@4.1.14": { 566 + "integrity": "sha512-hpz+8vFk3Ic2xssIA3e01R6jkmsAhvkQdXlEbRTk6S10xDAtiQiM3FyvZVGsucefq764euO/b8WUW9ysLdThHw==", 567 + "dependencies": [ 568 + "@jridgewell/remapping", 569 + "enhanced-resolve", 570 + "jiti", 571 + "lightningcss", 572 + "magic-string", 573 + "source-map-js", 574 + "tailwindcss" 575 + ] 576 + }, 577 + "@tailwindcss/oxide-android-arm64@4.1.14": { 578 + "integrity": "sha512-a94ifZrGwMvbdeAxWoSuGcIl6/DOP5cdxagid7xJv6bwFp3oebp7y2ImYsnZBMTwjn5Ev5xESvS3FFYUGgPODQ==", 579 + "os": ["android"], 580 + "cpu": ["arm64"] 581 + }, 582 + "@tailwindcss/oxide-darwin-arm64@4.1.14": { 583 + "integrity": "sha512-HkFP/CqfSh09xCnrPJA7jud7hij5ahKyWomrC3oiO2U9i0UjP17o9pJbxUN0IJ471GTQQmzwhp0DEcpbp4MZTA==", 584 + "os": ["darwin"], 585 + "cpu": ["arm64"] 586 + }, 587 + "@tailwindcss/oxide-darwin-x64@4.1.14": { 588 + "integrity": "sha512-eVNaWmCgdLf5iv6Qd3s7JI5SEFBFRtfm6W0mphJYXgvnDEAZ5sZzqmI06bK6xo0IErDHdTA5/t7d4eTfWbWOFw==", 589 + "os": ["darwin"], 590 + "cpu": ["x64"] 591 + }, 592 + "@tailwindcss/oxide-freebsd-x64@4.1.14": { 593 + "integrity": "sha512-QWLoRXNikEuqtNb0dhQN6wsSVVjX6dmUFzuuiL09ZeXju25dsei2uIPl71y2Ic6QbNBsB4scwBoFnlBfabHkEw==", 594 + "os": ["freebsd"], 595 + "cpu": ["x64"] 596 + }, 597 + "@tailwindcss/oxide-linux-arm-gnueabihf@4.1.14": { 598 + "integrity": "sha512-VB4gjQni9+F0VCASU+L8zSIyjrLLsy03sjcR3bM0V2g4SNamo0FakZFKyUQ96ZVwGK4CaJsc9zd/obQy74o0Fw==", 599 + "os": ["linux"], 600 + "cpu": ["arm"] 601 + }, 602 + "@tailwindcss/oxide-linux-arm64-gnu@4.1.14": { 603 + "integrity": "sha512-qaEy0dIZ6d9vyLnmeg24yzA8XuEAD9WjpM5nIM1sUgQ/Zv7cVkharPDQcmm/t/TvXoKo/0knI3me3AGfdx6w1w==", 604 + "os": ["linux"], 605 + "cpu": ["arm64"] 606 + }, 607 + "@tailwindcss/oxide-linux-arm64-musl@4.1.14": { 608 + "integrity": "sha512-ISZjT44s59O8xKsPEIesiIydMG/sCXoMBCqsphDm/WcbnuWLxxb+GcvSIIA5NjUw6F8Tex7s5/LM2yDy8RqYBQ==", 609 + "os": ["linux"], 610 + "cpu": ["arm64"] 611 + }, 612 + "@tailwindcss/oxide-linux-x64-gnu@4.1.14": { 613 + "integrity": "sha512-02c6JhLPJj10L2caH4U0zF8Hji4dOeahmuMl23stk0MU1wfd1OraE7rOloidSF8W5JTHkFdVo/O7uRUJJnUAJg==", 614 + "os": ["linux"], 615 + "cpu": ["x64"] 616 + }, 617 + "@tailwindcss/oxide-linux-x64-musl@4.1.14": { 618 + "integrity": "sha512-TNGeLiN1XS66kQhxHG/7wMeQDOoL0S33x9BgmydbrWAb9Qw0KYdd8o1ifx4HOGDWhVmJ+Ul+JQ7lyknQFilO3Q==", 619 + "os": ["linux"], 620 + "cpu": ["x64"] 621 + }, 622 + "@tailwindcss/oxide-wasm32-wasi@4.1.14": { 623 + "integrity": "sha512-uZYAsaW/jS/IYkd6EWPJKW/NlPNSkWkBlaeVBi/WsFQNP05/bzkebUL8FH1pdsqx4f2fH/bWFcUABOM9nfiJkQ==", 624 + "dependencies": [ 625 + "@emnapi/core", 626 + "@emnapi/runtime", 627 + "@emnapi/wasi-threads", 628 + "@napi-rs/wasm-runtime", 629 + "@tybys/wasm-util", 630 + "tslib" 631 + ], 632 + "cpu": ["wasm32"] 633 + }, 634 + "@tailwindcss/oxide-win32-arm64-msvc@4.1.14": { 635 + "integrity": "sha512-Az0RnnkcvRqsuoLH2Z4n3JfAef0wElgzHD5Aky/e+0tBUxUhIeIqFBTMNQvmMRSP15fWwmvjBxZ3Q8RhsDnxAA==", 636 + "os": ["win32"], 637 + "cpu": ["arm64"] 638 + }, 639 + "@tailwindcss/oxide-win32-x64-msvc@4.1.14": { 640 + "integrity": "sha512-ttblVGHgf68kEE4om1n/n44I0yGPkCPbLsqzjvybhpwa6mKKtgFfAzy6btc3HRmuW7nHe0OOrSeNP9sQmmH9XA==", 641 + "os": ["win32"], 642 + "cpu": ["x64"] 643 + }, 644 + "@tailwindcss/oxide@4.1.14": { 645 + "integrity": "sha512-23yx+VUbBwCg2x5XWdB8+1lkPajzLmALEfMb51zZUBYaYVPDQvBSD/WYDqiVyBIo2BZFa3yw1Rpy3G2Jp+K0dw==", 646 + "dependencies": [ 647 + "detect-libc", 648 + "tar" 649 + ], 650 + "optionalDependencies": [ 651 + "@tailwindcss/oxide-android-arm64", 652 + "@tailwindcss/oxide-darwin-arm64", 653 + "@tailwindcss/oxide-darwin-x64", 654 + "@tailwindcss/oxide-freebsd-x64", 655 + "@tailwindcss/oxide-linux-arm-gnueabihf", 656 + "@tailwindcss/oxide-linux-arm64-gnu", 657 + "@tailwindcss/oxide-linux-arm64-musl", 658 + "@tailwindcss/oxide-linux-x64-gnu", 659 + "@tailwindcss/oxide-linux-x64-musl", 660 + "@tailwindcss/oxide-wasm32-wasi", 661 + "@tailwindcss/oxide-win32-arm64-msvc", 662 + "@tailwindcss/oxide-win32-x64-msvc" 663 + ], 664 + "scripts": true 665 + }, 666 + "@tailwindcss/postcss@4.1.14": { 667 + "integrity": "sha512-BdMjIxy7HUNThK87C7BC8I1rE8BVUsfNQSI5siQ4JK3iIa3w0XyVvVL9SXLWO//CtYTcp1v7zci0fYwJOjB+Zg==", 668 + "dependencies": [ 669 + "@alloc/quick-lru", 670 + "@tailwindcss/node", 671 + "@tailwindcss/oxide", 672 + "postcss", 673 + "tailwindcss" 674 + ] 675 + }, 676 + "@tybys/wasm-util@0.10.1": { 677 + "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", 678 + "dependencies": [ 679 + "tslib" 680 + ] 681 + }, 682 + "@types/babel__core@7.20.5": { 683 + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", 684 + "dependencies": [ 685 + "@babel/parser", 686 + "@babel/types", 687 + "@types/babel__generator", 688 + "@types/babel__template", 689 + "@types/babel__traverse" 690 + ] 691 + }, 692 + "@types/babel__generator@7.27.0": { 693 + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", 694 + "dependencies": [ 695 + "@babel/types" 696 + ] 697 + }, 698 + "@types/babel__template@7.4.4": { 699 + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", 700 + "dependencies": [ 701 + "@babel/parser", 702 + "@babel/types" 703 + ] 704 + }, 705 + "@types/babel__traverse@7.28.0": { 706 + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", 707 + "dependencies": [ 708 + "@babel/types" 709 + ] 710 + }, 711 + "@types/estree@1.0.8": { 712 + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==" 713 + }, 714 + "@types/json-schema@7.0.15": { 715 + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==" 716 + }, 717 + "@types/node@24.6.2": { 718 + "integrity": "sha512-d2L25Y4j+W3ZlNAeMKcy7yDsK425ibcAOO2t7aPTz6gNMH0z2GThtwENCDc0d/Pw9wgyRqE5Px1wkV7naz8ang==", 719 + "dependencies": [ 720 + "undici-types" 721 + ] 722 + }, 723 + "@types/parse-json@4.0.2": { 724 + "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==" 725 + }, 726 + "@types/react-dom@19.2.0_@types+react@19.2.0": { 727 + "integrity": "sha512-brtBs0MnE9SMx7px208g39lRmC5uHZs96caOJfTjFcYSLHNamvaSMfJNagChVNkup2SdtOxKX1FDBkRSJe1ZAg==", 728 + "dependencies": [ 729 + "@types/react" 730 + ] 731 + }, 732 + "@types/react-relay@18.2.1": { 733 + "integrity": "sha512-KgmFapsxAylhxcFfaAv5GZZJhTHnDvV8IDZVsUm5afpJUvgZC1Y68ssfOGsFfiFY/2EhxHM/YPfpdKbfmF3Ecg==", 734 + "dependencies": [ 735 + "@types/react", 736 + "@types/relay-runtime" 737 + ] 738 + }, 739 + "@types/react@19.2.0": { 740 + "integrity": "sha512-1LOH8xovvsKsCBq1wnT4ntDUdCJKmnEakhsuoUSy6ExlHCkGP2hqnatagYTgFk6oeL0VU31u7SNjunPN+GchtA==", 741 + "dependencies": [ 742 + "csstype" 743 + ] 744 + }, 745 + "@types/relay-runtime@19.0.3": { 746 + "integrity": "sha512-pvpWWQq5e9KeESF8klQaP2igLLhr2bRd3XxVCxNpGElsPQiP6Mejr59RT9/OGY3O3i8jAGGQsshVe0QCQDbxNg==" 747 + }, 748 + "@typescript-eslint/eslint-plugin@8.45.0_@typescript-eslint+parser@8.45.0__eslint@9.36.0__typescript@5.9.3_eslint@9.36.0_typescript@5.9.3": { 749 + "integrity": "sha512-HC3y9CVuevvWCl/oyZuI47dOeDF9ztdMEfMH8/DW/Mhwa9cCLnK1oD7JoTVGW/u7kFzNZUKUoyJEqkaJh5y3Wg==", 750 + "dependencies": [ 751 + "@eslint-community/regexpp", 752 + "@typescript-eslint/parser", 753 + "@typescript-eslint/scope-manager", 754 + "@typescript-eslint/type-utils", 755 + "@typescript-eslint/utils", 756 + "@typescript-eslint/visitor-keys", 757 + "eslint", 758 + "graphemer", 759 + "ignore@7.0.5", 760 + "natural-compare", 761 + "ts-api-utils", 762 + "typescript" 763 + ] 764 + }, 765 + "@typescript-eslint/parser@8.45.0_eslint@9.36.0_typescript@5.9.3": { 766 + "integrity": "sha512-TGf22kon8KW+DeKaUmOibKWktRY8b2NSAZNdtWh798COm1NWx8+xJ6iFBtk3IvLdv6+LGLJLRlyhrhEDZWargQ==", 767 + "dependencies": [ 768 + "@typescript-eslint/scope-manager", 769 + "@typescript-eslint/types", 770 + "@typescript-eslint/typescript-estree", 771 + "@typescript-eslint/visitor-keys", 772 + "debug", 773 + "eslint", 774 + "typescript" 775 + ] 776 + }, 777 + "@typescript-eslint/project-service@8.45.0_typescript@5.9.3": { 778 + "integrity": "sha512-3pcVHwMG/iA8afdGLMuTibGR7pDsn9RjDev6CCB+naRsSYs2pns5QbinF4Xqw6YC/Sj3lMrm/Im0eMfaa61WUg==", 779 + "dependencies": [ 780 + "@typescript-eslint/tsconfig-utils", 781 + "@typescript-eslint/types", 782 + "debug", 783 + "typescript" 784 + ] 785 + }, 786 + "@typescript-eslint/scope-manager@8.45.0": { 787 + "integrity": "sha512-clmm8XSNj/1dGvJeO6VGH7EUSeA0FMs+5au/u3lrA3KfG8iJ4u8ym9/j2tTEoacAffdW1TVUzXO30W1JTJS7dA==", 788 + "dependencies": [ 789 + "@typescript-eslint/types", 790 + "@typescript-eslint/visitor-keys" 791 + ] 792 + }, 793 + "@typescript-eslint/tsconfig-utils@8.45.0_typescript@5.9.3": { 794 + "integrity": "sha512-aFdr+c37sc+jqNMGhH+ajxPXwjv9UtFZk79k8pLoJ6p4y0snmYpPA52GuWHgt2ZF4gRRW6odsEj41uZLojDt5w==", 795 + "dependencies": [ 796 + "typescript" 797 + ] 798 + }, 799 + "@typescript-eslint/type-utils@8.45.0_eslint@9.36.0_typescript@5.9.3": { 800 + "integrity": "sha512-bpjepLlHceKgyMEPglAeULX1vixJDgaKocp0RVJ5u4wLJIMNuKtUXIczpJCPcn2waII0yuvks/5m5/h3ZQKs0A==", 801 + "dependencies": [ 802 + "@typescript-eslint/types", 803 + "@typescript-eslint/typescript-estree", 804 + "@typescript-eslint/utils", 805 + "debug", 806 + "eslint", 807 + "ts-api-utils", 808 + "typescript" 809 + ] 810 + }, 811 + "@typescript-eslint/types@8.45.0": { 812 + "integrity": "sha512-WugXLuOIq67BMgQInIxxnsSyRLFxdkJEJu8r4ngLR56q/4Q5LrbfkFRH27vMTjxEK8Pyz7QfzuZe/G15qQnVRA==" 813 + }, 814 + "@typescript-eslint/typescript-estree@8.45.0_typescript@5.9.3": { 815 + "integrity": "sha512-GfE1NfVbLam6XQ0LcERKwdTTPlLvHvXXhOeUGC1OXi4eQBoyy1iVsW+uzJ/J9jtCz6/7GCQ9MtrQ0fml/jWCnA==", 816 + "dependencies": [ 817 + "@typescript-eslint/project-service", 818 + "@typescript-eslint/tsconfig-utils", 819 + "@typescript-eslint/types", 820 + "@typescript-eslint/visitor-keys", 821 + "debug", 822 + "fast-glob", 823 + "is-glob", 824 + "minimatch@9.0.5", 825 + "semver@7.7.2", 826 + "ts-api-utils", 827 + "typescript" 828 + ] 829 + }, 830 + "@typescript-eslint/utils@8.45.0_eslint@9.36.0_typescript@5.9.3": { 831 + "integrity": "sha512-bxi1ht+tLYg4+XV2knz/F7RVhU0k6VrSMc9sb8DQ6fyCTrGQLHfo7lDtN0QJjZjKkLA2ThrKuCdHEvLReqtIGg==", 832 + "dependencies": [ 833 + "@eslint-community/eslint-utils", 834 + "@typescript-eslint/scope-manager", 835 + "@typescript-eslint/types", 836 + "@typescript-eslint/typescript-estree", 837 + "eslint", 838 + "typescript" 839 + ] 840 + }, 841 + "@typescript-eslint/visitor-keys@8.45.0": { 842 + "integrity": "sha512-qsaFBA3e09MIDAGFUrTk+dzqtfv1XPVz8t8d1f0ybTzrCY7BKiMC5cjrl1O/P7UmHsNyW90EYSkU/ZWpmXelag==", 843 + "dependencies": [ 844 + "@typescript-eslint/types", 845 + "eslint-visitor-keys@4.2.1" 846 + ] 847 + }, 848 + "@vitejs/plugin-react@5.0.4_vite@7.1.8__@types+node@24.6.2__picomatch@4.0.3_@babel+core@7.28.4_@types+node@24.6.2": { 849 + "integrity": "sha512-La0KD0vGkVkSk6K+piWDKRUyg8Rl5iAIKRMH0vMJI0Eg47bq1eOxmoObAaQG37WMW9MSyk7Cs8EIWwJC1PtzKA==", 850 + "dependencies": [ 851 + "@babel/core", 852 + "@babel/plugin-transform-react-jsx-self", 853 + "@babel/plugin-transform-react-jsx-source", 854 + "@rolldown/pluginutils", 855 + "@types/babel__core", 856 + "react-refresh", 857 + "vite" 858 + ] 859 + }, 860 + "acorn-jsx@5.3.2_acorn@8.15.0": { 861 + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", 862 + "dependencies": [ 863 + "acorn" 864 + ] 865 + }, 866 + "acorn@8.15.0": { 867 + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", 868 + "bin": true 869 + }, 870 + "ajv@6.12.6": { 871 + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", 872 + "dependencies": [ 873 + "fast-deep-equal", 874 + "fast-json-stable-stringify", 875 + "json-schema-traverse", 876 + "uri-js" 877 + ] 878 + }, 879 + "ansi-styles@4.3.0": { 880 + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 881 + "dependencies": [ 882 + "color-convert" 883 + ] 884 + }, 885 + "argparse@1.0.10": { 886 + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", 887 + "dependencies": [ 888 + "sprintf-js" 889 + ] 890 + }, 891 + "argparse@2.0.1": { 892 + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" 893 + }, 894 + "asap@2.0.6": { 895 + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==" 896 + }, 897 + "autoprefixer@10.4.21_postcss@8.5.6": { 898 + "integrity": "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==", 899 + "dependencies": [ 900 + "browserslist", 901 + "caniuse-lite", 902 + "fraction.js", 903 + "normalize-range", 904 + "picocolors", 905 + "postcss", 906 + "postcss-value-parser" 907 + ], 908 + "bin": true 909 + }, 910 + "babel-plugin-macros@2.8.0": { 911 + "integrity": "sha512-SEP5kJpfGYqYKpBrj5XU3ahw5p5GOHJ0U5ssOSQ/WBVdwkD2Dzlce95exQTs3jOVWPPKLBN2rlEWkCK7dSmLvg==", 912 + "dependencies": [ 913 + "@babel/runtime", 914 + "cosmiconfig@6.0.0", 915 + "resolve" 916 + ] 917 + }, 918 + "babel-plugin-relay@20.1.1": { 919 + "integrity": "sha512-BWlqLPiHbxZTxlyng2rVgsZCwztHNje7H8FR4c+UKy3ErQJBG6BKLr9vUdeR7mAZCH2v0sOAxNhG6zR1FrWjAg==", 920 + "dependencies": [ 921 + "babel-plugin-macros", 922 + "cosmiconfig@5.2.1", 923 + "graphql@15.3.0" 924 + ] 925 + }, 926 + "balanced-match@1.0.2": { 927 + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" 928 + }, 929 + "baseline-browser-mapping@2.8.10": { 930 + "integrity": "sha512-uLfgBi+7IBNay8ECBO2mVMGZAc1VgZWEChxm4lv+TobGdG82LnXMjuNGo/BSSZZL4UmkWhxEHP2f5ziLNwGWMA==", 931 + "bin": true 932 + }, 933 + "brace-expansion@1.1.12": { 934 + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", 935 + "dependencies": [ 936 + "balanced-match", 937 + "concat-map" 938 + ] 939 + }, 940 + "brace-expansion@2.0.2": { 941 + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", 942 + "dependencies": [ 943 + "balanced-match" 944 + ] 945 + }, 946 + "braces@3.0.3": { 947 + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", 948 + "dependencies": [ 949 + "fill-range" 950 + ] 951 + }, 952 + "browserslist@4.26.3": { 953 + "integrity": "sha512-lAUU+02RFBuCKQPj/P6NgjlbCnLBMp4UtgTx7vNHd3XSIJF87s9a5rA3aH2yw3GS9DqZAUbOtZdCCiZeVRqt0w==", 954 + "dependencies": [ 955 + "baseline-browser-mapping", 956 + "caniuse-lite", 957 + "electron-to-chromium", 958 + "node-releases", 959 + "update-browserslist-db" 960 + ], 961 + "bin": true 962 + }, 963 + "caller-callsite@2.0.0": { 964 + "integrity": "sha512-JuG3qI4QOftFsZyOn1qq87fq5grLIyk1JYd5lJmdA+fG7aQ9pA/i3JIJGcO3q0MrRcHlOt1U+ZeHW8Dq9axALQ==", 965 + "dependencies": [ 966 + "callsites@2.0.0" 967 + ] 968 + }, 969 + "caller-path@2.0.0": { 970 + "integrity": "sha512-MCL3sf6nCSXOwCTzvPKhN18TU7AHTvdtam8DAogxcrJ8Rjfbbg7Lgng64H9Iy+vUV6VGFClN/TyxBkAebLRR4A==", 971 + "dependencies": [ 972 + "caller-callsite" 973 + ] 974 + }, 975 + "callsites@2.0.0": { 976 + "integrity": "sha512-ksWePWBloaWPxJYQ8TL0JHvtci6G5QTKwQ95RcWAa/lzoAKuAOflGdAK92hpHXjkwb8zLxoLNUoNYZgVsaJzvQ==" 977 + }, 978 + "callsites@3.1.0": { 979 + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==" 980 + }, 981 + "caniuse-lite@1.0.30001746": { 982 + "integrity": "sha512-eA7Ys/DGw+pnkWWSE/id29f2IcPHVoE8wxtvE5JdvD2V28VTDPy1yEeo11Guz0sJ4ZeGRcm3uaTcAqK1LXaphA==" 983 + }, 984 + "chalk@4.1.2": { 985 + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", 986 + "dependencies": [ 987 + "ansi-styles", 988 + "supports-color" 989 + ] 990 + }, 991 + "chownr@3.0.0": { 992 + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==" 993 + }, 994 + "color-convert@2.0.1": { 995 + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 996 + "dependencies": [ 997 + "color-name" 998 + ] 999 + }, 1000 + "color-name@1.1.4": { 1001 + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" 1002 + }, 1003 + "concat-map@0.0.1": { 1004 + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" 1005 + }, 1006 + "convert-source-map@2.0.0": { 1007 + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" 1008 + }, 1009 + "cookie@1.0.2": { 1010 + "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==" 1011 + }, 1012 + "cosmiconfig@5.2.1": { 1013 + "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==", 1014 + "dependencies": [ 1015 + "import-fresh@2.0.0", 1016 + "is-directory", 1017 + "js-yaml@3.14.1", 1018 + "parse-json@4.0.0" 1019 + ] 1020 + }, 1021 + "cosmiconfig@6.0.0": { 1022 + "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", 1023 + "dependencies": [ 1024 + "@types/parse-json", 1025 + "import-fresh@3.3.1", 1026 + "parse-json@5.2.0", 1027 + "path-type", 1028 + "yaml" 1029 + ] 1030 + }, 1031 + "cross-fetch@3.2.0": { 1032 + "integrity": "sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q==", 1033 + "dependencies": [ 1034 + "node-fetch" 1035 + ] 1036 + }, 1037 + "cross-spawn@7.0.6": { 1038 + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", 1039 + "dependencies": [ 1040 + "path-key", 1041 + "shebang-command", 1042 + "which" 1043 + ] 1044 + }, 1045 + "csstype@3.1.3": { 1046 + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" 1047 + }, 1048 + "debug@4.4.3": { 1049 + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", 1050 + "dependencies": [ 1051 + "ms" 1052 + ] 1053 + }, 1054 + "deep-is@0.1.4": { 1055 + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==" 1056 + }, 1057 + "detect-libc@2.1.1": { 1058 + "integrity": "sha512-ecqj/sy1jcK1uWrwpR67UhYrIFQ+5WlGxth34WquCbamhFA6hkkwiu37o6J5xCHdo1oixJRfVRw+ywV+Hq/0Aw==" 1059 + }, 1060 + "electron-to-chromium@1.5.229": { 1061 + "integrity": "sha512-cwhDcZKGcT/rEthLRJ9eBlMDkh1sorgsuk+6dpsehV0g9CABsIqBxU4rLRjG+d/U6pYU1s37A4lSKrVc5lSQYg==" 1062 + }, 1063 + "enhanced-resolve@5.18.3": { 1064 + "integrity": "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==", 1065 + "dependencies": [ 1066 + "graceful-fs", 1067 + "tapable" 1068 + ] 1069 + }, 1070 + "error-ex@1.3.2": { 1071 + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", 1072 + "dependencies": [ 1073 + "is-arrayish" 1074 + ] 1075 + }, 1076 + "esbuild@0.25.10": { 1077 + "integrity": "sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ==", 1078 + "optionalDependencies": [ 1079 + "@esbuild/aix-ppc64", 1080 + "@esbuild/android-arm", 1081 + "@esbuild/android-arm64", 1082 + "@esbuild/android-x64", 1083 + "@esbuild/darwin-arm64", 1084 + "@esbuild/darwin-x64", 1085 + "@esbuild/freebsd-arm64", 1086 + "@esbuild/freebsd-x64", 1087 + "@esbuild/linux-arm", 1088 + "@esbuild/linux-arm64", 1089 + "@esbuild/linux-ia32", 1090 + "@esbuild/linux-loong64", 1091 + "@esbuild/linux-mips64el", 1092 + "@esbuild/linux-ppc64", 1093 + "@esbuild/linux-riscv64", 1094 + "@esbuild/linux-s390x", 1095 + "@esbuild/linux-x64", 1096 + "@esbuild/netbsd-arm64", 1097 + "@esbuild/netbsd-x64", 1098 + "@esbuild/openbsd-arm64", 1099 + "@esbuild/openbsd-x64", 1100 + "@esbuild/openharmony-arm64", 1101 + "@esbuild/sunos-x64", 1102 + "@esbuild/win32-arm64", 1103 + "@esbuild/win32-ia32", 1104 + "@esbuild/win32-x64" 1105 + ], 1106 + "scripts": true, 1107 + "bin": true 1108 + }, 1109 + "escalade@3.2.0": { 1110 + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==" 1111 + }, 1112 + "escape-string-regexp@4.0.0": { 1113 + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==" 1114 + }, 1115 + "eslint-plugin-react-hooks@5.2.0_eslint@9.36.0": { 1116 + "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==", 1117 + "dependencies": [ 1118 + "eslint" 1119 + ] 1120 + }, 1121 + "eslint-plugin-react-refresh@0.4.23_eslint@9.36.0": { 1122 + "integrity": "sha512-G4j+rv0NmbIR45kni5xJOrYvCtyD3/7LjpVH8MPPcudXDcNu8gv+4ATTDXTtbRR8rTCM5HxECvCSsRmxKnWDsA==", 1123 + "dependencies": [ 1124 + "eslint" 1125 + ] 1126 + }, 1127 + "eslint-scope@8.4.0": { 1128 + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", 1129 + "dependencies": [ 1130 + "esrecurse", 1131 + "estraverse" 1132 + ] 1133 + }, 1134 + "eslint-visitor-keys@3.4.3": { 1135 + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==" 1136 + }, 1137 + "eslint-visitor-keys@4.2.1": { 1138 + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==" 1139 + }, 1140 + "eslint@9.36.0": { 1141 + "integrity": "sha512-hB4FIzXovouYzwzECDcUkJ4OcfOEkXTv2zRY6B9bkwjx/cprAq0uvm1nl7zvQ0/TsUk0zQiN4uPfJpB9m+rPMQ==", 1142 + "dependencies": [ 1143 + "@eslint-community/eslint-utils", 1144 + "@eslint-community/regexpp", 1145 + "@eslint/config-array", 1146 + "@eslint/config-helpers", 1147 + "@eslint/core", 1148 + "@eslint/eslintrc", 1149 + "@eslint/js", 1150 + "@eslint/plugin-kit", 1151 + "@humanfs/node", 1152 + "@humanwhocodes/module-importer", 1153 + "@humanwhocodes/retry", 1154 + "@types/estree", 1155 + "@types/json-schema", 1156 + "ajv", 1157 + "chalk", 1158 + "cross-spawn", 1159 + "debug", 1160 + "escape-string-regexp", 1161 + "eslint-scope", 1162 + "eslint-visitor-keys@4.2.1", 1163 + "espree", 1164 + "esquery", 1165 + "esutils", 1166 + "fast-deep-equal", 1167 + "file-entry-cache", 1168 + "find-up", 1169 + "glob-parent@6.0.2", 1170 + "ignore@5.3.2", 1171 + "imurmurhash", 1172 + "is-glob", 1173 + "json-stable-stringify-without-jsonify", 1174 + "lodash.merge", 1175 + "minimatch@3.1.2", 1176 + "natural-compare", 1177 + "optionator" 1178 + ], 1179 + "bin": true 1180 + }, 1181 + "espree@10.4.0_acorn@8.15.0": { 1182 + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", 1183 + "dependencies": [ 1184 + "acorn", 1185 + "acorn-jsx", 1186 + "eslint-visitor-keys@4.2.1" 1187 + ] 1188 + }, 1189 + "esprima@4.0.1": { 1190 + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", 1191 + "bin": true 1192 + }, 1193 + "esquery@1.6.0": { 1194 + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", 1195 + "dependencies": [ 1196 + "estraverse" 1197 + ] 1198 + }, 1199 + "esrecurse@4.3.0": { 1200 + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", 1201 + "dependencies": [ 1202 + "estraverse" 1203 + ] 1204 + }, 1205 + "estraverse@5.3.0": { 1206 + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==" 1207 + }, 1208 + "esutils@2.0.3": { 1209 + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" 1210 + }, 1211 + "fast-deep-equal@3.1.3": { 1212 + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" 1213 + }, 1214 + "fast-glob@3.3.3": { 1215 + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", 1216 + "dependencies": [ 1217 + "@nodelib/fs.stat", 1218 + "@nodelib/fs.walk", 1219 + "glob-parent@5.1.2", 1220 + "merge2", 1221 + "micromatch" 1222 + ] 1223 + }, 1224 + "fast-json-stable-stringify@2.1.0": { 1225 + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" 1226 + }, 1227 + "fast-levenshtein@2.0.6": { 1228 + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==" 1229 + }, 1230 + "fastq@1.19.1": { 1231 + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", 1232 + "dependencies": [ 1233 + "reusify" 1234 + ] 1235 + }, 1236 + "fbjs-css-vars@1.0.2": { 1237 + "integrity": "sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ==" 1238 + }, 1239 + "fbjs@3.0.5": { 1240 + "integrity": "sha512-ztsSx77JBtkuMrEypfhgc3cI0+0h+svqeie7xHbh1k/IKdcydnvadp/mUaGgjAOXQmQSxsqgaRhS3q9fy+1kxg==", 1241 + "dependencies": [ 1242 + "cross-fetch", 1243 + "fbjs-css-vars", 1244 + "loose-envify", 1245 + "object-assign", 1246 + "promise", 1247 + "setimmediate", 1248 + "ua-parser-js" 1249 + ] 1250 + }, 1251 + "fdir@6.5.0_picomatch@4.0.3": { 1252 + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", 1253 + "dependencies": [ 1254 + "picomatch@4.0.3" 1255 + ], 1256 + "optionalPeers": [ 1257 + "picomatch@4.0.3" 1258 + ] 1259 + }, 1260 + "file-entry-cache@8.0.0": { 1261 + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", 1262 + "dependencies": [ 1263 + "flat-cache" 1264 + ] 1265 + }, 1266 + "fill-range@7.1.1": { 1267 + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", 1268 + "dependencies": [ 1269 + "to-regex-range" 1270 + ] 1271 + }, 1272 + "find-up@5.0.0": { 1273 + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", 1274 + "dependencies": [ 1275 + "locate-path", 1276 + "path-exists" 1277 + ] 1278 + }, 1279 + "flat-cache@4.0.1": { 1280 + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", 1281 + "dependencies": [ 1282 + "flatted", 1283 + "keyv" 1284 + ] 1285 + }, 1286 + "flatted@3.3.3": { 1287 + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==" 1288 + }, 1289 + "fraction.js@4.3.7": { 1290 + "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==" 1291 + }, 1292 + "fsevents@2.3.3": { 1293 + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", 1294 + "os": ["darwin"], 1295 + "scripts": true 1296 + }, 1297 + "function-bind@1.1.2": { 1298 + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==" 1299 + }, 1300 + "gensync@1.0.0-beta.2": { 1301 + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==" 1302 + }, 1303 + "glob-parent@5.1.2": { 1304 + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", 1305 + "dependencies": [ 1306 + "is-glob" 1307 + ] 1308 + }, 1309 + "glob-parent@6.0.2": { 1310 + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", 1311 + "dependencies": [ 1312 + "is-glob" 1313 + ] 1314 + }, 1315 + "globals@14.0.0": { 1316 + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==" 1317 + }, 1318 + "globals@16.4.0": { 1319 + "integrity": "sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==" 1320 + }, 1321 + "graceful-fs@4.2.11": { 1322 + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" 1323 + }, 1324 + "graphemer@1.4.0": { 1325 + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==" 1326 + }, 1327 + "graphql@15.3.0": { 1328 + "integrity": "sha512-GTCJtzJmkFLWRfFJuoo9RWWa/FfamUHgiFosxi/X1Ani4AVWbeyBenZTNX6dM+7WSbbFfTo/25eh0LLkwHMw2w==" 1329 + }, 1330 + "graphql@16.11.0": { 1331 + "integrity": "sha512-mS1lbMsxgQj6hge1XZ6p7GPhbrtFwUFYi3wRzXAC/FmYnyXMTvvI3td3rjmQ2u8ewXueaSvRPWaEcgVVOT9Jnw==" 1332 + }, 1333 + "has-flag@4.0.0": { 1334 + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" 1335 + }, 1336 + "hasown@2.0.2": { 1337 + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", 1338 + "dependencies": [ 1339 + "function-bind" 1340 + ] 1341 + }, 1342 + "ignore@5.3.2": { 1343 + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==" 1344 + }, 1345 + "ignore@7.0.5": { 1346 + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==" 1347 + }, 1348 + "import-fresh@2.0.0": { 1349 + "integrity": "sha512-eZ5H8rcgYazHbKC3PG4ClHNykCSxtAhxSSEM+2mb+7evD2CKF5V7c0dNum7AdpDh0ZdICwZY9sRSn8f+KH96sg==", 1350 + "dependencies": [ 1351 + "caller-path", 1352 + "resolve-from@3.0.0" 1353 + ] 1354 + }, 1355 + "import-fresh@3.3.1": { 1356 + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", 1357 + "dependencies": [ 1358 + "parent-module", 1359 + "resolve-from@4.0.0" 1360 + ] 1361 + }, 1362 + "imurmurhash@0.1.4": { 1363 + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==" 1364 + }, 1365 + "invariant@2.2.4": { 1366 + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", 1367 + "dependencies": [ 1368 + "loose-envify" 1369 + ] 1370 + }, 1371 + "is-arrayish@0.2.1": { 1372 + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" 1373 + }, 1374 + "is-core-module@2.16.1": { 1375 + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", 1376 + "dependencies": [ 1377 + "hasown" 1378 + ] 1379 + }, 1380 + "is-directory@0.3.1": { 1381 + "integrity": "sha512-yVChGzahRFvbkscn2MlwGismPO12i9+znNruC5gVEntG3qu0xQMzsGg/JFbrsqDOHtHFPci+V5aP5T9I+yeKqw==" 1382 + }, 1383 + "is-extglob@2.1.1": { 1384 + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==" 1385 + }, 1386 + "is-glob@4.0.3": { 1387 + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", 1388 + "dependencies": [ 1389 + "is-extglob" 1390 + ] 1391 + }, 1392 + "is-number@7.0.0": { 1393 + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" 1394 + }, 1395 + "isexe@2.0.0": { 1396 + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" 1397 + }, 1398 + "jiti@2.6.1": { 1399 + "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", 1400 + "bin": true 1401 + }, 1402 + "js-tokens@4.0.0": { 1403 + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" 1404 + }, 1405 + "js-yaml@3.14.1": { 1406 + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", 1407 + "dependencies": [ 1408 + "argparse@1.0.10", 1409 + "esprima" 1410 + ], 1411 + "bin": true 1412 + }, 1413 + "js-yaml@4.1.0": { 1414 + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", 1415 + "dependencies": [ 1416 + "argparse@2.0.1" 1417 + ], 1418 + "bin": true 1419 + }, 1420 + "jsesc@3.1.0": { 1421 + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", 1422 + "bin": true 1423 + }, 1424 + "json-buffer@3.0.1": { 1425 + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" 1426 + }, 1427 + "json-parse-better-errors@1.0.2": { 1428 + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" 1429 + }, 1430 + "json-parse-even-better-errors@2.3.1": { 1431 + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" 1432 + }, 1433 + "json-schema-traverse@0.4.1": { 1434 + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" 1435 + }, 1436 + "json-stable-stringify-without-jsonify@1.0.1": { 1437 + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==" 1438 + }, 1439 + "json5@2.2.3": { 1440 + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", 1441 + "bin": true 1442 + }, 1443 + "keyv@4.5.4": { 1444 + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", 1445 + "dependencies": [ 1446 + "json-buffer" 1447 + ] 1448 + }, 1449 + "levn@0.4.1": { 1450 + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", 1451 + "dependencies": [ 1452 + "prelude-ls", 1453 + "type-check" 1454 + ] 1455 + }, 1456 + "lightningcss-darwin-arm64@1.30.1": { 1457 + "integrity": "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==", 1458 + "os": ["darwin"], 1459 + "cpu": ["arm64"] 1460 + }, 1461 + "lightningcss-darwin-x64@1.30.1": { 1462 + "integrity": "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==", 1463 + "os": ["darwin"], 1464 + "cpu": ["x64"] 1465 + }, 1466 + "lightningcss-freebsd-x64@1.30.1": { 1467 + "integrity": "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==", 1468 + "os": ["freebsd"], 1469 + "cpu": ["x64"] 1470 + }, 1471 + "lightningcss-linux-arm-gnueabihf@1.30.1": { 1472 + "integrity": "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==", 1473 + "os": ["linux"], 1474 + "cpu": ["arm"] 1475 + }, 1476 + "lightningcss-linux-arm64-gnu@1.30.1": { 1477 + "integrity": "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==", 1478 + "os": ["linux"], 1479 + "cpu": ["arm64"] 1480 + }, 1481 + "lightningcss-linux-arm64-musl@1.30.1": { 1482 + "integrity": "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==", 1483 + "os": ["linux"], 1484 + "cpu": ["arm64"] 1485 + }, 1486 + "lightningcss-linux-x64-gnu@1.30.1": { 1487 + "integrity": "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==", 1488 + "os": ["linux"], 1489 + "cpu": ["x64"] 1490 + }, 1491 + "lightningcss-linux-x64-musl@1.30.1": { 1492 + "integrity": "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==", 1493 + "os": ["linux"], 1494 + "cpu": ["x64"] 1495 + }, 1496 + "lightningcss-win32-arm64-msvc@1.30.1": { 1497 + "integrity": "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==", 1498 + "os": ["win32"], 1499 + "cpu": ["arm64"] 1500 + }, 1501 + "lightningcss-win32-x64-msvc@1.30.1": { 1502 + "integrity": "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==", 1503 + "os": ["win32"], 1504 + "cpu": ["x64"] 1505 + }, 1506 + "lightningcss@1.30.1": { 1507 + "integrity": "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==", 1508 + "dependencies": [ 1509 + "detect-libc" 1510 + ], 1511 + "optionalDependencies": [ 1512 + "lightningcss-darwin-arm64", 1513 + "lightningcss-darwin-x64", 1514 + "lightningcss-freebsd-x64", 1515 + "lightningcss-linux-arm-gnueabihf", 1516 + "lightningcss-linux-arm64-gnu", 1517 + "lightningcss-linux-arm64-musl", 1518 + "lightningcss-linux-x64-gnu", 1519 + "lightningcss-linux-x64-musl", 1520 + "lightningcss-win32-arm64-msvc", 1521 + "lightningcss-win32-x64-msvc" 1522 + ] 1523 + }, 1524 + "lines-and-columns@1.2.4": { 1525 + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" 1526 + }, 1527 + "locate-path@6.0.0": { 1528 + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", 1529 + "dependencies": [ 1530 + "p-locate" 1531 + ] 1532 + }, 1533 + "lodash.merge@4.6.2": { 1534 + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" 1535 + }, 1536 + "loose-envify@1.4.0": { 1537 + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", 1538 + "dependencies": [ 1539 + "js-tokens" 1540 + ], 1541 + "bin": true 1542 + }, 1543 + "lru-cache@5.1.1": { 1544 + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", 1545 + "dependencies": [ 1546 + "yallist@3.1.1" 1547 + ] 1548 + }, 1549 + "magic-string@0.30.19": { 1550 + "integrity": "sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==", 1551 + "dependencies": [ 1552 + "@jridgewell/sourcemap-codec" 1553 + ] 1554 + }, 1555 + "merge2@1.4.1": { 1556 + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==" 1557 + }, 1558 + "micromatch@4.0.8": { 1559 + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", 1560 + "dependencies": [ 1561 + "braces", 1562 + "picomatch@2.3.1" 1563 + ] 1564 + }, 1565 + "minimatch@3.1.2": { 1566 + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", 1567 + "dependencies": [ 1568 + "brace-expansion@1.1.12" 1569 + ] 1570 + }, 1571 + "minimatch@9.0.5": { 1572 + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", 1573 + "dependencies": [ 1574 + "brace-expansion@2.0.2" 1575 + ] 1576 + }, 1577 + "minipass@7.1.2": { 1578 + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==" 1579 + }, 1580 + "minizlib@3.1.0": { 1581 + "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", 1582 + "dependencies": [ 1583 + "minipass" 1584 + ] 1585 + }, 1586 + "ms@2.1.3": { 1587 + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" 1588 + }, 1589 + "nanoid@3.3.11": { 1590 + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", 1591 + "bin": true 1592 + }, 1593 + "natural-compare@1.4.0": { 1594 + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==" 1595 + }, 1596 + "node-fetch@2.7.0": { 1597 + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", 1598 + "dependencies": [ 1599 + "whatwg-url" 1600 + ] 1601 + }, 1602 + "node-releases@2.0.21": { 1603 + "integrity": "sha512-5b0pgg78U3hwXkCM8Z9b2FJdPZlr9Psr9V2gQPESdGHqbntyFJKFW4r5TeWGFzafGY3hzs1JC62VEQMbl1JFkw==" 1604 + }, 1605 + "normalize-range@0.1.2": { 1606 + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==" 1607 + }, 1608 + "nullthrows@1.1.1": { 1609 + "integrity": "sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==" 1610 + }, 1611 + "object-assign@4.1.1": { 1612 + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" 1613 + }, 1614 + "optionator@0.9.4": { 1615 + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", 1616 + "dependencies": [ 1617 + "deep-is", 1618 + "fast-levenshtein", 1619 + "levn", 1620 + "prelude-ls", 1621 + "type-check", 1622 + "word-wrap" 1623 + ] 1624 + }, 1625 + "p-limit@3.1.0": { 1626 + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", 1627 + "dependencies": [ 1628 + "yocto-queue" 1629 + ] 1630 + }, 1631 + "p-locate@5.0.0": { 1632 + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", 1633 + "dependencies": [ 1634 + "p-limit" 1635 + ] 1636 + }, 1637 + "parent-module@1.0.1": { 1638 + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", 1639 + "dependencies": [ 1640 + "callsites@3.1.0" 1641 + ] 1642 + }, 1643 + "parse-json@4.0.0": { 1644 + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", 1645 + "dependencies": [ 1646 + "error-ex", 1647 + "json-parse-better-errors" 1648 + ] 1649 + }, 1650 + "parse-json@5.2.0": { 1651 + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", 1652 + "dependencies": [ 1653 + "@babel/code-frame", 1654 + "error-ex", 1655 + "json-parse-even-better-errors", 1656 + "lines-and-columns" 1657 + ] 1658 + }, 1659 + "path-exists@4.0.0": { 1660 + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" 1661 + }, 1662 + "path-key@3.1.1": { 1663 + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" 1664 + }, 1665 + "path-parse@1.0.7": { 1666 + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" 1667 + }, 1668 + "path-type@4.0.0": { 1669 + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==" 1670 + }, 1671 + "picocolors@1.1.1": { 1672 + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" 1673 + }, 1674 + "picomatch@2.3.1": { 1675 + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" 1676 + }, 1677 + "picomatch@4.0.3": { 1678 + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==" 1679 + }, 1680 + "postcss-value-parser@4.2.0": { 1681 + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" 1682 + }, 1683 + "postcss@8.5.6": { 1684 + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", 1685 + "dependencies": [ 1686 + "nanoid", 1687 + "picocolors", 1688 + "source-map-js" 1689 + ] 1690 + }, 1691 + "prelude-ls@1.2.1": { 1692 + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==" 1693 + }, 1694 + "promise@7.3.1": { 1695 + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", 1696 + "dependencies": [ 1697 + "asap" 1698 + ] 1699 + }, 1700 + "punycode@2.3.1": { 1701 + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==" 1702 + }, 1703 + "queue-microtask@1.2.3": { 1704 + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==" 1705 + }, 1706 + "react-dom@19.2.0_react@19.2.0": { 1707 + "integrity": "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==", 1708 + "dependencies": [ 1709 + "react", 1710 + "scheduler" 1711 + ] 1712 + }, 1713 + "react-refresh@0.17.0": { 1714 + "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==" 1715 + }, 1716 + "react-relay@20.1.1_react@19.2.0": { 1717 + "integrity": "sha512-pwl7wHHXCOx32dOg4TVNkhVojgGVvOBMo9pcMGeM1BIFYvmwGauKTtBB+qr5buXHGooCL8TXjMVg+ZLizIsbeQ==", 1718 + "dependencies": [ 1719 + "@babel/runtime", 1720 + "fbjs", 1721 + "invariant", 1722 + "nullthrows", 1723 + "react", 1724 + "relay-runtime" 1725 + ] 1726 + }, 1727 + "react-router-dom@7.9.3_react@19.2.0_react-dom@19.2.0__react@19.2.0": { 1728 + "integrity": "sha512-1QSbA0TGGFKTAc/aWjpfW/zoEukYfU4dc1dLkT/vvf54JoGMkW+fNA+3oyo2gWVW1GM7BxjJVHz5GnPJv40rvg==", 1729 + "dependencies": [ 1730 + "react", 1731 + "react-dom", 1732 + "react-router" 1733 + ] 1734 + }, 1735 + "react-router@7.9.3_react@19.2.0_react-dom@19.2.0__react@19.2.0": { 1736 + "integrity": "sha512-4o2iWCFIwhI/eYAIL43+cjORXYn/aRQPgtFRRZb3VzoyQ5Uej0Bmqj7437L97N9NJW4wnicSwLOLS+yCXfAPgg==", 1737 + "dependencies": [ 1738 + "cookie", 1739 + "react", 1740 + "react-dom", 1741 + "set-cookie-parser" 1742 + ], 1743 + "optionalPeers": [ 1744 + "react-dom" 1745 + ] 1746 + }, 1747 + "react@19.2.0": { 1748 + "integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==" 1749 + }, 1750 + "relay-compiler@20.1.1": { 1751 + "integrity": "sha512-J/FFFLS/3vnbDkyQMw8l3Ev7dNHXMgC1RAs0fITz4Q63TFPOw142knKxY1Mm5ZZBABkAs9g2JghXaqM+phwTxw==", 1752 + "bin": true 1753 + }, 1754 + "relay-runtime@20.1.1": { 1755 + "integrity": "sha512-N98ZkkyuIHdXmHaPuljihM1QbFYXATF0gxA0CESFphszsQzXs9A/zZloVfzdOI/xg3B3CfM/5nozvIoeTDIcfw==", 1756 + "dependencies": [ 1757 + "@babel/runtime", 1758 + "fbjs", 1759 + "invariant" 1760 + ] 1761 + }, 1762 + "resolve-from@3.0.0": { 1763 + "integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==" 1764 + }, 1765 + "resolve-from@4.0.0": { 1766 + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==" 1767 + }, 1768 + "resolve@1.22.10": { 1769 + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", 1770 + "dependencies": [ 1771 + "is-core-module", 1772 + "path-parse", 1773 + "supports-preserve-symlinks-flag" 1774 + ], 1775 + "bin": true 1776 + }, 1777 + "reusify@1.1.0": { 1778 + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==" 1779 + }, 1780 + "rollup@4.52.3": { 1781 + "integrity": "sha512-RIDh866U8agLgiIcdpB+COKnlCreHJLfIhWC3LVflku5YHfpnsIKigRZeFfMfCc4dVcqNVfQQ5gO/afOck064A==", 1782 + "dependencies": [ 1783 + "@types/estree" 1784 + ], 1785 + "optionalDependencies": [ 1786 + "@rollup/rollup-android-arm-eabi", 1787 + "@rollup/rollup-android-arm64", 1788 + "@rollup/rollup-darwin-arm64", 1789 + "@rollup/rollup-darwin-x64", 1790 + "@rollup/rollup-freebsd-arm64", 1791 + "@rollup/rollup-freebsd-x64", 1792 + "@rollup/rollup-linux-arm-gnueabihf", 1793 + "@rollup/rollup-linux-arm-musleabihf", 1794 + "@rollup/rollup-linux-arm64-gnu", 1795 + "@rollup/rollup-linux-arm64-musl", 1796 + "@rollup/rollup-linux-loong64-gnu", 1797 + "@rollup/rollup-linux-ppc64-gnu", 1798 + "@rollup/rollup-linux-riscv64-gnu", 1799 + "@rollup/rollup-linux-riscv64-musl", 1800 + "@rollup/rollup-linux-s390x-gnu", 1801 + "@rollup/rollup-linux-x64-gnu", 1802 + "@rollup/rollup-linux-x64-musl", 1803 + "@rollup/rollup-openharmony-arm64", 1804 + "@rollup/rollup-win32-arm64-msvc", 1805 + "@rollup/rollup-win32-ia32-msvc", 1806 + "@rollup/rollup-win32-x64-gnu", 1807 + "@rollup/rollup-win32-x64-msvc", 1808 + "fsevents" 1809 + ], 1810 + "bin": true 1811 + }, 1812 + "run-parallel@1.2.0": { 1813 + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", 1814 + "dependencies": [ 1815 + "queue-microtask" 1816 + ] 1817 + }, 1818 + "scheduler@0.27.0": { 1819 + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==" 1820 + }, 1821 + "semver@6.3.1": { 1822 + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", 1823 + "bin": true 1824 + }, 1825 + "semver@7.7.2": { 1826 + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", 1827 + "bin": true 1828 + }, 1829 + "set-cookie-parser@2.7.1": { 1830 + "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==" 1831 + }, 1832 + "setimmediate@1.0.5": { 1833 + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==" 1834 + }, 1835 + "shebang-command@2.0.0": { 1836 + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", 1837 + "dependencies": [ 1838 + "shebang-regex" 1839 + ] 1840 + }, 1841 + "shebang-regex@3.0.0": { 1842 + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" 1843 + }, 1844 + "source-map-js@1.2.1": { 1845 + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==" 1846 + }, 1847 + "sprintf-js@1.0.3": { 1848 + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" 1849 + }, 1850 + "strip-json-comments@3.1.1": { 1851 + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==" 1852 + }, 1853 + "supports-color@7.2.0": { 1854 + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", 1855 + "dependencies": [ 1856 + "has-flag" 1857 + ] 1858 + }, 1859 + "supports-preserve-symlinks-flag@1.0.0": { 1860 + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" 1861 + }, 1862 + "tailwindcss@4.1.14": { 1863 + "integrity": "sha512-b7pCxjGO98LnxVkKjaZSDeNuljC4ueKUddjENJOADtubtdo8llTaJy7HwBMeLNSSo2N5QIAgklslK1+Ir8r6CA==" 1864 + }, 1865 + "tapable@2.2.3": { 1866 + "integrity": "sha512-ZL6DDuAlRlLGghwcfmSn9sK3Hr6ArtyudlSAiCqQ6IfE+b+HHbydbYDIG15IfS5do+7XQQBdBiubF/cV2dnDzg==" 1867 + }, 1868 + "tar@7.5.1": { 1869 + "integrity": "sha512-nlGpxf+hv0v7GkWBK2V9spgactGOp0qvfWRxUMjqHyzrt3SgwE48DIv/FhqPHJYLHpgW1opq3nERbz5Anq7n1g==", 1870 + "dependencies": [ 1871 + "@isaacs/fs-minipass", 1872 + "chownr", 1873 + "minipass", 1874 + "minizlib", 1875 + "yallist@5.0.0" 1876 + ] 1877 + }, 1878 + "tinyglobby@0.2.15_picomatch@4.0.3": { 1879 + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", 1880 + "dependencies": [ 1881 + "fdir", 1882 + "picomatch@4.0.3" 1883 + ] 1884 + }, 1885 + "to-regex-range@5.0.1": { 1886 + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 1887 + "dependencies": [ 1888 + "is-number" 1889 + ] 1890 + }, 1891 + "tr46@0.0.3": { 1892 + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" 1893 + }, 1894 + "ts-api-utils@2.1.0_typescript@5.9.3": { 1895 + "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", 1896 + "dependencies": [ 1897 + "typescript" 1898 + ] 1899 + }, 1900 + "tslib@2.8.1": { 1901 + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" 1902 + }, 1903 + "type-check@0.4.0": { 1904 + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", 1905 + "dependencies": [ 1906 + "prelude-ls" 1907 + ] 1908 + }, 1909 + "typescript-eslint@8.45.0_eslint@9.36.0_typescript@5.9.3_@typescript-eslint+parser@8.45.0__eslint@9.36.0__typescript@5.9.3": { 1910 + "integrity": "sha512-qzDmZw/Z5beNLUrXfd0HIW6MzIaAV5WNDxmMs9/3ojGOpYavofgNAAD/nC6tGV2PczIi0iw8vot2eAe/sBn7zg==", 1911 + "dependencies": [ 1912 + "@typescript-eslint/eslint-plugin", 1913 + "@typescript-eslint/parser", 1914 + "@typescript-eslint/typescript-estree", 1915 + "@typescript-eslint/utils", 1916 + "eslint", 1917 + "typescript" 1918 + ] 1919 + }, 1920 + "typescript@5.9.3": { 1921 + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", 1922 + "bin": true 1923 + }, 1924 + "ua-parser-js@1.0.41": { 1925 + "integrity": "sha512-LbBDqdIC5s8iROCUjMbW1f5dJQTEFB1+KO9ogbvlb3nm9n4YHa5p4KTvFPWvh2Hs8gZMBuiB1/8+pdfe/tDPug==", 1926 + "bin": true 1927 + }, 1928 + "undici-types@7.13.0": { 1929 + "integrity": "sha512-Ov2Rr9Sx+fRgagJ5AX0qvItZG/JKKoBRAVITs1zk7IqZGTJUwgUr7qoYBpWwakpWilTZFM98rG/AFRocu10iIQ==" 1930 + }, 1931 + "update-browserslist-db@1.1.3_browserslist@4.26.3": { 1932 + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", 1933 + "dependencies": [ 1934 + "browserslist", 1935 + "escalade", 1936 + "picocolors" 1937 + ], 1938 + "bin": true 1939 + }, 1940 + "uri-js@4.4.1": { 1941 + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", 1942 + "dependencies": [ 1943 + "punycode" 1944 + ] 1945 + }, 1946 + "vite@7.1.8_@types+node@24.6.2_picomatch@4.0.3": { 1947 + "integrity": "sha512-oBXvfSHEOL8jF+R9Am7h59Up07kVVGH1NrFGFoEG6bPDZP3tGpQhvkBpy5x7U6+E6wZCu9OihsWgJqDbQIm8LQ==", 1948 + "dependencies": [ 1949 + "@types/node", 1950 + "esbuild", 1951 + "fdir", 1952 + "picomatch@4.0.3", 1953 + "postcss", 1954 + "rollup", 1955 + "tinyglobby" 1956 + ], 1957 + "optionalDependencies": [ 1958 + "fsevents" 1959 + ], 1960 + "optionalPeers": [ 1961 + "@types/node" 1962 + ], 1963 + "bin": true 1964 + }, 1965 + "webidl-conversions@3.0.1": { 1966 + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" 1967 + }, 1968 + "whatwg-url@5.0.0": { 1969 + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", 1970 + "dependencies": [ 1971 + "tr46", 1972 + "webidl-conversions" 1973 + ] 1974 + }, 1975 + "which@2.0.2": { 1976 + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", 1977 + "dependencies": [ 1978 + "isexe" 1979 + ], 1980 + "bin": true 1981 + }, 1982 + "word-wrap@1.2.5": { 1983 + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==" 1984 + }, 1985 + "yallist@3.1.1": { 1986 + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" 1987 + }, 1988 + "yallist@5.0.0": { 1989 + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==" 1990 + }, 1991 + "yaml@1.10.2": { 1992 + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==" 1993 + }, 1994 + "yocto-queue@0.1.0": { 1995 + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==" 1996 + } 1997 + }, 1998 + "workspace": { 1999 + "packageJson": { 2000 + "dependencies": [ 2001 + "npm:@eslint/js@^9.36.0", 2002 + "npm:@tailwindcss/postcss@^4.1.14", 2003 + "npm:@types/node@^24.6.0", 2004 + "npm:@types/react-dom@^19.1.9", 2005 + "npm:@types/react-relay@^18.2.1", 2006 + "npm:@types/react@^19.1.16", 2007 + "npm:@types/relay-runtime@^19.0.3", 2008 + "npm:@vitejs/plugin-react@^5.0.4", 2009 + "npm:autoprefixer@^10.4.21", 2010 + "npm:babel-plugin-relay@^20.1.1", 2011 + "npm:eslint-plugin-react-hooks@^5.2.0", 2012 + "npm:eslint-plugin-react-refresh@~0.4.22", 2013 + "npm:eslint@^9.36.0", 2014 + "npm:globals@^16.4.0", 2015 + "npm:graphql@^16.11.0", 2016 + "npm:postcss@^8.5.6", 2017 + "npm:react-dom@^19.1.1", 2018 + "npm:react-relay@^20.1.1", 2019 + "npm:react-router-dom@^7.9.3", 2020 + "npm:react@^19.1.1", 2021 + "npm:relay-compiler@^20.1.1", 2022 + "npm:relay-runtime@^20.1.1", 2023 + "npm:tailwindcss@^4.1.14", 2024 + "npm:typescript-eslint@^8.45.0", 2025 + "npm:typescript@~5.9.3", 2026 + "npm:vite@^7.1.7" 2027 + ] 2028 + } 2029 + } 2030 + }
+23
eslint.config.js
··· 1 + import js from '@eslint/js' 2 + import globals from 'globals' 3 + import reactHooks from 'eslint-plugin-react-hooks' 4 + import reactRefresh from 'eslint-plugin-react-refresh' 5 + import tseslint from 'typescript-eslint' 6 + import { defineConfig, globalIgnores } from 'eslint/config' 7 + 8 + export default defineConfig([ 9 + globalIgnores(['dist']), 10 + { 11 + files: ['**/*.{ts,tsx}'], 12 + extends: [ 13 + js.configs.recommended, 14 + tseslint.configs.recommended, 15 + reactHooks.configs['recommended-latest'], 16 + reactRefresh.configs.vite, 17 + ], 18 + languageOptions: { 19 + ecmaVersion: 2020, 20 + globals: globals.browser, 21 + }, 22 + }, 23 + ])
+19
index.html
··· 1 + <!doctype html> 2 + <html lang="en"> 3 + <head> 4 + <meta charset="UTF-8" /> 5 + <link rel="icon" type="image/svg+xml" href="/vite.svg" /> 6 + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> 7 + <title>slices-relay</title> 8 + <style> 9 + body { 10 + background-color: #09090b; 11 + margin: 0; 12 + } 13 + </style> 14 + </head> 15 + <body> 16 + <div id="root"></div> 17 + <script type="module" src="/src/main.tsx"></script> 18 + </body> 19 + </html>
+47
package.json
··· 1 + { 2 + "name": "slices-relay", 3 + "private": true, 4 + "version": "0.0.0", 5 + "type": "module", 6 + "scripts": { 7 + "dev": "vite", 8 + "build": "tsc -b && vite build", 9 + "lint": "eslint .", 10 + "preview": "vite preview", 11 + "schema:dev": "npx get-graphql-schema 'http://localhost:3000/graphql?slice=at://did:plc:fpruhuo22xkm5o7ttr2ktxdo/network.slices.slice/3m257yljpbg2a' > schema.graphql", 12 + "schema:prod": "npx get-graphql-schema 'https://api.slices.network/graphql?slice=at://did:plc:fpruhuo22xkm5o7ttr2ktxdo/network.slices.slice/3m257yljpbg2a' > schema.graphql" 13 + }, 14 + "dependencies": { 15 + "react": "^19.1.1", 16 + "react-dom": "^19.1.1", 17 + "react-relay": "^20.1.1", 18 + "react-router-dom": "^7.9.3", 19 + "relay-runtime": "^20.1.1" 20 + }, 21 + "devDependencies": { 22 + "@eslint/js": "^9.36.0", 23 + "@tailwindcss/postcss": "^4.1.14", 24 + "@types/node": "^24.6.0", 25 + "@types/react": "^19.1.16", 26 + "@types/react-dom": "^19.1.9", 27 + "@types/react-relay": "^18.2.1", 28 + "@types/relay-runtime": "^19.0.3", 29 + "@vitejs/plugin-react": "^5.0.4", 30 + "autoprefixer": "^10.4.21", 31 + "babel-plugin-relay": "^20.1.1", 32 + "eslint": "^9.36.0", 33 + "eslint-plugin-react-hooks": "^5.2.0", 34 + "eslint-plugin-react-refresh": "^0.4.22", 35 + "globals": "^16.4.0", 36 + "graphql": "^16.11.0", 37 + "postcss": "^8.5.6", 38 + "relay-compiler": "^20.1.1", 39 + "tailwindcss": "^4.1.14", 40 + "typescript": "~5.9.3", 41 + "typescript-eslint": "^8.45.0", 42 + "vite": "^7.1.7" 43 + }, 44 + "overrides": { 45 + "graphql": "^16.11.0" 46 + } 47 + }
+6
postcss.config.js
··· 1 + export default { 2 + plugins: { 3 + '@tailwindcss/postcss': {}, 4 + autoprefixer: {}, 5 + }, 6 + }
+5
relay.config.json
··· 1 + { 2 + "src": "./src", 3 + "schema": "./schema.graphql", 4 + "language": "typescript" 5 + }
+586
schema.graphql
··· 1 + """ 2 + Indicates that an Input Object is a OneOf Input Object (and thus requires exactly one of its field be provided) 3 + """ 4 + directive @oneOf on INPUT_OBJECT 5 + 6 + """ 7 + Provides a scalar specification URL for specifying the behavior of custom scalar types. 8 + """ 9 + directive @specifiedBy( 10 + """URL that specifies the behavior of this scalar.""" 11 + url: String! 12 + ) on SCALAR 13 + 14 + input AggregationOrderBy { 15 + count: SortDirection 16 + } 17 + 18 + type AppBskyActorProfile { 19 + uri: String! 20 + cid: String! 21 + did: String! 22 + indexedAt: String! 23 + actorHandle: String 24 + avatar: Blob 25 + banner: Blob 26 + createdAt: String 27 + description: String 28 + displayName: String 29 + joinedViaStarterPack: JSON 30 + labels: JSON 31 + pinnedPost: JSON 32 + appBskyFeedPostgate(limit: Int): [AppBskyFeedPostgate!]! 33 + appBskyFeedThreadgate(limit: Int): [AppBskyFeedThreadgate!]! 34 + appBskyActorProfile: AppBskyActorProfile 35 + fmTealAlphaFeedPlay(limit: Int): [FmTealAlphaFeedPlay!]! 36 + appBskyFeedPostgates(limit: Int): [AppBskyFeedPostgate!]! 37 + appBskyFeedThreadgates(limit: Int): [AppBskyFeedThreadgate!]! 38 + appBskyActorProfiles(limit: Int): [AppBskyActorProfile!]! 39 + fmTealAlphaFeedPlays(limit: Int): [FmTealAlphaFeedPlay!]! 40 + } 41 + 42 + type AppBskyActorProfileAggregated { 43 + avatar: String 44 + banner: String 45 + createdAt: String 46 + description: String 47 + displayName: String 48 + joinedViaStarterPack: String 49 + labels: String 50 + pinnedPost: String 51 + count: Int! 52 + } 53 + 54 + type AppBskyActorProfileConnection { 55 + totalCount: Int! 56 + pageInfo: PageInfo! 57 + edges: [AppBskyActorProfileEdge!]! 58 + nodes: [AppBskyActorProfile!]! 59 + } 60 + 61 + type AppBskyActorProfileEdge { 62 + node: AppBskyActorProfile! 63 + cursor: String! 64 + } 65 + 66 + type AppBskyEmbedExternal { 67 + uri: String! 68 + cid: String! 69 + did: String! 70 + indexedAt: String! 71 + actorHandle: String 72 + external: JSON! 73 + appBskyFeedPostgate(limit: Int): [AppBskyFeedPostgate!]! 74 + appBskyFeedThreadgate(limit: Int): [AppBskyFeedThreadgate!]! 75 + appBskyActorProfile: AppBskyActorProfile 76 + fmTealAlphaFeedPlay(limit: Int): [FmTealAlphaFeedPlay!]! 77 + appBskyFeedPostgates(limit: Int): [AppBskyFeedPostgate!]! 78 + appBskyFeedThreadgates(limit: Int): [AppBskyFeedThreadgate!]! 79 + appBskyActorProfiles(limit: Int): [AppBskyActorProfile!]! 80 + fmTealAlphaFeedPlays(limit: Int): [FmTealAlphaFeedPlay!]! 81 + } 82 + 83 + type AppBskyEmbedExternalAggregated { 84 + external: String 85 + count: Int! 86 + } 87 + 88 + type AppBskyEmbedExternalConnection { 89 + totalCount: Int! 90 + pageInfo: PageInfo! 91 + edges: [AppBskyEmbedExternalEdge!]! 92 + nodes: [AppBskyEmbedExternal!]! 93 + } 94 + 95 + type AppBskyEmbedExternalEdge { 96 + node: AppBskyEmbedExternal! 97 + cursor: String! 98 + } 99 + 100 + type AppBskyEmbedImages { 101 + uri: String! 102 + cid: String! 103 + did: String! 104 + indexedAt: String! 105 + actorHandle: String 106 + images: JSON! 107 + appBskyFeedPostgate(limit: Int): [AppBskyFeedPostgate!]! 108 + appBskyFeedThreadgate(limit: Int): [AppBskyFeedThreadgate!]! 109 + appBskyActorProfile: AppBskyActorProfile 110 + fmTealAlphaFeedPlay(limit: Int): [FmTealAlphaFeedPlay!]! 111 + appBskyFeedPostgates(limit: Int): [AppBskyFeedPostgate!]! 112 + appBskyFeedThreadgates(limit: Int): [AppBskyFeedThreadgate!]! 113 + appBskyActorProfiles(limit: Int): [AppBskyActorProfile!]! 114 + fmTealAlphaFeedPlays(limit: Int): [FmTealAlphaFeedPlay!]! 115 + } 116 + 117 + type AppBskyEmbedImagesAggregated { 118 + images: String 119 + count: Int! 120 + } 121 + 122 + type AppBskyEmbedImagesConnection { 123 + totalCount: Int! 124 + pageInfo: PageInfo! 125 + edges: [AppBskyEmbedImagesEdge!]! 126 + nodes: [AppBskyEmbedImages!]! 127 + } 128 + 129 + type AppBskyEmbedImagesEdge { 130 + node: AppBskyEmbedImages! 131 + cursor: String! 132 + } 133 + 134 + type AppBskyEmbedRecord { 135 + uri: String! 136 + cid: String! 137 + did: String! 138 + indexedAt: String! 139 + actorHandle: String 140 + record: JSON! 141 + appBskyFeedPostgate(limit: Int): [AppBskyFeedPostgate!]! 142 + appBskyFeedThreadgate(limit: Int): [AppBskyFeedThreadgate!]! 143 + appBskyActorProfile: AppBskyActorProfile 144 + fmTealAlphaFeedPlay(limit: Int): [FmTealAlphaFeedPlay!]! 145 + appBskyFeedPostgates(limit: Int): [AppBskyFeedPostgate!]! 146 + appBskyFeedThreadgates(limit: Int): [AppBskyFeedThreadgate!]! 147 + appBskyActorProfiles(limit: Int): [AppBskyActorProfile!]! 148 + fmTealAlphaFeedPlays(limit: Int): [FmTealAlphaFeedPlay!]! 149 + } 150 + 151 + type AppBskyEmbedRecordAggregated { 152 + record: String 153 + count: Int! 154 + } 155 + 156 + type AppBskyEmbedRecordConnection { 157 + totalCount: Int! 158 + pageInfo: PageInfo! 159 + edges: [AppBskyEmbedRecordEdge!]! 160 + nodes: [AppBskyEmbedRecord!]! 161 + } 162 + 163 + type AppBskyEmbedRecordEdge { 164 + node: AppBskyEmbedRecord! 165 + cursor: String! 166 + } 167 + 168 + type AppBskyEmbedRecordWithMedia { 169 + uri: String! 170 + cid: String! 171 + did: String! 172 + indexedAt: String! 173 + actorHandle: String 174 + media: JSON! 175 + record: JSON! 176 + appBskyFeedPostgate(limit: Int): [AppBskyFeedPostgate!]! 177 + appBskyFeedThreadgate(limit: Int): [AppBskyFeedThreadgate!]! 178 + appBskyActorProfile: AppBskyActorProfile 179 + fmTealAlphaFeedPlay(limit: Int): [FmTealAlphaFeedPlay!]! 180 + appBskyFeedPostgates(limit: Int): [AppBskyFeedPostgate!]! 181 + appBskyFeedThreadgates(limit: Int): [AppBskyFeedThreadgate!]! 182 + appBskyActorProfiles(limit: Int): [AppBskyActorProfile!]! 183 + fmTealAlphaFeedPlays(limit: Int): [FmTealAlphaFeedPlay!]! 184 + } 185 + 186 + type AppBskyEmbedRecordWithMediaAggregated { 187 + media: String 188 + record: String 189 + count: Int! 190 + } 191 + 192 + type AppBskyEmbedRecordWithMediaConnection { 193 + totalCount: Int! 194 + pageInfo: PageInfo! 195 + edges: [AppBskyEmbedRecordWithMediaEdge!]! 196 + nodes: [AppBskyEmbedRecordWithMedia!]! 197 + } 198 + 199 + type AppBskyEmbedRecordWithMediaEdge { 200 + node: AppBskyEmbedRecordWithMedia! 201 + cursor: String! 202 + } 203 + 204 + type AppBskyEmbedVideo { 205 + uri: String! 206 + cid: String! 207 + did: String! 208 + indexedAt: String! 209 + actorHandle: String 210 + alt: String 211 + aspectRatio: JSON 212 + captions: JSON 213 + video: Blob! 214 + appBskyFeedPostgate(limit: Int): [AppBskyFeedPostgate!]! 215 + appBskyFeedThreadgate(limit: Int): [AppBskyFeedThreadgate!]! 216 + appBskyActorProfile: AppBskyActorProfile 217 + fmTealAlphaFeedPlay(limit: Int): [FmTealAlphaFeedPlay!]! 218 + appBskyFeedPostgates(limit: Int): [AppBskyFeedPostgate!]! 219 + appBskyFeedThreadgates(limit: Int): [AppBskyFeedThreadgate!]! 220 + appBskyActorProfiles(limit: Int): [AppBskyActorProfile!]! 221 + fmTealAlphaFeedPlays(limit: Int): [FmTealAlphaFeedPlay!]! 222 + } 223 + 224 + type AppBskyEmbedVideoAggregated { 225 + alt: String 226 + aspectRatio: String 227 + captions: String 228 + video: String 229 + count: Int! 230 + } 231 + 232 + type AppBskyEmbedVideoConnection { 233 + totalCount: Int! 234 + pageInfo: PageInfo! 235 + edges: [AppBskyEmbedVideoEdge!]! 236 + nodes: [AppBskyEmbedVideo!]! 237 + } 238 + 239 + type AppBskyEmbedVideoEdge { 240 + node: AppBskyEmbedVideo! 241 + cursor: String! 242 + } 243 + 244 + type AppBskyFeedPostgate { 245 + uri: String! 246 + cid: String! 247 + did: String! 248 + indexedAt: String! 249 + actorHandle: String 250 + createdAt: String! 251 + detachedEmbeddingUris: [String] 252 + embeddingRules: JSON 253 + post: String! 254 + appBskyFeedPostgate(limit: Int): [AppBskyFeedPostgate!]! 255 + appBskyFeedThreadgate(limit: Int): [AppBskyFeedThreadgate!]! 256 + appBskyActorProfile: AppBskyActorProfile 257 + fmTealAlphaFeedPlay(limit: Int): [FmTealAlphaFeedPlay!]! 258 + appBskyFeedPostgates(limit: Int): [AppBskyFeedPostgate!]! 259 + appBskyFeedThreadgates(limit: Int): [AppBskyFeedThreadgate!]! 260 + appBskyActorProfiles(limit: Int): [AppBskyActorProfile!]! 261 + fmTealAlphaFeedPlays(limit: Int): [FmTealAlphaFeedPlay!]! 262 + } 263 + 264 + type AppBskyFeedPostgateAggregated { 265 + createdAt: String 266 + detachedEmbeddingUris: String 267 + embeddingRules: String 268 + post: String 269 + count: Int! 270 + } 271 + 272 + type AppBskyFeedPostgateConnection { 273 + totalCount: Int! 274 + pageInfo: PageInfo! 275 + edges: [AppBskyFeedPostgateEdge!]! 276 + nodes: [AppBskyFeedPostgate!]! 277 + } 278 + 279 + type AppBskyFeedPostgateEdge { 280 + node: AppBskyFeedPostgate! 281 + cursor: String! 282 + } 283 + 284 + type AppBskyFeedThreadgate { 285 + uri: String! 286 + cid: String! 287 + did: String! 288 + indexedAt: String! 289 + actorHandle: String 290 + allow: JSON 291 + createdAt: String! 292 + hiddenReplies: [String] 293 + post: String! 294 + appBskyFeedPostgate(limit: Int): [AppBskyFeedPostgate!]! 295 + appBskyFeedThreadgate(limit: Int): [AppBskyFeedThreadgate!]! 296 + appBskyActorProfile: AppBskyActorProfile 297 + fmTealAlphaFeedPlay(limit: Int): [FmTealAlphaFeedPlay!]! 298 + appBskyFeedPostgates(limit: Int): [AppBskyFeedPostgate!]! 299 + appBskyFeedThreadgates(limit: Int): [AppBskyFeedThreadgate!]! 300 + appBskyActorProfiles(limit: Int): [AppBskyActorProfile!]! 301 + fmTealAlphaFeedPlays(limit: Int): [FmTealAlphaFeedPlay!]! 302 + } 303 + 304 + type AppBskyFeedThreadgateAggregated { 305 + allow: String 306 + createdAt: String 307 + hiddenReplies: String 308 + post: String 309 + count: Int! 310 + } 311 + 312 + type AppBskyFeedThreadgateConnection { 313 + totalCount: Int! 314 + pageInfo: PageInfo! 315 + edges: [AppBskyFeedThreadgateEdge!]! 316 + nodes: [AppBskyFeedThreadgate!]! 317 + } 318 + 319 + type AppBskyFeedThreadgateEdge { 320 + node: AppBskyFeedThreadgate! 321 + cursor: String! 322 + } 323 + 324 + type AppBskyRichtextFacet { 325 + uri: String! 326 + cid: String! 327 + did: String! 328 + indexedAt: String! 329 + actorHandle: String 330 + features: JSON! 331 + index: JSON! 332 + appBskyFeedPostgate(limit: Int): [AppBskyFeedPostgate!]! 333 + appBskyFeedThreadgate(limit: Int): [AppBskyFeedThreadgate!]! 334 + appBskyActorProfile: AppBskyActorProfile 335 + fmTealAlphaFeedPlay(limit: Int): [FmTealAlphaFeedPlay!]! 336 + appBskyFeedPostgates(limit: Int): [AppBskyFeedPostgate!]! 337 + appBskyFeedThreadgates(limit: Int): [AppBskyFeedThreadgate!]! 338 + appBskyActorProfiles(limit: Int): [AppBskyActorProfile!]! 339 + fmTealAlphaFeedPlays(limit: Int): [FmTealAlphaFeedPlay!]! 340 + } 341 + 342 + type AppBskyRichtextFacetAggregated { 343 + features: String 344 + index: String 345 + count: Int! 346 + } 347 + 348 + type AppBskyRichtextFacetConnection { 349 + totalCount: Int! 350 + pageInfo: PageInfo! 351 + edges: [AppBskyRichtextFacetEdge!]! 352 + nodes: [AppBskyRichtextFacet!]! 353 + } 354 + 355 + type AppBskyRichtextFacetEdge { 356 + node: AppBskyRichtextFacet! 357 + cursor: String! 358 + } 359 + 360 + type Blob { 361 + ref: String! 362 + mimeType: String! 363 + size: Int! 364 + 365 + """ 366 + Generate CDN URL for the blob with the specified preset (avatar, banner, feed_thumbnail, feed_fullsize) 367 + """ 368 + url(preset: String): String! 369 + } 370 + 371 + type ComAtprotoRepoStrongRef { 372 + did: String! 373 + indexedAt: String! 374 + actorHandle: String 375 + cid: String! 376 + uri: String! 377 + appBskyFeedPostgate(limit: Int): [AppBskyFeedPostgate!]! 378 + appBskyFeedThreadgate(limit: Int): [AppBskyFeedThreadgate!]! 379 + appBskyActorProfile: AppBskyActorProfile 380 + fmTealAlphaFeedPlay(limit: Int): [FmTealAlphaFeedPlay!]! 381 + appBskyFeedPostgates(limit: Int): [AppBskyFeedPostgate!]! 382 + appBskyFeedThreadgates(limit: Int): [AppBskyFeedThreadgate!]! 383 + appBskyActorProfiles(limit: Int): [AppBskyActorProfile!]! 384 + fmTealAlphaFeedPlays(limit: Int): [FmTealAlphaFeedPlay!]! 385 + } 386 + 387 + type ComAtprotoRepoStrongRefAggregated { 388 + cid: String 389 + uri: String 390 + count: Int! 391 + } 392 + 393 + type ComAtprotoRepoStrongRefConnection { 394 + totalCount: Int! 395 + pageInfo: PageInfo! 396 + edges: [ComAtprotoRepoStrongRefEdge!]! 397 + nodes: [ComAtprotoRepoStrongRef!]! 398 + } 399 + 400 + type ComAtprotoRepoStrongRefEdge { 401 + node: ComAtprotoRepoStrongRef! 402 + cursor: String! 403 + } 404 + 405 + type FmTealAlphaFeedPlay { 406 + uri: String! 407 + cid: String! 408 + did: String! 409 + indexedAt: String! 410 + actorHandle: String 411 + artistMbIds: [String] 412 + artistNames: [String] 413 + artists: JSON 414 + duration: Int 415 + isrc: String 416 + musicServiceBaseDomain: String 417 + originUrl: String 418 + playedTime: String 419 + recordingMbId: String 420 + releaseMbId: String 421 + releaseName: String 422 + submissionClientAgent: String 423 + trackMbId: String 424 + trackName: String! 425 + appBskyFeedPostgate(limit: Int): [AppBskyFeedPostgate!]! 426 + appBskyFeedThreadgate(limit: Int): [AppBskyFeedThreadgate!]! 427 + appBskyActorProfile: AppBskyActorProfile 428 + fmTealAlphaFeedPlay(limit: Int): [FmTealAlphaFeedPlay!]! 429 + appBskyFeedPostgates(limit: Int): [AppBskyFeedPostgate!]! 430 + appBskyFeedThreadgates(limit: Int): [AppBskyFeedThreadgate!]! 431 + appBskyActorProfiles(limit: Int): [AppBskyActorProfile!]! 432 + fmTealAlphaFeedPlays(limit: Int): [FmTealAlphaFeedPlay!]! 433 + } 434 + 435 + type FmTealAlphaFeedPlayAggregated { 436 + artistMbIds: String 437 + artistNames: String 438 + artists: String 439 + duration: String 440 + isrc: String 441 + musicServiceBaseDomain: String 442 + originUrl: String 443 + playedTime: String 444 + recordingMbId: String 445 + releaseMbId: String 446 + releaseName: String 447 + submissionClientAgent: String 448 + trackMbId: String 449 + trackName: String 450 + count: Int! 451 + } 452 + 453 + type FmTealAlphaFeedPlayConnection { 454 + totalCount: Int! 455 + pageInfo: PageInfo! 456 + edges: [FmTealAlphaFeedPlayEdge!]! 457 + nodes: [FmTealAlphaFeedPlay!]! 458 + } 459 + 460 + type FmTealAlphaFeedPlayEdge { 461 + node: FmTealAlphaFeedPlay! 462 + cursor: String! 463 + } 464 + 465 + scalar JSON 466 + 467 + type Mutation { 468 + """Sync user collections for a given DID""" 469 + syncUserCollections(did: String!): SyncResult! 470 + } 471 + 472 + type PageInfo { 473 + hasNextPage: Boolean! 474 + hasPreviousPage: Boolean! 475 + startCursor: String 476 + endCursor: String 477 + } 478 + 479 + type Query { 480 + """Query app.bsky.embed.record records""" 481 + appBskyEmbedRecords(first: Int, after: String, last: Int, before: String, sortBy: [SortField], where: JSON): AppBskyEmbedRecordConnection! 482 + 483 + """ 484 + Aggregated query for app.bsky.embed.record records with GROUP BY support 485 + """ 486 + appBskyEmbedRecordsAggregated(groupBy: [String!]!, where: JSON, orderBy: AggregationOrderBy, limit: Int): [AppBskyEmbedRecordAggregated!]! 487 + 488 + """Query app.bsky.embed.images records""" 489 + appBskyEmbedImageses(first: Int, after: String, last: Int, before: String, sortBy: [SortField], where: JSON): AppBskyEmbedImagesConnection! 490 + 491 + """ 492 + Aggregated query for app.bsky.embed.images records with GROUP BY support 493 + """ 494 + appBskyEmbedImagesesAggregated(groupBy: [String!]!, where: JSON, orderBy: AggregationOrderBy, limit: Int): [AppBskyEmbedImagesAggregated!]! 495 + 496 + """Query app.bsky.embed.video records""" 497 + appBskyEmbedVideos(first: Int, after: String, last: Int, before: String, sortBy: [SortField], where: JSON): AppBskyEmbedVideoConnection! 498 + 499 + """ 500 + Aggregated query for app.bsky.embed.video records with GROUP BY support 501 + """ 502 + appBskyEmbedVideosAggregated(groupBy: [String!]!, where: JSON, orderBy: AggregationOrderBy, limit: Int): [AppBskyEmbedVideoAggregated!]! 503 + 504 + """Query app.bsky.embed.recordWithMedia records""" 505 + appBskyEmbedRecordWithMedias(first: Int, after: String, last: Int, before: String, sortBy: [SortField], where: JSON): AppBskyEmbedRecordWithMediaConnection! 506 + 507 + """ 508 + Aggregated query for app.bsky.embed.recordWithMedia records with GROUP BY support 509 + """ 510 + appBskyEmbedRecordWithMediasAggregated(groupBy: [String!]!, where: JSON, orderBy: AggregationOrderBy, limit: Int): [AppBskyEmbedRecordWithMediaAggregated!]! 511 + 512 + """Query app.bsky.embed.external records""" 513 + appBskyEmbedExternals(first: Int, after: String, last: Int, before: String, sortBy: [SortField], where: JSON): AppBskyEmbedExternalConnection! 514 + 515 + """ 516 + Aggregated query for app.bsky.embed.external records with GROUP BY support 517 + """ 518 + appBskyEmbedExternalsAggregated(groupBy: [String!]!, where: JSON, orderBy: AggregationOrderBy, limit: Int): [AppBskyEmbedExternalAggregated!]! 519 + 520 + """Query app.bsky.feed.postgate records""" 521 + appBskyFeedPostgates(first: Int, after: String, last: Int, before: String, sortBy: [SortField], where: JSON): AppBskyFeedPostgateConnection! 522 + 523 + """ 524 + Aggregated query for app.bsky.feed.postgate records with GROUP BY support 525 + """ 526 + appBskyFeedPostgatesAggregated(groupBy: [String!]!, where: JSON, orderBy: AggregationOrderBy, limit: Int): [AppBskyFeedPostgateAggregated!]! 527 + 528 + """Query app.bsky.feed.threadgate records""" 529 + appBskyFeedThreadgates(first: Int, after: String, last: Int, before: String, sortBy: [SortField], where: JSON): AppBskyFeedThreadgateConnection! 530 + 531 + """ 532 + Aggregated query for app.bsky.feed.threadgate records with GROUP BY support 533 + """ 534 + appBskyFeedThreadgatesAggregated(groupBy: [String!]!, where: JSON, orderBy: AggregationOrderBy, limit: Int): [AppBskyFeedThreadgateAggregated!]! 535 + 536 + """Query app.bsky.richtext.facet records""" 537 + appBskyRichtextFacets(first: Int, after: String, last: Int, before: String, sortBy: [SortField], where: JSON): AppBskyRichtextFacetConnection! 538 + 539 + """ 540 + Aggregated query for app.bsky.richtext.facet records with GROUP BY support 541 + """ 542 + appBskyRichtextFacetsAggregated(groupBy: [String!]!, where: JSON, orderBy: AggregationOrderBy, limit: Int): [AppBskyRichtextFacetAggregated!]! 543 + 544 + """Query app.bsky.actor.profile records""" 545 + appBskyActorProfiles(first: Int, after: String, last: Int, before: String, sortBy: [SortField], where: JSON): AppBskyActorProfileConnection! 546 + 547 + """ 548 + Aggregated query for app.bsky.actor.profile records with GROUP BY support 549 + """ 550 + appBskyActorProfilesAggregated(groupBy: [String!]!, where: JSON, orderBy: AggregationOrderBy, limit: Int): [AppBskyActorProfileAggregated!]! 551 + 552 + """Query com.atproto.repo.strongRef records""" 553 + comAtprotoRepoStrongRefs(first: Int, after: String, last: Int, before: String, sortBy: [SortField], where: JSON): ComAtprotoRepoStrongRefConnection! 554 + 555 + """ 556 + Aggregated query for com.atproto.repo.strongRef records with GROUP BY support 557 + """ 558 + comAtprotoRepoStrongRefsAggregated(groupBy: [String!]!, where: JSON, orderBy: AggregationOrderBy, limit: Int): [ComAtprotoRepoStrongRefAggregated!]! 559 + 560 + """Query fm.teal.alpha.feed.play records""" 561 + fmTealAlphaFeedPlays(first: Int, after: String, last: Int, before: String, sortBy: [SortField], where: JSON): FmTealAlphaFeedPlayConnection! 562 + 563 + """ 564 + Aggregated query for fm.teal.alpha.feed.play records with GROUP BY support 565 + """ 566 + fmTealAlphaFeedPlaysAggregated(groupBy: [String!]!, where: JSON, orderBy: AggregationOrderBy, limit: Int): [FmTealAlphaFeedPlayAggregated!]! 567 + } 568 + 569 + enum SortDirection { 570 + asc 571 + desc 572 + } 573 + 574 + input SortField { 575 + field: String! 576 + direction: SortDirection! 577 + } 578 + 579 + type SyncResult { 580 + success: Boolean! 581 + reposProcessed: Int! 582 + recordsSynced: Int! 583 + timedOut: Boolean! 584 + message: String! 585 + } 586 +
+94
src/AlbumItem.tsx
··· 1 + import { useAlbumArt } from "./useAlbumArt"; 2 + 3 + interface Artist { 4 + artistName: string; 5 + } 6 + 7 + interface AlbumItemProps { 8 + releaseName: string; 9 + releaseMbId: string | null | undefined; 10 + artists: string | null | undefined; 11 + count: number; 12 + rank: number; 13 + maxCount: number; 14 + } 15 + 16 + export default function AlbumItem({ 17 + releaseName, 18 + releaseMbId, 19 + artists, 20 + count, 21 + rank, 22 + maxCount, 23 + }: AlbumItemProps) { 24 + const { albumArtUrl, isLoading } = useAlbumArt(releaseMbId); 25 + const barWidth = maxCount > 0 ? (count / maxCount) * 100 : 0; 26 + 27 + // Parse artists JSON 28 + let artistNames = "Unknown Artist"; 29 + if (artists) { 30 + try { 31 + const parsed = typeof artists === 'string' ? JSON.parse(artists) : artists; 32 + if (Array.isArray(parsed)) { 33 + artistNames = parsed.map((a: Artist) => a.artistName).join(", "); 34 + } else if (typeof parsed === 'string') { 35 + artistNames = parsed; 36 + } 37 + } catch (e) { 38 + console.log('Failed to parse artists:', artists, e); 39 + artistNames = String(artists); 40 + } 41 + } else { 42 + console.log('No artists data for:', releaseName); 43 + } 44 + 45 + return ( 46 + <div className="group py-3 px-4 hover:bg-zinc-900/50 transition-colors relative overflow-hidden"> 47 + <div 48 + className="absolute inset-y-0 left-0 bg-violet-500/10 transition-all" 49 + style={{ width: `${barWidth}%` }} 50 + /> 51 + <div className="flex items-center gap-4 relative"> 52 + <div className="text-xs text-zinc-600 w-8 text-right flex-shrink-0 font-medium"> 53 + {rank} 54 + </div> 55 + 56 + <div className="flex-shrink-0"> 57 + {isLoading ? ( 58 + <div className="w-10 h-10 bg-zinc-800 animate-pulse" /> 59 + ) : albumArtUrl ? ( 60 + <img 61 + src={albumArtUrl} 62 + alt={`${releaseName} album art`} 63 + className="w-10 h-10 object-cover" 64 + loading="lazy" 65 + /> 66 + ) : ( 67 + <div className="w-10 h-10 bg-zinc-800 flex items-center justify-center"> 68 + <svg 69 + className="w-5 h-5 text-zinc-600" 70 + fill="currentColor" 71 + viewBox="0 0 20 20" 72 + > 73 + <path d="M18 3a1 1 0 00-1.196-.98l-10 2A1 1 0 006 5v9.114A4.369 4.369 0 005 14c-1.657 0-3 .895-3 2s1.343 2 3 2 3-.895 3-2V7.82l8-1.6v5.894A4.37 4.37 0 0015 12c-1.657 0-3 .895-3 2s1.343 2 3 2 3-.895 3-2V3z" /> 74 + </svg> 75 + </div> 76 + )} 77 + </div> 78 + 79 + <div className="flex-1 min-w-0"> 80 + <h3 className="text-sm font-medium text-zinc-100 truncate"> 81 + {releaseName} 82 + </h3> 83 + <p className="text-xs text-zinc-500 truncate">{artistNames}</p> 84 + </div> 85 + 86 + <div className="text-right flex-shrink-0"> 87 + <p className="text-xs text-zinc-400 font-medium"> 88 + {count.toLocaleString()} 89 + </p> 90 + </div> 91 + </div> 92 + </div> 93 + ); 94 + }
+183
src/App.tsx
··· 1 + import { graphql, useLazyLoadQuery, usePaginationFragment } from "react-relay"; 2 + import { useEffect, useRef, useState } from "react"; 3 + import type { AppQuery } from "./__generated__/AppQuery.graphql"; 4 + import type { App_plays$key } from "./__generated__/App_plays.graphql"; 5 + import TrackItem from "./TrackItem"; 6 + import TopAlbums from "./TopAlbums"; 7 + import TopTracks from "./TopTracks"; 8 + 9 + export default function App() { 10 + const [activeTab, setActiveTab] = useState<"recent" | "tracks" | "albums">("recent"); 11 + const queryData = useLazyLoadQuery<AppQuery>( 12 + graphql` 13 + query AppQuery { 14 + ...App_plays 15 + } 16 + `, 17 + {} 18 + ); 19 + 20 + const { data, loadNext, hasNext, isLoadingNext } = usePaginationFragment< 21 + AppQuery, 22 + App_plays$key 23 + >( 24 + graphql` 25 + fragment App_plays on Query 26 + @refetchable(queryName: "AppPaginationQuery") 27 + @argumentDefinitions( 28 + cursor: { type: "String" } 29 + count: { type: "Int", defaultValue: 20 } 30 + ) { 31 + fmTealAlphaFeedPlays( 32 + first: $count 33 + after: $cursor 34 + sortBy: [{ field: "playedTime", direction: desc }] 35 + ) @connection(key: "App_fmTealAlphaFeedPlays", filters: ["sortBy"]) { 36 + totalCount 37 + edges { 38 + node { 39 + playedTime 40 + ...TrackItem_play 41 + } 42 + } 43 + } 44 + } 45 + `, 46 + queryData 47 + ); 48 + 49 + const loadMoreRef = useRef<HTMLDivElement>(null); 50 + 51 + useEffect(() => { 52 + window.scrollTo(0, 0); 53 + }, []); 54 + 55 + const plays = 56 + data?.fmTealAlphaFeedPlays?.edges 57 + ?.map((edge) => edge.node) 58 + .filter((n) => n != null) || []; 59 + 60 + useEffect(() => { 61 + if (!loadMoreRef.current || !hasNext || activeTab !== "recent") return; 62 + 63 + const observer = new IntersectionObserver( 64 + (entries) => { 65 + if (entries[0].isIntersecting && hasNext && !isLoadingNext) { 66 + loadNext(20); 67 + } 68 + }, 69 + { threshold: 0.1 } 70 + ); 71 + 72 + observer.observe(loadMoreRef.current); 73 + 74 + return () => observer.disconnect(); 75 + }, [hasNext, isLoadingNext, loadNext, activeTab]); 76 + 77 + // Group plays by date 78 + const groupedPlays: { date: string; plays: typeof plays }[] = []; 79 + let currentDate = ""; 80 + 81 + plays.forEach((play) => { 82 + if (!play?.playedTime) return; 83 + 84 + const playDate = new Date(play.playedTime).toLocaleDateString("en-US", { 85 + weekday: "long", 86 + day: "numeric", 87 + month: "long", 88 + year: "numeric", 89 + }); 90 + 91 + if (playDate !== currentDate) { 92 + currentDate = playDate; 93 + groupedPlays.push({ date: playDate, plays: [play] }); 94 + } else { 95 + groupedPlays[groupedPlays.length - 1].plays.push(play); 96 + } 97 + }); 98 + 99 + return ( 100 + <div className="min-h-screen bg-zinc-950 text-zinc-300 font-mono"> 101 + <div className="max-w-4xl mx-auto px-6 py-12"> 102 + <div className="mb-12 flex items-end justify-between border-b border-zinc-800 pb-6"> 103 + <div> 104 + <h1 className="text-xs font-medium uppercase tracking-wider text-zinc-500">Listening History</h1> 105 + <p className="text-xs text-zinc-600 mt-1">fm.teal.alpha.feed.play</p> 106 + </div> 107 + 108 + <div className="flex gap-4 text-xs"> 109 + <button 110 + onClick={() => setActiveTab("recent")} 111 + className={`px-2 py-1 transition-colors ${ 112 + activeTab === "recent" 113 + ? "text-zinc-400" 114 + : "text-zinc-500 hover:text-zinc-300" 115 + }`} 116 + > 117 + Recent 118 + </button> 119 + <button 120 + onClick={() => setActiveTab("tracks")} 121 + className={`px-2 py-1 transition-colors ${ 122 + activeTab === "tracks" 123 + ? "text-zinc-400" 124 + : "text-zinc-500 hover:text-zinc-300" 125 + }`} 126 + > 127 + Top Tracks 128 + </button> 129 + <button 130 + onClick={() => setActiveTab("albums")} 131 + className={`px-2 py-1 transition-colors ${ 132 + activeTab === "albums" 133 + ? "text-zinc-400" 134 + : "text-zinc-500 hover:text-zinc-300" 135 + }`} 136 + > 137 + Top Albums 138 + </button> 139 + </div> 140 + </div> 141 + 142 + {activeTab === "recent" ? ( 143 + <> 144 + <div className="mb-8"> 145 + <p className="text-xs text-zinc-500 uppercase tracking-wider"> 146 + {data?.fmTealAlphaFeedPlays?.totalCount?.toLocaleString()} scrobbles 147 + </p> 148 + </div> 149 + 150 + <div> 151 + {groupedPlays.map((group, groupIndex) => ( 152 + <div key={groupIndex} className="mb-12"> 153 + <h2 className="text-xs text-zinc-600 font-medium mb-6 uppercase tracking-wider"> 154 + {group.date} 155 + </h2> 156 + <div className="space-y-1"> 157 + {group.plays.map((play, index) => ( 158 + <TrackItem key={index} play={play} /> 159 + ))} 160 + </div> 161 + </div> 162 + ))} 163 + </div> 164 + 165 + {hasNext && ( 166 + <div ref={loadMoreRef} className="py-12 text-center"> 167 + {isLoadingNext ? ( 168 + <p className="text-xs text-zinc-600 uppercase tracking-wider">Loading...</p> 169 + ) : ( 170 + <p className="text-xs text-zinc-700 uppercase tracking-wider">·</p> 171 + )} 172 + </div> 173 + )} 174 + </> 175 + ) : activeTab === "tracks" ? ( 176 + <TopTracks /> 177 + ) : ( 178 + <TopAlbums /> 179 + )} 180 + </div> 181 + </div> 182 + ); 183 + }
+3
src/LoadingFallback.tsx
··· 1 + export default function LoadingFallback() { 2 + return <div className="min-h-screen bg-zinc-950" />; 3 + }
+148
src/Profile.tsx
··· 1 + import { graphql, useLazyLoadQuery, usePaginationFragment } from "react-relay"; 2 + import { useParams, Link } from "react-router-dom"; 3 + import { useEffect, useRef } from "react"; 4 + import type { ProfileQuery as ProfileQueryType } from "./__generated__/ProfileQuery.graphql"; 5 + import type { Profile_plays$key } from "./__generated__/Profile_plays.graphql"; 6 + import TrackItem from "./TrackItem"; 7 + 8 + export default function Profile() { 9 + const { handle } = useParams<{ handle: string }>(); 10 + 11 + const queryData = useLazyLoadQuery<ProfileQueryType>( 12 + graphql` 13 + query ProfileQuery($where: JSON!) { 14 + ...Profile_plays @arguments(where: $where) 15 + } 16 + `, 17 + { 18 + where: { actorHandle: { eq: handle } }, 19 + } 20 + ); 21 + 22 + const { data, loadNext, hasNext, isLoadingNext } = usePaginationFragment< 23 + ProfileQueryType, 24 + Profile_plays$key 25 + >( 26 + graphql` 27 + fragment Profile_plays on Query 28 + @refetchable(queryName: "ProfilePaginationQuery") 29 + @argumentDefinitions( 30 + cursor: { type: "String" } 31 + count: { type: "Int", defaultValue: 20 } 32 + where: { type: "JSON!" } 33 + ) { 34 + fmTealAlphaFeedPlays( 35 + first: $count 36 + after: $cursor 37 + sortBy: [{ field: "playedTime", direction: desc }] 38 + where: $where 39 + ) 40 + @connection( 41 + key: "Profile_fmTealAlphaFeedPlays" 42 + filters: ["where", "sortBy"] 43 + ) { 44 + totalCount 45 + edges { 46 + node { 47 + ...TrackItem_play 48 + actorHandle 49 + appBskyActorProfile { 50 + displayName 51 + description 52 + avatar { 53 + url(preset: "avatar") 54 + } 55 + } 56 + } 57 + } 58 + } 59 + } 60 + `, 61 + queryData 62 + ); 63 + 64 + const loadMoreRef = useRef<HTMLDivElement>(null); 65 + 66 + const plays = data?.fmTealAlphaFeedPlays?.edges?.map((edge) => edge.node).filter((n) => n != null) || []; 67 + const profile = plays?.[0]?.appBskyActorProfile; 68 + 69 + useEffect(() => { 70 + window.scrollTo(0, 0); 71 + }, [handle]); 72 + 73 + useEffect(() => { 74 + if (!loadMoreRef.current || !hasNext) return; 75 + 76 + const observer = new IntersectionObserver( 77 + (entries) => { 78 + if (entries[0].isIntersecting && hasNext && !isLoadingNext) { 79 + loadNext(20); 80 + } 81 + }, 82 + { threshold: 0.1 } 83 + ); 84 + 85 + observer.observe(loadMoreRef.current); 86 + 87 + return () => observer.disconnect(); 88 + }, [hasNext, isLoadingNext, loadNext]); 89 + 90 + return ( 91 + <div className="min-h-screen bg-zinc-950 text-zinc-300 font-mono"> 92 + <div className="max-w-4xl mx-auto px-6 py-12"> 93 + <Link 94 + to="/" 95 + className="px-2 py-1 text-xs text-zinc-500 hover:text-zinc-300 transition-colors inline-block mb-8" 96 + > 97 + ← Back 98 + </Link> 99 + 100 + <div className="mb-12 flex items-start gap-6 border-b border-zinc-800 pb-6"> 101 + {profile?.avatar?.url && ( 102 + <img 103 + src={profile.avatar.url} 104 + alt={profile.displayName ?? handle ?? "User"} 105 + className="w-16 h-16 flex-shrink-0 object-cover" 106 + /> 107 + )} 108 + <div className="flex-1"> 109 + <h1 className="text-lg font-medium mb-1 text-zinc-100"> 110 + {profile?.displayName ?? handle} 111 + </h1> 112 + <p className="text-xs text-zinc-500 mb-2">@{handle}</p> 113 + {profile?.description && ( 114 + <p className="text-xs text-zinc-400">{profile.description}</p> 115 + )} 116 + </div> 117 + </div> 118 + 119 + <div className="mb-8"> 120 + <h2 className="text-sm font-medium uppercase tracking-wider text-zinc-400 mb-2">Recent Tracks</h2> 121 + <p className="text-xs text-zinc-500 uppercase tracking-wider"> 122 + {(data?.fmTealAlphaFeedPlays?.totalCount ?? 0).toLocaleString()} scrobbles 123 + </p> 124 + </div> 125 + 126 + <div className="space-y-1"> 127 + {plays && plays.length > 0 ? ( 128 + plays.map((play, index) => <TrackItem key={index} play={play} />) 129 + ) : ( 130 + <p className="text-zinc-600 text-center py-8 text-xs uppercase tracking-wider"> 131 + No tracks found for this user 132 + </p> 133 + )} 134 + </div> 135 + 136 + {hasNext && ( 137 + <div ref={loadMoreRef} className="py-12 text-center"> 138 + {isLoadingNext ? ( 139 + <p className="text-xs text-zinc-600 uppercase tracking-wider">Loading...</p> 140 + ) : ( 141 + <p className="text-xs text-zinc-700 uppercase tracking-wider">·</p> 142 + )} 143 + </div> 144 + )} 145 + </div> 146 + </div> 147 + ); 148 + }
+65
src/TopAlbums.tsx
··· 1 + import { graphql, useLazyLoadQuery } from "react-relay"; 2 + import type { TopAlbumsQuery } from "./__generated__/TopAlbumsQuery.graphql"; 3 + import AlbumItem from "./AlbumItem"; 4 + 5 + export default function TopAlbums() { 6 + const data = useLazyLoadQuery<TopAlbumsQuery>( 7 + graphql` 8 + query TopAlbumsQuery { 9 + fmTealAlphaFeedPlaysAggregated( 10 + groupBy: ["releaseMbId", "releaseName", "artists"] 11 + orderBy: { count: desc } 12 + limit: 100 13 + ) { 14 + releaseMbId 15 + releaseName 16 + artists 17 + count 18 + } 19 + } 20 + `, 21 + {} 22 + ); 23 + 24 + const albums = [...(data.fmTealAlphaFeedPlaysAggregated || [])]; 25 + 26 + // Deduplicate by release name, keeping the one with highest count 27 + // Prefer entries with artist data 28 + const seenNames = new Set<string>(); 29 + const dedupedAlbums = albums 30 + .sort((a, b) => { 31 + // First sort by count (already sorted from query) 32 + if (b.count !== a.count) return b.count - a.count; 33 + // Then prefer entries with artists data 34 + if (a.artists && !b.artists) return -1; 35 + if (!a.artists && b.artists) return 1; 36 + return 0; 37 + }) 38 + .filter((album) => { 39 + const name = album.releaseName || "Unknown Album"; 40 + if (seenNames.has(name)) { 41 + return false; 42 + } 43 + seenNames.add(name); 44 + return true; 45 + }) 46 + .slice(0, 50); 47 + 48 + const maxCount = dedupedAlbums.length > 0 ? dedupedAlbums[0].count : 0; 49 + 50 + return ( 51 + <div className="space-y-1"> 52 + {dedupedAlbums.map((album, index) => ( 53 + <AlbumItem 54 + key={album.releaseMbId || index} 55 + releaseName={album.releaseName || "Unknown Album"} 56 + releaseMbId={album.releaseMbId} 57 + artists={album.artists} 58 + count={album.count} 59 + rank={index + 1} 60 + maxCount={maxCount} 61 + /> 62 + ))} 63 + </div> 64 + ); 65 + }
+91
src/TopTrackItem.tsx
··· 1 + import { useAlbumArt } from "./useAlbumArt"; 2 + 3 + interface Artist { 4 + artistName: string; 5 + } 6 + 7 + interface TopTrackItemProps { 8 + trackName: string; 9 + releaseMbId: string | null | undefined; 10 + artists: string | null | undefined; 11 + count: number; 12 + rank: number; 13 + maxCount: number; 14 + } 15 + 16 + export default function TopTrackItem({ 17 + trackName, 18 + releaseMbId, 19 + artists, 20 + count, 21 + rank, 22 + maxCount, 23 + }: TopTrackItemProps) { 24 + const { albumArtUrl, isLoading } = useAlbumArt(releaseMbId); 25 + const barWidth = maxCount > 0 ? (count / maxCount) * 100 : 0; 26 + 27 + // Parse artists JSON 28 + let artistNames = "Unknown Artist"; 29 + if (artists) { 30 + try { 31 + const parsed = typeof artists === 'string' ? JSON.parse(artists) : artists; 32 + if (Array.isArray(parsed)) { 33 + artistNames = parsed.map((a: Artist) => a.artistName).join(", "); 34 + } else if (typeof parsed === 'string') { 35 + artistNames = parsed; 36 + } 37 + } catch { 38 + artistNames = String(artists); 39 + } 40 + } 41 + 42 + return ( 43 + <div className="group py-3 px-4 hover:bg-zinc-900/50 transition-colors relative overflow-hidden"> 44 + <div 45 + className="absolute inset-y-0 left-0 bg-violet-500/10 transition-all" 46 + style={{ width: `${barWidth}%` }} 47 + /> 48 + <div className="flex items-center gap-4 relative"> 49 + <div className="text-xs text-zinc-600 w-8 text-right flex-shrink-0 font-medium"> 50 + {rank} 51 + </div> 52 + 53 + <div className="flex-shrink-0"> 54 + {isLoading ? ( 55 + <div className="w-10 h-10 bg-zinc-800 animate-pulse" /> 56 + ) : albumArtUrl ? ( 57 + <img 58 + src={albumArtUrl} 59 + alt={`${trackName} album art`} 60 + className="w-10 h-10 object-cover" 61 + loading="lazy" 62 + /> 63 + ) : ( 64 + <div className="w-10 h-10 bg-zinc-800 flex items-center justify-center"> 65 + <svg 66 + className="w-5 h-5 text-zinc-600" 67 + fill="currentColor" 68 + viewBox="0 0 20 20" 69 + > 70 + <path d="M18 3a1 1 0 00-1.196-.98l-10 2A1 1 0 006 5v9.114A4.369 4.369 0 005 14c-1.657 0-3 .895-3 2s1.343 2 3 2 3-.895 3-2V7.82l8-1.6v5.894A4.37 4.37 0 0015 12c-1.657 0-3 .895-3 2s1.343 2 3 2 3-.895 3-2V3z" /> 71 + </svg> 72 + </div> 73 + )} 74 + </div> 75 + 76 + <div className="flex-1 min-w-0"> 77 + <h3 className="text-sm font-medium text-zinc-100 truncate"> 78 + {trackName} 79 + </h3> 80 + <p className="text-xs text-zinc-500 truncate">{artistNames}</p> 81 + </div> 82 + 83 + <div className="text-right flex-shrink-0"> 84 + <p className="text-xs text-zinc-400 font-medium"> 85 + {count.toLocaleString()} 86 + </p> 87 + </div> 88 + </div> 89 + </div> 90 + ); 91 + }
+42
src/TopTracks.tsx
··· 1 + import { graphql, useLazyLoadQuery } from "react-relay"; 2 + import type { TopTracksQuery } from "./__generated__/TopTracksQuery.graphql"; 3 + import TopTrackItem from "./TopTrackItem"; 4 + 5 + export default function TopTracks() { 6 + const data = useLazyLoadQuery<TopTracksQuery>( 7 + graphql` 8 + query TopTracksQuery { 9 + fmTealAlphaFeedPlaysAggregated( 10 + groupBy: ["trackName", "releaseMbId", "artists"] 11 + orderBy: { count: desc } 12 + limit: 50 13 + ) { 14 + trackName 15 + releaseMbId 16 + artists 17 + count 18 + } 19 + } 20 + `, 21 + {} 22 + ); 23 + 24 + const tracks = data.fmTealAlphaFeedPlaysAggregated || []; 25 + const maxCount = tracks.length > 0 ? tracks[0].count : 0; 26 + 27 + return ( 28 + <div className="space-y-1"> 29 + {tracks.map((track, index) => ( 30 + <TopTrackItem 31 + key={`${track.trackName}-${index}`} 32 + trackName={track.trackName || "Unknown Track"} 33 + releaseMbId={track.releaseMbId} 34 + artists={track.artists || "Unknown Artist"} 35 + count={track.count} 36 + rank={index + 1} 37 + maxCount={maxCount} 38 + /> 39 + ))} 40 + </div> 41 + ); 42 + }
+88
src/TrackItem.tsx
··· 1 + import { graphql, useFragment } from "react-relay"; 2 + import type { TrackItem_play$key } from "./__generated__/TrackItem_play.graphql"; 3 + import { useAlbumArt } from "./useAlbumArt"; 4 + 5 + interface TrackItemProps { 6 + play: TrackItem_play$key; 7 + } 8 + 9 + export default function TrackItem({ play }: TrackItemProps) { 10 + const data = useFragment( 11 + graphql` 12 + fragment TrackItem_play on FmTealAlphaFeedPlay { 13 + trackName 14 + playedTime 15 + artists 16 + releaseName 17 + releaseMbId 18 + actorHandle 19 + appBskyActorProfile { 20 + displayName 21 + } 22 + } 23 + `, 24 + play 25 + ); 26 + 27 + const { albumArtUrl, isLoading } = useAlbumArt(data.releaseMbId); 28 + 29 + return ( 30 + <div className="group py-3 px-4 hover:bg-zinc-900/50 transition-colors"> 31 + <div className="flex items-center gap-4"> 32 + <div className="flex-shrink-0"> 33 + {isLoading ? ( 34 + <div className="w-10 h-10 bg-zinc-800 animate-pulse" /> 35 + ) : albumArtUrl ? ( 36 + <img 37 + src={albumArtUrl} 38 + alt={`${data.trackName} album art`} 39 + className="w-10 h-10 object-cover" 40 + loading="lazy" 41 + /> 42 + ) : ( 43 + <div className="w-10 h-10 bg-zinc-800 flex items-center justify-center"> 44 + <svg className="w-5 h-5 text-zinc-600" fill="currentColor" viewBox="0 0 20 20"> 45 + <path d="M18 3a1 1 0 00-1.196-.98l-10 2A1 1 0 006 5v9.114A4.369 4.369 0 005 14c-1.657 0-3 .895-3 2s1.343 2 3 2 3-.895 3-2V7.82l8-1.6v5.894A4.37 4.37 0 0015 12c-1.657 0-3 .895-3 2s1.343 2 3 2 3-.895 3-2V3z" /> 46 + </svg> 47 + </div> 48 + )} 49 + </div> 50 + 51 + <div className="flex-1 min-w-0 grid grid-cols-2 gap-4"> 52 + <div className="min-w-0"> 53 + <h3 className="text-sm font-medium text-zinc-100 truncate"> 54 + {data.trackName} 55 + </h3> 56 + <p className="text-xs text-zinc-500 truncate"> 57 + {Array.isArray(data.artists) 58 + ? data.artists.map((a) => a.artistName).join(", ") 59 + : data.artists} 60 + </p> 61 + </div> 62 + 63 + <div className="text-right min-w-0"> 64 + <p className="text-xs text-zinc-400 truncate"> 65 + {data.releaseName} 66 + </p> 67 + <div className="flex items-center justify-end gap-2 mt-0.5 min-w-0 overflow-hidden"> 68 + {data.playedTime && ( 69 + <p className="text-xs text-zinc-600 flex-shrink-0"> 70 + {new Date(data.playedTime).toLocaleTimeString("en-US", { 71 + hour: "numeric", 72 + minute: "2-digit", 73 + })} 74 + </p> 75 + )} 76 + <a 77 + href={`/profile/${data.actorHandle}`} 78 + className="text-xs text-violet-500 hover:text-violet-400 transition-colors truncate block max-w-[120px]" 79 + > 80 + @{data.actorHandle} 81 + </a> 82 + </div> 83 + </div> 84 + </div> 85 + </div> 86 + </div> 87 + ); 88 + }
+258
src/__generated__/AppPaginationQuery.graphql.ts
··· 1 + /** 2 + * @generated SignedSource<<cef2df106afea24fa8527f2def8e9991>> 3 + * @lightSyntaxTransform 4 + * @nogrep 5 + */ 6 + 7 + /* tslint:disable */ 8 + /* eslint-disable */ 9 + // @ts-nocheck 10 + 11 + import { ConcreteRequest } from 'relay-runtime'; 12 + import { FragmentRefs } from "relay-runtime"; 13 + export type AppPaginationQuery$variables = { 14 + count?: number | null | undefined; 15 + cursor?: string | null | undefined; 16 + }; 17 + export type AppPaginationQuery$data = { 18 + readonly " $fragmentSpreads": FragmentRefs<"App_plays">; 19 + }; 20 + export type AppPaginationQuery = { 21 + response: AppPaginationQuery$data; 22 + variables: AppPaginationQuery$variables; 23 + }; 24 + 25 + const node: ConcreteRequest = (function(){ 26 + var v0 = [ 27 + { 28 + "defaultValue": 20, 29 + "kind": "LocalArgument", 30 + "name": "count" 31 + }, 32 + { 33 + "defaultValue": null, 34 + "kind": "LocalArgument", 35 + "name": "cursor" 36 + } 37 + ], 38 + v1 = [ 39 + { 40 + "kind": "Variable", 41 + "name": "after", 42 + "variableName": "cursor" 43 + }, 44 + { 45 + "kind": "Variable", 46 + "name": "first", 47 + "variableName": "count" 48 + }, 49 + { 50 + "kind": "Literal", 51 + "name": "sortBy", 52 + "value": [ 53 + { 54 + "direction": "desc", 55 + "field": "playedTime" 56 + } 57 + ] 58 + } 59 + ]; 60 + return { 61 + "fragment": { 62 + "argumentDefinitions": (v0/*: any*/), 63 + "kind": "Fragment", 64 + "metadata": null, 65 + "name": "AppPaginationQuery", 66 + "selections": [ 67 + { 68 + "args": [ 69 + { 70 + "kind": "Variable", 71 + "name": "count", 72 + "variableName": "count" 73 + }, 74 + { 75 + "kind": "Variable", 76 + "name": "cursor", 77 + "variableName": "cursor" 78 + } 79 + ], 80 + "kind": "FragmentSpread", 81 + "name": "App_plays" 82 + } 83 + ], 84 + "type": "Query", 85 + "abstractKey": null 86 + }, 87 + "kind": "Request", 88 + "operation": { 89 + "argumentDefinitions": (v0/*: any*/), 90 + "kind": "Operation", 91 + "name": "AppPaginationQuery", 92 + "selections": [ 93 + { 94 + "alias": null, 95 + "args": (v1/*: any*/), 96 + "concreteType": "FmTealAlphaFeedPlayConnection", 97 + "kind": "LinkedField", 98 + "name": "fmTealAlphaFeedPlays", 99 + "plural": false, 100 + "selections": [ 101 + { 102 + "alias": null, 103 + "args": null, 104 + "kind": "ScalarField", 105 + "name": "totalCount", 106 + "storageKey": null 107 + }, 108 + { 109 + "alias": null, 110 + "args": null, 111 + "concreteType": "FmTealAlphaFeedPlayEdge", 112 + "kind": "LinkedField", 113 + "name": "edges", 114 + "plural": true, 115 + "selections": [ 116 + { 117 + "alias": null, 118 + "args": null, 119 + "concreteType": "FmTealAlphaFeedPlay", 120 + "kind": "LinkedField", 121 + "name": "node", 122 + "plural": false, 123 + "selections": [ 124 + { 125 + "alias": null, 126 + "args": null, 127 + "kind": "ScalarField", 128 + "name": "playedTime", 129 + "storageKey": null 130 + }, 131 + { 132 + "alias": null, 133 + "args": null, 134 + "kind": "ScalarField", 135 + "name": "trackName", 136 + "storageKey": null 137 + }, 138 + { 139 + "alias": null, 140 + "args": null, 141 + "kind": "ScalarField", 142 + "name": "artists", 143 + "storageKey": null 144 + }, 145 + { 146 + "alias": null, 147 + "args": null, 148 + "kind": "ScalarField", 149 + "name": "releaseName", 150 + "storageKey": null 151 + }, 152 + { 153 + "alias": null, 154 + "args": null, 155 + "kind": "ScalarField", 156 + "name": "releaseMbId", 157 + "storageKey": null 158 + }, 159 + { 160 + "alias": null, 161 + "args": null, 162 + "kind": "ScalarField", 163 + "name": "actorHandle", 164 + "storageKey": null 165 + }, 166 + { 167 + "alias": null, 168 + "args": null, 169 + "concreteType": "AppBskyActorProfile", 170 + "kind": "LinkedField", 171 + "name": "appBskyActorProfile", 172 + "plural": false, 173 + "selections": [ 174 + { 175 + "alias": null, 176 + "args": null, 177 + "kind": "ScalarField", 178 + "name": "displayName", 179 + "storageKey": null 180 + } 181 + ], 182 + "storageKey": null 183 + }, 184 + { 185 + "alias": null, 186 + "args": null, 187 + "kind": "ScalarField", 188 + "name": "__typename", 189 + "storageKey": null 190 + } 191 + ], 192 + "storageKey": null 193 + }, 194 + { 195 + "alias": null, 196 + "args": null, 197 + "kind": "ScalarField", 198 + "name": "cursor", 199 + "storageKey": null 200 + } 201 + ], 202 + "storageKey": null 203 + }, 204 + { 205 + "alias": null, 206 + "args": null, 207 + "concreteType": "PageInfo", 208 + "kind": "LinkedField", 209 + "name": "pageInfo", 210 + "plural": false, 211 + "selections": [ 212 + { 213 + "alias": null, 214 + "args": null, 215 + "kind": "ScalarField", 216 + "name": "endCursor", 217 + "storageKey": null 218 + }, 219 + { 220 + "alias": null, 221 + "args": null, 222 + "kind": "ScalarField", 223 + "name": "hasNextPage", 224 + "storageKey": null 225 + } 226 + ], 227 + "storageKey": null 228 + } 229 + ], 230 + "storageKey": null 231 + }, 232 + { 233 + "alias": null, 234 + "args": (v1/*: any*/), 235 + "filters": [ 236 + "sortBy" 237 + ], 238 + "handle": "connection", 239 + "key": "App_fmTealAlphaFeedPlays", 240 + "kind": "LinkedHandle", 241 + "name": "fmTealAlphaFeedPlays" 242 + } 243 + ] 244 + }, 245 + "params": { 246 + "cacheID": "e115a73de49cf6f84a35f172a7910c5c", 247 + "id": null, 248 + "metadata": {}, 249 + "name": "AppPaginationQuery", 250 + "operationKind": "query", 251 + "text": "query AppPaginationQuery(\n $count: Int = 20\n $cursor: String\n) {\n ...App_plays_1G22uz\n}\n\nfragment App_plays_1G22uz on Query {\n fmTealAlphaFeedPlays(first: $count, after: $cursor, sortBy: [{field: \"playedTime\", direction: desc}]) {\n totalCount\n edges {\n node {\n playedTime\n ...TrackItem_play\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n}\n\nfragment TrackItem_play on FmTealAlphaFeedPlay {\n trackName\n playedTime\n artists\n releaseName\n releaseMbId\n actorHandle\n appBskyActorProfile {\n displayName\n }\n}\n" 252 + } 253 + }; 254 + })(); 255 + 256 + (node as any).hash = "0e4acf96fedae07af90ce6e9e3bf18d6"; 257 + 258 + export default node;
+227
src/__generated__/AppQuery.graphql.ts
··· 1 + /** 2 + * @generated SignedSource<<541e6114682aef7988bd233592085337>> 3 + * @lightSyntaxTransform 4 + * @nogrep 5 + */ 6 + 7 + /* tslint:disable */ 8 + /* eslint-disable */ 9 + // @ts-nocheck 10 + 11 + import { ConcreteRequest } from 'relay-runtime'; 12 + import { FragmentRefs } from "relay-runtime"; 13 + export type AppQuery$variables = Record<PropertyKey, never>; 14 + export type AppQuery$data = { 15 + readonly " $fragmentSpreads": FragmentRefs<"App_plays">; 16 + }; 17 + export type AppQuery = { 18 + response: AppQuery$data; 19 + variables: AppQuery$variables; 20 + }; 21 + 22 + const node: ConcreteRequest = (function(){ 23 + var v0 = [ 24 + { 25 + "kind": "Literal", 26 + "name": "first", 27 + "value": 20 28 + }, 29 + { 30 + "kind": "Literal", 31 + "name": "sortBy", 32 + "value": [ 33 + { 34 + "direction": "desc", 35 + "field": "playedTime" 36 + } 37 + ] 38 + } 39 + ]; 40 + return { 41 + "fragment": { 42 + "argumentDefinitions": [], 43 + "kind": "Fragment", 44 + "metadata": null, 45 + "name": "AppQuery", 46 + "selections": [ 47 + { 48 + "args": null, 49 + "kind": "FragmentSpread", 50 + "name": "App_plays" 51 + } 52 + ], 53 + "type": "Query", 54 + "abstractKey": null 55 + }, 56 + "kind": "Request", 57 + "operation": { 58 + "argumentDefinitions": [], 59 + "kind": "Operation", 60 + "name": "AppQuery", 61 + "selections": [ 62 + { 63 + "alias": null, 64 + "args": (v0/*: any*/), 65 + "concreteType": "FmTealAlphaFeedPlayConnection", 66 + "kind": "LinkedField", 67 + "name": "fmTealAlphaFeedPlays", 68 + "plural": false, 69 + "selections": [ 70 + { 71 + "alias": null, 72 + "args": null, 73 + "kind": "ScalarField", 74 + "name": "totalCount", 75 + "storageKey": null 76 + }, 77 + { 78 + "alias": null, 79 + "args": null, 80 + "concreteType": "FmTealAlphaFeedPlayEdge", 81 + "kind": "LinkedField", 82 + "name": "edges", 83 + "plural": true, 84 + "selections": [ 85 + { 86 + "alias": null, 87 + "args": null, 88 + "concreteType": "FmTealAlphaFeedPlay", 89 + "kind": "LinkedField", 90 + "name": "node", 91 + "plural": false, 92 + "selections": [ 93 + { 94 + "alias": null, 95 + "args": null, 96 + "kind": "ScalarField", 97 + "name": "playedTime", 98 + "storageKey": null 99 + }, 100 + { 101 + "alias": null, 102 + "args": null, 103 + "kind": "ScalarField", 104 + "name": "trackName", 105 + "storageKey": null 106 + }, 107 + { 108 + "alias": null, 109 + "args": null, 110 + "kind": "ScalarField", 111 + "name": "artists", 112 + "storageKey": null 113 + }, 114 + { 115 + "alias": null, 116 + "args": null, 117 + "kind": "ScalarField", 118 + "name": "releaseName", 119 + "storageKey": null 120 + }, 121 + { 122 + "alias": null, 123 + "args": null, 124 + "kind": "ScalarField", 125 + "name": "releaseMbId", 126 + "storageKey": null 127 + }, 128 + { 129 + "alias": null, 130 + "args": null, 131 + "kind": "ScalarField", 132 + "name": "actorHandle", 133 + "storageKey": null 134 + }, 135 + { 136 + "alias": null, 137 + "args": null, 138 + "concreteType": "AppBskyActorProfile", 139 + "kind": "LinkedField", 140 + "name": "appBskyActorProfile", 141 + "plural": false, 142 + "selections": [ 143 + { 144 + "alias": null, 145 + "args": null, 146 + "kind": "ScalarField", 147 + "name": "displayName", 148 + "storageKey": null 149 + } 150 + ], 151 + "storageKey": null 152 + }, 153 + { 154 + "alias": null, 155 + "args": null, 156 + "kind": "ScalarField", 157 + "name": "__typename", 158 + "storageKey": null 159 + } 160 + ], 161 + "storageKey": null 162 + }, 163 + { 164 + "alias": null, 165 + "args": null, 166 + "kind": "ScalarField", 167 + "name": "cursor", 168 + "storageKey": null 169 + } 170 + ], 171 + "storageKey": null 172 + }, 173 + { 174 + "alias": null, 175 + "args": null, 176 + "concreteType": "PageInfo", 177 + "kind": "LinkedField", 178 + "name": "pageInfo", 179 + "plural": false, 180 + "selections": [ 181 + { 182 + "alias": null, 183 + "args": null, 184 + "kind": "ScalarField", 185 + "name": "endCursor", 186 + "storageKey": null 187 + }, 188 + { 189 + "alias": null, 190 + "args": null, 191 + "kind": "ScalarField", 192 + "name": "hasNextPage", 193 + "storageKey": null 194 + } 195 + ], 196 + "storageKey": null 197 + } 198 + ], 199 + "storageKey": "fmTealAlphaFeedPlays(first:20,sortBy:[{\"direction\":\"desc\",\"field\":\"playedTime\"}])" 200 + }, 201 + { 202 + "alias": null, 203 + "args": (v0/*: any*/), 204 + "filters": [ 205 + "sortBy" 206 + ], 207 + "handle": "connection", 208 + "key": "App_fmTealAlphaFeedPlays", 209 + "kind": "LinkedHandle", 210 + "name": "fmTealAlphaFeedPlays" 211 + } 212 + ] 213 + }, 214 + "params": { 215 + "cacheID": "1cacfb0aa5545cf84688b8396079b855", 216 + "id": null, 217 + "metadata": {}, 218 + "name": "AppQuery", 219 + "operationKind": "query", 220 + "text": "query AppQuery {\n ...App_plays\n}\n\nfragment App_plays on Query {\n fmTealAlphaFeedPlays(first: 20, sortBy: [{field: \"playedTime\", direction: desc}]) {\n totalCount\n edges {\n node {\n playedTime\n ...TrackItem_play\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n}\n\nfragment TrackItem_play on FmTealAlphaFeedPlay {\n trackName\n playedTime\n artists\n releaseName\n releaseMbId\n actorHandle\n appBskyActorProfile {\n displayName\n }\n}\n" 221 + } 222 + }; 223 + })(); 224 + 225 + (node as any).hash = "4b1837f6cd874e31461fbead77c1b012"; 226 + 227 + export default node;
+184
src/__generated__/App_plays.graphql.ts
··· 1 + /** 2 + * @generated SignedSource<<a3ae5f31f618986fb12e6c57458c9853>> 3 + * @lightSyntaxTransform 4 + * @nogrep 5 + */ 6 + 7 + /* tslint:disable */ 8 + /* eslint-disable */ 9 + // @ts-nocheck 10 + 11 + import { ReaderFragment } from 'relay-runtime'; 12 + import { FragmentRefs } from "relay-runtime"; 13 + export type App_plays$data = { 14 + readonly fmTealAlphaFeedPlays: { 15 + readonly edges: ReadonlyArray<{ 16 + readonly node: { 17 + readonly playedTime: string | null | undefined; 18 + readonly " $fragmentSpreads": FragmentRefs<"TrackItem_play">; 19 + }; 20 + }>; 21 + readonly totalCount: number; 22 + }; 23 + readonly " $fragmentType": "App_plays"; 24 + }; 25 + export type App_plays$key = { 26 + readonly " $data"?: App_plays$data; 27 + readonly " $fragmentSpreads": FragmentRefs<"App_plays">; 28 + }; 29 + 30 + import AppPaginationQuery_graphql from './AppPaginationQuery.graphql'; 31 + 32 + const node: ReaderFragment = (function(){ 33 + var v0 = [ 34 + "fmTealAlphaFeedPlays" 35 + ]; 36 + return { 37 + "argumentDefinitions": [ 38 + { 39 + "defaultValue": 20, 40 + "kind": "LocalArgument", 41 + "name": "count" 42 + }, 43 + { 44 + "defaultValue": null, 45 + "kind": "LocalArgument", 46 + "name": "cursor" 47 + } 48 + ], 49 + "kind": "Fragment", 50 + "metadata": { 51 + "connection": [ 52 + { 53 + "count": "count", 54 + "cursor": "cursor", 55 + "direction": "forward", 56 + "path": (v0/*: any*/) 57 + } 58 + ], 59 + "refetch": { 60 + "connection": { 61 + "forward": { 62 + "count": "count", 63 + "cursor": "cursor" 64 + }, 65 + "backward": null, 66 + "path": (v0/*: any*/) 67 + }, 68 + "fragmentPathInResult": [], 69 + "operation": AppPaginationQuery_graphql 70 + } 71 + }, 72 + "name": "App_plays", 73 + "selections": [ 74 + { 75 + "alias": "fmTealAlphaFeedPlays", 76 + "args": [ 77 + { 78 + "kind": "Literal", 79 + "name": "sortBy", 80 + "value": [ 81 + { 82 + "direction": "desc", 83 + "field": "playedTime" 84 + } 85 + ] 86 + } 87 + ], 88 + "concreteType": "FmTealAlphaFeedPlayConnection", 89 + "kind": "LinkedField", 90 + "name": "__App_fmTealAlphaFeedPlays_connection", 91 + "plural": false, 92 + "selections": [ 93 + { 94 + "alias": null, 95 + "args": null, 96 + "kind": "ScalarField", 97 + "name": "totalCount", 98 + "storageKey": null 99 + }, 100 + { 101 + "alias": null, 102 + "args": null, 103 + "concreteType": "FmTealAlphaFeedPlayEdge", 104 + "kind": "LinkedField", 105 + "name": "edges", 106 + "plural": true, 107 + "selections": [ 108 + { 109 + "alias": null, 110 + "args": null, 111 + "concreteType": "FmTealAlphaFeedPlay", 112 + "kind": "LinkedField", 113 + "name": "node", 114 + "plural": false, 115 + "selections": [ 116 + { 117 + "alias": null, 118 + "args": null, 119 + "kind": "ScalarField", 120 + "name": "playedTime", 121 + "storageKey": null 122 + }, 123 + { 124 + "args": null, 125 + "kind": "FragmentSpread", 126 + "name": "TrackItem_play" 127 + }, 128 + { 129 + "alias": null, 130 + "args": null, 131 + "kind": "ScalarField", 132 + "name": "__typename", 133 + "storageKey": null 134 + } 135 + ], 136 + "storageKey": null 137 + }, 138 + { 139 + "alias": null, 140 + "args": null, 141 + "kind": "ScalarField", 142 + "name": "cursor", 143 + "storageKey": null 144 + } 145 + ], 146 + "storageKey": null 147 + }, 148 + { 149 + "alias": null, 150 + "args": null, 151 + "concreteType": "PageInfo", 152 + "kind": "LinkedField", 153 + "name": "pageInfo", 154 + "plural": false, 155 + "selections": [ 156 + { 157 + "alias": null, 158 + "args": null, 159 + "kind": "ScalarField", 160 + "name": "endCursor", 161 + "storageKey": null 162 + }, 163 + { 164 + "alias": null, 165 + "args": null, 166 + "kind": "ScalarField", 167 + "name": "hasNextPage", 168 + "storageKey": null 169 + } 170 + ], 171 + "storageKey": null 172 + } 173 + ], 174 + "storageKey": "__App_fmTealAlphaFeedPlays_connection(sortBy:[{\"direction\":\"desc\",\"field\":\"playedTime\"}])" 175 + } 176 + ], 177 + "type": "Query", 178 + "abstractKey": null 179 + }; 180 + })(); 181 + 182 + (node as any).hash = "0e4acf96fedae07af90ce6e9e3bf18d6"; 183 + 184 + export default node;
+303
src/__generated__/ProfilePaginationQuery.graphql.ts
··· 1 + /** 2 + * @generated SignedSource<<97625934b32c4079cc58877234aeac04>> 3 + * @lightSyntaxTransform 4 + * @nogrep 5 + */ 6 + 7 + /* tslint:disable */ 8 + /* eslint-disable */ 9 + // @ts-nocheck 10 + 11 + import { ConcreteRequest } from 'relay-runtime'; 12 + import { FragmentRefs } from "relay-runtime"; 13 + export type ProfilePaginationQuery$variables = { 14 + count?: number | null | undefined; 15 + cursor?: string | null | undefined; 16 + where: any; 17 + }; 18 + export type ProfilePaginationQuery$data = { 19 + readonly " $fragmentSpreads": FragmentRefs<"Profile_plays">; 20 + }; 21 + export type ProfilePaginationQuery = { 22 + response: ProfilePaginationQuery$data; 23 + variables: ProfilePaginationQuery$variables; 24 + }; 25 + 26 + const node: ConcreteRequest = (function(){ 27 + var v0 = [ 28 + { 29 + "defaultValue": 20, 30 + "kind": "LocalArgument", 31 + "name": "count" 32 + }, 33 + { 34 + "defaultValue": null, 35 + "kind": "LocalArgument", 36 + "name": "cursor" 37 + }, 38 + { 39 + "defaultValue": null, 40 + "kind": "LocalArgument", 41 + "name": "where" 42 + } 43 + ], 44 + v1 = { 45 + "kind": "Variable", 46 + "name": "where", 47 + "variableName": "where" 48 + }, 49 + v2 = [ 50 + { 51 + "kind": "Variable", 52 + "name": "after", 53 + "variableName": "cursor" 54 + }, 55 + { 56 + "kind": "Variable", 57 + "name": "first", 58 + "variableName": "count" 59 + }, 60 + { 61 + "kind": "Literal", 62 + "name": "sortBy", 63 + "value": [ 64 + { 65 + "direction": "desc", 66 + "field": "playedTime" 67 + } 68 + ] 69 + }, 70 + (v1/*: any*/) 71 + ]; 72 + return { 73 + "fragment": { 74 + "argumentDefinitions": (v0/*: any*/), 75 + "kind": "Fragment", 76 + "metadata": null, 77 + "name": "ProfilePaginationQuery", 78 + "selections": [ 79 + { 80 + "args": [ 81 + { 82 + "kind": "Variable", 83 + "name": "count", 84 + "variableName": "count" 85 + }, 86 + { 87 + "kind": "Variable", 88 + "name": "cursor", 89 + "variableName": "cursor" 90 + }, 91 + (v1/*: any*/) 92 + ], 93 + "kind": "FragmentSpread", 94 + "name": "Profile_plays" 95 + } 96 + ], 97 + "type": "Query", 98 + "abstractKey": null 99 + }, 100 + "kind": "Request", 101 + "operation": { 102 + "argumentDefinitions": (v0/*: any*/), 103 + "kind": "Operation", 104 + "name": "ProfilePaginationQuery", 105 + "selections": [ 106 + { 107 + "alias": null, 108 + "args": (v2/*: any*/), 109 + "concreteType": "FmTealAlphaFeedPlayConnection", 110 + "kind": "LinkedField", 111 + "name": "fmTealAlphaFeedPlays", 112 + "plural": false, 113 + "selections": [ 114 + { 115 + "alias": null, 116 + "args": null, 117 + "kind": "ScalarField", 118 + "name": "totalCount", 119 + "storageKey": null 120 + }, 121 + { 122 + "alias": null, 123 + "args": null, 124 + "concreteType": "FmTealAlphaFeedPlayEdge", 125 + "kind": "LinkedField", 126 + "name": "edges", 127 + "plural": true, 128 + "selections": [ 129 + { 130 + "alias": null, 131 + "args": null, 132 + "concreteType": "FmTealAlphaFeedPlay", 133 + "kind": "LinkedField", 134 + "name": "node", 135 + "plural": false, 136 + "selections": [ 137 + { 138 + "alias": null, 139 + "args": null, 140 + "kind": "ScalarField", 141 + "name": "trackName", 142 + "storageKey": null 143 + }, 144 + { 145 + "alias": null, 146 + "args": null, 147 + "kind": "ScalarField", 148 + "name": "playedTime", 149 + "storageKey": null 150 + }, 151 + { 152 + "alias": null, 153 + "args": null, 154 + "kind": "ScalarField", 155 + "name": "artists", 156 + "storageKey": null 157 + }, 158 + { 159 + "alias": null, 160 + "args": null, 161 + "kind": "ScalarField", 162 + "name": "releaseName", 163 + "storageKey": null 164 + }, 165 + { 166 + "alias": null, 167 + "args": null, 168 + "kind": "ScalarField", 169 + "name": "releaseMbId", 170 + "storageKey": null 171 + }, 172 + { 173 + "alias": null, 174 + "args": null, 175 + "kind": "ScalarField", 176 + "name": "actorHandle", 177 + "storageKey": null 178 + }, 179 + { 180 + "alias": null, 181 + "args": null, 182 + "concreteType": "AppBskyActorProfile", 183 + "kind": "LinkedField", 184 + "name": "appBskyActorProfile", 185 + "plural": false, 186 + "selections": [ 187 + { 188 + "alias": null, 189 + "args": null, 190 + "kind": "ScalarField", 191 + "name": "displayName", 192 + "storageKey": null 193 + }, 194 + { 195 + "alias": null, 196 + "args": null, 197 + "kind": "ScalarField", 198 + "name": "description", 199 + "storageKey": null 200 + }, 201 + { 202 + "alias": null, 203 + "args": null, 204 + "concreteType": "Blob", 205 + "kind": "LinkedField", 206 + "name": "avatar", 207 + "plural": false, 208 + "selections": [ 209 + { 210 + "alias": null, 211 + "args": [ 212 + { 213 + "kind": "Literal", 214 + "name": "preset", 215 + "value": "avatar" 216 + } 217 + ], 218 + "kind": "ScalarField", 219 + "name": "url", 220 + "storageKey": "url(preset:\"avatar\")" 221 + } 222 + ], 223 + "storageKey": null 224 + } 225 + ], 226 + "storageKey": null 227 + }, 228 + { 229 + "alias": null, 230 + "args": null, 231 + "kind": "ScalarField", 232 + "name": "__typename", 233 + "storageKey": null 234 + } 235 + ], 236 + "storageKey": null 237 + }, 238 + { 239 + "alias": null, 240 + "args": null, 241 + "kind": "ScalarField", 242 + "name": "cursor", 243 + "storageKey": null 244 + } 245 + ], 246 + "storageKey": null 247 + }, 248 + { 249 + "alias": null, 250 + "args": null, 251 + "concreteType": "PageInfo", 252 + "kind": "LinkedField", 253 + "name": "pageInfo", 254 + "plural": false, 255 + "selections": [ 256 + { 257 + "alias": null, 258 + "args": null, 259 + "kind": "ScalarField", 260 + "name": "endCursor", 261 + "storageKey": null 262 + }, 263 + { 264 + "alias": null, 265 + "args": null, 266 + "kind": "ScalarField", 267 + "name": "hasNextPage", 268 + "storageKey": null 269 + } 270 + ], 271 + "storageKey": null 272 + } 273 + ], 274 + "storageKey": null 275 + }, 276 + { 277 + "alias": null, 278 + "args": (v2/*: any*/), 279 + "filters": [ 280 + "where", 281 + "sortBy" 282 + ], 283 + "handle": "connection", 284 + "key": "Profile_fmTealAlphaFeedPlays", 285 + "kind": "LinkedHandle", 286 + "name": "fmTealAlphaFeedPlays" 287 + } 288 + ] 289 + }, 290 + "params": { 291 + "cacheID": "08e603fb4052c3556739bda428413453", 292 + "id": null, 293 + "metadata": {}, 294 + "name": "ProfilePaginationQuery", 295 + "operationKind": "query", 296 + "text": "query ProfilePaginationQuery(\n $count: Int = 20\n $cursor: String\n $where: JSON!\n) {\n ...Profile_plays_mjR8k\n}\n\nfragment Profile_plays_mjR8k on Query {\n fmTealAlphaFeedPlays(first: $count, after: $cursor, sortBy: [{field: \"playedTime\", direction: desc}], where: $where) {\n totalCount\n edges {\n node {\n ...TrackItem_play\n actorHandle\n appBskyActorProfile {\n displayName\n description\n avatar {\n url(preset: \"avatar\")\n }\n }\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n}\n\nfragment TrackItem_play on FmTealAlphaFeedPlay {\n trackName\n playedTime\n artists\n releaseName\n releaseMbId\n actorHandle\n appBskyActorProfile {\n displayName\n }\n}\n" 297 + } 298 + }; 299 + })(); 300 + 301 + (node as any).hash = "474168bb0d13417c1b7067c09a82f7a2"; 302 + 303 + export default node;
+276
src/__generated__/ProfileQuery.graphql.ts
··· 1 + /** 2 + * @generated SignedSource<<c47dafb8d21963c2a9dcbcd54d7bd8d8>> 3 + * @lightSyntaxTransform 4 + * @nogrep 5 + */ 6 + 7 + /* tslint:disable */ 8 + /* eslint-disable */ 9 + // @ts-nocheck 10 + 11 + import { ConcreteRequest } from 'relay-runtime'; 12 + import { FragmentRefs } from "relay-runtime"; 13 + export type ProfileQuery$variables = { 14 + where: any; 15 + }; 16 + export type ProfileQuery$data = { 17 + readonly " $fragmentSpreads": FragmentRefs<"Profile_plays">; 18 + }; 19 + export type ProfileQuery = { 20 + response: ProfileQuery$data; 21 + variables: ProfileQuery$variables; 22 + }; 23 + 24 + const node: ConcreteRequest = (function(){ 25 + var v0 = [ 26 + { 27 + "defaultValue": null, 28 + "kind": "LocalArgument", 29 + "name": "where" 30 + } 31 + ], 32 + v1 = { 33 + "kind": "Variable", 34 + "name": "where", 35 + "variableName": "where" 36 + }, 37 + v2 = [ 38 + { 39 + "kind": "Literal", 40 + "name": "first", 41 + "value": 20 42 + }, 43 + { 44 + "kind": "Literal", 45 + "name": "sortBy", 46 + "value": [ 47 + { 48 + "direction": "desc", 49 + "field": "playedTime" 50 + } 51 + ] 52 + }, 53 + (v1/*: any*/) 54 + ]; 55 + return { 56 + "fragment": { 57 + "argumentDefinitions": (v0/*: any*/), 58 + "kind": "Fragment", 59 + "metadata": null, 60 + "name": "ProfileQuery", 61 + "selections": [ 62 + { 63 + "args": [ 64 + (v1/*: any*/) 65 + ], 66 + "kind": "FragmentSpread", 67 + "name": "Profile_plays" 68 + } 69 + ], 70 + "type": "Query", 71 + "abstractKey": null 72 + }, 73 + "kind": "Request", 74 + "operation": { 75 + "argumentDefinitions": (v0/*: any*/), 76 + "kind": "Operation", 77 + "name": "ProfileQuery", 78 + "selections": [ 79 + { 80 + "alias": null, 81 + "args": (v2/*: any*/), 82 + "concreteType": "FmTealAlphaFeedPlayConnection", 83 + "kind": "LinkedField", 84 + "name": "fmTealAlphaFeedPlays", 85 + "plural": false, 86 + "selections": [ 87 + { 88 + "alias": null, 89 + "args": null, 90 + "kind": "ScalarField", 91 + "name": "totalCount", 92 + "storageKey": null 93 + }, 94 + { 95 + "alias": null, 96 + "args": null, 97 + "concreteType": "FmTealAlphaFeedPlayEdge", 98 + "kind": "LinkedField", 99 + "name": "edges", 100 + "plural": true, 101 + "selections": [ 102 + { 103 + "alias": null, 104 + "args": null, 105 + "concreteType": "FmTealAlphaFeedPlay", 106 + "kind": "LinkedField", 107 + "name": "node", 108 + "plural": false, 109 + "selections": [ 110 + { 111 + "alias": null, 112 + "args": null, 113 + "kind": "ScalarField", 114 + "name": "trackName", 115 + "storageKey": null 116 + }, 117 + { 118 + "alias": null, 119 + "args": null, 120 + "kind": "ScalarField", 121 + "name": "playedTime", 122 + "storageKey": null 123 + }, 124 + { 125 + "alias": null, 126 + "args": null, 127 + "kind": "ScalarField", 128 + "name": "artists", 129 + "storageKey": null 130 + }, 131 + { 132 + "alias": null, 133 + "args": null, 134 + "kind": "ScalarField", 135 + "name": "releaseName", 136 + "storageKey": null 137 + }, 138 + { 139 + "alias": null, 140 + "args": null, 141 + "kind": "ScalarField", 142 + "name": "releaseMbId", 143 + "storageKey": null 144 + }, 145 + { 146 + "alias": null, 147 + "args": null, 148 + "kind": "ScalarField", 149 + "name": "actorHandle", 150 + "storageKey": null 151 + }, 152 + { 153 + "alias": null, 154 + "args": null, 155 + "concreteType": "AppBskyActorProfile", 156 + "kind": "LinkedField", 157 + "name": "appBskyActorProfile", 158 + "plural": false, 159 + "selections": [ 160 + { 161 + "alias": null, 162 + "args": null, 163 + "kind": "ScalarField", 164 + "name": "displayName", 165 + "storageKey": null 166 + }, 167 + { 168 + "alias": null, 169 + "args": null, 170 + "kind": "ScalarField", 171 + "name": "description", 172 + "storageKey": null 173 + }, 174 + { 175 + "alias": null, 176 + "args": null, 177 + "concreteType": "Blob", 178 + "kind": "LinkedField", 179 + "name": "avatar", 180 + "plural": false, 181 + "selections": [ 182 + { 183 + "alias": null, 184 + "args": [ 185 + { 186 + "kind": "Literal", 187 + "name": "preset", 188 + "value": "avatar" 189 + } 190 + ], 191 + "kind": "ScalarField", 192 + "name": "url", 193 + "storageKey": "url(preset:\"avatar\")" 194 + } 195 + ], 196 + "storageKey": null 197 + } 198 + ], 199 + "storageKey": null 200 + }, 201 + { 202 + "alias": null, 203 + "args": null, 204 + "kind": "ScalarField", 205 + "name": "__typename", 206 + "storageKey": null 207 + } 208 + ], 209 + "storageKey": null 210 + }, 211 + { 212 + "alias": null, 213 + "args": null, 214 + "kind": "ScalarField", 215 + "name": "cursor", 216 + "storageKey": null 217 + } 218 + ], 219 + "storageKey": null 220 + }, 221 + { 222 + "alias": null, 223 + "args": null, 224 + "concreteType": "PageInfo", 225 + "kind": "LinkedField", 226 + "name": "pageInfo", 227 + "plural": false, 228 + "selections": [ 229 + { 230 + "alias": null, 231 + "args": null, 232 + "kind": "ScalarField", 233 + "name": "endCursor", 234 + "storageKey": null 235 + }, 236 + { 237 + "alias": null, 238 + "args": null, 239 + "kind": "ScalarField", 240 + "name": "hasNextPage", 241 + "storageKey": null 242 + } 243 + ], 244 + "storageKey": null 245 + } 246 + ], 247 + "storageKey": null 248 + }, 249 + { 250 + "alias": null, 251 + "args": (v2/*: any*/), 252 + "filters": [ 253 + "where", 254 + "sortBy" 255 + ], 256 + "handle": "connection", 257 + "key": "Profile_fmTealAlphaFeedPlays", 258 + "kind": "LinkedHandle", 259 + "name": "fmTealAlphaFeedPlays" 260 + } 261 + ] 262 + }, 263 + "params": { 264 + "cacheID": "3137e7b6ec5148299e7a7c7f4edf07b2", 265 + "id": null, 266 + "metadata": {}, 267 + "name": "ProfileQuery", 268 + "operationKind": "query", 269 + "text": "query ProfileQuery(\n $where: JSON!\n) {\n ...Profile_plays_3FC4Qo\n}\n\nfragment Profile_plays_3FC4Qo on Query {\n fmTealAlphaFeedPlays(first: 20, sortBy: [{field: \"playedTime\", direction: desc}], where: $where) {\n totalCount\n edges {\n node {\n ...TrackItem_play\n actorHandle\n appBskyActorProfile {\n displayName\n description\n avatar {\n url(preset: \"avatar\")\n }\n }\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n}\n\nfragment TrackItem_play on FmTealAlphaFeedPlay {\n trackName\n playedTime\n artists\n releaseName\n releaseMbId\n actorHandle\n appBskyActorProfile {\n displayName\n }\n}\n" 270 + } 271 + }; 272 + })(); 273 + 274 + (node as any).hash = "267039e382b3b95a739ff3cdced3211e"; 275 + 276 + export default node;
+250
src/__generated__/Profile_plays.graphql.ts
··· 1 + /** 2 + * @generated SignedSource<<bd63c9e74b05810076b60ad0e51cb230>> 3 + * @lightSyntaxTransform 4 + * @nogrep 5 + */ 6 + 7 + /* tslint:disable */ 8 + /* eslint-disable */ 9 + // @ts-nocheck 10 + 11 + import { ReaderFragment } from 'relay-runtime'; 12 + import { FragmentRefs } from "relay-runtime"; 13 + export type Profile_plays$data = { 14 + readonly fmTealAlphaFeedPlays: { 15 + readonly edges: ReadonlyArray<{ 16 + readonly node: { 17 + readonly actorHandle: string | null | undefined; 18 + readonly appBskyActorProfile: { 19 + readonly avatar: { 20 + readonly url: string; 21 + } | null | undefined; 22 + readonly description: string | null | undefined; 23 + readonly displayName: string | null | undefined; 24 + } | null | undefined; 25 + readonly " $fragmentSpreads": FragmentRefs<"TrackItem_play">; 26 + }; 27 + }>; 28 + readonly totalCount: number; 29 + }; 30 + readonly " $fragmentType": "Profile_plays"; 31 + }; 32 + export type Profile_plays$key = { 33 + readonly " $data"?: Profile_plays$data; 34 + readonly " $fragmentSpreads": FragmentRefs<"Profile_plays">; 35 + }; 36 + 37 + import ProfilePaginationQuery_graphql from './ProfilePaginationQuery.graphql'; 38 + 39 + const node: ReaderFragment = (function(){ 40 + var v0 = [ 41 + "fmTealAlphaFeedPlays" 42 + ]; 43 + return { 44 + "argumentDefinitions": [ 45 + { 46 + "defaultValue": 20, 47 + "kind": "LocalArgument", 48 + "name": "count" 49 + }, 50 + { 51 + "defaultValue": null, 52 + "kind": "LocalArgument", 53 + "name": "cursor" 54 + }, 55 + { 56 + "defaultValue": null, 57 + "kind": "LocalArgument", 58 + "name": "where" 59 + } 60 + ], 61 + "kind": "Fragment", 62 + "metadata": { 63 + "connection": [ 64 + { 65 + "count": "count", 66 + "cursor": "cursor", 67 + "direction": "forward", 68 + "path": (v0/*: any*/) 69 + } 70 + ], 71 + "refetch": { 72 + "connection": { 73 + "forward": { 74 + "count": "count", 75 + "cursor": "cursor" 76 + }, 77 + "backward": null, 78 + "path": (v0/*: any*/) 79 + }, 80 + "fragmentPathInResult": [], 81 + "operation": ProfilePaginationQuery_graphql 82 + } 83 + }, 84 + "name": "Profile_plays", 85 + "selections": [ 86 + { 87 + "alias": "fmTealAlphaFeedPlays", 88 + "args": [ 89 + { 90 + "kind": "Literal", 91 + "name": "sortBy", 92 + "value": [ 93 + { 94 + "direction": "desc", 95 + "field": "playedTime" 96 + } 97 + ] 98 + }, 99 + { 100 + "kind": "Variable", 101 + "name": "where", 102 + "variableName": "where" 103 + } 104 + ], 105 + "concreteType": "FmTealAlphaFeedPlayConnection", 106 + "kind": "LinkedField", 107 + "name": "__Profile_fmTealAlphaFeedPlays_connection", 108 + "plural": false, 109 + "selections": [ 110 + { 111 + "alias": null, 112 + "args": null, 113 + "kind": "ScalarField", 114 + "name": "totalCount", 115 + "storageKey": null 116 + }, 117 + { 118 + "alias": null, 119 + "args": null, 120 + "concreteType": "FmTealAlphaFeedPlayEdge", 121 + "kind": "LinkedField", 122 + "name": "edges", 123 + "plural": true, 124 + "selections": [ 125 + { 126 + "alias": null, 127 + "args": null, 128 + "concreteType": "FmTealAlphaFeedPlay", 129 + "kind": "LinkedField", 130 + "name": "node", 131 + "plural": false, 132 + "selections": [ 133 + { 134 + "args": null, 135 + "kind": "FragmentSpread", 136 + "name": "TrackItem_play" 137 + }, 138 + { 139 + "alias": null, 140 + "args": null, 141 + "kind": "ScalarField", 142 + "name": "actorHandle", 143 + "storageKey": null 144 + }, 145 + { 146 + "alias": null, 147 + "args": null, 148 + "concreteType": "AppBskyActorProfile", 149 + "kind": "LinkedField", 150 + "name": "appBskyActorProfile", 151 + "plural": false, 152 + "selections": [ 153 + { 154 + "alias": null, 155 + "args": null, 156 + "kind": "ScalarField", 157 + "name": "displayName", 158 + "storageKey": null 159 + }, 160 + { 161 + "alias": null, 162 + "args": null, 163 + "kind": "ScalarField", 164 + "name": "description", 165 + "storageKey": null 166 + }, 167 + { 168 + "alias": null, 169 + "args": null, 170 + "concreteType": "Blob", 171 + "kind": "LinkedField", 172 + "name": "avatar", 173 + "plural": false, 174 + "selections": [ 175 + { 176 + "alias": null, 177 + "args": [ 178 + { 179 + "kind": "Literal", 180 + "name": "preset", 181 + "value": "avatar" 182 + } 183 + ], 184 + "kind": "ScalarField", 185 + "name": "url", 186 + "storageKey": "url(preset:\"avatar\")" 187 + } 188 + ], 189 + "storageKey": null 190 + } 191 + ], 192 + "storageKey": null 193 + }, 194 + { 195 + "alias": null, 196 + "args": null, 197 + "kind": "ScalarField", 198 + "name": "__typename", 199 + "storageKey": null 200 + } 201 + ], 202 + "storageKey": null 203 + }, 204 + { 205 + "alias": null, 206 + "args": null, 207 + "kind": "ScalarField", 208 + "name": "cursor", 209 + "storageKey": null 210 + } 211 + ], 212 + "storageKey": null 213 + }, 214 + { 215 + "alias": null, 216 + "args": null, 217 + "concreteType": "PageInfo", 218 + "kind": "LinkedField", 219 + "name": "pageInfo", 220 + "plural": false, 221 + "selections": [ 222 + { 223 + "alias": null, 224 + "args": null, 225 + "kind": "ScalarField", 226 + "name": "endCursor", 227 + "storageKey": null 228 + }, 229 + { 230 + "alias": null, 231 + "args": null, 232 + "kind": "ScalarField", 233 + "name": "hasNextPage", 234 + "storageKey": null 235 + } 236 + ], 237 + "storageKey": null 238 + } 239 + ], 240 + "storageKey": null 241 + } 242 + ], 243 + "type": "Query", 244 + "abstractKey": null 245 + }; 246 + })(); 247 + 248 + (node as any).hash = "474168bb0d13417c1b7067c09a82f7a2"; 249 + 250 + export default node;
+120
src/__generated__/TopAlbumsQuery.graphql.ts
··· 1 + /** 2 + * @generated SignedSource<<a492b6190b60e9be64d199702b76977a>> 3 + * @lightSyntaxTransform 4 + * @nogrep 5 + */ 6 + 7 + /* tslint:disable */ 8 + /* eslint-disable */ 9 + // @ts-nocheck 10 + 11 + import { ConcreteRequest } from 'relay-runtime'; 12 + export type TopAlbumsQuery$variables = Record<PropertyKey, never>; 13 + export type TopAlbumsQuery$data = { 14 + readonly fmTealAlphaFeedPlaysAggregated: ReadonlyArray<{ 15 + readonly artists: string | null | undefined; 16 + readonly count: number; 17 + readonly releaseMbId: string | null | undefined; 18 + readonly releaseName: string | null | undefined; 19 + }>; 20 + }; 21 + export type TopAlbumsQuery = { 22 + response: TopAlbumsQuery$data; 23 + variables: TopAlbumsQuery$variables; 24 + }; 25 + 26 + const node: ConcreteRequest = (function(){ 27 + var v0 = [ 28 + { 29 + "alias": null, 30 + "args": [ 31 + { 32 + "kind": "Literal", 33 + "name": "groupBy", 34 + "value": [ 35 + "releaseMbId", 36 + "releaseName", 37 + "artists" 38 + ] 39 + }, 40 + { 41 + "kind": "Literal", 42 + "name": "limit", 43 + "value": 100 44 + }, 45 + { 46 + "kind": "Literal", 47 + "name": "orderBy", 48 + "value": { 49 + "count": "desc" 50 + } 51 + } 52 + ], 53 + "concreteType": "FmTealAlphaFeedPlayAggregated", 54 + "kind": "LinkedField", 55 + "name": "fmTealAlphaFeedPlaysAggregated", 56 + "plural": true, 57 + "selections": [ 58 + { 59 + "alias": null, 60 + "args": null, 61 + "kind": "ScalarField", 62 + "name": "releaseMbId", 63 + "storageKey": null 64 + }, 65 + { 66 + "alias": null, 67 + "args": null, 68 + "kind": "ScalarField", 69 + "name": "releaseName", 70 + "storageKey": null 71 + }, 72 + { 73 + "alias": null, 74 + "args": null, 75 + "kind": "ScalarField", 76 + "name": "artists", 77 + "storageKey": null 78 + }, 79 + { 80 + "alias": null, 81 + "args": null, 82 + "kind": "ScalarField", 83 + "name": "count", 84 + "storageKey": null 85 + } 86 + ], 87 + "storageKey": "fmTealAlphaFeedPlaysAggregated(groupBy:[\"releaseMbId\",\"releaseName\",\"artists\"],limit:100,orderBy:{\"count\":\"desc\"})" 88 + } 89 + ]; 90 + return { 91 + "fragment": { 92 + "argumentDefinitions": [], 93 + "kind": "Fragment", 94 + "metadata": null, 95 + "name": "TopAlbumsQuery", 96 + "selections": (v0/*: any*/), 97 + "type": "Query", 98 + "abstractKey": null 99 + }, 100 + "kind": "Request", 101 + "operation": { 102 + "argumentDefinitions": [], 103 + "kind": "Operation", 104 + "name": "TopAlbumsQuery", 105 + "selections": (v0/*: any*/) 106 + }, 107 + "params": { 108 + "cacheID": "65b42ff33b8a5de6eb4785e764ae70fc", 109 + "id": null, 110 + "metadata": {}, 111 + "name": "TopAlbumsQuery", 112 + "operationKind": "query", 113 + "text": "query TopAlbumsQuery {\n fmTealAlphaFeedPlaysAggregated(groupBy: [\"releaseMbId\", \"releaseName\", \"artists\"], orderBy: {count: desc}, limit: 100) {\n releaseMbId\n releaseName\n artists\n count\n }\n}\n" 114 + } 115 + }; 116 + })(); 117 + 118 + (node as any).hash = "b5748a3a4af3140d3cff228e7462f73d"; 119 + 120 + export default node;
+120
src/__generated__/TopTracksQuery.graphql.ts
··· 1 + /** 2 + * @generated SignedSource<<ed55197dadbf24b9cf975295d63a2436>> 3 + * @lightSyntaxTransform 4 + * @nogrep 5 + */ 6 + 7 + /* tslint:disable */ 8 + /* eslint-disable */ 9 + // @ts-nocheck 10 + 11 + import { ConcreteRequest } from 'relay-runtime'; 12 + export type TopTracksQuery$variables = Record<PropertyKey, never>; 13 + export type TopTracksQuery$data = { 14 + readonly fmTealAlphaFeedPlaysAggregated: ReadonlyArray<{ 15 + readonly artists: string | null | undefined; 16 + readonly count: number; 17 + readonly releaseMbId: string | null | undefined; 18 + readonly trackName: string | null | undefined; 19 + }>; 20 + }; 21 + export type TopTracksQuery = { 22 + response: TopTracksQuery$data; 23 + variables: TopTracksQuery$variables; 24 + }; 25 + 26 + const node: ConcreteRequest = (function(){ 27 + var v0 = [ 28 + { 29 + "alias": null, 30 + "args": [ 31 + { 32 + "kind": "Literal", 33 + "name": "groupBy", 34 + "value": [ 35 + "trackName", 36 + "releaseMbId", 37 + "artists" 38 + ] 39 + }, 40 + { 41 + "kind": "Literal", 42 + "name": "limit", 43 + "value": 50 44 + }, 45 + { 46 + "kind": "Literal", 47 + "name": "orderBy", 48 + "value": { 49 + "count": "desc" 50 + } 51 + } 52 + ], 53 + "concreteType": "FmTealAlphaFeedPlayAggregated", 54 + "kind": "LinkedField", 55 + "name": "fmTealAlphaFeedPlaysAggregated", 56 + "plural": true, 57 + "selections": [ 58 + { 59 + "alias": null, 60 + "args": null, 61 + "kind": "ScalarField", 62 + "name": "trackName", 63 + "storageKey": null 64 + }, 65 + { 66 + "alias": null, 67 + "args": null, 68 + "kind": "ScalarField", 69 + "name": "releaseMbId", 70 + "storageKey": null 71 + }, 72 + { 73 + "alias": null, 74 + "args": null, 75 + "kind": "ScalarField", 76 + "name": "artists", 77 + "storageKey": null 78 + }, 79 + { 80 + "alias": null, 81 + "args": null, 82 + "kind": "ScalarField", 83 + "name": "count", 84 + "storageKey": null 85 + } 86 + ], 87 + "storageKey": "fmTealAlphaFeedPlaysAggregated(groupBy:[\"trackName\",\"releaseMbId\",\"artists\"],limit:50,orderBy:{\"count\":\"desc\"})" 88 + } 89 + ]; 90 + return { 91 + "fragment": { 92 + "argumentDefinitions": [], 93 + "kind": "Fragment", 94 + "metadata": null, 95 + "name": "TopTracksQuery", 96 + "selections": (v0/*: any*/), 97 + "type": "Query", 98 + "abstractKey": null 99 + }, 100 + "kind": "Request", 101 + "operation": { 102 + "argumentDefinitions": [], 103 + "kind": "Operation", 104 + "name": "TopTracksQuery", 105 + "selections": (v0/*: any*/) 106 + }, 107 + "params": { 108 + "cacheID": "61e9f7886dfe9eaeb599b939f2d636e5", 109 + "id": null, 110 + "metadata": {}, 111 + "name": "TopTracksQuery", 112 + "operationKind": "query", 113 + "text": "query TopTracksQuery {\n fmTealAlphaFeedPlaysAggregated(groupBy: [\"trackName\", \"releaseMbId\", \"artists\"], orderBy: {count: desc}, limit: 50) {\n trackName\n releaseMbId\n artists\n count\n }\n}\n" 114 + } 115 + }; 116 + })(); 117 + 118 + (node as any).hash = "536f8ddb64daa09017abff121d7ea8ce"; 119 + 120 + export default node;
+103
src/__generated__/TrackItem_play.graphql.ts
··· 1 + /** 2 + * @generated SignedSource<<1403d6e3403844ad955c2fe48c8a66c9>> 3 + * @lightSyntaxTransform 4 + * @nogrep 5 + */ 6 + 7 + /* tslint:disable */ 8 + /* eslint-disable */ 9 + // @ts-nocheck 10 + 11 + import { ReaderFragment } from 'relay-runtime'; 12 + import { FragmentRefs } from "relay-runtime"; 13 + export type TrackItem_play$data = { 14 + readonly actorHandle: string | null | undefined; 15 + readonly appBskyActorProfile: { 16 + readonly displayName: string | null | undefined; 17 + } | null | undefined; 18 + readonly artists: any | null | undefined; 19 + readonly playedTime: string | null | undefined; 20 + readonly releaseMbId: string | null | undefined; 21 + readonly releaseName: string | null | undefined; 22 + readonly trackName: string; 23 + readonly " $fragmentType": "TrackItem_play"; 24 + }; 25 + export type TrackItem_play$key = { 26 + readonly " $data"?: TrackItem_play$data; 27 + readonly " $fragmentSpreads": FragmentRefs<"TrackItem_play">; 28 + }; 29 + 30 + const node: ReaderFragment = { 31 + "argumentDefinitions": [], 32 + "kind": "Fragment", 33 + "metadata": null, 34 + "name": "TrackItem_play", 35 + "selections": [ 36 + { 37 + "alias": null, 38 + "args": null, 39 + "kind": "ScalarField", 40 + "name": "trackName", 41 + "storageKey": null 42 + }, 43 + { 44 + "alias": null, 45 + "args": null, 46 + "kind": "ScalarField", 47 + "name": "playedTime", 48 + "storageKey": null 49 + }, 50 + { 51 + "alias": null, 52 + "args": null, 53 + "kind": "ScalarField", 54 + "name": "artists", 55 + "storageKey": null 56 + }, 57 + { 58 + "alias": null, 59 + "args": null, 60 + "kind": "ScalarField", 61 + "name": "releaseName", 62 + "storageKey": null 63 + }, 64 + { 65 + "alias": null, 66 + "args": null, 67 + "kind": "ScalarField", 68 + "name": "releaseMbId", 69 + "storageKey": null 70 + }, 71 + { 72 + "alias": null, 73 + "args": null, 74 + "kind": "ScalarField", 75 + "name": "actorHandle", 76 + "storageKey": null 77 + }, 78 + { 79 + "alias": null, 80 + "args": null, 81 + "concreteType": "AppBskyActorProfile", 82 + "kind": "LinkedField", 83 + "name": "appBskyActorProfile", 84 + "plural": false, 85 + "selections": [ 86 + { 87 + "alias": null, 88 + "args": null, 89 + "kind": "ScalarField", 90 + "name": "displayName", 91 + "storageKey": null 92 + } 93 + ], 94 + "storageKey": null 95 + } 96 + ], 97 + "type": "FmTealAlphaFeedPlay", 98 + "abstractKey": null 99 + }; 100 + 101 + (node as any).hash = "08e8e2c14a894471e9a3153f8918e02e"; 102 + 103 + export default node;
+1
src/albumArtCache.ts
··· 1 + export const albumArtCache = new Map<string, string | null>();
+1
src/index.css
··· 1 + @import "tailwindcss";
+43
src/main.tsx
··· 1 + import { StrictMode, Suspense } from "react"; 2 + import { createRoot } from "react-dom/client"; 3 + import { BrowserRouter, Routes, Route } from "react-router-dom"; 4 + import "./index.css"; 5 + import App from "./App.tsx"; 6 + import Profile from "./Profile.tsx"; 7 + import LoadingFallback from "./LoadingFallback.tsx"; 8 + import { RelayEnvironmentProvider } from "react-relay"; 9 + import { Environment, Network, type FetchFunction } from "relay-runtime"; 10 + 11 + const HTTP_ENDPOINT = 12 + "https://api.slices.network/graphql?slice=at://did:plc:fpruhuo22xkm5o7ttr2ktxdo/network.slices.slice/3m257yljpbg2a"; 13 + 14 + const fetchGraphQL: FetchFunction = async (request, variables) => { 15 + const resp = await fetch(HTTP_ENDPOINT, { 16 + method: "POST", 17 + headers: { "Content-Type": "application/json" }, 18 + body: JSON.stringify({ query: request.text, variables }), 19 + }); 20 + if (!resp.ok) { 21 + throw new Error("Response failed."); 22 + } 23 + return await resp.json(); 24 + }; 25 + 26 + const environment = new Environment({ 27 + network: Network.create(fetchGraphQL), 28 + }); 29 + 30 + createRoot(document.getElementById("root")!).render( 31 + <StrictMode> 32 + <BrowserRouter> 33 + <RelayEnvironmentProvider environment={environment}> 34 + <Suspense fallback={<LoadingFallback />}> 35 + <Routes> 36 + <Route path="/" element={<App />} /> 37 + <Route path="/profile/:handle" element={<Profile />} /> 38 + </Routes> 39 + </Suspense> 40 + </RelayEnvironmentProvider> 41 + </BrowserRouter> 42 + </StrictMode> 43 + );
+48
src/useAlbumArt.ts
··· 1 + import { useState, useEffect } from "react"; 2 + import { albumArtCache } from "./albumArtCache"; 3 + 4 + export function useAlbumArt(releaseMbId: string | null | undefined) { 5 + const [albumArtUrl, setAlbumArtUrl] = useState<string | null>(null); 6 + const [isLoading, setIsLoading] = useState(false); 7 + 8 + useEffect(() => { 9 + if (!releaseMbId) return; 10 + 11 + // Check cache first 12 + if (albumArtCache.has(releaseMbId)) { 13 + setAlbumArtUrl(albumArtCache.get(releaseMbId) || null); 14 + setIsLoading(false); 15 + return; 16 + } 17 + 18 + setIsLoading(true); 19 + 20 + const fetchAlbumArt = async () => { 21 + try { 22 + // Fetch cover art from Cover Art Archive 23 + const coverArtUrl = `https://coverartarchive.org/release/${releaseMbId}/front-250`; 24 + 25 + // Check if cover art exists 26 + const coverArtResponse = await fetch(coverArtUrl, { method: "HEAD" }); 27 + 28 + if (coverArtResponse.ok) { 29 + setAlbumArtUrl(coverArtUrl); 30 + albumArtCache.set(releaseMbId, coverArtUrl); 31 + } else { 32 + setAlbumArtUrl(null); 33 + albumArtCache.set(releaseMbId, null); 34 + } 35 + } catch (error) { 36 + console.error("Error fetching album art:", error); 37 + setAlbumArtUrl(null); 38 + albumArtCache.set(releaseMbId, null); 39 + } finally { 40 + setIsLoading(false); 41 + } 42 + }; 43 + 44 + fetchAlbumArt(); 45 + }, [releaseMbId]); 46 + 47 + return { albumArtUrl, isLoading }; 48 + }
+28
tsconfig.app.json
··· 1 + { 2 + "compilerOptions": { 3 + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", 4 + "target": "ES2022", 5 + "useDefineForClassFields": true, 6 + "lib": ["ES2022", "DOM", "DOM.Iterable"], 7 + "module": "ESNext", 8 + "types": ["vite/client"], 9 + "skipLibCheck": true, 10 + 11 + /* Bundler mode */ 12 + "moduleResolution": "bundler", 13 + "allowImportingTsExtensions": true, 14 + "verbatimModuleSyntax": true, 15 + "moduleDetection": "force", 16 + "noEmit": true, 17 + "jsx": "react-jsx", 18 + 19 + /* Linting */ 20 + "strict": true, 21 + "noUnusedLocals": true, 22 + "noUnusedParameters": true, 23 + "erasableSyntaxOnly": true, 24 + "noFallthroughCasesInSwitch": true, 25 + "noUncheckedSideEffectImports": true 26 + }, 27 + "include": ["src"] 28 + }
+7
tsconfig.json
··· 1 + { 2 + "files": [], 3 + "references": [ 4 + { "path": "./tsconfig.app.json" }, 5 + { "path": "./tsconfig.node.json" } 6 + ] 7 + }
+26
tsconfig.node.json
··· 1 + { 2 + "compilerOptions": { 3 + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", 4 + "target": "ES2023", 5 + "lib": ["ES2023"], 6 + "module": "ESNext", 7 + "types": ["node"], 8 + "skipLibCheck": true, 9 + 10 + /* Bundler mode */ 11 + "moduleResolution": "bundler", 12 + "allowImportingTsExtensions": true, 13 + "verbatimModuleSyntax": true, 14 + "moduleDetection": "force", 15 + "noEmit": true, 16 + 17 + /* Linting */ 18 + "strict": true, 19 + "noUnusedLocals": true, 20 + "noUnusedParameters": true, 21 + "erasableSyntaxOnly": true, 22 + "noFallthroughCasesInSwitch": true, 23 + "noUncheckedSideEffectImports": true 24 + }, 25 + "include": ["vite.config.ts"] 26 + }
+7
vite.config.ts
··· 1 + import { defineConfig } from "vite"; 2 + import react from "@vitejs/plugin-react"; 3 + 4 + // https://vite.dev/config/ 5 + export default defineConfig({ 6 + plugins: [react({ babel: { plugins: ["relay"] } })], 7 + });