[READ-ONLY] Mirror of https://github.com/flo-bit/atmo-tools. atmo.tools
0

Configure Feed

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

try turso

+1296 -577
+1 -2
package.json
··· 51 51 "dependencies": { 52 52 "@foxui/core": "^0.5.2", 53 53 "@foxui/social": "^0.5.4", 54 - "dexie": "^4.3.0", 55 - "flexsearch": "^0.8.212", 54 + "@tursodatabase/database-wasm": "^0.5.0", 56 55 "valibot": "^1.2.0", 57 56 "wrangler": "^4.69.0" 58 57 },
+248 -174
pnpm-lock.yaml
··· 14 14 '@foxui/social': 15 15 specifier: ^0.5.4 16 16 version: 0.5.4(@internationalized/date@3.11.0)(@sveltejs/kit@2.53.4(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.53.6)(vite@7.3.1(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)))(svelte@5.53.6)(typescript@5.9.3)(vite@7.3.1(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)))(svelte@5.53.6)(tailwindcss@4.2.1) 17 - dexie: 18 - specifier: ^4.3.0 19 - version: 4.3.0 20 - flexsearch: 21 - specifier: ^0.8.212 22 - version: 0.8.212 17 + '@tursodatabase/database-wasm': 18 + specifier: ^0.5.0 19 + version: 0.5.0 23 20 valibot: 24 21 specifier: ^1.2.0 25 22 version: 1.2.0(typescript@5.9.3) ··· 121 118 resolution: {integrity: sha512-+GKZpOc0PJcdWMQEkTfg/rSNDAAHxmAUGBl60g2az15etqJn5WaUPNGFE2sB7hKpwi5Ue2h/L0OacINcE/JDDQ==} 122 119 123 120 '@atcute/bluesky-richtext-segmenter@3.0.0': 124 - resolution: {integrity: sha512-NhZTUKtFpeBBbILwAcxj5u4RobIoHOmGw3CAaaEFNebKYSvmTecrXJ7XufHw5DFOUdr8SiKXQVRQxGAxulMNWg==, tarball: https://registry.npmjs.org/@atcute/bluesky-richtext-segmenter/-/bluesky-richtext-segmenter-3.0.0.tgz} 121 + resolution: {integrity: sha512-NhZTUKtFpeBBbILwAcxj5u4RobIoHOmGw3CAaaEFNebKYSvmTecrXJ7XufHw5DFOUdr8SiKXQVRQxGAxulMNWg==} 125 122 126 123 '@atcute/bluesky@3.2.19': 127 124 resolution: {integrity: sha512-W/Sy2671A5eKkdWJn6nfCGoixqMFOouEKRbqRV3630I13Er9iS+gA/rGWUXlp5xNpbfCNmsQ6C1pEidUmO+00g==} ··· 175 172 engines: {node: '>= 18'} 176 173 177 174 '@cloudflare/kv-asset-handler@0.4.2': 178 - resolution: {integrity: sha512-SIOD2DxrRRwQ+jgzlXCqoEFiKOFqaPjhnNTGKXSRLvp1HiOvapLaFG2kEr9dYQTYe8rKrd9uvDUzmAITeNyaHQ==, tarball: https://registry.npmjs.org/@cloudflare/kv-asset-handler/-/kv-asset-handler-0.4.2.tgz} 175 + resolution: {integrity: sha512-SIOD2DxrRRwQ+jgzlXCqoEFiKOFqaPjhnNTGKXSRLvp1HiOvapLaFG2kEr9dYQTYe8rKrd9uvDUzmAITeNyaHQ==} 179 176 engines: {node: '>=18.0.0'} 180 177 181 178 '@cloudflare/unenv-preset@2.14.0': 182 - resolution: {integrity: sha512-XKAkWhi1nBdNsSEoNG9nkcbyvfUrSjSf+VYVPfOto3gLTZVc3F4g6RASCMh6IixBKCG2yDgZKQIHGKtjcnLnKg==, tarball: https://registry.npmjs.org/@cloudflare/unenv-preset/-/unenv-preset-2.14.0.tgz} 179 + resolution: {integrity: sha512-XKAkWhi1nBdNsSEoNG9nkcbyvfUrSjSf+VYVPfOto3gLTZVc3F4g6RASCMh6IixBKCG2yDgZKQIHGKtjcnLnKg==} 183 180 peerDependencies: 184 181 unenv: 2.0.0-rc.24 185 182 workerd: ^1.20260218.0 ··· 188 185 optional: true 189 186 190 187 '@cloudflare/workerd-darwin-64@1.20260305.0': 191 - resolution: {integrity: sha512-chhKOpymo0Eh9J3nymrauMqKGboCc4uz/j0gA1G4gioMnKsN2ZDKJ+qjRZDnCoVGy8u2C4pxlmyIfsXCAfIzhQ==, tarball: https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20260305.0.tgz} 188 + resolution: {integrity: sha512-chhKOpymo0Eh9J3nymrauMqKGboCc4uz/j0gA1G4gioMnKsN2ZDKJ+qjRZDnCoVGy8u2C4pxlmyIfsXCAfIzhQ==} 192 189 engines: {node: '>=16'} 193 190 cpu: [x64] 194 191 os: [darwin] 195 192 196 193 '@cloudflare/workerd-darwin-arm64@1.20260305.0': 197 - resolution: {integrity: sha512-K9aG2OQk5bBfOP+fyGPqLcqZ9OR3ra6uwnxJ8f2mveq2A2LsCI7ZeGxQiAj75Ti80ytH/gJffZIx4Np2JtU3aQ==, tarball: https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20260305.0.tgz} 194 + resolution: {integrity: sha512-K9aG2OQk5bBfOP+fyGPqLcqZ9OR3ra6uwnxJ8f2mveq2A2LsCI7ZeGxQiAj75Ti80ytH/gJffZIx4Np2JtU3aQ==} 198 195 engines: {node: '>=16'} 199 196 cpu: [arm64] 200 197 os: [darwin] 201 198 202 199 '@cloudflare/workerd-linux-64@1.20260305.0': 203 - resolution: {integrity: sha512-tt7XUoIw/cYFeGbkPkcZ6XX1aZm26Aju/4ih+DXxOosbBeGshFSrNJDBfAKKOvkjsAZymJ+WWVDBU+hmNaGfwA==, tarball: https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20260305.0.tgz} 200 + resolution: {integrity: sha512-tt7XUoIw/cYFeGbkPkcZ6XX1aZm26Aju/4ih+DXxOosbBeGshFSrNJDBfAKKOvkjsAZymJ+WWVDBU+hmNaGfwA==} 204 201 engines: {node: '>=16'} 205 202 cpu: [x64] 206 203 os: [linux] 207 204 208 205 '@cloudflare/workerd-linux-arm64@1.20260305.0': 209 - resolution: {integrity: sha512-72QTkY5EzylmvCZ8ZTrnJ9DctmQsfSof1OKyOWqu/pv/B2yACfuPMikq8RpPxvVu7hhS0ztGP6ZvXz72Htq4Zg==, tarball: https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20260305.0.tgz} 206 + resolution: {integrity: sha512-72QTkY5EzylmvCZ8ZTrnJ9DctmQsfSof1OKyOWqu/pv/B2yACfuPMikq8RpPxvVu7hhS0ztGP6ZvXz72Htq4Zg==} 210 207 engines: {node: '>=16'} 211 208 cpu: [arm64] 212 209 os: [linux] 213 210 214 211 '@cloudflare/workerd-windows-64@1.20260305.0': 215 - resolution: {integrity: sha512-BA0uaQPOaI2F6mJtBDqplGnQQhpXCzwEMI33p/TnDxtSk9u8CGIfBFuI6uqo8mJ6ijIaPjeBLGOn2CiRMET4qg==, tarball: https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20260305.0.tgz} 212 + resolution: {integrity: sha512-BA0uaQPOaI2F6mJtBDqplGnQQhpXCzwEMI33p/TnDxtSk9u8CGIfBFuI6uqo8mJ6ijIaPjeBLGOn2CiRMET4qg==} 216 213 engines: {node: '>=16'} 217 214 cpu: [x64] 218 215 os: [win32] ··· 221 218 resolution: {integrity: sha512-klKnECMb5A4GtVF0P5NH6rCjtyjqIEKJaz6kEtx9YPHhfFO2HUEarO+MI4F8WPchgeZqpGlEpDhRapzrOTw51Q==} 222 219 223 220 '@cspotcode/source-map-support@0.8.1': 224 - resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==, tarball: https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz} 221 + resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} 225 222 engines: {node: '>=12'} 226 223 224 + '@emnapi/core@1.8.1': 225 + resolution: {integrity: sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==} 226 + 227 227 '@emnapi/runtime@1.8.1': 228 - resolution: {integrity: sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==, tarball: https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.8.1.tgz} 228 + resolution: {integrity: sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==} 229 + 230 + '@emnapi/wasi-threads@1.1.0': 231 + resolution: {integrity: sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==} 229 232 230 233 '@esbuild/aix-ppc64@0.27.3': 231 - resolution: {integrity: sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==, tarball: https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz} 234 + resolution: {integrity: sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==} 232 235 engines: {node: '>=18'} 233 236 cpu: [ppc64] 234 237 os: [aix] 235 238 236 239 '@esbuild/android-arm64@0.27.3': 237 - resolution: {integrity: sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==, tarball: https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz} 240 + resolution: {integrity: sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==} 238 241 engines: {node: '>=18'} 239 242 cpu: [arm64] 240 243 os: [android] 241 244 242 245 '@esbuild/android-arm@0.27.3': 243 - resolution: {integrity: sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==, tarball: https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz} 246 + resolution: {integrity: sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==} 244 247 engines: {node: '>=18'} 245 248 cpu: [arm] 246 249 os: [android] 247 250 248 251 '@esbuild/android-x64@0.27.3': 249 - resolution: {integrity: sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==, tarball: https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz} 252 + resolution: {integrity: sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==} 250 253 engines: {node: '>=18'} 251 254 cpu: [x64] 252 255 os: [android] 253 256 254 257 '@esbuild/darwin-arm64@0.27.3': 255 - resolution: {integrity: sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==, tarball: https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz} 258 + resolution: {integrity: sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==} 256 259 engines: {node: '>=18'} 257 260 cpu: [arm64] 258 261 os: [darwin] 259 262 260 263 '@esbuild/darwin-x64@0.27.3': 261 - resolution: {integrity: sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==, tarball: https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz} 264 + resolution: {integrity: sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==} 262 265 engines: {node: '>=18'} 263 266 cpu: [x64] 264 267 os: [darwin] 265 268 266 269 '@esbuild/freebsd-arm64@0.27.3': 267 - resolution: {integrity: sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==, tarball: https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz} 270 + resolution: {integrity: sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==} 268 271 engines: {node: '>=18'} 269 272 cpu: [arm64] 270 273 os: [freebsd] 271 274 272 275 '@esbuild/freebsd-x64@0.27.3': 273 - resolution: {integrity: sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==, tarball: https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz} 276 + resolution: {integrity: sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==} 274 277 engines: {node: '>=18'} 275 278 cpu: [x64] 276 279 os: [freebsd] 277 280 278 281 '@esbuild/linux-arm64@0.27.3': 279 - resolution: {integrity: sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==, tarball: https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz} 282 + resolution: {integrity: sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==} 280 283 engines: {node: '>=18'} 281 284 cpu: [arm64] 282 285 os: [linux] 283 286 284 287 '@esbuild/linux-arm@0.27.3': 285 - resolution: {integrity: sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==, tarball: https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz} 288 + resolution: {integrity: sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==} 286 289 engines: {node: '>=18'} 287 290 cpu: [arm] 288 291 os: [linux] 289 292 290 293 '@esbuild/linux-ia32@0.27.3': 291 - resolution: {integrity: sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==, tarball: https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz} 294 + resolution: {integrity: sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==} 292 295 engines: {node: '>=18'} 293 296 cpu: [ia32] 294 297 os: [linux] 295 298 296 299 '@esbuild/linux-loong64@0.27.3': 297 - resolution: {integrity: sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==, tarball: https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz} 300 + resolution: {integrity: sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==} 298 301 engines: {node: '>=18'} 299 302 cpu: [loong64] 300 303 os: [linux] 301 304 302 305 '@esbuild/linux-mips64el@0.27.3': 303 - resolution: {integrity: sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==, tarball: https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz} 306 + resolution: {integrity: sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==} 304 307 engines: {node: '>=18'} 305 308 cpu: [mips64el] 306 309 os: [linux] 307 310 308 311 '@esbuild/linux-ppc64@0.27.3': 309 - resolution: {integrity: sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==, tarball: https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz} 312 + resolution: {integrity: sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==} 310 313 engines: {node: '>=18'} 311 314 cpu: [ppc64] 312 315 os: [linux] 313 316 314 317 '@esbuild/linux-riscv64@0.27.3': 315 - resolution: {integrity: sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==, tarball: https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz} 318 + resolution: {integrity: sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==} 316 319 engines: {node: '>=18'} 317 320 cpu: [riscv64] 318 321 os: [linux] 319 322 320 323 '@esbuild/linux-s390x@0.27.3': 321 - resolution: {integrity: sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==, tarball: https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz} 324 + resolution: {integrity: sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==} 322 325 engines: {node: '>=18'} 323 326 cpu: [s390x] 324 327 os: [linux] 325 328 326 329 '@esbuild/linux-x64@0.27.3': 327 - resolution: {integrity: sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==, tarball: https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz} 330 + resolution: {integrity: sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==} 328 331 engines: {node: '>=18'} 329 332 cpu: [x64] 330 333 os: [linux] 331 334 332 335 '@esbuild/netbsd-arm64@0.27.3': 333 - resolution: {integrity: sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==, tarball: https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz} 336 + resolution: {integrity: sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==} 334 337 engines: {node: '>=18'} 335 338 cpu: [arm64] 336 339 os: [netbsd] 337 340 338 341 '@esbuild/netbsd-x64@0.27.3': 339 - resolution: {integrity: sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==, tarball: https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz} 342 + resolution: {integrity: sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==} 340 343 engines: {node: '>=18'} 341 344 cpu: [x64] 342 345 os: [netbsd] 343 346 344 347 '@esbuild/openbsd-arm64@0.27.3': 345 - resolution: {integrity: sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==, tarball: https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz} 348 + resolution: {integrity: sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==} 346 349 engines: {node: '>=18'} 347 350 cpu: [arm64] 348 351 os: [openbsd] 349 352 350 353 '@esbuild/openbsd-x64@0.27.3': 351 - resolution: {integrity: sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==, tarball: https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz} 354 + resolution: {integrity: sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==} 352 355 engines: {node: '>=18'} 353 356 cpu: [x64] 354 357 os: [openbsd] 355 358 356 359 '@esbuild/openharmony-arm64@0.27.3': 357 - resolution: {integrity: sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==, tarball: https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz} 360 + resolution: {integrity: sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==} 358 361 engines: {node: '>=18'} 359 362 cpu: [arm64] 360 363 os: [openharmony] 361 364 362 365 '@esbuild/sunos-x64@0.27.3': 363 - resolution: {integrity: sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==, tarball: https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz} 366 + resolution: {integrity: sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==} 364 367 engines: {node: '>=18'} 365 368 cpu: [x64] 366 369 os: [sunos] 367 370 368 371 '@esbuild/win32-arm64@0.27.3': 369 - resolution: {integrity: sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==, tarball: https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz} 372 + resolution: {integrity: sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==} 370 373 engines: {node: '>=18'} 371 374 cpu: [arm64] 372 375 os: [win32] 373 376 374 377 '@esbuild/win32-ia32@0.27.3': 375 - resolution: {integrity: sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==, tarball: https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz} 378 + resolution: {integrity: sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==} 376 379 engines: {node: '>=18'} 377 380 cpu: [ia32] 378 381 os: [win32] 379 382 380 383 '@esbuild/win32-x64@0.27.3': 381 - resolution: {integrity: sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==, tarball: https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz} 384 + resolution: {integrity: sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==} 382 385 engines: {node: '>=18'} 383 386 cpu: [x64] 384 387 os: [win32] ··· 441 444 resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==} 442 445 443 446 '@foxui/core@0.5.2': 444 - resolution: {integrity: sha512-SwzKizuPCpiD47YfDfZ4Ut0KX+WlmidbBTaNafQaSYpQA45D5avRksgbJWsUgIaLx2oyH8PwBq7r/KFjCuKx/g==, tarball: https://registry.npmjs.org/@foxui/core/-/core-0.5.2.tgz} 447 + resolution: {integrity: sha512-SwzKizuPCpiD47YfDfZ4Ut0KX+WlmidbBTaNafQaSYpQA45D5avRksgbJWsUgIaLx2oyH8PwBq7r/KFjCuKx/g==} 445 448 peerDependencies: 446 449 svelte: '>=5' 447 450 tailwindcss: '>=3' 448 451 449 452 '@foxui/social@0.5.4': 450 - resolution: {integrity: sha512-6IvVJv6+NyEf0MbLZiOsNW11qK6QDAIMGzF6Wa4v5xmE8F5HCIOsx7am7JRS8zE+4da8rQ5otM8NpAIQhb5Edw==, tarball: https://registry.npmjs.org/@foxui/social/-/social-0.5.4.tgz} 453 + resolution: {integrity: sha512-6IvVJv6+NyEf0MbLZiOsNW11qK6QDAIMGzF6Wa4v5xmE8F5HCIOsx7am7JRS8zE+4da8rQ5otM8NpAIQhb5Edw==} 451 454 peerDependencies: 452 455 svelte: '>=5' 453 456 tailwindcss: '>=3' 454 457 455 458 '@foxui/time@0.5.2': 456 - resolution: {integrity: sha512-CSIzydngnijCFY/W2PsVuWjIGKfSDMIs6bxvVwlpIWV8e8gzrzfK+DCwL5RO9rhZOppEOqN7VptItOcIplCALw==, tarball: https://registry.npmjs.org/@foxui/time/-/time-0.5.2.tgz} 459 + resolution: {integrity: sha512-CSIzydngnijCFY/W2PsVuWjIGKfSDMIs6bxvVwlpIWV8e8gzrzfK+DCwL5RO9rhZOppEOqN7VptItOcIplCALw==} 457 460 peerDependencies: 458 461 svelte: '>=5' 459 462 tailwindcss: '>=3' ··· 475 478 engines: {node: '>=18.18'} 476 479 477 480 '@img/colour@1.1.0': 478 - resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==, tarball: https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz} 481 + resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==} 479 482 engines: {node: '>=18'} 480 483 481 484 '@img/sharp-darwin-arm64@0.34.5': 482 - resolution: {integrity: sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==, tarball: https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz} 485 + resolution: {integrity: sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==} 483 486 engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 484 487 cpu: [arm64] 485 488 os: [darwin] 486 489 487 490 '@img/sharp-darwin-x64@0.34.5': 488 - resolution: {integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==, tarball: https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz} 491 + resolution: {integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==} 489 492 engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 490 493 cpu: [x64] 491 494 os: [darwin] 492 495 493 496 '@img/sharp-libvips-darwin-arm64@1.2.4': 494 - resolution: {integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==, tarball: https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz} 497 + resolution: {integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==} 495 498 cpu: [arm64] 496 499 os: [darwin] 497 500 498 501 '@img/sharp-libvips-darwin-x64@1.2.4': 499 - resolution: {integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==, tarball: https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz} 502 + resolution: {integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==} 500 503 cpu: [x64] 501 504 os: [darwin] 502 505 503 506 '@img/sharp-libvips-linux-arm64@1.2.4': 504 - resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==, tarball: https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz} 507 + resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==} 505 508 cpu: [arm64] 506 509 os: [linux] 510 + libc: [glibc] 507 511 508 512 '@img/sharp-libvips-linux-arm@1.2.4': 509 - resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==, tarball: https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz} 513 + resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==} 510 514 cpu: [arm] 511 515 os: [linux] 516 + libc: [glibc] 512 517 513 518 '@img/sharp-libvips-linux-ppc64@1.2.4': 514 - resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==, tarball: https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz} 519 + resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==} 515 520 cpu: [ppc64] 516 521 os: [linux] 522 + libc: [glibc] 517 523 518 524 '@img/sharp-libvips-linux-riscv64@1.2.4': 519 - resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==, tarball: https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz} 525 + resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==} 520 526 cpu: [riscv64] 521 527 os: [linux] 528 + libc: [glibc] 522 529 523 530 '@img/sharp-libvips-linux-s390x@1.2.4': 524 - resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==, tarball: https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz} 531 + resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==} 525 532 cpu: [s390x] 526 533 os: [linux] 534 + libc: [glibc] 527 535 528 536 '@img/sharp-libvips-linux-x64@1.2.4': 529 - resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==, tarball: https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz} 537 + resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==} 530 538 cpu: [x64] 531 539 os: [linux] 540 + libc: [glibc] 532 541 533 542 '@img/sharp-libvips-linuxmusl-arm64@1.2.4': 534 - resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==, tarball: https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz} 543 + resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==} 535 544 cpu: [arm64] 536 545 os: [linux] 546 + libc: [musl] 537 547 538 548 '@img/sharp-libvips-linuxmusl-x64@1.2.4': 539 - resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==, tarball: https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz} 549 + resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==} 540 550 cpu: [x64] 541 551 os: [linux] 552 + libc: [musl] 542 553 543 554 '@img/sharp-linux-arm64@0.34.5': 544 - resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==, tarball: https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz} 555 + resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==} 545 556 engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 546 557 cpu: [arm64] 547 558 os: [linux] 559 + libc: [glibc] 548 560 549 561 '@img/sharp-linux-arm@0.34.5': 550 - resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==, tarball: https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz} 562 + resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==} 551 563 engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 552 564 cpu: [arm] 553 565 os: [linux] 566 + libc: [glibc] 554 567 555 568 '@img/sharp-linux-ppc64@0.34.5': 556 - resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==, tarball: https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz} 569 + resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==} 557 570 engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 558 571 cpu: [ppc64] 559 572 os: [linux] 573 + libc: [glibc] 560 574 561 575 '@img/sharp-linux-riscv64@0.34.5': 562 - resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==, tarball: https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz} 576 + resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==} 563 577 engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 564 578 cpu: [riscv64] 565 579 os: [linux] 580 + libc: [glibc] 566 581 567 582 '@img/sharp-linux-s390x@0.34.5': 568 - resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==, tarball: https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz} 583 + resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==} 569 584 engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 570 585 cpu: [s390x] 571 586 os: [linux] 587 + libc: [glibc] 572 588 573 589 '@img/sharp-linux-x64@0.34.5': 574 - resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==, tarball: https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz} 590 + resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==} 575 591 engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 576 592 cpu: [x64] 577 593 os: [linux] 594 + libc: [glibc] 578 595 579 596 '@img/sharp-linuxmusl-arm64@0.34.5': 580 - resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==, tarball: https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz} 597 + resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==} 581 598 engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 582 599 cpu: [arm64] 583 600 os: [linux] 601 + libc: [musl] 584 602 585 603 '@img/sharp-linuxmusl-x64@0.34.5': 586 - resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==, tarball: https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz} 604 + resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==} 587 605 engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 588 606 cpu: [x64] 589 607 os: [linux] 608 + libc: [musl] 590 609 591 610 '@img/sharp-wasm32@0.34.5': 592 - resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==, tarball: https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz} 611 + resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==} 593 612 engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 594 613 cpu: [wasm32] 595 614 596 615 '@img/sharp-win32-arm64@0.34.5': 597 - resolution: {integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==, tarball: https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz} 616 + resolution: {integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==} 598 617 engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 599 618 cpu: [arm64] 600 619 os: [win32] 601 620 602 621 '@img/sharp-win32-ia32@0.34.5': 603 - resolution: {integrity: sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==, tarball: https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz} 622 + resolution: {integrity: sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==} 604 623 engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 605 624 cpu: [ia32] 606 625 os: [win32] 607 626 608 627 '@img/sharp-win32-x64@0.34.5': 609 - resolution: {integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==, tarball: https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz} 628 + resolution: {integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==} 610 629 engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 611 630 cpu: [x64] 612 631 os: [win32] 613 632 614 633 '@internationalized/date@3.11.0': 615 - resolution: {integrity: sha512-BOx5huLAWhicM9/ZFs84CzP+V3gBW6vlpM02yzsdYC7TGlZJX1OJiEEHcSayF00Z+3jLlm4w79amvSt6RqKN3Q==, tarball: https://registry.npmjs.org/@internationalized/date/-/date-3.11.0.tgz} 634 + resolution: {integrity: sha512-BOx5huLAWhicM9/ZFs84CzP+V3gBW6vlpM02yzsdYC7TGlZJX1OJiEEHcSayF00Z+3jLlm4w79amvSt6RqKN3Q==} 616 635 617 636 '@jridgewell/gen-mapping@0.3.13': 618 637 resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} ··· 631 650 resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} 632 651 633 652 '@jridgewell/trace-mapping@0.3.9': 634 - resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==, tarball: https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz} 653 + resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} 654 + 655 + '@napi-rs/wasm-runtime@1.1.1': 656 + resolution: {integrity: sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A==} 635 657 636 658 '@number-flow/svelte@0.3.13': 637 659 resolution: {integrity: sha512-mvbxDeSFa1o/E4vGhrWuawAFCgcn5qTQ/s++FIoD88es5+JQa/aMQUypTy7qXIreTtTvncpIbkKdw9DMnweaSw==} ··· 642 664 resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} 643 665 644 666 '@poppinss/colors@4.1.6': 645 - resolution: {integrity: sha512-H9xkIdFswbS8n1d6vmRd8+c10t2Qe+rZITbbDHHkQixH5+2x1FDGmi/0K+WgWiqQFKPSlIYB7jlH6Kpfn6Fleg==, tarball: https://registry.npmjs.org/@poppinss/colors/-/colors-4.1.6.tgz} 667 + resolution: {integrity: sha512-H9xkIdFswbS8n1d6vmRd8+c10t2Qe+rZITbbDHHkQixH5+2x1FDGmi/0K+WgWiqQFKPSlIYB7jlH6Kpfn6Fleg==} 646 668 647 669 '@poppinss/dumper@0.6.5': 648 - resolution: {integrity: sha512-NBdYIb90J7LfOI32dOewKI1r7wnkiH6m920puQ3qHUeZkxNkQiFnXVWoE6YtFSv6QOiPPf7ys6i+HWWecDz7sw==, tarball: https://registry.npmjs.org/@poppinss/dumper/-/dumper-0.6.5.tgz} 670 + resolution: {integrity: sha512-NBdYIb90J7LfOI32dOewKI1r7wnkiH6m920puQ3qHUeZkxNkQiFnXVWoE6YtFSv6QOiPPf7ys6i+HWWecDz7sw==} 649 671 650 672 '@poppinss/exception@1.2.3': 651 - resolution: {integrity: sha512-dCED+QRChTVatE9ibtoaxc+WkdzOSjYTKi/+uacHWIsfodVfpsueo3+DKpgU5Px8qXjgmXkSvhXvSCz3fnP9lw==, tarball: https://registry.npmjs.org/@poppinss/exception/-/exception-1.2.3.tgz} 673 + resolution: {integrity: sha512-dCED+QRChTVatE9ibtoaxc+WkdzOSjYTKi/+uacHWIsfodVfpsueo3+DKpgU5Px8qXjgmXkSvhXvSCz3fnP9lw==} 652 674 653 675 '@rollup/rollup-android-arm-eabi@4.59.0': 654 - resolution: {integrity: sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==, tarball: https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz} 676 + resolution: {integrity: sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==} 655 677 cpu: [arm] 656 678 os: [android] 657 679 658 680 '@rollup/rollup-android-arm64@4.59.0': 659 - resolution: {integrity: sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==, tarball: https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.59.0.tgz} 681 + resolution: {integrity: sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==} 660 682 cpu: [arm64] 661 683 os: [android] 662 684 663 685 '@rollup/rollup-darwin-arm64@4.59.0': 664 - resolution: {integrity: sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==, tarball: https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.59.0.tgz} 686 + resolution: {integrity: sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==} 665 687 cpu: [arm64] 666 688 os: [darwin] 667 689 668 690 '@rollup/rollup-darwin-x64@4.59.0': 669 - resolution: {integrity: sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==, tarball: https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.59.0.tgz} 691 + resolution: {integrity: sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==} 670 692 cpu: [x64] 671 693 os: [darwin] 672 694 673 695 '@rollup/rollup-freebsd-arm64@4.59.0': 674 - resolution: {integrity: sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==, tarball: https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.59.0.tgz} 696 + resolution: {integrity: sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==} 675 697 cpu: [arm64] 676 698 os: [freebsd] 677 699 678 700 '@rollup/rollup-freebsd-x64@4.59.0': 679 - resolution: {integrity: sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==, tarball: https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.59.0.tgz} 701 + resolution: {integrity: sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==} 680 702 cpu: [x64] 681 703 os: [freebsd] 682 704 683 705 '@rollup/rollup-linux-arm-gnueabihf@4.59.0': 684 - resolution: {integrity: sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==, tarball: https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.59.0.tgz} 706 + resolution: {integrity: sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==} 685 707 cpu: [arm] 686 708 os: [linux] 709 + libc: [glibc] 687 710 688 711 '@rollup/rollup-linux-arm-musleabihf@4.59.0': 689 - resolution: {integrity: sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==, tarball: https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.59.0.tgz} 712 + resolution: {integrity: sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==} 690 713 cpu: [arm] 691 714 os: [linux] 715 + libc: [musl] 692 716 693 717 '@rollup/rollup-linux-arm64-gnu@4.59.0': 694 - resolution: {integrity: sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==, tarball: https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.59.0.tgz} 718 + resolution: {integrity: sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==} 695 719 cpu: [arm64] 696 720 os: [linux] 721 + libc: [glibc] 697 722 698 723 '@rollup/rollup-linux-arm64-musl@4.59.0': 699 - resolution: {integrity: sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==, tarball: https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.59.0.tgz} 724 + resolution: {integrity: sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==} 700 725 cpu: [arm64] 701 726 os: [linux] 727 + libc: [musl] 702 728 703 729 '@rollup/rollup-linux-loong64-gnu@4.59.0': 704 - resolution: {integrity: sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==, tarball: https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.59.0.tgz} 730 + resolution: {integrity: sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==} 705 731 cpu: [loong64] 706 732 os: [linux] 733 + libc: [glibc] 707 734 708 735 '@rollup/rollup-linux-loong64-musl@4.59.0': 709 - resolution: {integrity: sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==, tarball: https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.59.0.tgz} 736 + resolution: {integrity: sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==} 710 737 cpu: [loong64] 711 738 os: [linux] 739 + libc: [musl] 712 740 713 741 '@rollup/rollup-linux-ppc64-gnu@4.59.0': 714 - resolution: {integrity: sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==, tarball: https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.59.0.tgz} 742 + resolution: {integrity: sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==} 715 743 cpu: [ppc64] 716 744 os: [linux] 745 + libc: [glibc] 717 746 718 747 '@rollup/rollup-linux-ppc64-musl@4.59.0': 719 - resolution: {integrity: sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==, tarball: https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.59.0.tgz} 748 + resolution: {integrity: sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==} 720 749 cpu: [ppc64] 721 750 os: [linux] 751 + libc: [musl] 722 752 723 753 '@rollup/rollup-linux-riscv64-gnu@4.59.0': 724 - resolution: {integrity: sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==, tarball: https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.59.0.tgz} 754 + resolution: {integrity: sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==} 725 755 cpu: [riscv64] 726 756 os: [linux] 757 + libc: [glibc] 727 758 728 759 '@rollup/rollup-linux-riscv64-musl@4.59.0': 729 - resolution: {integrity: sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==, tarball: https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.59.0.tgz} 760 + resolution: {integrity: sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==} 730 761 cpu: [riscv64] 731 762 os: [linux] 763 + libc: [musl] 732 764 733 765 '@rollup/rollup-linux-s390x-gnu@4.59.0': 734 - resolution: {integrity: sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==, tarball: https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.59.0.tgz} 766 + resolution: {integrity: sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==} 735 767 cpu: [s390x] 736 768 os: [linux] 769 + libc: [glibc] 737 770 738 771 '@rollup/rollup-linux-x64-gnu@4.59.0': 739 - resolution: {integrity: sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==, tarball: https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.59.0.tgz} 772 + resolution: {integrity: sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==} 740 773 cpu: [x64] 741 774 os: [linux] 775 + libc: [glibc] 742 776 743 777 '@rollup/rollup-linux-x64-musl@4.59.0': 744 - resolution: {integrity: sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==, tarball: https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.59.0.tgz} 778 + resolution: {integrity: sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==} 745 779 cpu: [x64] 746 780 os: [linux] 781 + libc: [musl] 747 782 748 783 '@rollup/rollup-openbsd-x64@4.59.0': 749 - resolution: {integrity: sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==, tarball: https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.59.0.tgz} 784 + resolution: {integrity: sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==} 750 785 cpu: [x64] 751 786 os: [openbsd] 752 787 753 788 '@rollup/rollup-openharmony-arm64@4.59.0': 754 - resolution: {integrity: sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==, tarball: https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.59.0.tgz} 789 + resolution: {integrity: sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==} 755 790 cpu: [arm64] 756 791 os: [openharmony] 757 792 758 793 '@rollup/rollup-win32-arm64-msvc@4.59.0': 759 - resolution: {integrity: sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==, tarball: https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.59.0.tgz} 794 + resolution: {integrity: sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==} 760 795 cpu: [arm64] 761 796 os: [win32] 762 797 763 798 '@rollup/rollup-win32-ia32-msvc@4.59.0': 764 - resolution: {integrity: sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==, tarball: https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.59.0.tgz} 799 + resolution: {integrity: sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==} 765 800 cpu: [ia32] 766 801 os: [win32] 767 802 768 803 '@rollup/rollup-win32-x64-gnu@4.59.0': 769 - resolution: {integrity: sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==, tarball: https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.59.0.tgz} 804 + resolution: {integrity: sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==} 770 805 cpu: [x64] 771 806 os: [win32] 772 807 773 808 '@rollup/rollup-win32-x64-msvc@4.59.0': 774 - resolution: {integrity: sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==, tarball: https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.59.0.tgz} 809 + resolution: {integrity: sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==} 775 810 cpu: [x64] 776 811 os: [win32] 777 812 778 813 '@sindresorhus/is@7.2.0': 779 - resolution: {integrity: sha512-P1Cz1dWaFfR4IR+U13mqqiGsLFf1KbayybWwdd2vfctdV6hDpUkgCY0nKOLLTMSoRd/jJNjtbqzf13K8DCCXQw==, tarball: https://registry.npmjs.org/@sindresorhus/is/-/is-7.2.0.tgz} 814 + resolution: {integrity: sha512-P1Cz1dWaFfR4IR+U13mqqiGsLFf1KbayybWwdd2vfctdV6hDpUkgCY0nKOLLTMSoRd/jJNjtbqzf13K8DCCXQw==} 780 815 engines: {node: '>=18'} 781 816 782 817 '@speed-highlight/core@1.2.14': 783 - resolution: {integrity: sha512-G4ewlBNhUtlLvrJTb88d2mdy2KRijzs4UhnlrOSRT4bmjh/IqNElZa3zkrZ+TC47TwtlDWzVLFADljF1Ijp5hA==, tarball: https://registry.npmjs.org/@speed-highlight/core/-/core-1.2.14.tgz} 818 + resolution: {integrity: sha512-G4ewlBNhUtlLvrJTb88d2mdy2KRijzs4UhnlrOSRT4bmjh/IqNElZa3zkrZ+TC47TwtlDWzVLFADljF1Ijp5hA==} 784 819 785 820 '@standard-schema/spec@1.1.0': 786 821 resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} ··· 828 863 vite: ^6.3.0 || ^7.0.0 829 864 830 865 '@swc/helpers@0.5.19': 831 - resolution: {integrity: sha512-QamiFeIK3txNjgUTNppE6MiG3p7TdninpZu0E0PbqVh1a9FNLT2FRhisaa4NcaX52XVhA5l7Pk58Ft7Sqi/2sA==, tarball: https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.19.tgz} 866 + resolution: {integrity: sha512-QamiFeIK3txNjgUTNppE6MiG3p7TdninpZu0E0PbqVh1a9FNLT2FRhisaa4NcaX52XVhA5l7Pk58Ft7Sqi/2sA==} 832 867 833 868 '@tailwindcss/forms@0.5.11': 834 869 resolution: {integrity: sha512-h9wegbZDPurxG22xZSoWtdzc41/OlNEUQERNqI/0fOwa2aVlWGu7C35E/x6LDyD3lgtztFSSjKZyuVM0hxhbgA==} ··· 839 874 resolution: {integrity: sha512-jlx6sLk4EOwO6hHe1oCGm1Q4AN/s0rSrTTPBGPM0/RQ6Uylwq17FuU8IeJJKEjtc6K6O07zsvP+gDO6MMWo7pg==} 840 875 841 876 '@tailwindcss/oxide-android-arm64@4.2.1': 842 - resolution: {integrity: sha512-eZ7G1Zm5EC8OOKaesIKuw77jw++QJ2lL9N+dDpdQiAB/c/B2wDh0QPFHbkBVrXnwNugvrbJFk1gK2SsVjwWReg==, tarball: https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.2.1.tgz} 877 + resolution: {integrity: sha512-eZ7G1Zm5EC8OOKaesIKuw77jw++QJ2lL9N+dDpdQiAB/c/B2wDh0QPFHbkBVrXnwNugvrbJFk1gK2SsVjwWReg==} 843 878 engines: {node: '>= 20'} 844 879 cpu: [arm64] 845 880 os: [android] 846 881 847 882 '@tailwindcss/oxide-darwin-arm64@4.2.1': 848 - resolution: {integrity: sha512-q/LHkOstoJ7pI1J0q6djesLzRvQSIfEto148ppAd+BVQK0JYjQIFSK3JgYZJa+Yzi0DDa52ZsQx2rqytBnf8Hw==, tarball: https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.2.1.tgz} 883 + resolution: {integrity: sha512-q/LHkOstoJ7pI1J0q6djesLzRvQSIfEto148ppAd+BVQK0JYjQIFSK3JgYZJa+Yzi0DDa52ZsQx2rqytBnf8Hw==} 849 884 engines: {node: '>= 20'} 850 885 cpu: [arm64] 851 886 os: [darwin] 852 887 853 888 '@tailwindcss/oxide-darwin-x64@4.2.1': 854 - resolution: {integrity: sha512-/f/ozlaXGY6QLbpvd/kFTro2l18f7dHKpB+ieXz+Cijl4Mt9AI2rTrpq7V+t04nK+j9XBQHnSMdeQRhbGyt6fw==, tarball: https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.2.1.tgz} 889 + resolution: {integrity: sha512-/f/ozlaXGY6QLbpvd/kFTro2l18f7dHKpB+ieXz+Cijl4Mt9AI2rTrpq7V+t04nK+j9XBQHnSMdeQRhbGyt6fw==} 855 890 engines: {node: '>= 20'} 856 891 cpu: [x64] 857 892 os: [darwin] 858 893 859 894 '@tailwindcss/oxide-freebsd-x64@4.2.1': 860 - resolution: {integrity: sha512-5e/AkgYJT/cpbkys/OU2Ei2jdETCLlifwm7ogMC7/hksI2fC3iiq6OcXwjibcIjPung0kRtR3TxEITkqgn0TcA==, tarball: https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.2.1.tgz} 895 + resolution: {integrity: sha512-5e/AkgYJT/cpbkys/OU2Ei2jdETCLlifwm7ogMC7/hksI2fC3iiq6OcXwjibcIjPung0kRtR3TxEITkqgn0TcA==} 861 896 engines: {node: '>= 20'} 862 897 cpu: [x64] 863 898 os: [freebsd] 864 899 865 900 '@tailwindcss/oxide-linux-arm-gnueabihf@4.2.1': 866 - resolution: {integrity: sha512-Uny1EcVTTmerCKt/1ZuKTkb0x8ZaiuYucg2/kImO5A5Y/kBz41/+j0gxUZl+hTF3xkWpDmHX+TaWhOtba2Fyuw==, tarball: https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.2.1.tgz} 901 + resolution: {integrity: sha512-Uny1EcVTTmerCKt/1ZuKTkb0x8ZaiuYucg2/kImO5A5Y/kBz41/+j0gxUZl+hTF3xkWpDmHX+TaWhOtba2Fyuw==} 867 902 engines: {node: '>= 20'} 868 903 cpu: [arm] 869 904 os: [linux] 870 905 871 906 '@tailwindcss/oxide-linux-arm64-gnu@4.2.1': 872 - resolution: {integrity: sha512-CTrwomI+c7n6aSSQlsPL0roRiNMDQ/YzMD9EjcR+H4f0I1SQ8QqIuPnsVp7QgMkC1Qi8rtkekLkOFjo7OlEFRQ==, tarball: https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.2.1.tgz} 907 + resolution: {integrity: sha512-CTrwomI+c7n6aSSQlsPL0roRiNMDQ/YzMD9EjcR+H4f0I1SQ8QqIuPnsVp7QgMkC1Qi8rtkekLkOFjo7OlEFRQ==} 873 908 engines: {node: '>= 20'} 874 909 cpu: [arm64] 875 910 os: [linux] 911 + libc: [glibc] 876 912 877 913 '@tailwindcss/oxide-linux-arm64-musl@4.2.1': 878 - resolution: {integrity: sha512-WZA0CHRL/SP1TRbA5mp9htsppSEkWuQ4KsSUumYQnyl8ZdT39ntwqmz4IUHGN6p4XdSlYfJwM4rRzZLShHsGAQ==, tarball: https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.2.1.tgz} 914 + resolution: {integrity: sha512-WZA0CHRL/SP1TRbA5mp9htsppSEkWuQ4KsSUumYQnyl8ZdT39ntwqmz4IUHGN6p4XdSlYfJwM4rRzZLShHsGAQ==} 879 915 engines: {node: '>= 20'} 880 916 cpu: [arm64] 881 917 os: [linux] 918 + libc: [musl] 882 919 883 920 '@tailwindcss/oxide-linux-x64-gnu@4.2.1': 884 - resolution: {integrity: sha512-qMFzxI2YlBOLW5PhblzuSWlWfwLHaneBE0xHzLrBgNtqN6mWfs+qYbhryGSXQjFYB1Dzf5w+LN5qbUTPhW7Y5g==, tarball: https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.2.1.tgz} 921 + resolution: {integrity: sha512-qMFzxI2YlBOLW5PhblzuSWlWfwLHaneBE0xHzLrBgNtqN6mWfs+qYbhryGSXQjFYB1Dzf5w+LN5qbUTPhW7Y5g==} 885 922 engines: {node: '>= 20'} 886 923 cpu: [x64] 887 924 os: [linux] 925 + libc: [glibc] 888 926 889 927 '@tailwindcss/oxide-linux-x64-musl@4.2.1': 890 - resolution: {integrity: sha512-5r1X2FKnCMUPlXTWRYpHdPYUY6a1Ar/t7P24OuiEdEOmms5lyqjDRvVY1yy9Rmioh+AunQ0rWiOTPE8F9A3v5g==, tarball: https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.2.1.tgz} 928 + resolution: {integrity: sha512-5r1X2FKnCMUPlXTWRYpHdPYUY6a1Ar/t7P24OuiEdEOmms5lyqjDRvVY1yy9Rmioh+AunQ0rWiOTPE8F9A3v5g==} 891 929 engines: {node: '>= 20'} 892 930 cpu: [x64] 893 931 os: [linux] 932 + libc: [musl] 894 933 895 934 '@tailwindcss/oxide-wasm32-wasi@4.2.1': 896 - resolution: {integrity: sha512-MGFB5cVPvshR85MTJkEvqDUnuNoysrsRxd6vnk1Lf2tbiqNlXpHYZqkqOQalydienEWOHHFyyuTSYRsLfxFJ2Q==, tarball: https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.2.1.tgz} 935 + resolution: {integrity: sha512-MGFB5cVPvshR85MTJkEvqDUnuNoysrsRxd6vnk1Lf2tbiqNlXpHYZqkqOQalydienEWOHHFyyuTSYRsLfxFJ2Q==} 897 936 engines: {node: '>=14.0.0'} 898 937 cpu: [wasm32] 899 938 bundledDependencies: ··· 905 944 - tslib 906 945 907 946 '@tailwindcss/oxide-win32-arm64-msvc@4.2.1': 908 - resolution: {integrity: sha512-YlUEHRHBGnCMh4Nj4GnqQyBtsshUPdiNroZj8VPkvTZSoHsilRCwXcVKnG9kyi0ZFAS/3u+qKHBdDc81SADTRA==, tarball: https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.2.1.tgz} 947 + resolution: {integrity: sha512-YlUEHRHBGnCMh4Nj4GnqQyBtsshUPdiNroZj8VPkvTZSoHsilRCwXcVKnG9kyi0ZFAS/3u+qKHBdDc81SADTRA==} 909 948 engines: {node: '>= 20'} 910 949 cpu: [arm64] 911 950 os: [win32] 912 951 913 952 '@tailwindcss/oxide-win32-x64-msvc@4.2.1': 914 - resolution: {integrity: sha512-rbO34G5sMWWyrN/idLeVxAZgAKWrn5LiR3/I90Q9MkA67s6T1oB0xtTe+0heoBvHSpbU9Mk7i6uwJnpo4u21XQ==, tarball: https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.2.1.tgz} 953 + resolution: {integrity: sha512-rbO34G5sMWWyrN/idLeVxAZgAKWrn5LiR3/I90Q9MkA67s6T1oB0xtTe+0heoBvHSpbU9Mk7i6uwJnpo4u21XQ==} 915 954 engines: {node: '>= 20'} 916 955 cpu: [x64] 917 956 os: [win32] ··· 925 964 peerDependencies: 926 965 vite: ^5.2.0 || ^6 || ^7 927 966 967 + '@tursodatabase/database-common@0.5.0': 968 + resolution: {integrity: sha512-Yw1aJ7rw5Sshy7VvkLk3gZlHqNeM3nqs11G3qzhu1gjqLfVxT1MupVfB4AWLrvOwVMYWs560/Xfl9ITooJu5HQ==} 969 + 970 + '@tursodatabase/database-wasm-common@0.5.0': 971 + resolution: {integrity: sha512-l7Tf7m52sS0mRWD584WBpFXACZtdcd2iGHXa5Jsjr3Ia3X9sRmDNy/SQkncLQIHXoQ+EPFUC951v9C79Hdgg/w==} 972 + 973 + '@tursodatabase/database-wasm@0.5.0': 974 + resolution: {integrity: sha512-94ax/1oENl9wfOyQGIzWkcLE16FwPnOtM5zVTSRJ5NkOC/+FqbIGxWYL+jS4M8dd+U8FXSB2soQXLOWvCSHosg==} 975 + 976 + '@tybys/wasm-util@0.10.1': 977 + resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} 978 + 928 979 '@types/bun@1.3.10': 929 980 resolution: {integrity: sha512-0+rlrUrOrTSskibryHbvQkDOWRJwJZqZlxrUs1u4oOoTln8+WIXBPmAuCF35SWB2z4Zl3E84Nl/D0P7803nigQ==} 930 981 ··· 1006 1057 engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 1007 1058 1008 1059 '@use-gesture/core@10.3.1': 1009 - resolution: {integrity: sha512-WcINiDt8WjqBdUXye25anHiNxPc0VOrlT8F6LLkU6cycrOGUDyY/yyFmsg3k8i5OLvv25llc0QC45GhR/C8llw==, tarball: https://registry.npmjs.org/@use-gesture/core/-/core-10.3.1.tgz} 1060 + resolution: {integrity: sha512-WcINiDt8WjqBdUXye25anHiNxPc0VOrlT8F6LLkU6cycrOGUDyY/yyFmsg3k8i5OLvv25llc0QC45GhR/C8llw==} 1010 1061 1011 1062 '@use-gesture/vanilla@10.3.1': 1012 - resolution: {integrity: sha512-lT4scGLu59ovA3zmtUonukAGcA0AdOOh+iwNDS05Bsu7Lq9aZToDHhI6D8Q2qvsVraovtsLLYwPrWdG/noMAKw==, tarball: https://registry.npmjs.org/@use-gesture/vanilla/-/vanilla-10.3.1.tgz} 1063 + resolution: {integrity: sha512-lT4scGLu59ovA3zmtUonukAGcA0AdOOh+iwNDS05Bsu7Lq9aZToDHhI6D8Q2qvsVraovtsLLYwPrWdG/noMAKw==} 1013 1064 1014 1065 acorn-jsx@5.3.2: 1015 1066 resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} ··· 1044 1095 svelte: ^5.33.0 1045 1096 1046 1097 blake3-wasm@2.1.5: 1047 - resolution: {integrity: sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g==, tarball: https://registry.npmjs.org/blake3-wasm/-/blake3-wasm-2.1.5.tgz} 1098 + resolution: {integrity: sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g==} 1048 1099 1049 1100 brace-expansion@5.0.4: 1050 1101 resolution: {integrity: sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==} ··· 1066 1117 engines: {node: '>= 0.6'} 1067 1118 1068 1119 cookie@1.1.1: 1069 - resolution: {integrity: sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==, tarball: https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz} 1120 + resolution: {integrity: sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==} 1070 1121 engines: {node: '>=18'} 1071 1122 1072 1123 core-js@3.48.0: 1073 - resolution: {integrity: sha512-zpEHTy1fjTMZCKLHUZoVeylt9XrzaIN2rbPXEt0k+q7JE5CkCZdo6bNq55bn24a69CH7ErAVLKijxJja4fw+UQ==, tarball: https://registry.npmjs.org/core-js/-/core-js-3.48.0.tgz} 1124 + resolution: {integrity: sha512-zpEHTy1fjTMZCKLHUZoVeylt9XrzaIN2rbPXEt0k+q7JE5CkCZdo6bNq55bn24a69CH7ErAVLKijxJja4fw+UQ==} 1074 1125 1075 1126 cross-spawn@7.0.6: 1076 1127 resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} ··· 1082 1133 hasBin: true 1083 1134 1084 1135 custom-event-polyfill@1.0.7: 1085 - resolution: {integrity: sha512-TDDkd5DkaZxZFM8p+1I3yAlvM3rSr1wbrOliG4yJiwinMZN8z/iGL7BTlDkrJcYTmgUSb4ywVCc3ZaUtOtC76w==, tarball: https://registry.npmjs.org/custom-event-polyfill/-/custom-event-polyfill-1.0.7.tgz} 1136 + resolution: {integrity: sha512-TDDkd5DkaZxZFM8p+1I3yAlvM3rSr1wbrOliG4yJiwinMZN8z/iGL7BTlDkrJcYTmgUSb4ywVCc3ZaUtOtC76w==} 1086 1137 1087 1138 debug@4.4.3: 1088 1139 resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} ··· 1111 1162 devalue@5.6.3: 1112 1163 resolution: {integrity: sha512-nc7XjUU/2Lb+SvEFVGcWLiKkzfw8+qHI7zn8WYXKkLMgfGSHbgCEaR6bJpev8Cm6Rmrb19Gfd/tZvGqx9is3wg==} 1113 1164 1114 - dexie@4.3.0: 1115 - resolution: {integrity: sha512-5EeoQpJvMKHe6zWt/FSIIuRa3CWlZeIl6zKXt+Lz7BU6RoRRLgX9dZEynRfXrkLcldKYCBiz7xekTEylnie1Ug==, tarball: https://registry.npmjs.org/dexie/-/dexie-4.3.0.tgz} 1116 - 1117 1165 dompurify@3.3.1: 1118 - resolution: {integrity: sha512-qkdCKzLNtrgPFP1Vo+98FRzJnBRGe4ffyCea9IwHB1fyxPOeNTHpLKYGd4Uk9xvNoH0ZoOjwZxNptyMwqrId1Q==, tarball: https://registry.npmjs.org/dompurify/-/dompurify-3.3.1.tgz} 1166 + resolution: {integrity: sha512-qkdCKzLNtrgPFP1Vo+98FRzJnBRGe4ffyCea9IwHB1fyxPOeNTHpLKYGd4Uk9xvNoH0ZoOjwZxNptyMwqrId1Q==} 1119 1167 1120 1168 emoji-picker-element@1.29.1: 1121 - resolution: {integrity: sha512-TOiHzu9Dqib3x4MwcAi3wi3RdyT4SoeB4b15AvH1ks4SBwTl7DeebhZ0d3x6dNi4XfNU7IGRZ7NBQllj0RqwrQ==, tarball: https://registry.npmjs.org/emoji-picker-element/-/emoji-picker-element-1.29.1.tgz} 1169 + resolution: {integrity: sha512-TOiHzu9Dqib3x4MwcAi3wi3RdyT4SoeB4b15AvH1ks4SBwTl7DeebhZ0d3x6dNi4XfNU7IGRZ7NBQllj0RqwrQ==} 1122 1170 1123 1171 enhanced-resolve@5.20.0: 1124 1172 resolution: {integrity: sha512-/ce7+jQ1PQ6rVXwe+jKEg5hW5ciicHwIQUagZkp6IufBoY3YDgdTTY1azVs0qoRgVmvsNB+rbjLJxDAeHHtwsQ==} 1125 1173 engines: {node: '>=10.13.0'} 1126 1174 1127 1175 error-stack-parser-es@1.0.5: 1128 - resolution: {integrity: sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA==, tarball: https://registry.npmjs.org/error-stack-parser-es/-/error-stack-parser-es-1.0.5.tgz} 1176 + resolution: {integrity: sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA==} 1129 1177 1130 1178 esbuild@0.27.3: 1131 - resolution: {integrity: sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==, tarball: https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz} 1179 + resolution: {integrity: sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==} 1132 1180 engines: {node: '>=18'} 1133 1181 hasBin: true 1134 1182 ··· 1245 1293 flatted@3.3.4: 1246 1294 resolution: {integrity: sha512-3+mMldrTAPdta5kjX2G2J7iX4zxtnwpdA8Tr2ZSjkyPSanvbZAcy6flmtnXbEybHrDcU9641lxrMfFuUxVz9vA==} 1247 1295 1248 - flexsearch@0.8.212: 1249 - resolution: {integrity: sha512-wSyJr1GUWoOOIISRu+X2IXiOcVfg9qqBRyCPRUdLMIGJqPzMo+jMRlvE83t14v1j0dRMEaBbER/adQjp6Du2pw==} 1250 - 1251 1296 fsevents@2.3.3: 1252 - resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==, tarball: https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz} 1297 + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} 1253 1298 engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} 1254 1299 os: [darwin] 1255 1300 ··· 1272 1317 resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} 1273 1318 1274 1319 hls.js@1.6.15: 1275 - resolution: {integrity: sha512-E3a5VwgXimGHwpRGV+WxRTKeSp2DW5DI5MWv34ulL3t5UNmyJWCQ1KmLEHbYzcfThfXG8amBL+fCYPneGHC4VA==, tarball: https://registry.npmjs.org/hls.js/-/hls.js-1.6.15.tgz} 1320 + resolution: {integrity: sha512-E3a5VwgXimGHwpRGV+WxRTKeSp2DW5DI5MWv34ulL3t5UNmyJWCQ1KmLEHbYzcfThfXG8amBL+fCYPneGHC4VA==} 1276 1321 1277 1322 ignore@5.3.2: 1278 1323 resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} ··· 1290 1335 resolution: {integrity: sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==} 1291 1336 1292 1337 is-emoji-supported@0.0.5: 1293 - resolution: {integrity: sha512-WOlXUhDDHxYqcSmFZis+xWhhqXiK2SU0iYiqmth5Ip0FHLZQAt9rKL5ahnilE8/86WH8tZ3bmNNNC+bTzamqlw==, tarball: https://registry.npmjs.org/is-emoji-supported/-/is-emoji-supported-0.0.5.tgz} 1338 + resolution: {integrity: sha512-WOlXUhDDHxYqcSmFZis+xWhhqXiK2SU0iYiqmth5Ip0FHLZQAt9rKL5ahnilE8/86WH8tZ3bmNNNC+bTzamqlw==} 1294 1339 1295 1340 is-extglob@2.1.1: 1296 1341 resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} ··· 1334 1379 engines: {node: '>= 0.8.0'} 1335 1380 1336 1381 lightningcss-android-arm64@1.31.1: 1337 - resolution: {integrity: sha512-HXJF3x8w9nQ4jbXRiNppBCqeZPIAfUo8zE/kOEGbW5NZvGc/K7nMxbhIr+YlFlHW5mpbg/YFPdbnCh1wAXCKFg==, tarball: https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.31.1.tgz} 1382 + resolution: {integrity: sha512-HXJF3x8w9nQ4jbXRiNppBCqeZPIAfUo8zE/kOEGbW5NZvGc/K7nMxbhIr+YlFlHW5mpbg/YFPdbnCh1wAXCKFg==} 1338 1383 engines: {node: '>= 12.0.0'} 1339 1384 cpu: [arm64] 1340 1385 os: [android] 1341 1386 1342 1387 lightningcss-darwin-arm64@1.31.1: 1343 - resolution: {integrity: sha512-02uTEqf3vIfNMq3h/z2cJfcOXnQ0GRwQrkmPafhueLb2h7mqEidiCzkE4gBMEH65abHRiQvhdcQ+aP0D0g67sg==, tarball: https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.31.1.tgz} 1388 + resolution: {integrity: sha512-02uTEqf3vIfNMq3h/z2cJfcOXnQ0GRwQrkmPafhueLb2h7mqEidiCzkE4gBMEH65abHRiQvhdcQ+aP0D0g67sg==} 1344 1389 engines: {node: '>= 12.0.0'} 1345 1390 cpu: [arm64] 1346 1391 os: [darwin] 1347 1392 1348 1393 lightningcss-darwin-x64@1.31.1: 1349 - resolution: {integrity: sha512-1ObhyoCY+tGxtsz1lSx5NXCj3nirk0Y0kB/g8B8DT+sSx4G9djitg9ejFnjb3gJNWo7qXH4DIy2SUHvpoFwfTA==, tarball: https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.31.1.tgz} 1394 + resolution: {integrity: sha512-1ObhyoCY+tGxtsz1lSx5NXCj3nirk0Y0kB/g8B8DT+sSx4G9djitg9ejFnjb3gJNWo7qXH4DIy2SUHvpoFwfTA==} 1350 1395 engines: {node: '>= 12.0.0'} 1351 1396 cpu: [x64] 1352 1397 os: [darwin] 1353 1398 1354 1399 lightningcss-freebsd-x64@1.31.1: 1355 - resolution: {integrity: sha512-1RINmQKAItO6ISxYgPwszQE1BrsVU5aB45ho6O42mu96UiZBxEXsuQ7cJW4zs4CEodPUioj/QrXW1r9pLUM74A==, tarball: https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.31.1.tgz} 1400 + resolution: {integrity: sha512-1RINmQKAItO6ISxYgPwszQE1BrsVU5aB45ho6O42mu96UiZBxEXsuQ7cJW4zs4CEodPUioj/QrXW1r9pLUM74A==} 1356 1401 engines: {node: '>= 12.0.0'} 1357 1402 cpu: [x64] 1358 1403 os: [freebsd] 1359 1404 1360 1405 lightningcss-linux-arm-gnueabihf@1.31.1: 1361 - resolution: {integrity: sha512-OOCm2//MZJ87CdDK62rZIu+aw9gBv4azMJuA8/KB74wmfS3lnC4yoPHm0uXZ/dvNNHmnZnB8XLAZzObeG0nS1g==, tarball: https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.31.1.tgz} 1406 + resolution: {integrity: sha512-OOCm2//MZJ87CdDK62rZIu+aw9gBv4azMJuA8/KB74wmfS3lnC4yoPHm0uXZ/dvNNHmnZnB8XLAZzObeG0nS1g==} 1362 1407 engines: {node: '>= 12.0.0'} 1363 1408 cpu: [arm] 1364 1409 os: [linux] 1365 1410 1366 1411 lightningcss-linux-arm64-gnu@1.31.1: 1367 - resolution: {integrity: sha512-WKyLWztD71rTnou4xAD5kQT+982wvca7E6QoLpoawZ1gP9JM0GJj4Tp5jMUh9B3AitHbRZ2/H3W5xQmdEOUlLg==, tarball: https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.31.1.tgz} 1412 + resolution: {integrity: sha512-WKyLWztD71rTnou4xAD5kQT+982wvca7E6QoLpoawZ1gP9JM0GJj4Tp5jMUh9B3AitHbRZ2/H3W5xQmdEOUlLg==} 1368 1413 engines: {node: '>= 12.0.0'} 1369 1414 cpu: [arm64] 1370 1415 os: [linux] 1416 + libc: [glibc] 1371 1417 1372 1418 lightningcss-linux-arm64-musl@1.31.1: 1373 - resolution: {integrity: sha512-mVZ7Pg2zIbe3XlNbZJdjs86YViQFoJSpc41CbVmKBPiGmC4YrfeOyz65ms2qpAobVd7WQsbW4PdsSJEMymyIMg==, tarball: https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.31.1.tgz} 1419 + resolution: {integrity: sha512-mVZ7Pg2zIbe3XlNbZJdjs86YViQFoJSpc41CbVmKBPiGmC4YrfeOyz65ms2qpAobVd7WQsbW4PdsSJEMymyIMg==} 1374 1420 engines: {node: '>= 12.0.0'} 1375 1421 cpu: [arm64] 1376 1422 os: [linux] 1423 + libc: [musl] 1377 1424 1378 1425 lightningcss-linux-x64-gnu@1.31.1: 1379 - resolution: {integrity: sha512-xGlFWRMl+0KvUhgySdIaReQdB4FNudfUTARn7q0hh/V67PVGCs3ADFjw+6++kG1RNd0zdGRlEKa+T13/tQjPMA==, tarball: https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.31.1.tgz} 1426 + resolution: {integrity: sha512-xGlFWRMl+0KvUhgySdIaReQdB4FNudfUTARn7q0hh/V67PVGCs3ADFjw+6++kG1RNd0zdGRlEKa+T13/tQjPMA==} 1380 1427 engines: {node: '>= 12.0.0'} 1381 1428 cpu: [x64] 1382 1429 os: [linux] 1430 + libc: [glibc] 1383 1431 1384 1432 lightningcss-linux-x64-musl@1.31.1: 1385 - resolution: {integrity: sha512-eowF8PrKHw9LpoZii5tdZwnBcYDxRw2rRCyvAXLi34iyeYfqCQNA9rmUM0ce62NlPhCvof1+9ivRaTY6pSKDaA==, tarball: https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.31.1.tgz} 1433 + resolution: {integrity: sha512-eowF8PrKHw9LpoZii5tdZwnBcYDxRw2rRCyvAXLi34iyeYfqCQNA9rmUM0ce62NlPhCvof1+9ivRaTY6pSKDaA==} 1386 1434 engines: {node: '>= 12.0.0'} 1387 1435 cpu: [x64] 1388 1436 os: [linux] 1437 + libc: [musl] 1389 1438 1390 1439 lightningcss-win32-arm64-msvc@1.31.1: 1391 - resolution: {integrity: sha512-aJReEbSEQzx1uBlQizAOBSjcmr9dCdL3XuC/6HLXAxmtErsj2ICo5yYggg1qOODQMtnjNQv2UHb9NpOuFtYe4w==, tarball: https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.31.1.tgz} 1440 + resolution: {integrity: sha512-aJReEbSEQzx1uBlQizAOBSjcmr9dCdL3XuC/6HLXAxmtErsj2ICo5yYggg1qOODQMtnjNQv2UHb9NpOuFtYe4w==} 1392 1441 engines: {node: '>= 12.0.0'} 1393 1442 cpu: [arm64] 1394 1443 os: [win32] 1395 1444 1396 1445 lightningcss-win32-x64-msvc@1.31.1: 1397 - resolution: {integrity: sha512-I9aiFrbd7oYHwlnQDqr1Roz+fTz61oDDJX7n9tYF9FJymH1cIN1DtKw3iYt6b8WZgEjoNwVSncwF4wx/ZedMhw==, tarball: https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.31.1.tgz} 1446 + resolution: {integrity: sha512-I9aiFrbd7oYHwlnQDqr1Roz+fTz61oDDJX7n9tYF9FJymH1cIN1DtKw3iYt6b8WZgEjoNwVSncwF4wx/ZedMhw==} 1398 1447 engines: {node: '>= 12.0.0'} 1399 1448 cpu: [x64] 1400 1449 os: [win32] ··· 1408 1457 engines: {node: '>=10'} 1409 1458 1410 1459 loadjs@4.3.0: 1411 - resolution: {integrity: sha512-vNX4ZZLJBeDEOBvdr2v/F+0aN5oMuPu7JTqrMwp+DtgK+AryOlpy6Xtm2/HpNr+azEa828oQjOtWsB6iDtSfSQ==, tarball: https://registry.npmjs.org/loadjs/-/loadjs-4.3.0.tgz} 1460 + resolution: {integrity: sha512-vNX4ZZLJBeDEOBvdr2v/F+0aN5oMuPu7JTqrMwp+DtgK+AryOlpy6Xtm2/HpNr+azEa828oQjOtWsB6iDtSfSQ==} 1412 1461 1413 1462 locate-character@3.0.0: 1414 1463 resolution: {integrity: sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==} ··· 1429 1478 hasBin: true 1430 1479 1431 1480 miniflare@4.20260305.0: 1432 - resolution: {integrity: sha512-jVhtKJtiwaZa3rI+WgoLvSJmEazDsoUmAPYRUmEe2VO6VSbvkhbnDRm+dsPbYRatgNIExwrpqG1rv96jHiSb0w==, tarball: https://registry.npmjs.org/miniflare/-/miniflare-4.20260305.0.tgz} 1481 + resolution: {integrity: sha512-jVhtKJtiwaZa3rI+WgoLvSJmEazDsoUmAPYRUmEe2VO6VSbvkhbnDRm+dsPbYRatgNIExwrpqG1rv96jHiSb0w==} 1433 1482 engines: {node: '>=18.0.0'} 1434 1483 hasBin: true 1435 1484 ··· 1438 1487 engines: {node: 18 || 20 || >=22} 1439 1488 1440 1489 mode-watcher@1.1.0: 1441 - resolution: {integrity: sha512-mUT9RRGPDYenk59qJauN1rhsIMKBmWA3xMF+uRwE8MW/tjhaDSCCARqkSuDTq8vr4/2KcAxIGVjACxTjdk5C3g==, tarball: https://registry.npmjs.org/mode-watcher/-/mode-watcher-1.1.0.tgz} 1490 + resolution: {integrity: sha512-mUT9RRGPDYenk59qJauN1rhsIMKBmWA3xMF+uRwE8MW/tjhaDSCCARqkSuDTq8vr4/2KcAxIGVjACxTjdk5C3g==} 1442 1491 peerDependencies: 1443 1492 svelte: ^5.27.0 1444 1493 ··· 1497 1546 engines: {node: '>=8'} 1498 1547 1499 1548 path-to-regexp@6.3.0: 1500 - resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==, tarball: https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz} 1549 + resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==} 1501 1550 1502 1551 pathe@2.0.3: 1503 - resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==, tarball: https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz} 1552 + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} 1504 1553 1505 1554 picocolors@1.1.1: 1506 1555 resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} ··· 1510 1559 engines: {node: '>=12'} 1511 1560 1512 1561 plyr@3.8.4: 1513 - resolution: {integrity: sha512-DrzLbK9Wol3zeiuZCleD9aUOl0KAaBHR9H6WVVVYPZ4Ya+LYxUFTgSF1jooHcMQCv96Ws96wCaZzIoP3bES8pQ==, tarball: https://registry.npmjs.org/plyr/-/plyr-3.8.4.tgz} 1562 + resolution: {integrity: sha512-DrzLbK9Wol3zeiuZCleD9aUOl0KAaBHR9H6WVVVYPZ4Ya+LYxUFTgSF1jooHcMQCv96Ws96wCaZzIoP3bES8pQ==} 1514 1563 1515 1564 postcss-load-config@3.1.4: 1516 1565 resolution: {integrity: sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==} ··· 1619 1668 engines: {node: '>=6'} 1620 1669 1621 1670 rangetouch@2.0.1: 1622 - resolution: {integrity: sha512-sln+pNSc8NGaHoLzwNBssFSf/rSYkqeBXzX1AtJlkJiUaVSJSbRAWJk+4omsXkN+EJalzkZhWQ3th1m0FpR5xA==, tarball: https://registry.npmjs.org/rangetouch/-/rangetouch-2.0.1.tgz} 1671 + resolution: {integrity: sha512-sln+pNSc8NGaHoLzwNBssFSf/rSYkqeBXzX1AtJlkJiUaVSJSbRAWJk+4omsXkN+EJalzkZhWQ3th1m0FpR5xA==} 1623 1672 1624 1673 readdirp@4.1.2: 1625 1674 resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} ··· 1638 1687 hasBin: true 1639 1688 1640 1689 runed@0.23.4: 1641 - resolution: {integrity: sha512-9q8oUiBYeXIDLWNK5DfCWlkL0EW3oGbk845VdKlPeia28l751VpfesaB/+7pI6rnbx1I6rqoZ2fZxptOJLxILA==, tarball: https://registry.npmjs.org/runed/-/runed-0.23.4.tgz} 1690 + resolution: {integrity: sha512-9q8oUiBYeXIDLWNK5DfCWlkL0EW3oGbk845VdKlPeia28l751VpfesaB/+7pI6rnbx1I6rqoZ2fZxptOJLxILA==} 1642 1691 peerDependencies: 1643 1692 svelte: ^5.7.0 1644 1693 1645 1694 runed@0.25.0: 1646 - resolution: {integrity: sha512-7+ma4AG9FT2sWQEA0Egf6mb7PBT2vHyuHail1ie8ropfSjvZGtEAx8YTmUjv/APCsdRRxEVvArNjALk9zFSOrg==, tarball: https://registry.npmjs.org/runed/-/runed-0.25.0.tgz} 1695 + resolution: {integrity: sha512-7+ma4AG9FT2sWQEA0Egf6mb7PBT2vHyuHail1ie8ropfSjvZGtEAx8YTmUjv/APCsdRRxEVvArNjALk9zFSOrg==} 1647 1696 peerDependencies: 1648 1697 svelte: ^5.7.0 1649 1698 1650 1699 runed@0.28.0: 1651 - resolution: {integrity: sha512-k2xx7RuO9hWcdd9f+8JoBeqWtYrm5CALfgpkg2YDB80ds/QE4w0qqu34A7fqiAwiBBSBQOid7TLxwxVC27ymWQ==, tarball: https://registry.npmjs.org/runed/-/runed-0.28.0.tgz} 1700 + resolution: {integrity: sha512-k2xx7RuO9hWcdd9f+8JoBeqWtYrm5CALfgpkg2YDB80ds/QE4w0qqu34A7fqiAwiBBSBQOid7TLxwxVC27ymWQ==} 1652 1701 peerDependencies: 1653 1702 svelte: ^5.7.0 1654 1703 ··· 1674 1723 resolution: {integrity: sha512-n7Z7dXZhJbwuAHhNzkTti6Aw9QDDjZtm3JTpTGATIdNzdQz5GuFs22w90BcvF4INfnrL5xrX3oGsuqO5Dx3A1Q==} 1675 1724 1676 1725 sharp@0.34.5: 1677 - resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==, tarball: https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz} 1726 + resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==} 1678 1727 engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 1679 1728 1680 1729 shebang-command@2.0.0: ··· 1697 1746 resolution: {integrity: sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==} 1698 1747 1699 1748 supports-color@10.2.2: 1700 - resolution: {integrity: sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==, tarball: https://registry.npmjs.org/supports-color/-/supports-color-10.2.2.tgz} 1749 + resolution: {integrity: sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==} 1701 1750 engines: {node: '>=18'} 1702 1751 1703 1752 svelte-check@4.4.4: ··· 1718 1767 optional: true 1719 1768 1720 1769 svelte-sonner@1.0.7: 1721 - resolution: {integrity: sha512-1EUFYmd7q/xfs2qCHwJzGPh9n5VJ3X6QjBN10fof2vxgy8fYE7kVfZ7uGnd7i6fQaWIr5KvXcwYXE/cmTEjk5A==, tarball: https://registry.npmjs.org/svelte-sonner/-/svelte-sonner-1.0.7.tgz} 1770 + resolution: {integrity: sha512-1EUFYmd7q/xfs2qCHwJzGPh9n5VJ3X6QjBN10fof2vxgy8fYE7kVfZ7uGnd7i6fQaWIr5KvXcwYXE/cmTEjk5A==} 1722 1771 peerDependencies: 1723 1772 svelte: ^5.0.0 1724 1773 ··· 1729 1778 svelte: ^5.30.2 1730 1779 1731 1780 svelte-toolbelt@0.7.1: 1732 - resolution: {integrity: sha512-HcBOcR17Vx9bjaOceUvxkY3nGmbBmCBBbuWLLEWO6jtmWH8f/QoWmbyUfQZrpDINH39en1b8mptfPQT9VKQ1xQ==, tarball: https://registry.npmjs.org/svelte-toolbelt/-/svelte-toolbelt-0.7.1.tgz} 1781 + resolution: {integrity: sha512-HcBOcR17Vx9bjaOceUvxkY3nGmbBmCBBbuWLLEWO6jtmWH8f/QoWmbyUfQZrpDINH39en1b8mptfPQT9VKQ1xQ==} 1733 1782 engines: {node: '>=18', pnpm: '>=8.7.0'} 1734 1783 peerDependencies: 1735 1784 svelte: ^5.0.0 ··· 1745 1794 resolution: {integrity: sha512-I8K9wewnVDkL1NTGoqWmVEIlUcB9gFriAEkXkfCjX5ib8ezGxtR3xD7iZIxrfArjEsH7F1CHD4RFUtxefdqV/A==} 1746 1795 1747 1796 tailwind-variants@3.2.2: 1748 - resolution: {integrity: sha512-Mi4kHeMTLvKlM98XPnK+7HoBPmf4gygdFmqQPaDivc3DpYS6aIY6KiG/PgThrGvii5YZJqRsPz0aPyhoFzmZgg==, tarball: https://registry.npmjs.org/tailwind-variants/-/tailwind-variants-3.2.2.tgz} 1797 + resolution: {integrity: sha512-Mi4kHeMTLvKlM98XPnK+7HoBPmf4gygdFmqQPaDivc3DpYS6aIY6KiG/PgThrGvii5YZJqRsPz0aPyhoFzmZgg==} 1749 1798 engines: {node: '>=16.x', pnpm: '>=7.x'} 1750 1799 peerDependencies: 1751 1800 tailwind-merge: '>=3.0.0' ··· 1776 1825 typescript: '>=4.8.4' 1777 1826 1778 1827 tslib@2.8.1: 1779 - resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==, tarball: https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz} 1828 + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} 1780 1829 1781 1830 tsx@4.21.0: 1782 1831 resolution: {integrity: sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==} ··· 1803 1852 resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==} 1804 1853 1805 1854 undici@7.18.2: 1806 - resolution: {integrity: sha512-y+8YjDFzWdQlSE9N5nzKMT3g4a5UBX1HKowfdXh0uvAnTaqqwqB92Jt4UXBAeKekDs5IaDKyJFR4X1gYVCgXcw==, tarball: https://registry.npmjs.org/undici/-/undici-7.18.2.tgz} 1855 + resolution: {integrity: sha512-y+8YjDFzWdQlSE9N5nzKMT3g4a5UBX1HKowfdXh0uvAnTaqqwqB92Jt4UXBAeKekDs5IaDKyJFR4X1gYVCgXcw==} 1807 1856 engines: {node: '>=20.18.1'} 1808 1857 1809 1858 unenv@2.0.0-rc.24: 1810 - resolution: {integrity: sha512-i7qRCmY42zmCwnYlh9H2SvLEypEFGye5iRmEMKjcGi7zk9UquigRjFtTLz0TYqr0ZGLZhaMHl/foy1bZR+Cwlw==, tarball: https://registry.npmjs.org/unenv/-/unenv-2.0.0-rc.24.tgz} 1859 + resolution: {integrity: sha512-i7qRCmY42zmCwnYlh9H2SvLEypEFGye5iRmEMKjcGi7zk9UquigRjFtTLz0TYqr0ZGLZhaMHl/foy1bZR+Cwlw==} 1811 1860 1812 1861 unicode-segmenter@0.14.5: 1813 1862 resolution: {integrity: sha512-jHGmj2LUuqDcX3hqY12Ql+uhUTn8huuxNZGq7GvtF6bSybzH3aFgedYu/KTzQStEgt1Ra2F3HxadNXsNjb3m3g==} ··· 1816 1865 resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} 1817 1866 1818 1867 url-polyfill@1.1.14: 1819 - resolution: {integrity: sha512-p4f3TTAG6ADVF3mwbXw7hGw+QJyw5CnNGvYh5fCuQQZIiuKUswqcznyV3pGDP9j0TSmC4UvRKm8kl1QsX1diiQ==, tarball: https://registry.npmjs.org/url-polyfill/-/url-polyfill-1.1.14.tgz} 1868 + resolution: {integrity: sha512-p4f3TTAG6ADVF3mwbXw7hGw+QJyw5CnNGvYh5fCuQQZIiuKUswqcznyV3pGDP9j0TSmC4UvRKm8kl1QsX1diiQ==} 1820 1869 1821 1870 util-deprecate@1.0.2: 1822 1871 resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} ··· 1887 1936 engines: {node: '>=0.10.0'} 1888 1937 1889 1938 workerd@1.20260305.0: 1890 - resolution: {integrity: sha512-JkhfCLU+w+KbQmZ9k49IcDYc78GBo7eG8Mir8E2+KVjR7otQAmpcLlsous09YLh8WQ3Bt3Mi6/WMStvMAPukeA==, tarball: https://registry.npmjs.org/workerd/-/workerd-1.20260305.0.tgz} 1939 + resolution: {integrity: sha512-JkhfCLU+w+KbQmZ9k49IcDYc78GBo7eG8Mir8E2+KVjR7otQAmpcLlsous09YLh8WQ3Bt3Mi6/WMStvMAPukeA==} 1891 1940 engines: {node: '>=16'} 1892 1941 hasBin: true 1893 1942 ··· 1896 1945 engines: {node: '>=12'} 1897 1946 1898 1947 wrangler@4.69.0: 1899 - resolution: {integrity: sha512-EmVfIM65I5b4ITHe3Y9R7zQyf4NUBQ1leStakMlWiVR9n6VlDwuEltyQI2l3i0JciDnWyR3uqe+T6C08ivniTQ==, tarball: https://registry.npmjs.org/wrangler/-/wrangler-4.69.0.tgz} 1948 + resolution: {integrity: sha512-EmVfIM65I5b4ITHe3Y9R7zQyf4NUBQ1leStakMlWiVR9n6VlDwuEltyQI2l3i0JciDnWyR3uqe+T6C08ivniTQ==} 1900 1949 engines: {node: '>=20.0.0'} 1901 1950 hasBin: true 1902 1951 peerDependencies: ··· 1906 1955 optional: true 1907 1956 1908 1957 ws@8.18.0: 1909 - resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==, tarball: https://registry.npmjs.org/ws/-/ws-8.18.0.tgz} 1958 + resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} 1910 1959 engines: {node: '>=10.0.0'} 1911 1960 peerDependencies: 1912 1961 bufferutil: ^4.0.1 ··· 1926 1975 engines: {node: '>=10'} 1927 1976 1928 1977 youch-core@0.3.3: 1929 - resolution: {integrity: sha512-ho7XuGjLaJ2hWHoK8yFnsUGy2Y5uDpqSTq1FkHLK4/oqKtyUU1AFbOOxY4IpC9f0fTLjwYbslUz0Po5BpD1wrA==, tarball: https://registry.npmjs.org/youch-core/-/youch-core-0.3.3.tgz} 1978 + resolution: {integrity: sha512-ho7XuGjLaJ2hWHoK8yFnsUGy2Y5uDpqSTq1FkHLK4/oqKtyUU1AFbOOxY4IpC9f0fTLjwYbslUz0Po5BpD1wrA==} 1930 1979 1931 1980 youch@4.1.0-beta.10: 1932 - resolution: {integrity: sha512-rLfVLB4FgQneDr0dv1oddCVZmKjcJ6yX6mS4pU82Mq/Dt9a3cLZQ62pDBL4AUO+uVrCvtWz3ZFUL2HFAFJ/BXQ==, tarball: https://registry.npmjs.org/youch/-/youch-4.1.0-beta.10.tgz} 1981 + resolution: {integrity: sha512-rLfVLB4FgQneDr0dv1oddCVZmKjcJ6yX6mS4pU82Mq/Dt9a3cLZQ62pDBL4AUO+uVrCvtWz3ZFUL2HFAFJ/BXQ==} 1933 1982 1934 1983 zimmerframe@1.1.4: 1935 1984 resolution: {integrity: sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ==} ··· 2056 2105 dependencies: 2057 2106 '@jridgewell/trace-mapping': 0.3.9 2058 2107 2108 + '@emnapi/core@1.8.1': 2109 + dependencies: 2110 + '@emnapi/wasi-threads': 1.1.0 2111 + tslib: 2.8.1 2112 + 2059 2113 '@emnapi/runtime@1.8.1': 2060 2114 dependencies: 2061 2115 tslib: 2.8.1 2062 - optional: true 2116 + 2117 + '@emnapi/wasi-threads@1.1.0': 2118 + dependencies: 2119 + tslib: 2.8.1 2063 2120 2064 2121 '@esbuild/aix-ppc64@0.27.3': 2065 2122 optional: true ··· 2370 2427 '@jridgewell/resolve-uri': 3.1.2 2371 2428 '@jridgewell/sourcemap-codec': 1.5.5 2372 2429 2430 + '@napi-rs/wasm-runtime@1.1.1': 2431 + dependencies: 2432 + '@emnapi/core': 1.8.1 2433 + '@emnapi/runtime': 1.8.1 2434 + '@tybys/wasm-util': 0.10.1 2435 + 2373 2436 '@number-flow/svelte@0.3.13(svelte@5.53.6)': 2374 2437 dependencies: 2375 2438 esm-env: 1.2.2 ··· 2596 2659 tailwindcss: 4.2.1 2597 2660 vite: 7.3.1(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0) 2598 2661 2662 + '@tursodatabase/database-common@0.5.0': {} 2663 + 2664 + '@tursodatabase/database-wasm-common@0.5.0': 2665 + dependencies: 2666 + '@napi-rs/wasm-runtime': 1.1.1 2667 + 2668 + '@tursodatabase/database-wasm@0.5.0': 2669 + dependencies: 2670 + '@tursodatabase/database-common': 0.5.0 2671 + '@tursodatabase/database-wasm-common': 0.5.0 2672 + 2673 + '@tybys/wasm-util@0.10.1': 2674 + dependencies: 2675 + tslib: 2.8.1 2676 + 2599 2677 '@types/bun@1.3.10': 2600 2678 dependencies: 2601 2679 bun-types: 1.3.10 ··· 2789 2867 2790 2868 devalue@5.6.3: {} 2791 2869 2792 - dexie@4.3.0: {} 2793 - 2794 2870 dompurify@3.3.1: 2795 2871 optionalDependencies: 2796 2872 '@types/trusted-types': 2.0.7 ··· 2967 3043 keyv: 4.5.4 2968 3044 2969 3045 flatted@3.3.4: {} 2970 - 2971 - flexsearch@0.8.212: {} 2972 3046 2973 3047 fsevents@2.3.3: 2974 3048 optional: true
+8 -1
src/hooks.server.ts
··· 6 6 event.locals.session = session; 7 7 event.locals.client = client; 8 8 event.locals.did = did; 9 - return resolve(event); 9 + 10 + const response = await resolve(event); 11 + 12 + // Required for @tursodatabase/database-wasm (OPFS + SharedArrayBuffer) 13 + response.headers.set('Cross-Origin-Opener-Policy', 'same-origin'); 14 + response.headers.set('Cross-Origin-Embedder-Policy', 'credentialless'); 15 + 16 + return response; 10 17 };
+647 -17
src/lib/db.ts
··· 1 - import Dexie, { type EntityTable } from 'dexie'; 1 + import type { Database } from '@tursodatabase/database-wasm/vite'; 2 + 3 + export type SourceType = 'likes' | 'bookmarks' | 'posts' | 'reposts'; 4 + export const ALL_SOURCES: SourceType[] = ['likes', 'bookmarks', 'posts', 'reposts']; 5 + 6 + let dbInstance: Database | null = null; 7 + let dbReady: Promise<Database> | null = null; 8 + 9 + const SCHEMA_VERSION = 2; 10 + 11 + export async function getDb(): Promise<Database> { 12 + if (dbInstance) return dbInstance; 13 + if (dbReady) return dbReady; 14 + dbReady = initDb(); 15 + dbInstance = await dbReady; 16 + return dbInstance; 17 + } 18 + 19 + async function initDb(): Promise<Database> { 20 + const { connect } = await import('@tursodatabase/database-wasm/vite'); 21 + const db: Database = await connect('atmo.db'); 22 + 23 + await db.exec('PRAGMA journal_mode = WAL'); 24 + await db.exec('PRAGMA foreign_keys = ON'); 25 + 26 + await db.exec(`CREATE TABLE IF NOT EXISTS schema_version (version INTEGER PRIMARY KEY)`); 27 + const row = await (await db.prepare('SELECT version FROM schema_version ORDER BY version DESC LIMIT 1')).get() as 28 + | { version: number } 29 + | undefined; 30 + const currentVersion = row?.version ?? 0; 31 + 32 + if (currentVersion < SCHEMA_VERSION) { 33 + await applyMigrations(db, currentVersion); 34 + } 35 + 36 + // Clean up legacy IndexedDB databases from Dexie + FlexSearch 37 + if (typeof indexedDB !== 'undefined') { 38 + try { 39 + const dbs = await indexedDB.databases(); 40 + for (const dbInfo of dbs) { 41 + if (dbInfo.name) indexedDB.deleteDatabase(dbInfo.name); 42 + } 43 + } catch { 44 + // databases() may not be supported in all browsers 45 + } 46 + } 47 + 48 + // Clean up legacy localStorage 49 + for (const source of ALL_SOURCES) { 50 + localStorage.removeItem(`${source}-ids`); 51 + localStorage.removeItem(`${source}-cursor`); 52 + } 53 + 54 + return db; 55 + } 56 + 57 + async function applyMigrations(db: Database, fromVersion: number) { 58 + if (fromVersion < 1) { 59 + // Drop any partially-created tables from a previous failed migration 60 + await db.exec(` 61 + DROP TABLE IF EXISTS post_sources; 62 + DROP TABLE IF EXISTS sync_cursors; 63 + DROP TABLE IF EXISTS posts; 64 + DROP TABLE IF EXISTS accounts; 65 + `); 66 + await db.exec(` 67 + CREATE TABLE IF NOT EXISTS accounts ( 68 + did TEXT PRIMARY KEY, 69 + handle TEXT NOT NULL, 70 + display_name TEXT, 71 + avatar TEXT, 72 + added_at INTEGER NOT NULL DEFAULT (unixepoch('now')) 73 + ); 2 74 3 - export interface StoredPost { 75 + CREATE TABLE IF NOT EXISTS posts ( 76 + uri TEXT PRIMARY KEY, 77 + author_did TEXT NOT NULL, 78 + author_handle TEXT NOT NULL DEFAULT '', 79 + author_display_name TEXT NOT NULL DEFAULT '', 80 + text TEXT NOT NULL DEFAULT '', 81 + created_at TEXT, 82 + like_count INTEGER NOT NULL DEFAULT 0, 83 + repost_count INTEGER NOT NULL DEFAULT 0, 84 + reply_count INTEGER NOT NULL DEFAULT 0, 85 + has_image INTEGER NOT NULL DEFAULT 0, 86 + has_video INTEGER NOT NULL DEFAULT 0, 87 + has_link INTEGER NOT NULL DEFAULT 0, 88 + is_reply INTEGER NOT NULL DEFAULT 0, 89 + embed_type TEXT, 90 + embed_title TEXT NOT NULL DEFAULT '', 91 + embed_description TEXT NOT NULL DEFAULT '', 92 + quote_text TEXT NOT NULL DEFAULT '', 93 + quote_author_handle TEXT NOT NULL DEFAULT '', 94 + search_text TEXT NOT NULL DEFAULT '', 95 + raw TEXT NOT NULL, 96 + saved_at INTEGER NOT NULL, 97 + fetched_at INTEGER NOT NULL 98 + ); 99 + 100 + CREATE TABLE IF NOT EXISTS post_sources ( 101 + uri TEXT NOT NULL REFERENCES posts(uri) ON DELETE CASCADE, 102 + account_did TEXT NOT NULL REFERENCES accounts(did) ON DELETE CASCADE, 103 + source TEXT NOT NULL CHECK(source IN ('likes','bookmarks','posts','reposts')), 104 + PRIMARY KEY (uri, account_did, source) 105 + ); 106 + 107 + CREATE TABLE IF NOT EXISTS sync_cursors ( 108 + account_did TEXT NOT NULL, 109 + source TEXT NOT NULL CHECK(source IN ('likes','bookmarks','posts','reposts')), 110 + tail_cursor TEXT, 111 + PRIMARY KEY (account_did, source) 112 + ); 113 + 114 + -- Indexes for common query patterns 115 + CREATE INDEX IF NOT EXISTS idx_posts_created_at ON posts(created_at DESC); 116 + CREATE INDEX IF NOT EXISTS idx_posts_like_count ON posts(like_count); 117 + CREATE INDEX IF NOT EXISTS idx_posts_repost_count ON posts(repost_count); 118 + CREATE INDEX IF NOT EXISTS idx_posts_reply_count ON posts(reply_count); 119 + CREATE INDEX IF NOT EXISTS idx_posts_author_handle ON posts(author_handle); 120 + CREATE INDEX IF NOT EXISTS idx_posts_author_did ON posts(author_did); 121 + CREATE INDEX IF NOT EXISTS idx_post_sources_account ON post_sources(account_did, source); 122 + CREATE INDEX IF NOT EXISTS idx_post_sources_uri ON post_sources(uri); 123 + `); 124 + 125 + } 126 + 127 + if (fromVersion >= 1 && fromVersion < 2) { 128 + // v2: Added search_text column, removed FTS5. 129 + // Easiest to drop and recreate since there's no user-created data to preserve. 130 + await db.exec(` 131 + DROP TABLE IF EXISTS post_sources; 132 + DROP TABLE IF EXISTS sync_cursors; 133 + DROP TABLE IF EXISTS posts; 134 + DROP TABLE IF EXISTS accounts; 135 + `); 136 + // Re-create tables with the current v1 DDL (which includes search_text) 137 + fromVersion = 0; 138 + await applyMigrations(db, fromVersion); 139 + } 140 + 141 + await (await db.prepare('INSERT OR REPLACE INTO schema_version (version) VALUES (?)')).run(SCHEMA_VERSION); 142 + } 143 + 144 + // --- PostView field extraction --- 145 + 146 + export interface ExtractedPost { 4 147 uri: string; 5 - sources: string[]; 6 - savedAt: number; 7 - fetchedAt: number; 8 - // PostView fields stored inline 9 - [key: string]: any; 148 + author_did: string; 149 + author_handle: string; 150 + author_display_name: string; 151 + text: string; 152 + created_at: string | null; 153 + like_count: number; 154 + repost_count: number; 155 + reply_count: number; 156 + has_image: number; 157 + has_video: number; 158 + has_link: number; 159 + is_reply: number; 160 + embed_type: string | null; 161 + embed_title: string; 162 + embed_description: string; 163 + quote_text: string; 164 + quote_author_handle: string; 165 + search_text: string; 166 + raw: string; 167 + saved_at: number; 168 + fetched_at: number; 10 169 } 11 170 12 - export interface SourceMeta { 13 - source: string; 14 - tailCursor?: string; // deepest point reached paginating into the past 171 + // eslint-disable-next-line @typescript-eslint/no-explicit-any 172 + export function extractPostFields(postView: any, savedAt: number, fetchedAt: number): ExtractedPost { 173 + const embed = postView.embed; 174 + const record = postView.record; 175 + 176 + let hasImage = 0, 177 + hasVideo = 0, 178 + hasLink = 0; 179 + let embedType: string | null = null; 180 + let embedTitle = '', 181 + embedDescription = ''; 182 + let quoteText = '', 183 + quoteAuthorHandle = ''; 184 + 185 + if (embed) { 186 + embedType = embed.$type; 187 + 188 + if (embed.$type === 'app.bsky.embed.images#view') { 189 + hasImage = 1; 190 + } else if (embed.$type === 'app.bsky.embed.video#view') { 191 + hasVideo = 1; 192 + } else if (embed.$type === 'app.bsky.embed.external#view') { 193 + hasLink = 1; 194 + embedTitle = embed.external?.title ?? ''; 195 + embedDescription = embed.external?.description ?? ''; 196 + } else if (embed.$type === 'app.bsky.embed.record#view') { 197 + const embeddedRecord = embed.record; 198 + if (embeddedRecord?.value?.text) { 199 + quoteText = embeddedRecord.value.text; 200 + quoteAuthorHandle = embeddedRecord.author?.handle ?? ''; 201 + } 202 + } else if (embed.$type === 'app.bsky.embed.recordWithMedia#view') { 203 + const media = embed.media; 204 + if (media?.$type === 'app.bsky.embed.images#view') hasImage = 1; 205 + if (media?.$type === 'app.bsky.embed.video#view') hasVideo = 1; 206 + if (media?.$type === 'app.bsky.embed.external#view') { 207 + hasLink = 1; 208 + embedTitle = media.external?.title ?? ''; 209 + embedDescription = media.external?.description ?? ''; 210 + } 211 + const embeddedRecord = embed.record?.record; 212 + if (embeddedRecord?.value?.text) { 213 + quoteText = embeddedRecord.value.text; 214 + quoteAuthorHandle = embeddedRecord.author?.handle ?? ''; 215 + } 216 + } 217 + } 218 + 219 + // Check for links in facets 220 + if (!hasLink && Array.isArray(record?.facets)) { 221 + hasLink = record.facets.some( 222 + // eslint-disable-next-line @typescript-eslint/no-explicit-any 223 + (f: any) => f.features?.some((feat: any) => feat.$type === 'app.bsky.richtext.facet#link') 224 + ) 225 + ? 1 226 + : 0; 227 + } 228 + 229 + const authorHandle = postView.author?.handle ?? ''; 230 + const authorDisplayName = postView.author?.displayName ?? ''; 231 + const postText = record?.text ?? ''; 232 + 233 + // Concatenate all searchable fields, lowercased, for LIKE-based search 234 + const searchText = [authorHandle, authorDisplayName, postText, embedTitle, embedDescription, quoteText, quoteAuthorHandle] 235 + .filter(Boolean) 236 + .join(' ') 237 + .toLowerCase(); 238 + 239 + return { 240 + uri: postView.uri, 241 + author_did: postView.author?.did ?? '', 242 + author_handle: authorHandle, 243 + author_display_name: authorDisplayName, 244 + text: postText, 245 + created_at: record?.createdAt ?? null, 246 + like_count: postView.likeCount ?? 0, 247 + repost_count: postView.repostCount ?? 0, 248 + reply_count: postView.replyCount ?? 0, 249 + has_image: hasImage, 250 + has_video: hasVideo, 251 + has_link: hasLink, 252 + is_reply: record?.reply ? 1 : 0, 253 + embed_type: embedType, 254 + embed_title: embedTitle, 255 + embed_description: embedDescription, 256 + quote_text: quoteText, 257 + quote_author_handle: quoteAuthorHandle, 258 + search_text: searchText, 259 + raw: JSON.stringify(postView), 260 + saved_at: savedAt, 261 + fetched_at: fetchedAt 262 + }; 15 263 } 16 264 17 - export const db = new Dexie('atmo') as Dexie & { 18 - posts: EntityTable<StoredPost, 'uri'>; 19 - meta: EntityTable<SourceMeta, 'source'>; 265 + // --- CRUD helpers --- 266 + 267 + export async function upsertAccount( 268 + db: Database, 269 + did: string, 270 + handle: string, 271 + displayName?: string | null, 272 + avatar?: string | null 273 + ) { 274 + await (await db.prepare( 275 + `INSERT INTO accounts (did, handle, display_name, avatar) 276 + VALUES (?, ?, ?, ?) 277 + ON CONFLICT(did) DO UPDATE SET 278 + handle = excluded.handle, 279 + display_name = excluded.display_name, 280 + avatar = excluded.avatar` 281 + )).run(did, handle, displayName ?? null, avatar ?? null); 282 + } 283 + 284 + export async function getAccounts(db: Database): Promise<{ did: string; handle: string; display_name: string | null }[]> { 285 + return (await db.prepare('SELECT did, handle, display_name FROM accounts ORDER BY added_at')).all() as { 286 + did: string; 287 + handle: string; 288 + display_name: string | null; 289 + }[]; 290 + } 291 + 292 + export async function postExistsForSource( 293 + db: Database, 294 + uri: string, 295 + accountDid: string, 296 + source: SourceType 297 + ): Promise<boolean> { 298 + const row = await (await db.prepare( 299 + 'SELECT 1 FROM post_sources WHERE uri = ? AND account_did = ? AND source = ?' 300 + )).get(uri, accountDid, source); 301 + return !!row; 302 + } 303 + 304 + export async function postExists(db: Database, uri: string): Promise<boolean> { 305 + const row = await (await db.prepare('SELECT 1 FROM posts WHERE uri = ?')).get(uri); 306 + return !!row; 307 + } 308 + 309 + export async function getSourceCount(db: Database, accountDid: string, source: SourceType): Promise<number> { 310 + const row = await (await db.prepare( 311 + 'SELECT COUNT(*) as count FROM post_sources WHERE account_did = ? AND source = ?' 312 + )).get(accountDid, source) as { count: number } | undefined; 313 + return row?.count ?? 0; 314 + } 315 + 316 + export async function getCursor(db: Database, accountDid: string, source: SourceType): Promise<string | undefined> { 317 + const row = await (await db.prepare( 318 + 'SELECT tail_cursor FROM sync_cursors WHERE account_did = ? AND source = ?' 319 + )).get(accountDid, source) as { tail_cursor: string | null } | undefined; 320 + return row?.tail_cursor ?? undefined; 321 + } 322 + 323 + export async function setCursor(db: Database, accountDid: string, source: SourceType, cursor: string | undefined) { 324 + await (await db.prepare( 325 + `INSERT OR REPLACE INTO sync_cursors (account_did, source, tail_cursor) 326 + VALUES (?, ?, ?)` 327 + )).run(accountDid, source, cursor ?? null); 328 + } 329 + 330 + // --- Batch operations --- 331 + 332 + export async function bulkUpsertPosts( 333 + db: Database, 334 + posts: ExtractedPost[], 335 + accountDid: string, 336 + source: SourceType 337 + ): Promise<number> { 338 + if (posts.length === 0) return 0; 339 + 340 + const insertPost = await db.prepare( 341 + `INSERT INTO posts (uri, author_did, author_handle, author_display_name, text, 342 + created_at, like_count, repost_count, reply_count, has_image, has_video, 343 + has_link, is_reply, embed_type, embed_title, embed_description, 344 + quote_text, quote_author_handle, search_text, raw, saved_at, fetched_at) 345 + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) 346 + ON CONFLICT(uri) DO UPDATE SET 347 + like_count = excluded.like_count, 348 + repost_count = excluded.repost_count, 349 + reply_count = excluded.reply_count, 350 + raw = excluded.raw, 351 + fetched_at = excluded.fetched_at` 352 + ); 353 + 354 + const insertSource = await db.prepare( 355 + `INSERT OR IGNORE INTO post_sources (uri, account_did, source) VALUES (?, ?, ?)` 356 + ); 357 + 358 + let count = 0; 359 + await db.exec('BEGIN TRANSACTION'); 360 + try { 361 + for (const post of posts) { 362 + await insertPost.run( 363 + post.uri, 364 + post.author_did, 365 + post.author_handle, 366 + post.author_display_name, 367 + post.text, 368 + post.created_at, 369 + post.like_count, 370 + post.repost_count, 371 + post.reply_count, 372 + post.has_image, 373 + post.has_video, 374 + post.has_link, 375 + post.is_reply, 376 + post.embed_type, 377 + post.embed_title, 378 + post.embed_description, 379 + post.quote_text, 380 + post.quote_author_handle, 381 + post.search_text, 382 + post.raw, 383 + post.saved_at, 384 + post.fetched_at 385 + ); 386 + await insertSource.run(post.uri, accountDid, source); 387 + count++; 388 + } 389 + await db.exec('COMMIT'); 390 + } catch (e) { 391 + await db.exec('ROLLBACK'); 392 + throw e; 393 + } 394 + return count; 395 + } 396 + 397 + /** 398 + * Tag existing posts with a source for this account (they already exist from another source). 399 + * Returns number of posts that were newly tagged. 400 + */ 401 + export async function tagExistingPosts( 402 + db: Database, 403 + uris: string[], 404 + accountDid: string, 405 + source: SourceType 406 + ): Promise<number> { 407 + if (uris.length === 0) return 0; 408 + 409 + const insertSource = await db.prepare( 410 + `INSERT OR IGNORE INTO post_sources (uri, account_did, source) VALUES (?, ?, ?)` 411 + ); 412 + 413 + let count = 0; 414 + await db.exec('BEGIN TRANSACTION'); 415 + try { 416 + for (const uri of uris) { 417 + const result = await insertSource.run(uri, accountDid, source); 418 + if (result.changes > 0) count++; 419 + } 420 + await db.exec('COMMIT'); 421 + } catch (e) { 422 + await db.exec('ROLLBACK'); 423 + throw e; 424 + } 425 + return count; 426 + } 427 + 428 + // --- Search --- 429 + 430 + export type SearchFilters = { 431 + handles: string[]; 432 + minLikes: number; 433 + minReposts: number; 434 + minReplies: number; 435 + dateAfter: string; 436 + dateBefore: string; 437 + hasImage: boolean; 438 + hasLink: boolean; 439 + hasVideo: boolean; 440 + showReplies: boolean; 20 441 }; 21 442 22 - db.version(1).stores({ 23 - posts: 'uri, *sources, savedAt, fetchedAt, likeCount, repostCount, replyCount', 24 - meta: 'source' 25 - }); 443 + export const DEFAULT_FILTERS: SearchFilters = { 444 + handles: [], 445 + minLikes: 0, 446 + minReposts: 0, 447 + minReplies: 0, 448 + dateAfter: '', 449 + dateBefore: '', 450 + hasImage: false, 451 + hasLink: false, 452 + hasVideo: false, 453 + showReplies: true 454 + }; 455 + 456 + export function filtersActive(filters: SearchFilters): boolean { 457 + return ( 458 + filters.handles.length > 0 || 459 + filters.minLikes > 0 || 460 + filters.minReposts > 0 || 461 + filters.minReplies > 0 || 462 + !!filters.dateAfter || 463 + !!filters.dateBefore || 464 + filters.hasImage || 465 + filters.hasLink || 466 + filters.hasVideo || 467 + !filters.showReplies 468 + ); 469 + } 470 + 471 + export interface SearchParams { 472 + query: string; 473 + filters: SearchFilters; 474 + sources: SourceType[]; 475 + accountDids: string[]; 476 + limit: number; 477 + offset: number; 478 + } 479 + 480 + export interface SearchResult { 481 + // eslint-disable-next-line @typescript-eslint/no-explicit-any 482 + doc: any; 483 + isLiked: boolean; 484 + isBookmarked: boolean; 485 + } 486 + 487 + export async function searchPosts( 488 + db: Database, 489 + params: SearchParams 490 + ): Promise<{ results: SearchResult[]; hasMore: boolean }> { 491 + const conditions: string[] = []; 492 + // eslint-disable-next-line @typescript-eslint/no-explicit-any 493 + const bindings: any[] = []; 494 + 495 + // Source filter 496 + if (params.sources.length > 0) { 497 + const placeholders = params.sources.map(() => '?').join(', '); 498 + conditions.push(`ps.source IN (${placeholders})`); 499 + bindings.push(...params.sources); 500 + } 501 + 502 + // Account filter 503 + if (params.accountDids.length > 0) { 504 + const placeholders = params.accountDids.map(() => '?').join(', '); 505 + conditions.push(`ps.account_did IN (${placeholders})`); 506 + bindings.push(...params.accountDids); 507 + } 508 + 509 + // Text search via LIKE on the pre-built search_text column 510 + if (params.query.trim()) { 511 + const terms = params.query.trim().toLowerCase().split(/\s+/).filter(Boolean); 512 + for (const term of terms) { 513 + conditions.push('p.search_text LIKE ?'); 514 + bindings.push(`%${term}%`); 515 + } 516 + } 517 + 518 + // Structured filters 519 + if (params.filters.handles.length > 0) { 520 + const handleConditions = params.filters.handles.map(() => 'p.author_handle LIKE ?'); 521 + conditions.push(`(${handleConditions.join(' OR ')})`); 522 + bindings.push(...params.filters.handles.map((h) => `%${h.toLowerCase()}%`)); 523 + } 524 + if (params.filters.minLikes > 0) { 525 + conditions.push('p.like_count >= ?'); 526 + bindings.push(params.filters.minLikes); 527 + } 528 + if (params.filters.minReposts > 0) { 529 + conditions.push('p.repost_count >= ?'); 530 + bindings.push(params.filters.minReposts); 531 + } 532 + if (params.filters.minReplies > 0) { 533 + conditions.push('p.reply_count >= ?'); 534 + bindings.push(params.filters.minReplies); 535 + } 536 + if (params.filters.dateAfter) { 537 + conditions.push('p.created_at >= ?'); 538 + bindings.push(params.filters.dateAfter); 539 + } 540 + if (params.filters.dateBefore) { 541 + const before = new Date(new Date(params.filters.dateBefore).getTime() + 86400000).toISOString(); 542 + conditions.push('p.created_at <= ?'); 543 + bindings.push(before); 544 + } 545 + if (params.filters.hasImage) { 546 + conditions.push('p.has_image = 1'); 547 + } 548 + if (params.filters.hasLink) { 549 + conditions.push('p.has_link = 1'); 550 + } 551 + if (params.filters.hasVideo) { 552 + conditions.push('p.has_video = 1'); 553 + } 554 + if (!params.filters.showReplies) { 555 + conditions.push('p.is_reply = 0'); 556 + } 557 + 558 + const whereClause = conditions.length > 0 ? 'WHERE ' + conditions.join(' AND ') : ''; 559 + 560 + // Always sort by date descending (no relevance ranking without FTS5) 561 + const orderClause = 'ORDER BY p.created_at DESC'; 562 + 563 + // We need the current user's DID for liked/bookmarked status. 564 + const currentDid = params.accountDids[0] ?? ''; 565 + 566 + // Build final bindings in positional order: 567 + // 1. currentDid x2 (for EXISTS subqueries in SELECT) 568 + // 2. WHERE clause bindings 569 + // 3. LIMIT, OFFSET 570 + const finalBindings = [currentDid, currentDid, ...bindings, params.limit + 1, params.offset]; 571 + 572 + const sql = ` 573 + SELECT DISTINCT p.raw, p.uri, 574 + EXISTS(SELECT 1 FROM post_sources WHERE uri = p.uri AND account_did = ? AND source = 'likes') as is_liked, 575 + EXISTS(SELECT 1 FROM post_sources WHERE uri = p.uri AND account_did = ? AND source = 'bookmarks') as is_bookmarked 576 + FROM posts p 577 + JOIN post_sources ps ON ps.uri = p.uri 578 + ${whereClause} 579 + ${orderClause} 580 + LIMIT ? OFFSET ? 581 + `; 582 + 583 + const rows = await (await db.prepare(sql)).all(...finalBindings) as { 584 + raw: string; 585 + uri: string; 586 + is_liked: number; 587 + is_bookmarked: number; 588 + }[]; 589 + 590 + const hasMore = rows.length > params.limit; 591 + const results: SearchResult[] = rows.slice(0, params.limit).map((row) => ({ 592 + doc: JSON.parse(row.raw), 593 + isLiked: row.is_liked === 1, 594 + isBookmarked: row.is_bookmarked === 1 595 + })); 596 + 597 + return { results, hasMore }; 598 + } 599 + 600 + // --- Clear operations --- 601 + 602 + export async function clearSource(db: Database, accountDid: string, source: SourceType) { 603 + await db.exec('BEGIN TRANSACTION'); 604 + try { 605 + await (await db.prepare('DELETE FROM post_sources WHERE account_did = ? AND source = ?')).run( 606 + accountDid, 607 + source 608 + ); 609 + await (await db.prepare('DELETE FROM sync_cursors WHERE account_did = ? AND source = ?')).run( 610 + accountDid, 611 + source 612 + ); 613 + // Clean up orphaned posts 614 + await db.exec('DELETE FROM posts WHERE uri NOT IN (SELECT DISTINCT uri FROM post_sources)'); 615 + await db.exec('COMMIT'); 616 + } catch (e) { 617 + await db.exec('ROLLBACK'); 618 + throw e; 619 + } 620 + } 621 + 622 + export async function clearAccountData(db: Database, accountDid: string) { 623 + await db.exec('BEGIN TRANSACTION'); 624 + try { 625 + await (await db.prepare('DELETE FROM post_sources WHERE account_did = ?')).run(accountDid); 626 + await (await db.prepare('DELETE FROM sync_cursors WHERE account_did = ?')).run(accountDid); 627 + await (await db.prepare('DELETE FROM accounts WHERE did = ?')).run(accountDid); 628 + await db.exec('DELETE FROM posts WHERE uri NOT IN (SELECT DISTINCT uri FROM post_sources)'); 629 + await db.exec('COMMIT'); 630 + } catch (e) { 631 + await db.exec('ROLLBACK'); 632 + throw e; 633 + } 634 + } 635 + 636 + export async function clearAllData(db: Database) { 637 + await db.exec('BEGIN TRANSACTION'); 638 + try { 639 + await db.exec('DELETE FROM post_sources'); 640 + await db.exec('DELETE FROM posts'); 641 + await db.exec('DELETE FROM sync_cursors'); 642 + await db.exec('DELETE FROM accounts'); 643 + await db.exec('COMMIT'); 644 + } catch (e) { 645 + await db.exec('ROLLBACK'); 646 + throw e; 647 + } 648 + } 649 + 650 + // --- Utilities --- 651 + 652 + export function getLink(uri: string, handle: string) { 653 + const [, , rkey] = uri.replace('at://', '').split('/'); 654 + return `https://bsky.app/profile/${handle}/post/${rkey}`; 655 + }
+262 -334
src/lib/search-state.svelte.ts
··· 1 1 import { user } from '$lib/atproto'; 2 2 import { toast } from '@foxui/core'; 3 - import { Document, Charset, IndexedDB } from 'flexsearch'; 4 3 import { Client, simpleFetchHandler } from '@atcute/client'; 5 - import { db, type StoredPost } from '$lib/db'; 4 + import { 5 + getDb, 6 + extractPostFields, 7 + upsertAccount, 8 + postExistsForSource, 9 + postExists, 10 + getSourceCount, 11 + getCursor, 12 + setCursor, 13 + bulkUpsertPosts, 14 + tagExistingPosts, 15 + searchPosts, 16 + clearSource as dbClearSource, 17 + clearAllData as dbClearAllData, 18 + getAccounts as dbGetAccounts, 19 + clearAccountData as dbClearAccountData, 20 + ALL_SOURCES, 21 + DEFAULT_FILTERS, 22 + type SourceType, 23 + type SearchFilters, 24 + type ExtractedPost 25 + } from '$lib/db'; 6 26 7 - export type SourceType = 'likes' | 'bookmarks' | 'posts' | 'reposts'; 8 - 9 - export type SearchFilters = { 10 - handles: string[]; 11 - minLikes: number; 12 - minReposts: number; 13 - minReplies: number; 14 - dateAfter: string; 15 - dateBefore: string; 16 - hasImage: boolean; 17 - hasLink: boolean; 18 - hasVideo: boolean; 19 - showReplies: boolean; 20 - }; 21 - 22 - export const DEFAULT_FILTERS: SearchFilters = { 23 - handles: [], 24 - minLikes: 0, 25 - minReposts: 0, 26 - minReplies: 0, 27 - dateAfter: '', 28 - dateBefore: '', 29 - hasImage: false, 30 - hasLink: false, 31 - hasVideo: false, 32 - showReplies: true 33 - }; 27 + export { ALL_SOURCES, DEFAULT_FILTERS, type SourceType, type SearchFilters }; 34 28 35 29 export const SOURCE_LABELS: Record<SourceType, string> = { 36 30 likes: 'Likes', ··· 39 33 reposts: 'Reposts' 40 34 }; 41 35 42 - export const PLACEHOLDERS: Record<SourceType, string> = { 43 - likes: 'Search liked posts', 44 - bookmarks: 'Search bookmarks', 45 - posts: 'Search my posts', 46 - reposts: 'Search reposted posts' 47 - }; 48 - 49 - export const ALL_SOURCES: SourceType[] = ['likes', 'bookmarks', 'posts', 'reposts']; 36 + export function getPlaceholder(sources: SourceType[]): string { 37 + if (sources.length === ALL_SOURCES.length) return 'Search all posts'; 38 + if (sources.length > 1) 39 + return `Search ${sources.map((s) => SOURCE_LABELS[s].toLowerCase()).join(' & ')}`; 40 + if (sources.length === 1) { 41 + const labels: Record<SourceType, string> = { 42 + likes: 'Search liked posts', 43 + bookmarks: 'Search bookmarks', 44 + posts: 'Search my posts', 45 + reposts: 'Search reposted posts' 46 + }; 47 + return labels[sources[0]]; 48 + } 49 + return 'Select a source to search'; 50 + } 50 51 51 52 type SourceState = { 52 - index: Document | null; 53 53 count: number; 54 54 indexed: number; 55 55 totalToIndex: number; ··· 60 60 61 61 function createSourceState(): SourceState { 62 62 return { 63 - index: null, 64 63 count: 0, 65 64 indexed: 0, 66 65 totalToIndex: 0, ··· 70 69 }; 71 70 } 72 71 73 - function createIndex() { 74 - return new Document({ 75 - document: { 76 - id: 'uri', 77 - store: false, 78 - index: [ 79 - { 80 - field: 'author:handle', 81 - tokenize: 'forward', 82 - encoder: Charset.LatinBalance 83 - }, 84 - { 85 - field: 'author:displayName', 86 - tokenize: 'forward', 87 - encoder: Charset.LatinBalance 88 - }, 89 - { 90 - field: 'record:text', 91 - tokenize: 'forward', 92 - encoder: Charset.LatinBalance 93 - } 94 - ] 95 - } 96 - }); 97 - } 98 - 99 72 const publicClient = new Client({ 100 73 handler: simpleFetchHandler({ service: 'https://public.api.bsky.app' }) 101 74 }); ··· 107 80 posts: createSourceState(), 108 81 reposts: createSourceState() 109 82 } as Record<SourceType, SourceState>, 110 - activeSource: 'likes' as SourceType 83 + activeSources: ['likes'] as SourceType[] 111 84 }); 112 85 113 86 let generation = 0; 114 87 115 88 export async function initSources() { 116 - // Clean up legacy localStorage data 89 + const db = await getDb(); 90 + const accountDid = user.did; 91 + if (!accountDid) return; 92 + 93 + // Register/update current account 94 + await upsertAccount( 95 + db, 96 + accountDid, 97 + user.profile?.handle ?? '', 98 + user.profile?.displayName, 99 + user.profile?.avatar 100 + ); 101 + 102 + // Get counts for each source 117 103 for (const source of ALL_SOURCES) { 118 - localStorage.removeItem(`${source}-ids`); 119 - localStorage.removeItem(`${source}-cursor`); 104 + searchState.sources[source].count = await getSourceCount(db, accountDid, source); 120 105 } 121 106 122 - await Promise.all( 123 - ALL_SOURCES.map(async (source) => { 124 - const flexDb = new IndexedDB(`${source}-idx`); 125 - searchState.sources[source].index = createIndex(); 126 - await searchState.sources[source].index!.mount(flexDb); 107 + // Start loading active source first 108 + startLoading(searchState.activeSources[0] ?? 'likes'); 109 + } 127 110 128 - // Get count from Dexie 129 - searchState.sources[source].count = await db.posts 130 - .where('sources') 131 - .equals(source) 132 - .count(); 133 - }) 134 - ); 111 + export function toggleSource(source: SourceType) { 112 + const current = searchState.activeSources; 113 + if (current.includes(source)) { 114 + // Don't allow deselecting all 115 + if (current.length <= 1) return; 116 + searchState.activeSources = current.filter((s) => s !== source); 117 + } else { 118 + searchState.activeSources = [...current, source]; 119 + } 135 120 136 - // Start loading (active source first via loadNext) 137 - startLoading(searchState.activeSource); 121 + generation++; 122 + // Start loading any sources that aren't done yet 123 + for (const s of searchState.activeSources) { 124 + if (searchState.sources[s].phase !== 'done') { 125 + startLoading(s); 126 + return; 127 + } 128 + } 129 + loadNext(generation); 138 130 } 139 131 140 - export function switchSource(source: SourceType) { 141 - if (source === searchState.activeSource) return; 142 - searchState.activeSource = source; 132 + export function setActiveSources(sources: SourceType[]) { 133 + if (sources.length === 0) return; 134 + searchState.activeSources = sources; 143 135 generation++; 144 - if (searchState.sources[source].phase !== 'done') { 145 - startLoading(source); 146 - } else { 147 - loadNext(generation); 136 + for (const s of sources) { 137 + if (searchState.sources[s].phase !== 'done') { 138 + startLoading(s); 139 + return; 140 + } 148 141 } 142 + loadNext(generation); 149 143 } 150 144 151 145 export function startLoading(source: SourceType) { ··· 162 156 function loadNext(currentGen: number) { 163 157 if (currentGen !== generation) return; 164 158 159 + // Prioritize active sources, then others 165 160 const sorted = [ 166 - searchState.activeSource, 167 - ...ALL_SOURCES.filter((s) => s !== searchState.activeSource) 161 + ...searchState.activeSources, 162 + ...ALL_SOURCES.filter((s) => !searchState.activeSources.includes(s)) 168 163 ]; 169 164 for (const source of sorted) { 170 165 if (searchState.sources[source].phase !== 'done') { ··· 177 172 async function fetchRecordPage( 178 173 source: SourceType, 179 174 s: SourceState, 175 + // eslint-disable-next-line @typescript-eslint/no-explicit-any 180 176 fetchFn: Function, 181 177 cursor: string | undefined, 182 178 myGen: number 183 179 ): Promise<{ cursor: string | undefined; done: boolean }> { 184 180 if (myGen !== generation) return { cursor, done: true }; 185 181 182 + const accountDid = user.did; 183 + if (!accountDid) return { cursor, done: true }; 184 + 185 + // eslint-disable-next-line @typescript-eslint/no-explicit-any 186 186 let data: any; 187 187 try { 188 188 data = await fetchFn({ limit: 100, cursor }); ··· 192 192 return { cursor, done: true }; 193 193 } 194 194 195 - const uris = data.records.map((r: any) => 196 - source === 'posts' ? r.uri : r.value.subject.uri 197 - ); 198 - const existingDocs = await db.posts.bulkGet(uris); 199 - const toUpdate: any[] = []; 200 - const now = Date.now(); 195 + const db = await getDb(); 196 + 197 + // eslint-disable-next-line @typescript-eslint/no-explicit-any 198 + const uris = data.records.map((r: any) => (source === 'posts' ? r.uri : r.value.subject.uri)); 199 + 200 + // Check which URIs already exist for this source 201 + const toTag: string[] = []; 201 202 202 203 for (let j = 0; j < uris.length; j++) { 203 204 const subjectUri = uris[j]; 204 - const existing = existingDocs[j]; 205 205 206 - if (existing) { 207 - if (existing.sources.includes(source)) { 208 - // Already indexed for this source — flush updates and stop. 209 - // Use the last record's rkey as cursor so the tail resumes past this point. 210 - const lastRecord = data.records[j]; 211 - const stopCursor = lastRecord?.uri?.split('/').pop() ?? data.cursor; 212 - if (toUpdate.length > 0) await db.posts.bulkPut(toUpdate); 213 - return { cursor: stopCursor, done: true }; 206 + if (await postExistsForSource(db, subjectUri, accountDid, source)) { 207 + // Already indexed for this source — flush tags and stop 208 + if (toTag.length > 0) { 209 + const tagged = await tagExistingPosts(db, toTag, accountDid, source); 210 + s.count += tagged; 214 211 } 215 - // Post exists from another source — queue source tag update 216 - toUpdate.push({ ...existing, sources: [...existing.sources, source], fetchedAt: now }); 217 - s.index!.add(existing as any); 218 - s.count++; 219 - continue; 212 + const lastRecord = data.records[j]; 213 + const stopCursor = lastRecord?.uri?.split('/').pop() ?? data.cursor; 214 + return { cursor: stopCursor, done: true }; 220 215 } 221 216 222 - s.pendingUris.push(subjectUri); 217 + // Check if post exists from another source (already in posts table) 218 + if (await postExists(db, subjectUri)) { 219 + toTag.push(subjectUri); 220 + } else { 221 + s.pendingUris.push(subjectUri); 222 + } 223 223 } 224 224 225 - if (toUpdate.length > 0) await db.posts.bulkPut(toUpdate); 225 + if (toTag.length > 0) { 226 + const tagged = await tagExistingPosts(db, toTag, accountDid, source); 227 + s.count += tagged; 228 + } 226 229 227 230 const nextCursor = data.records.length > 0 ? data.cursor : undefined; 228 - // Detect stalled cursor — if the API returns the same cursor we sent, stop. 229 231 if (nextCursor && nextCursor === cursor) { 230 232 return { cursor: nextCursor, done: true }; 231 233 } ··· 233 235 } 234 236 235 237 async function loadRecords(source: SourceType, myGen: number) { 236 - if (!user.did || !searchState.sources[source].index) return; 238 + if (!user.did) return; 237 239 238 240 const { listLikeRecords, listPostRecords, listRepostRecords } = await import( 239 241 '$lib/atproto/server/search.remote' ··· 246 248 : listPostRecords; 247 249 248 250 const s = searchState.sources[source]; 251 + const db = await getDb(); 252 + const accountDid = user.did; 249 253 250 254 if (s.phase === 'idle' || s.phase === 'fetching') { 251 255 s.phase = 'fetching'; 252 256 253 - const meta = await db.meta.get(source); 257 + const tailCursor = await getCursor(db, accountDid, source); 254 258 255 - // Step 1: Fetch new posts from the top until we hit one we already have 259 + // Step 1: Fetch new posts from the top 256 260 let result = { cursor: undefined as string | undefined, done: false }; 257 261 do { 258 262 result = await fetchRecordPage(source, s, fetchFn, result.cursor, myGen); ··· 260 264 261 265 if (myGen !== generation) return; 262 266 263 - // Step 2: Continue from where we left off last time (tail) 264 - if (meta?.tailCursor) { 265 - result = { cursor: meta.tailCursor, done: false }; 267 + // Step 2: Continue from tail cursor 268 + if (tailCursor) { 269 + result = { cursor: tailCursor, done: false }; 266 270 do { 267 271 result = await fetchRecordPage(source, s, fetchFn, result.cursor, myGen); 268 272 } while (!result.done && myGen === generation); ··· 270 274 if (myGen !== generation) return; 271 275 } 272 276 273 - // Save how far we got into the past 274 - if (result.cursor) { 275 - await db.meta.put({ source, tailCursor: result.cursor }); 276 - } else { 277 - // We've reached the very end — no tail cursor needed 278 - await db.meta.put({ source, tailCursor: undefined }); 279 - } 277 + // Save cursor progress 278 + await setCursor(db, accountDid, source, result.cursor); 280 279 281 280 s.totalToIndex = s.pendingUris.length; 282 281 s.phase = 'hydrating'; ··· 289 288 290 289 async function hydrateUris(source: SourceType, myGen: number) { 291 290 const s = searchState.sources[source]; 291 + const accountDid = user.did; 292 + if (!accountDid) return; 293 + 292 294 const BATCH_SIZE = 25; 293 295 const CONCURRENCY = 5; 294 296 ··· 298 300 batches.push(remaining.slice(i, i + BATCH_SIZE)); 299 301 } 300 302 303 + const db = await getDb(); 304 + 301 305 for (let i = 0; i < batches.length; i += CONCURRENCY) { 302 306 if (myGen !== generation) return; 303 307 ··· 305 309 const results = await Promise.all( 306 310 chunk.map((uris) => 307 311 publicClient 312 + // eslint-disable-next-line @typescript-eslint/no-explicit-any 308 313 .get('app.bsky.feed.getPosts', { params: { uris: uris as any } }) 309 314 .catch((e) => { 310 315 console.error(`Failed to hydrate ${source} batch:`, e); ··· 314 319 ); 315 320 316 321 // Collect all posts from this chunk 317 - const allPosts: any[] = []; 322 + const now = Date.now(); 323 + const extracted: ExtractedPost[] = []; 318 324 for (const result of results) { 319 325 if (result?.ok) { 320 - allPosts.push(...result.data.posts); 326 + for (const post of result.data.posts) { 327 + extracted.push(extractPostFields(post, now, now)); 328 + } 321 329 } 322 330 } 323 331 324 - if (allPosts.length > 0) { 325 - // Batch-check which already exist in Dexie 326 - const uris = allPosts.map((p) => p.uri); 327 - const existing = await db.posts.bulkGet(uris); 328 - const existingMap = new Map<string, StoredPost>(); 329 - for (const doc of existing) { 330 - if (doc) existingMap.set(doc.uri, doc); 331 - } 332 - 333 - // Build batch for bulkPut 334 - const now = Date.now(); 335 - const toPut: any[] = []; 336 - for (const post of allPosts) { 337 - const ex = existingMap.get(post.uri); 338 - if (ex) { 339 - toPut.push({ ...post, sources: [...ex.sources, source], savedAt: ex.savedAt, fetchedAt: now }); 340 - } else { 341 - toPut.push({ ...post, sources: [source], savedAt: now, fetchedAt: now }); 342 - } 343 - s.index!.add(post as any); 344 - s.indexed++; 345 - s.count++; 346 - } 347 - 348 - await db.posts.bulkPut(toPut); 332 + if (extracted.length > 0) { 333 + const count = await bulkUpsertPosts(db, extracted, accountDid, source); 334 + s.indexed += count; 335 + s.count += count; 349 336 } 350 337 351 338 s.pendingIndex += chunk.reduce((sum, b) => sum + b.length, 0); 352 - await s.index!.commit(); 353 339 } 354 340 355 341 s.phase = 'done'; ··· 359 345 async function fetchBookmarkPage( 360 346 source: SourceType, 361 347 s: SourceState, 362 - getBookmarks: Function, 348 + // eslint-disable-next-line @typescript-eslint/no-explicit-any 349 + getBookmarksFn: Function, 363 350 cursor: string | undefined, 364 351 myGen: number 365 352 ): Promise<{ cursor: string | undefined; done: boolean }> { 366 353 if (myGen !== generation) return { cursor, done: true }; 367 354 355 + const accountDid = user.did; 356 + if (!accountDid) return { cursor, done: true }; 357 + 358 + // eslint-disable-next-line @typescript-eslint/no-explicit-any 368 359 let data: any; 369 360 try { 370 - data = await getBookmarks({ limit: 100, cursor }); 371 - } catch (err: any) { 361 + data = await getBookmarksFn({ limit: 100, cursor }); 362 + } catch (err: unknown) { 372 363 console.error('Failed to fetch bookmarks:', err); 373 - toast.error(`Failed to fetch bookmarks: ${err?.body?.message ?? err?.message ?? err}`); 364 + const message = 365 + err && typeof err === 'object' && 'body' in err 366 + ? (err as { body?: { message?: string } }).body?.message 367 + : err instanceof Error 368 + ? err.message 369 + : String(err); 370 + toast.error(`Failed to fetch bookmarks: ${message}`); 374 371 return { cursor, done: true }; 375 372 } 376 373 374 + const db = await getDb(); 375 + // eslint-disable-next-line @typescript-eslint/no-explicit-any 377 376 const validBookmarks = data.bookmarks.filter((b: any) => b.item?.uri); 378 - const uris = validBookmarks.map((b: any) => b.item.uri); 379 - const existingDocs = await db.posts.bulkGet(uris); 380 - const toPut: any[] = []; 381 377 const now = Date.now(); 378 + const toInsert: ExtractedPost[] = []; 379 + const toTag: string[] = []; 382 380 383 - for (let j = 0; j < validBookmarks.length; j++) { 384 - const bookmark = validBookmarks[j]; 385 - const postUri = uris[j]; 386 - const existing = existingDocs[j]; 381 + for (const bookmark of validBookmarks) { 382 + const postUri = bookmark.item.uri; 387 383 388 - if (existing) { 389 - if (existing.sources.includes(source)) { 390 - // Already indexed for this source — flush and stop 391 - if (toPut.length > 0) await db.posts.bulkPut(toPut); 392 - return { cursor: data.cursor ?? cursor, done: true }; 384 + if (await postExistsForSource(db, postUri, accountDid, source)) { 385 + // Already indexed — flush and stop 386 + if (toInsert.length > 0) { 387 + const count = await bulkUpsertPosts(db, toInsert, accountDid, source); 388 + s.count += count; 389 + s.indexed += count; 393 390 } 394 - // Exists from another source — queue tag update 395 - toPut.push({ ...existing, sources: [...existing.sources, source], fetchedAt: now }); 396 - s.index!.add(existing as any); 397 - s.count++; 398 - continue; 391 + if (toTag.length > 0) { 392 + s.count += await tagExistingPosts(db, toTag, accountDid, source); 393 + } 394 + return { cursor: data.cursor ?? cursor, done: true }; 399 395 } 400 396 401 - // Bookmarks come with full PostView — queue for batch write 402 - toPut.push({ ...bookmark.item, sources: [source], savedAt: now, fetchedAt: now }); 403 - s.index!.add(bookmark.item as any); 404 - s.indexed++; 405 - s.count++; 397 + // Check if post exists from another source 398 + if (await postExists(db, postUri)) { 399 + toTag.push(postUri); 400 + } else { 401 + // Bookmarks come with full PostView 402 + toInsert.push(extractPostFields(bookmark.item, now, now)); 403 + } 406 404 } 407 405 408 - if (toPut.length > 0) await db.posts.bulkPut(toPut); 406 + if (toInsert.length > 0) { 407 + const count = await bulkUpsertPosts(db, toInsert, accountDid, source); 408 + s.count += count; 409 + s.indexed += count; 410 + } 411 + if (toTag.length > 0) { 412 + s.count += await tagExistingPosts(db, toTag, accountDid, source); 413 + } 409 414 410 415 const nextCursor = data.bookmarks.length > 0 ? data.cursor : undefined; 411 - // Detect stalled cursor — if the API returns the same cursor we sent, stop. 412 416 if (nextCursor && nextCursor === cursor) { 413 417 return { cursor: nextCursor, done: true }; 414 418 } ··· 416 420 } 417 421 418 422 async function loadBookmarks(source: SourceType, myGen: number) { 419 - if (!user.did || !searchState.sources[source].index) return; 423 + if (!user.did) return; 420 424 421 425 const { getBookmarks } = await import('$lib/atproto/server/search.remote'); 422 426 423 427 const s = searchState.sources[source]; 428 + const db = await getDb(); 429 + const accountDid = user.did; 424 430 s.phase = 'fetching'; 425 431 426 - const meta = await db.meta.get(source); 432 + const tailCursor = await getCursor(db, accountDid, source); 427 433 428 - // Step 1: Fetch new bookmarks from the top until we hit one we already have 434 + // Step 1: Fetch new bookmarks from the top 429 435 let result = { cursor: undefined as string | undefined, done: false }; 430 436 do { 431 437 result = await fetchBookmarkPage(source, s, getBookmarks, result.cursor, myGen); 432 - await s.index!.commit(); 433 438 } while (!result.done && myGen === generation); 434 439 435 440 if (myGen !== generation) return; 436 441 437 - // Step 2: Continue from where we left off last time (tail) 438 - if (meta?.tailCursor) { 439 - result = { cursor: meta.tailCursor, done: false }; 442 + // Step 2: Continue from tail cursor 443 + if (tailCursor) { 444 + result = { cursor: tailCursor, done: false }; 440 445 do { 441 446 result = await fetchBookmarkPage(source, s, getBookmarks, result.cursor, myGen); 442 - await s.index!.commit(); 443 447 } while (!result.done && myGen === generation); 444 448 445 449 if (myGen !== generation) return; 446 450 } 447 451 448 - // Save how far we got into the past 449 - if (result.cursor) { 450 - await db.meta.put({ source, tailCursor: result.cursor }); 451 - } else { 452 - await db.meta.put({ source, tailCursor: undefined }); 453 - } 452 + await setCursor(db, accountDid, source, result.cursor); 454 453 455 - await s.index!.commit(); 456 454 s.phase = 'done'; 457 455 loadNext(myGen); 458 456 } 459 457 460 - // --- Filter helpers --- 461 - 462 - function hasEmbedType(doc: any, type: string): boolean { 463 - const embed = doc.embed; 464 - if (!embed) return false; 465 - if (embed.$type === type) return true; 466 - if (embed.$type === 'app.bsky.embed.recordWithMedia#view') { 467 - return embed.media?.$type === type; 468 - } 469 - return false; 470 - } 471 - 472 - function hasLinkInFacets(doc: any): boolean { 473 - const facets = doc.record?.facets; 474 - if (!Array.isArray(facets)) return false; 475 - return facets.some( 476 - (f: any) => f.features?.some((feat: any) => feat.$type === 'app.bsky.richtext.facet#link') 477 - ); 478 - } 479 - 480 - function applyFilters(results: any[], filters: SearchFilters): any[] { 481 - let r = results; 482 - if (filters.handles.length > 0) { 483 - r = r.filter((item) => { 484 - const h = item.doc.author?.handle?.toLowerCase(); 485 - return h && filters.handles.some((fh) => h.includes(fh.toLowerCase())); 486 - }); 487 - } 488 - if (filters.minLikes > 0) { 489 - r = r.filter((item) => (item.doc.likeCount ?? 0) >= filters.minLikes); 490 - } 491 - if (filters.minReposts > 0) { 492 - r = r.filter((item) => (item.doc.repostCount ?? 0) >= filters.minReposts); 493 - } 494 - if (filters.minReplies > 0) { 495 - r = r.filter((item) => (item.doc.replyCount ?? 0) >= filters.minReplies); 496 - } 497 - if (filters.dateAfter) { 498 - const after = new Date(filters.dateAfter).getTime(); 499 - r = r.filter((item) => { 500 - const created = item.doc.record?.createdAt; 501 - return created && new Date(created).getTime() >= after; 502 - }); 503 - } 504 - if (filters.dateBefore) { 505 - const before = new Date(filters.dateBefore).getTime() + 86400000; 506 - r = r.filter((item) => { 507 - const created = item.doc.record?.createdAt; 508 - return created && new Date(created).getTime() <= before; 509 - }); 510 - } 511 - if (filters.hasImage) { 512 - r = r.filter((item) => hasEmbedType(item.doc, 'app.bsky.embed.images#view')); 513 - } 514 - if (filters.hasLink) { 515 - r = r.filter( 516 - (item) => 517 - hasEmbedType(item.doc, 'app.bsky.embed.external#view') || hasLinkInFacets(item.doc) 518 - ); 519 - } 520 - if (filters.hasVideo) { 521 - r = r.filter((item) => hasEmbedType(item.doc, 'app.bsky.embed.video#view')); 522 - } 523 - if (!filters.showReplies) { 524 - r = r.filter((item) => !item.doc.record?.reply); 525 - } 526 - return r; 527 - } 458 + // --- Public search API --- 528 459 529 - export function filtersActive(filters: SearchFilters): boolean { 530 - return ( 531 - filters.handles.length > 0 || 532 - filters.minLikes > 0 || 533 - filters.minReposts > 0 || 534 - filters.minReplies > 0 || 535 - !!filters.dateAfter || 536 - !!filters.dateBefore || 537 - filters.hasImage || 538 - filters.hasLink || 539 - filters.hasVideo || 540 - !filters.showReplies 541 - ); 542 - } 543 - 544 - function sortByDate(items: any[]): any[] { 545 - return items.sort((a, b) => { 546 - const dateA = a.doc.record?.createdAt ? new Date(a.doc.record.createdAt).getTime() : 0; 547 - const dateB = b.doc.record?.createdAt ? new Date(b.doc.record.createdAt).getTime() : 0; 548 - return dateB - dateA; 549 - }); 550 - } 460 + export { filtersActive } from '$lib/db'; 551 461 552 462 export async function searchIndex( 553 463 query: string, 554 464 filters: SearchFilters = DEFAULT_FILTERS, 555 - limit: number = 50 556 - ): Promise<{ results: any[]; hasMore: boolean }> { 557 - const source = searchState.activeSource; 558 - const hasFilters = filtersActive(filters); 465 + sources: SourceType[] = searchState.activeSources, 466 + limit: number = 50, 467 + offset: number = 0 468 + ): Promise<{ 469 + results: { doc: unknown; isLiked: boolean; isBookmarked: boolean }[]; 470 + hasMore: boolean; 471 + }> { 472 + const accountDid = user.did; 473 + if (!accountDid) return { results: [], hasMore: false }; 559 474 560 - let docs: any[]; 475 + const db = await getDb(); 561 476 562 - if (query) { 563 - // Text search via FlexSearch → get URIs → lookup in Dexie 564 - const idx = searchState.sources[source].index; 565 - if (!idx) return { results: [], hasMore: false }; 477 + // Get all account DIDs for cross-account search (for now, just current account) 478 + const accountDids = [accountDid]; 566 479 567 - const raw = await idx.search({ 568 - query, 569 - merge: true, 570 - limit: hasFilters ? 5000 : limit + 1 571 - }); 480 + return searchPosts(db, { 481 + query, 482 + filters, 483 + sources, 484 + accountDids, 485 + limit, 486 + offset 487 + }); 488 + } 572 489 573 - const uris = raw.map((r: any) => r.id); 574 - docs = (await db.posts.bulkGet(uris)).filter(Boolean) as any[]; 575 - } else { 576 - // No query: get all docs for this source from Dexie 577 - docs = await db.posts.where('sources').equals(source).toArray(); 578 - } 490 + // --- Clear operations --- 579 491 580 - let wrapped = docs.map((doc) => ({ doc })); 581 - if (hasFilters) wrapped = applyFilters(wrapped, filters); 582 - if (!query) wrapped = sortByDate(wrapped); 492 + export async function clearSource(source: SourceType) { 493 + const accountDid = user.did; 494 + if (!accountDid) return; 583 495 584 - const hasMore = wrapped.length > limit; 585 - return { results: wrapped.slice(0, limit), hasMore }; 586 - } 496 + const db = await getDb(); 497 + await dbClearSource(db, accountDid, source); 587 498 588 - export async function clearSource(source: SourceType) { 589 - // Remove source tag from all posts, delete orphans 590 - const posts = await db.posts.where('sources').equals(source).toArray(); 591 - await db.transaction('rw', db.posts, async () => { 592 - for (const post of posts) { 593 - const newSources = post.sources.filter((s) => s !== source); 594 - if (newSources.length === 0) { 595 - await db.posts.delete(post.uri); 596 - } else { 597 - await db.posts.update(post.uri, { sources: newSources }); 598 - } 599 - } 600 - }); 601 - await db.meta.delete(source); 602 - 603 - searchState.sources[source].index?.clear(); 604 499 searchState.sources[source].count = 0; 605 500 searchState.sources[source].indexed = 0; 606 501 searchState.sources[source].totalToIndex = 0; ··· 609 504 searchState.sources[source].pendingIndex = 0; 610 505 } 611 506 612 - export function getLink(uri: string, handle: string) { 613 - const [, , rkey] = uri.replace('at://', '').split('/'); 614 - return `https://bsky.app/profile/${handle}/post/${rkey}`; 507 + export async function clearAll() { 508 + const db = await getDb(); 509 + await dbClearAllData(db); 510 + 511 + for (const source of ALL_SOURCES) { 512 + searchState.sources[source].count = 0; 513 + searchState.sources[source].indexed = 0; 514 + searchState.sources[source].totalToIndex = 0; 515 + searchState.sources[source].phase = 'idle'; 516 + searchState.sources[source].pendingUris = []; 517 + searchState.sources[source].pendingIndex = 0; 518 + } 519 + } 520 + 521 + export async function clearAccount(accountDid: string) { 522 + const db = await getDb(); 523 + await dbClearAccountData(db, accountDid); 524 + 525 + // If clearing current account, reset state 526 + if (accountDid === user.did) { 527 + for (const source of ALL_SOURCES) { 528 + searchState.sources[source].count = 0; 529 + searchState.sources[source].indexed = 0; 530 + searchState.sources[source].totalToIndex = 0; 531 + searchState.sources[source].phase = 'idle'; 532 + searchState.sources[source].pendingUris = []; 533 + searchState.sources[source].pendingIndex = 0; 534 + } 535 + } 536 + } 537 + 538 + export async function listAccounts(): Promise< 539 + { did: string; handle: string; display_name: string | null }[] 540 + > { 541 + const db = await getDb(); 542 + return dbGetAccounts(db); 615 543 }
+75 -38
src/routes/search/Search.svelte
··· 1 1 <script lang="ts"> 2 2 import { Input, Navbar } from '@foxui/core'; 3 - import { AtprotoHandlePopup, BlueskyPost, blueskyPostToPostData, Post } from '@foxui/social'; 3 + import { AtprotoHandlePopup, BlueskyPost } from '@foxui/social'; 4 4 import { onMount } from 'svelte'; 5 5 import { 6 6 searchState, 7 7 initSources, 8 - switchSource, 8 + toggleSource, 9 + setActiveSources, 9 10 searchIndex, 10 - filtersActive, 11 - DEFAULT_FILTERS, 12 11 SOURCE_LABELS, 13 - PLACEHOLDERS, 12 + getPlaceholder, 14 13 ALL_SOURCES, 14 + DEFAULT_FILTERS, 15 15 type SourceType, 16 16 type SearchFilters 17 17 } from '$lib/search-state.svelte'; 18 + import { filtersActive } from '$lib/db'; 18 19 19 20 let input: HTMLInputElement | null = $state(null); 20 21 let search = $state(''); 21 - let results: any[] = $state([]); 22 + let results: { doc: any; isLiked: boolean; isBookmarked: boolean }[] = $state([]); 22 23 let hasMore = $state(false); 23 - let limit = $state(50); 24 + let offset = $state(0); 24 25 let sentinel: HTMLElement | null = $state(null); 25 26 let scrollY = $state(0); 26 27 ··· 29 30 let handleInput = $state(''); 30 31 let hasActiveFilters = $derived(filtersActive(filters)); 31 32 33 + const PAGE_SIZE = 50; 34 + 32 35 onMount(() => { 33 36 initSources(); 34 37 }); 35 38 36 - function handleSwitchSource(source: SourceType) { 37 - switchSource(source); 38 - } 39 - 40 - // Reset limit when search params change 39 + // Reset offset when search params change 41 40 $effect(() => { 42 - searchState.activeSource; 41 + searchState.activeSources; 43 42 $state.snapshot(filters); 44 43 search; 45 - limit = 50; 44 + offset = 0; 45 + results = []; 46 46 }); 47 47 48 48 // Fetch results 49 49 $effect(() => { 50 - const _source = searchState.activeSource; 50 + const _sources = searchState.activeSources; 51 51 const _filters = $state.snapshot(filters); 52 - const _count = searchState.sources[_source].count; 53 - const _limit = limit; 52 + const _offset = offset; 53 + // Track counts of active sources so we re-query as data loads in 54 + for (const s of _sources) { 55 + searchState.sources[s].count; 56 + } 54 57 55 - searchIndex(search, _filters, _limit).then((res) => { 56 - results = res.results; 58 + searchIndex(search, _filters, _sources, PAGE_SIZE, _offset).then((res) => { 59 + if (_offset === 0) { 60 + results = res.results; 61 + } else { 62 + results = [...results, ...res.results]; 63 + } 57 64 hasMore = res.hasMore; 58 65 }); 59 66 }); ··· 66 73 const observer = new IntersectionObserver( 67 74 (entries) => { 68 75 if (entries[0].isIntersecting && hasMore) { 69 - limit += 50; 76 + offset += PAGE_SIZE; 70 77 } 71 78 }, 72 79 { rootMargin: '400px' } ··· 82 89 ? 'bg-accent-600 text-white' 83 90 : 'text-base-600 dark:text-base-400 hover:bg-base-200 dark:hover:bg-base-800' 84 91 }`; 92 + 93 + const allSourcesActive = $derived(searchState.activeSources.length === ALL_SOURCES.length); 94 + 95 + // Check if only "posts" source is selected (hide handle filter for own posts) 96 + const onlyPosts = $derived( 97 + searchState.activeSources.length === 1 && searchState.activeSources[0] === 'posts' 98 + ); 85 99 </script> 86 100 87 101 <svelte:window ··· 112 126 <div class="flex gap-1"> 113 127 {#each ALL_SOURCES as source} 114 128 <button 115 - class="cursor-pointer rounded-full px-3 py-1 text-sm font-medium transition-colors {searchState.activeSource === 116 - source 129 + class="cursor-pointer rounded-full px-3 py-1 text-sm font-medium transition-colors {searchState.activeSources.includes( 130 + source 131 + ) 117 132 ? 'bg-accent-600 text-white' 118 133 : 'text-base-600 dark:text-base-400 hover:bg-base-200 dark:hover:bg-base-800'}" 119 - onclick={() => handleSwitchSource(source)} 134 + onclick={() => toggleSource(source)} 120 135 > 121 136 {SOURCE_LABELS[source]} 122 137 </button> 123 138 {/each} 139 + <button 140 + class="cursor-pointer rounded-full px-3 py-1 text-sm font-medium transition-colors {allSourcesActive 141 + ? 'bg-accent-600 text-white' 142 + : 'text-base-600 dark:text-base-400 hover:bg-base-200 dark:hover:bg-base-800'}" 143 + onclick={() => setActiveSources([...ALL_SOURCES])} 144 + > 145 + All 146 + </button> 124 147 </div> 125 148 </div> 126 149 ··· 130 153 bind:value={search} 131 154 class="w-full" 132 155 sizeVariant="lg" 133 - placeholder={PLACEHOLDERS[searchState.activeSource]} 156 + placeholder={getPlaceholder(searchState.activeSources)} 134 157 /> 135 158 <button 136 159 class="text-base-500 dark:text-base-400 hover:text-accent-600 dark:hover:text-accent-400 relative cursor-pointer p-2 transition-colors" ··· 157 180 158 181 {#if showFilters} 159 182 <div class="mt-3 flex w-full flex-col gap-3 px-2"> 160 - {#if searchState.activeSource !== 'posts'} 183 + {#if !onlyPosts} 161 184 <div class="flex flex-col gap-1.5"> 162 185 <div class="flex items-center gap-2"> 163 186 <label class="text-base-500 dark:text-base-400 w-16 shrink-0 text-xs">Handle</label> ··· 284 307 </div> 285 308 {/if} 286 309 287 - {@const src = searchState.sources[searchState.activeSource]} 288 310 <span class="text-base-600 dark:text-base-400 mx-4 mt-2 text-xs"> 289 - {#if src.phase === 'idle'} 290 - loading... 291 - {:else if src.phase === 'fetching'} 292 - fetching {SOURCE_LABELS[searchState.activeSource].toLowerCase()}... ({src.count + 293 - src.pendingUris.length} found) 294 - {:else if src.phase === 'hydrating'} 295 - indexing {src.indexed}/{src.totalToIndex} 296 - {SOURCE_LABELS[searchState.activeSource].toLowerCase()}... ({src.count} searchable) 311 + {#if searchState.activeSources.length === 1} 312 + {@const src = searchState.sources[searchState.activeSources[0]]} 313 + {#if src.phase === 'idle'} 314 + loading... 315 + {:else if src.phase === 'fetching'} 316 + fetching {SOURCE_LABELS[searchState.activeSources[0]].toLowerCase()}... ({src.count + 317 + src.pendingUris.length} found) 318 + {:else if src.phase === 'hydrating'} 319 + indexing {src.indexed}/{src.totalToIndex} 320 + {SOURCE_LABELS[searchState.activeSources[0]].toLowerCase()}... ({src.count} searchable) 321 + {:else} 322 + {SOURCE_LABELS[searchState.activeSources[0]].toLowerCase()}: {src.count}{#if results.length > 0}, 323 + {results.length} 324 + results{/if} 325 + {/if} 297 326 {:else} 298 - {SOURCE_LABELS[searchState.activeSource].toLowerCase()}: {src.count}{#if results.length > 0}, {results.length} 299 - results{/if} 327 + {@const totalCount = searchState.activeSources.reduce( 328 + (sum, s) => sum + searchState.sources[s].count, 329 + 0 330 + )} 331 + {@const allDone = searchState.activeSources.every((s) => searchState.sources[s].phase === 'done')} 332 + {#if allDone} 333 + {totalCount} posts{#if results.length > 0}, {results.length} results{/if} 334 + {:else} 335 + loading... ({totalCount} found) 336 + {/if} 300 337 {/if} 301 338 </span> 302 339 </Navbar> ··· 310 347 {#each results as result (result.doc.uri)} 311 348 <li class="py-4"> 312 349 <BlueskyPost 313 - liked={result.doc.sources?.includes('likes')} 314 - bookmarked={result.doc.sources?.includes('bookmarks')} 350 + liked={result.isLiked} 351 + bookmarked={result.isBookmarked} 315 352 showLogo 316 353 feedViewPost={result.doc} 317 354 />
+37 -9
src/routes/settings/+page.svelte
··· 1 1 <script lang="ts"> 2 2 import { user, logout } from '$lib/atproto'; 3 3 import { Button, Heading, toast } from '@foxui/core'; 4 - import { clearSource, ALL_SOURCES } from '$lib/search-state.svelte'; 4 + import { clearAll, clearAccount, listAccounts } from '$lib/search-state.svelte'; 5 5 import { goto } from '$app/navigation'; 6 + import { onMount } from 'svelte'; 7 + 8 + let accounts: { did: string; handle: string; display_name: string | null }[] = $state([]); 9 + 10 + onMount(async () => { 11 + accounts = await listAccounts(); 12 + }); 6 13 </script> 7 14 8 15 {#if user.isLoggedIn} ··· 21 28 <p class="text-base-600 dark:text-base-400 text-sm">Signed in as <span class="text-base-800 dark:text-base-200 font-medium">@{user.profile?.handle}</span></p> 22 29 </div> 23 30 31 + {#if accounts.length > 0} 32 + <div class="border-base-300 dark:border-base-800 flex flex-col gap-3 border-t pt-4"> 33 + <p class="text-base-600 dark:text-base-400 text-xs font-medium uppercase tracking-wide">Cached accounts</p> 34 + {#each accounts as account} 35 + <div class="flex items-center justify-between"> 36 + <div class="flex items-center gap-2"> 37 + <span class="text-base-800 dark:text-base-200 text-sm">@{account.handle}</span> 38 + {#if account.did === user.did} 39 + <span class="text-base-500 dark:text-base-400 text-xs">(current)</span> 40 + {/if} 41 + </div> 42 + <Button 43 + variant="ghost" 44 + size="sm" 45 + onclick={async () => { 46 + await clearAccount(account.did); 47 + accounts = accounts.filter((a) => a.did !== account.did); 48 + toast.success(`Cleared data for @${account.handle}`); 49 + }} 50 + > 51 + Clear data 52 + </Button> 53 + </div> 54 + {/each} 55 + </div> 56 + {/if} 57 + 24 58 <div class="border-base-300 dark:border-base-800 flex flex-col gap-3 border-t pt-4"> 25 59 <Button 26 60 variant="ghost" 27 61 onclick={async () => { 28 - for (const source of ALL_SOURCES) { 29 - await clearSource(source); 30 - } 31 - const dbs = await indexedDB.databases(); 32 - for (const dbInfo of dbs) { 33 - if (dbInfo.name) indexedDB.deleteDatabase(dbInfo.name); 34 - } 62 + await clearAll(); 63 + accounts = []; 35 64 toast.success('All data cleared'); 36 - setTimeout(() => location.reload(), 500); 37 65 }} 38 66 > 39 67 Reset all data
+18 -2
vite.config.ts
··· 1 1 import tailwindcss from '@tailwindcss/vite'; 2 2 import { sveltekit } from '@sveltejs/kit/vite'; 3 - import { defineConfig } from 'vite'; 3 + import { defineConfig, type Plugin } from 'vite'; 4 4 import { DEV_PORT } from './src/lib/atproto/port'; 5 5 6 + function coopCoepHeaders(): Plugin { 7 + return { 8 + name: 'coop-coep-headers', 9 + configureServer(server) { 10 + server.middlewares.use((_req, res, next) => { 11 + res.setHeader('Cross-Origin-Opener-Policy', 'same-origin'); 12 + res.setHeader('Cross-Origin-Embedder-Policy', 'credentialless'); 13 + next(); 14 + }); 15 + } 16 + }; 17 + } 18 + 6 19 export default defineConfig({ 7 - plugins: [sveltekit(), tailwindcss()], 20 + plugins: [sveltekit(), tailwindcss(), coopCoepHeaders()], 8 21 server: { 9 22 host: '127.0.0.1', 10 23 port: DEV_PORT 24 + }, 25 + optimizeDeps: { 26 + exclude: ['@tursodatabase/database-wasm'] 11 27 } 12 28 });