diff --git a/package.json b/package.json index 5b8b346..7da6ae8 100644 --- a/package.json +++ b/package.json @@ -51,8 +51,7 @@ "dependencies": { "@foxui/core": "^0.5.2", "@foxui/social": "^0.5.4", - "dexie": "^4.3.0", - "flexsearch": "^0.8.212", + "@tursodatabase/database-wasm": "^0.5.0", "valibot": "^1.2.0", "wrangler": "^4.69.0" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e271126..e21d76c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -14,12 +14,9 @@ importers: '@foxui/social': specifier: ^0.5.4 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) - dexie: - specifier: ^4.3.0 - version: 4.3.0 - flexsearch: - specifier: ^0.8.212 - version: 0.8.212 + '@tursodatabase/database-wasm': + specifier: ^0.5.0 + version: 0.5.0 valibot: specifier: ^1.2.0 version: 1.2.0(typescript@5.9.3) @@ -121,7 +118,7 @@ packages: resolution: {integrity: sha512-+GKZpOc0PJcdWMQEkTfg/rSNDAAHxmAUGBl60g2az15etqJn5WaUPNGFE2sB7hKpwi5Ue2h/L0OacINcE/JDDQ==} '@atcute/bluesky-richtext-segmenter@3.0.0': - resolution: {integrity: sha512-NhZTUKtFpeBBbILwAcxj5u4RobIoHOmGw3CAaaEFNebKYSvmTecrXJ7XufHw5DFOUdr8SiKXQVRQxGAxulMNWg==, tarball: https://registry.npmjs.org/@atcute/bluesky-richtext-segmenter/-/bluesky-richtext-segmenter-3.0.0.tgz} + resolution: {integrity: sha512-NhZTUKtFpeBBbILwAcxj5u4RobIoHOmGw3CAaaEFNebKYSvmTecrXJ7XufHw5DFOUdr8SiKXQVRQxGAxulMNWg==} '@atcute/bluesky@3.2.19': resolution: {integrity: sha512-W/Sy2671A5eKkdWJn6nfCGoixqMFOouEKRbqRV3630I13Er9iS+gA/rGWUXlp5xNpbfCNmsQ6C1pEidUmO+00g==} @@ -175,11 +172,11 @@ packages: engines: {node: '>= 18'} '@cloudflare/kv-asset-handler@0.4.2': - resolution: {integrity: sha512-SIOD2DxrRRwQ+jgzlXCqoEFiKOFqaPjhnNTGKXSRLvp1HiOvapLaFG2kEr9dYQTYe8rKrd9uvDUzmAITeNyaHQ==, tarball: https://registry.npmjs.org/@cloudflare/kv-asset-handler/-/kv-asset-handler-0.4.2.tgz} + resolution: {integrity: sha512-SIOD2DxrRRwQ+jgzlXCqoEFiKOFqaPjhnNTGKXSRLvp1HiOvapLaFG2kEr9dYQTYe8rKrd9uvDUzmAITeNyaHQ==} engines: {node: '>=18.0.0'} '@cloudflare/unenv-preset@2.14.0': - resolution: {integrity: sha512-XKAkWhi1nBdNsSEoNG9nkcbyvfUrSjSf+VYVPfOto3gLTZVc3F4g6RASCMh6IixBKCG2yDgZKQIHGKtjcnLnKg==, tarball: https://registry.npmjs.org/@cloudflare/unenv-preset/-/unenv-preset-2.14.0.tgz} + resolution: {integrity: sha512-XKAkWhi1nBdNsSEoNG9nkcbyvfUrSjSf+VYVPfOto3gLTZVc3F4g6RASCMh6IixBKCG2yDgZKQIHGKtjcnLnKg==} peerDependencies: unenv: 2.0.0-rc.24 workerd: ^1.20260218.0 @@ -188,31 +185,31 @@ packages: optional: true '@cloudflare/workerd-darwin-64@1.20260305.0': - resolution: {integrity: sha512-chhKOpymo0Eh9J3nymrauMqKGboCc4uz/j0gA1G4gioMnKsN2ZDKJ+qjRZDnCoVGy8u2C4pxlmyIfsXCAfIzhQ==, tarball: https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20260305.0.tgz} + resolution: {integrity: sha512-chhKOpymo0Eh9J3nymrauMqKGboCc4uz/j0gA1G4gioMnKsN2ZDKJ+qjRZDnCoVGy8u2C4pxlmyIfsXCAfIzhQ==} engines: {node: '>=16'} cpu: [x64] os: [darwin] '@cloudflare/workerd-darwin-arm64@1.20260305.0': - resolution: {integrity: sha512-K9aG2OQk5bBfOP+fyGPqLcqZ9OR3ra6uwnxJ8f2mveq2A2LsCI7ZeGxQiAj75Ti80ytH/gJffZIx4Np2JtU3aQ==, tarball: https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20260305.0.tgz} + resolution: {integrity: sha512-K9aG2OQk5bBfOP+fyGPqLcqZ9OR3ra6uwnxJ8f2mveq2A2LsCI7ZeGxQiAj75Ti80ytH/gJffZIx4Np2JtU3aQ==} engines: {node: '>=16'} cpu: [arm64] os: [darwin] '@cloudflare/workerd-linux-64@1.20260305.0': - 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} + resolution: {integrity: sha512-tt7XUoIw/cYFeGbkPkcZ6XX1aZm26Aju/4ih+DXxOosbBeGshFSrNJDBfAKKOvkjsAZymJ+WWVDBU+hmNaGfwA==} engines: {node: '>=16'} cpu: [x64] os: [linux] '@cloudflare/workerd-linux-arm64@1.20260305.0': - resolution: {integrity: sha512-72QTkY5EzylmvCZ8ZTrnJ9DctmQsfSof1OKyOWqu/pv/B2yACfuPMikq8RpPxvVu7hhS0ztGP6ZvXz72Htq4Zg==, tarball: https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20260305.0.tgz} + resolution: {integrity: sha512-72QTkY5EzylmvCZ8ZTrnJ9DctmQsfSof1OKyOWqu/pv/B2yACfuPMikq8RpPxvVu7hhS0ztGP6ZvXz72Htq4Zg==} engines: {node: '>=16'} cpu: [arm64] os: [linux] '@cloudflare/workerd-windows-64@1.20260305.0': - resolution: {integrity: sha512-BA0uaQPOaI2F6mJtBDqplGnQQhpXCzwEMI33p/TnDxtSk9u8CGIfBFuI6uqo8mJ6ijIaPjeBLGOn2CiRMET4qg==, tarball: https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20260305.0.tgz} + resolution: {integrity: sha512-BA0uaQPOaI2F6mJtBDqplGnQQhpXCzwEMI33p/TnDxtSk9u8CGIfBFuI6uqo8mJ6ijIaPjeBLGOn2CiRMET4qg==} engines: {node: '>=16'} cpu: [x64] os: [win32] @@ -221,164 +218,170 @@ packages: resolution: {integrity: sha512-klKnECMb5A4GtVF0P5NH6rCjtyjqIEKJaz6kEtx9YPHhfFO2HUEarO+MI4F8WPchgeZqpGlEpDhRapzrOTw51Q==} '@cspotcode/source-map-support@0.8.1': - resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==, tarball: https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz} + resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} engines: {node: '>=12'} + '@emnapi/core@1.8.1': + resolution: {integrity: sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==} + '@emnapi/runtime@1.8.1': - resolution: {integrity: sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==, tarball: https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.8.1.tgz} + resolution: {integrity: sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==} + + '@emnapi/wasi-threads@1.1.0': + resolution: {integrity: sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==} '@esbuild/aix-ppc64@0.27.3': - resolution: {integrity: sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==, tarball: https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz} + resolution: {integrity: sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] '@esbuild/android-arm64@0.27.3': - resolution: {integrity: sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==, tarball: https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz} + resolution: {integrity: sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==} engines: {node: '>=18'} cpu: [arm64] os: [android] '@esbuild/android-arm@0.27.3': - resolution: {integrity: sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==, tarball: https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz} + resolution: {integrity: sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==} engines: {node: '>=18'} cpu: [arm] os: [android] '@esbuild/android-x64@0.27.3': - resolution: {integrity: sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==, tarball: https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz} + resolution: {integrity: sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==} engines: {node: '>=18'} cpu: [x64] os: [android] '@esbuild/darwin-arm64@0.27.3': - resolution: {integrity: sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==, tarball: https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz} + resolution: {integrity: sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] '@esbuild/darwin-x64@0.27.3': - resolution: {integrity: sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==, tarball: https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz} + resolution: {integrity: sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==} engines: {node: '>=18'} cpu: [x64] os: [darwin] '@esbuild/freebsd-arm64@0.27.3': - resolution: {integrity: sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==, tarball: https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz} + resolution: {integrity: sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] '@esbuild/freebsd-x64@0.27.3': - resolution: {integrity: sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==, tarball: https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz} + resolution: {integrity: sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] '@esbuild/linux-arm64@0.27.3': - resolution: {integrity: sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==, tarball: https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz} + resolution: {integrity: sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==} engines: {node: '>=18'} cpu: [arm64] os: [linux] '@esbuild/linux-arm@0.27.3': - resolution: {integrity: sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==, tarball: https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz} + resolution: {integrity: sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==} engines: {node: '>=18'} cpu: [arm] os: [linux] '@esbuild/linux-ia32@0.27.3': - resolution: {integrity: sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==, tarball: https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz} + resolution: {integrity: sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==} engines: {node: '>=18'} cpu: [ia32] os: [linux] '@esbuild/linux-loong64@0.27.3': - resolution: {integrity: sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==, tarball: https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz} + resolution: {integrity: sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==} engines: {node: '>=18'} cpu: [loong64] os: [linux] '@esbuild/linux-mips64el@0.27.3': - resolution: {integrity: sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==, tarball: https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz} + resolution: {integrity: sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] '@esbuild/linux-ppc64@0.27.3': - resolution: {integrity: sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==, tarball: https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz} + resolution: {integrity: sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] '@esbuild/linux-riscv64@0.27.3': - resolution: {integrity: sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==, tarball: https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz} + resolution: {integrity: sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] '@esbuild/linux-s390x@0.27.3': - resolution: {integrity: sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==, tarball: https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz} + resolution: {integrity: sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==} engines: {node: '>=18'} cpu: [s390x] os: [linux] '@esbuild/linux-x64@0.27.3': - resolution: {integrity: sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==, tarball: https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz} + resolution: {integrity: sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==} engines: {node: '>=18'} cpu: [x64] os: [linux] '@esbuild/netbsd-arm64@0.27.3': - resolution: {integrity: sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==, tarball: https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz} + resolution: {integrity: sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] '@esbuild/netbsd-x64@0.27.3': - resolution: {integrity: sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==, tarball: https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz} + resolution: {integrity: sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] '@esbuild/openbsd-arm64@0.27.3': - resolution: {integrity: sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==, tarball: https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz} + resolution: {integrity: sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] '@esbuild/openbsd-x64@0.27.3': - resolution: {integrity: sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==, tarball: https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz} + resolution: {integrity: sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] '@esbuild/openharmony-arm64@0.27.3': - resolution: {integrity: sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==, tarball: https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz} + resolution: {integrity: sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==} engines: {node: '>=18'} cpu: [arm64] os: [openharmony] '@esbuild/sunos-x64@0.27.3': - resolution: {integrity: sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==, tarball: https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz} + resolution: {integrity: sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==} engines: {node: '>=18'} cpu: [x64] os: [sunos] '@esbuild/win32-arm64@0.27.3': - resolution: {integrity: sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==, tarball: https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz} + resolution: {integrity: sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==} engines: {node: '>=18'} cpu: [arm64] os: [win32] '@esbuild/win32-ia32@0.27.3': - resolution: {integrity: sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==, tarball: https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz} + resolution: {integrity: sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==} engines: {node: '>=18'} cpu: [ia32] os: [win32] '@esbuild/win32-x64@0.27.3': - resolution: {integrity: sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==, tarball: https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz} + resolution: {integrity: sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==} engines: {node: '>=18'} cpu: [x64] os: [win32] @@ -441,19 +444,19 @@ packages: resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==} '@foxui/core@0.5.2': - resolution: {integrity: sha512-SwzKizuPCpiD47YfDfZ4Ut0KX+WlmidbBTaNafQaSYpQA45D5avRksgbJWsUgIaLx2oyH8PwBq7r/KFjCuKx/g==, tarball: https://registry.npmjs.org/@foxui/core/-/core-0.5.2.tgz} + resolution: {integrity: sha512-SwzKizuPCpiD47YfDfZ4Ut0KX+WlmidbBTaNafQaSYpQA45D5avRksgbJWsUgIaLx2oyH8PwBq7r/KFjCuKx/g==} peerDependencies: svelte: '>=5' tailwindcss: '>=3' '@foxui/social@0.5.4': - resolution: {integrity: sha512-6IvVJv6+NyEf0MbLZiOsNW11qK6QDAIMGzF6Wa4v5xmE8F5HCIOsx7am7JRS8zE+4da8rQ5otM8NpAIQhb5Edw==, tarball: https://registry.npmjs.org/@foxui/social/-/social-0.5.4.tgz} + resolution: {integrity: sha512-6IvVJv6+NyEf0MbLZiOsNW11qK6QDAIMGzF6Wa4v5xmE8F5HCIOsx7am7JRS8zE+4da8rQ5otM8NpAIQhb5Edw==} peerDependencies: svelte: '>=5' tailwindcss: '>=3' '@foxui/time@0.5.2': - resolution: {integrity: sha512-CSIzydngnijCFY/W2PsVuWjIGKfSDMIs6bxvVwlpIWV8e8gzrzfK+DCwL5RO9rhZOppEOqN7VptItOcIplCALw==, tarball: https://registry.npmjs.org/@foxui/time/-/time-0.5.2.tgz} + resolution: {integrity: sha512-CSIzydngnijCFY/W2PsVuWjIGKfSDMIs6bxvVwlpIWV8e8gzrzfK+DCwL5RO9rhZOppEOqN7VptItOcIplCALw==} peerDependencies: svelte: '>=5' tailwindcss: '>=3' @@ -475,144 +478,160 @@ packages: engines: {node: '>=18.18'} '@img/colour@1.1.0': - resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==, tarball: https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz} + resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==} engines: {node: '>=18'} '@img/sharp-darwin-arm64@0.34.5': - 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} + resolution: {integrity: sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [darwin] '@img/sharp-darwin-x64@0.34.5': - 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} + resolution: {integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [darwin] '@img/sharp-libvips-darwin-arm64@1.2.4': - resolution: {integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==, tarball: https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz} + resolution: {integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==} cpu: [arm64] os: [darwin] '@img/sharp-libvips-darwin-x64@1.2.4': - 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} + resolution: {integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==} cpu: [x64] os: [darwin] '@img/sharp-libvips-linux-arm64@1.2.4': - 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} + resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==} cpu: [arm64] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-arm@1.2.4': - 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} + resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==} cpu: [arm] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-ppc64@1.2.4': - 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} + resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==} cpu: [ppc64] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-riscv64@1.2.4': - 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} + resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==} cpu: [riscv64] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-s390x@1.2.4': - resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==, tarball: https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz} + resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==} cpu: [s390x] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-x64@1.2.4': - resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==, tarball: https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz} + resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==} cpu: [x64] os: [linux] + libc: [glibc] '@img/sharp-libvips-linuxmusl-arm64@1.2.4': - resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==, tarball: https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz} + resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==} cpu: [arm64] os: [linux] + libc: [musl] '@img/sharp-libvips-linuxmusl-x64@1.2.4': - resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==, tarball: https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz} + resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==} cpu: [x64] os: [linux] + libc: [musl] '@img/sharp-linux-arm64@0.34.5': - resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==, tarball: https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz} + resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] + libc: [glibc] '@img/sharp-linux-arm@0.34.5': - 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} + resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm] os: [linux] + libc: [glibc] '@img/sharp-linux-ppc64@0.34.5': - resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==, tarball: https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz} + resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [ppc64] os: [linux] + libc: [glibc] '@img/sharp-linux-riscv64@0.34.5': - resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==, tarball: https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz} + resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [riscv64] os: [linux] + libc: [glibc] '@img/sharp-linux-s390x@0.34.5': - resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==, tarball: https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz} + resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [s390x] os: [linux] + libc: [glibc] '@img/sharp-linux-x64@0.34.5': - resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==, tarball: https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz} + resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] + libc: [glibc] '@img/sharp-linuxmusl-arm64@0.34.5': - resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==, tarball: https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz} + resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] + libc: [musl] '@img/sharp-linuxmusl-x64@0.34.5': - resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==, tarball: https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz} + resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] + libc: [musl] '@img/sharp-wasm32@0.34.5': - resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==, tarball: https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz} + resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [wasm32] '@img/sharp-win32-arm64@0.34.5': - resolution: {integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==, tarball: https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz} + resolution: {integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [win32] '@img/sharp-win32-ia32@0.34.5': - resolution: {integrity: sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==, tarball: https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz} + resolution: {integrity: sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [ia32] os: [win32] '@img/sharp-win32-x64@0.34.5': - resolution: {integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==, tarball: https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz} + resolution: {integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [win32] '@internationalized/date@3.11.0': - resolution: {integrity: sha512-BOx5huLAWhicM9/ZFs84CzP+V3gBW6vlpM02yzsdYC7TGlZJX1OJiEEHcSayF00Z+3jLlm4w79amvSt6RqKN3Q==, tarball: https://registry.npmjs.org/@internationalized/date/-/date-3.11.0.tgz} + resolution: {integrity: sha512-BOx5huLAWhicM9/ZFs84CzP+V3gBW6vlpM02yzsdYC7TGlZJX1OJiEEHcSayF00Z+3jLlm4w79amvSt6RqKN3Q==} '@jridgewell/gen-mapping@0.3.13': resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} @@ -631,7 +650,10 @@ packages: resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} '@jridgewell/trace-mapping@0.3.9': - resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==, tarball: https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz} + resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + + '@napi-rs/wasm-runtime@1.1.1': + resolution: {integrity: sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A==} '@number-flow/svelte@0.3.13': resolution: {integrity: sha512-mvbxDeSFa1o/E4vGhrWuawAFCgcn5qTQ/s++FIoD88es5+JQa/aMQUypTy7qXIreTtTvncpIbkKdw9DMnweaSw==} @@ -642,145 +664,158 @@ packages: resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} '@poppinss/colors@4.1.6': - resolution: {integrity: sha512-H9xkIdFswbS8n1d6vmRd8+c10t2Qe+rZITbbDHHkQixH5+2x1FDGmi/0K+WgWiqQFKPSlIYB7jlH6Kpfn6Fleg==, tarball: https://registry.npmjs.org/@poppinss/colors/-/colors-4.1.6.tgz} + resolution: {integrity: sha512-H9xkIdFswbS8n1d6vmRd8+c10t2Qe+rZITbbDHHkQixH5+2x1FDGmi/0K+WgWiqQFKPSlIYB7jlH6Kpfn6Fleg==} '@poppinss/dumper@0.6.5': - resolution: {integrity: sha512-NBdYIb90J7LfOI32dOewKI1r7wnkiH6m920puQ3qHUeZkxNkQiFnXVWoE6YtFSv6QOiPPf7ys6i+HWWecDz7sw==, tarball: https://registry.npmjs.org/@poppinss/dumper/-/dumper-0.6.5.tgz} + resolution: {integrity: sha512-NBdYIb90J7LfOI32dOewKI1r7wnkiH6m920puQ3qHUeZkxNkQiFnXVWoE6YtFSv6QOiPPf7ys6i+HWWecDz7sw==} '@poppinss/exception@1.2.3': - resolution: {integrity: sha512-dCED+QRChTVatE9ibtoaxc+WkdzOSjYTKi/+uacHWIsfodVfpsueo3+DKpgU5Px8qXjgmXkSvhXvSCz3fnP9lw==, tarball: https://registry.npmjs.org/@poppinss/exception/-/exception-1.2.3.tgz} + resolution: {integrity: sha512-dCED+QRChTVatE9ibtoaxc+WkdzOSjYTKi/+uacHWIsfodVfpsueo3+DKpgU5Px8qXjgmXkSvhXvSCz3fnP9lw==} '@rollup/rollup-android-arm-eabi@4.59.0': - resolution: {integrity: sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==, tarball: https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz} + resolution: {integrity: sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==} cpu: [arm] os: [android] '@rollup/rollup-android-arm64@4.59.0': - resolution: {integrity: sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==, tarball: https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.59.0.tgz} + resolution: {integrity: sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==} cpu: [arm64] os: [android] '@rollup/rollup-darwin-arm64@4.59.0': - resolution: {integrity: sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==, tarball: https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.59.0.tgz} + resolution: {integrity: sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==} cpu: [arm64] os: [darwin] '@rollup/rollup-darwin-x64@4.59.0': - resolution: {integrity: sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==, tarball: https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.59.0.tgz} + resolution: {integrity: sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==} cpu: [x64] os: [darwin] '@rollup/rollup-freebsd-arm64@4.59.0': - resolution: {integrity: sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==, tarball: https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.59.0.tgz} + resolution: {integrity: sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==} cpu: [arm64] os: [freebsd] '@rollup/rollup-freebsd-x64@4.59.0': - resolution: {integrity: sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==, tarball: https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.59.0.tgz} + resolution: {integrity: sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==} cpu: [x64] os: [freebsd] '@rollup/rollup-linux-arm-gnueabihf@4.59.0': - resolution: {integrity: sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==, tarball: https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.59.0.tgz} + resolution: {integrity: sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==} cpu: [arm] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm-musleabihf@4.59.0': - resolution: {integrity: sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==, tarball: https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.59.0.tgz} + resolution: {integrity: sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==} cpu: [arm] os: [linux] + libc: [musl] '@rollup/rollup-linux-arm64-gnu@4.59.0': - 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} + resolution: {integrity: sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==} cpu: [arm64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm64-musl@4.59.0': - resolution: {integrity: sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==, tarball: https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.59.0.tgz} + resolution: {integrity: sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==} cpu: [arm64] os: [linux] + libc: [musl] '@rollup/rollup-linux-loong64-gnu@4.59.0': - 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} + resolution: {integrity: sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==} cpu: [loong64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-loong64-musl@4.59.0': - resolution: {integrity: sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==, tarball: https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.59.0.tgz} + resolution: {integrity: sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==} cpu: [loong64] os: [linux] + libc: [musl] '@rollup/rollup-linux-ppc64-gnu@4.59.0': - resolution: {integrity: sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==, tarball: https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.59.0.tgz} + resolution: {integrity: sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==} cpu: [ppc64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-ppc64-musl@4.59.0': - 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} + resolution: {integrity: sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==} cpu: [ppc64] os: [linux] + libc: [musl] '@rollup/rollup-linux-riscv64-gnu@4.59.0': - 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} + resolution: {integrity: sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==} cpu: [riscv64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-riscv64-musl@4.59.0': - 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} + resolution: {integrity: sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==} cpu: [riscv64] os: [linux] + libc: [musl] '@rollup/rollup-linux-s390x-gnu@4.59.0': - 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} + resolution: {integrity: sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==} cpu: [s390x] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-gnu@4.59.0': - resolution: {integrity: sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==, tarball: https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.59.0.tgz} + resolution: {integrity: sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==} cpu: [x64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-musl@4.59.0': - 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} + resolution: {integrity: sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==} cpu: [x64] os: [linux] + libc: [musl] '@rollup/rollup-openbsd-x64@4.59.0': - resolution: {integrity: sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==, tarball: https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.59.0.tgz} + resolution: {integrity: sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==} cpu: [x64] os: [openbsd] '@rollup/rollup-openharmony-arm64@4.59.0': - resolution: {integrity: sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==, tarball: https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.59.0.tgz} + resolution: {integrity: sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==} cpu: [arm64] os: [openharmony] '@rollup/rollup-win32-arm64-msvc@4.59.0': - resolution: {integrity: sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==, tarball: https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.59.0.tgz} + resolution: {integrity: sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==} cpu: [arm64] os: [win32] '@rollup/rollup-win32-ia32-msvc@4.59.0': - resolution: {integrity: sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==, tarball: https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.59.0.tgz} + resolution: {integrity: sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==} cpu: [ia32] os: [win32] '@rollup/rollup-win32-x64-gnu@4.59.0': - resolution: {integrity: sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==, tarball: https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.59.0.tgz} + resolution: {integrity: sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==} cpu: [x64] os: [win32] '@rollup/rollup-win32-x64-msvc@4.59.0': - resolution: {integrity: sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==, tarball: https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.59.0.tgz} + resolution: {integrity: sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==} cpu: [x64] os: [win32] '@sindresorhus/is@7.2.0': - resolution: {integrity: sha512-P1Cz1dWaFfR4IR+U13mqqiGsLFf1KbayybWwdd2vfctdV6hDpUkgCY0nKOLLTMSoRd/jJNjtbqzf13K8DCCXQw==, tarball: https://registry.npmjs.org/@sindresorhus/is/-/is-7.2.0.tgz} + resolution: {integrity: sha512-P1Cz1dWaFfR4IR+U13mqqiGsLFf1KbayybWwdd2vfctdV6hDpUkgCY0nKOLLTMSoRd/jJNjtbqzf13K8DCCXQw==} engines: {node: '>=18'} '@speed-highlight/core@1.2.14': - resolution: {integrity: sha512-G4ewlBNhUtlLvrJTb88d2mdy2KRijzs4UhnlrOSRT4bmjh/IqNElZa3zkrZ+TC47TwtlDWzVLFADljF1Ijp5hA==, tarball: https://registry.npmjs.org/@speed-highlight/core/-/core-1.2.14.tgz} + resolution: {integrity: sha512-G4ewlBNhUtlLvrJTb88d2mdy2KRijzs4UhnlrOSRT4bmjh/IqNElZa3zkrZ+TC47TwtlDWzVLFADljF1Ijp5hA==} '@standard-schema/spec@1.1.0': resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} @@ -828,7 +863,7 @@ packages: vite: ^6.3.0 || ^7.0.0 '@swc/helpers@0.5.19': - resolution: {integrity: sha512-QamiFeIK3txNjgUTNppE6MiG3p7TdninpZu0E0PbqVh1a9FNLT2FRhisaa4NcaX52XVhA5l7Pk58Ft7Sqi/2sA==, tarball: https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.19.tgz} + resolution: {integrity: sha512-QamiFeIK3txNjgUTNppE6MiG3p7TdninpZu0E0PbqVh1a9FNLT2FRhisaa4NcaX52XVhA5l7Pk58Ft7Sqi/2sA==} '@tailwindcss/forms@0.5.11': resolution: {integrity: sha512-h9wegbZDPurxG22xZSoWtdzc41/OlNEUQERNqI/0fOwa2aVlWGu7C35E/x6LDyD3lgtztFSSjKZyuVM0hxhbgA==} @@ -839,61 +874,65 @@ packages: resolution: {integrity: sha512-jlx6sLk4EOwO6hHe1oCGm1Q4AN/s0rSrTTPBGPM0/RQ6Uylwq17FuU8IeJJKEjtc6K6O07zsvP+gDO6MMWo7pg==} '@tailwindcss/oxide-android-arm64@4.2.1': - resolution: {integrity: sha512-eZ7G1Zm5EC8OOKaesIKuw77jw++QJ2lL9N+dDpdQiAB/c/B2wDh0QPFHbkBVrXnwNugvrbJFk1gK2SsVjwWReg==, tarball: https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.2.1.tgz} + resolution: {integrity: sha512-eZ7G1Zm5EC8OOKaesIKuw77jw++QJ2lL9N+dDpdQiAB/c/B2wDh0QPFHbkBVrXnwNugvrbJFk1gK2SsVjwWReg==} engines: {node: '>= 20'} cpu: [arm64] os: [android] '@tailwindcss/oxide-darwin-arm64@4.2.1': - resolution: {integrity: sha512-q/LHkOstoJ7pI1J0q6djesLzRvQSIfEto148ppAd+BVQK0JYjQIFSK3JgYZJa+Yzi0DDa52ZsQx2rqytBnf8Hw==, tarball: https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.2.1.tgz} + resolution: {integrity: sha512-q/LHkOstoJ7pI1J0q6djesLzRvQSIfEto148ppAd+BVQK0JYjQIFSK3JgYZJa+Yzi0DDa52ZsQx2rqytBnf8Hw==} engines: {node: '>= 20'} cpu: [arm64] os: [darwin] '@tailwindcss/oxide-darwin-x64@4.2.1': - 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} + resolution: {integrity: sha512-/f/ozlaXGY6QLbpvd/kFTro2l18f7dHKpB+ieXz+Cijl4Mt9AI2rTrpq7V+t04nK+j9XBQHnSMdeQRhbGyt6fw==} engines: {node: '>= 20'} cpu: [x64] os: [darwin] '@tailwindcss/oxide-freebsd-x64@4.2.1': - resolution: {integrity: sha512-5e/AkgYJT/cpbkys/OU2Ei2jdETCLlifwm7ogMC7/hksI2fC3iiq6OcXwjibcIjPung0kRtR3TxEITkqgn0TcA==, tarball: https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.2.1.tgz} + resolution: {integrity: sha512-5e/AkgYJT/cpbkys/OU2Ei2jdETCLlifwm7ogMC7/hksI2fC3iiq6OcXwjibcIjPung0kRtR3TxEITkqgn0TcA==} engines: {node: '>= 20'} cpu: [x64] os: [freebsd] '@tailwindcss/oxide-linux-arm-gnueabihf@4.2.1': - 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} + resolution: {integrity: sha512-Uny1EcVTTmerCKt/1ZuKTkb0x8ZaiuYucg2/kImO5A5Y/kBz41/+j0gxUZl+hTF3xkWpDmHX+TaWhOtba2Fyuw==} engines: {node: '>= 20'} cpu: [arm] os: [linux] '@tailwindcss/oxide-linux-arm64-gnu@4.2.1': - 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} + resolution: {integrity: sha512-CTrwomI+c7n6aSSQlsPL0roRiNMDQ/YzMD9EjcR+H4f0I1SQ8QqIuPnsVp7QgMkC1Qi8rtkekLkOFjo7OlEFRQ==} engines: {node: '>= 20'} cpu: [arm64] os: [linux] + libc: [glibc] '@tailwindcss/oxide-linux-arm64-musl@4.2.1': - resolution: {integrity: sha512-WZA0CHRL/SP1TRbA5mp9htsppSEkWuQ4KsSUumYQnyl8ZdT39ntwqmz4IUHGN6p4XdSlYfJwM4rRzZLShHsGAQ==, tarball: https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.2.1.tgz} + resolution: {integrity: sha512-WZA0CHRL/SP1TRbA5mp9htsppSEkWuQ4KsSUumYQnyl8ZdT39ntwqmz4IUHGN6p4XdSlYfJwM4rRzZLShHsGAQ==} engines: {node: '>= 20'} cpu: [arm64] os: [linux] + libc: [musl] '@tailwindcss/oxide-linux-x64-gnu@4.2.1': - resolution: {integrity: sha512-qMFzxI2YlBOLW5PhblzuSWlWfwLHaneBE0xHzLrBgNtqN6mWfs+qYbhryGSXQjFYB1Dzf5w+LN5qbUTPhW7Y5g==, tarball: https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.2.1.tgz} + resolution: {integrity: sha512-qMFzxI2YlBOLW5PhblzuSWlWfwLHaneBE0xHzLrBgNtqN6mWfs+qYbhryGSXQjFYB1Dzf5w+LN5qbUTPhW7Y5g==} engines: {node: '>= 20'} cpu: [x64] os: [linux] + libc: [glibc] '@tailwindcss/oxide-linux-x64-musl@4.2.1': - resolution: {integrity: sha512-5r1X2FKnCMUPlXTWRYpHdPYUY6a1Ar/t7P24OuiEdEOmms5lyqjDRvVY1yy9Rmioh+AunQ0rWiOTPE8F9A3v5g==, tarball: https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.2.1.tgz} + resolution: {integrity: sha512-5r1X2FKnCMUPlXTWRYpHdPYUY6a1Ar/t7P24OuiEdEOmms5lyqjDRvVY1yy9Rmioh+AunQ0rWiOTPE8F9A3v5g==} engines: {node: '>= 20'} cpu: [x64] os: [linux] + libc: [musl] '@tailwindcss/oxide-wasm32-wasi@4.2.1': - resolution: {integrity: sha512-MGFB5cVPvshR85MTJkEvqDUnuNoysrsRxd6vnk1Lf2tbiqNlXpHYZqkqOQalydienEWOHHFyyuTSYRsLfxFJ2Q==, tarball: https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.2.1.tgz} + resolution: {integrity: sha512-MGFB5cVPvshR85MTJkEvqDUnuNoysrsRxd6vnk1Lf2tbiqNlXpHYZqkqOQalydienEWOHHFyyuTSYRsLfxFJ2Q==} engines: {node: '>=14.0.0'} cpu: [wasm32] bundledDependencies: @@ -905,13 +944,13 @@ packages: - tslib '@tailwindcss/oxide-win32-arm64-msvc@4.2.1': - resolution: {integrity: sha512-YlUEHRHBGnCMh4Nj4GnqQyBtsshUPdiNroZj8VPkvTZSoHsilRCwXcVKnG9kyi0ZFAS/3u+qKHBdDc81SADTRA==, tarball: https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.2.1.tgz} + resolution: {integrity: sha512-YlUEHRHBGnCMh4Nj4GnqQyBtsshUPdiNroZj8VPkvTZSoHsilRCwXcVKnG9kyi0ZFAS/3u+qKHBdDc81SADTRA==} engines: {node: '>= 20'} cpu: [arm64] os: [win32] '@tailwindcss/oxide-win32-x64-msvc@4.2.1': - 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} + resolution: {integrity: sha512-rbO34G5sMWWyrN/idLeVxAZgAKWrn5LiR3/I90Q9MkA67s6T1oB0xtTe+0heoBvHSpbU9Mk7i6uwJnpo4u21XQ==} engines: {node: '>= 20'} cpu: [x64] os: [win32] @@ -925,6 +964,18 @@ packages: peerDependencies: vite: ^5.2.0 || ^6 || ^7 + '@tursodatabase/database-common@0.5.0': + resolution: {integrity: sha512-Yw1aJ7rw5Sshy7VvkLk3gZlHqNeM3nqs11G3qzhu1gjqLfVxT1MupVfB4AWLrvOwVMYWs560/Xfl9ITooJu5HQ==} + + '@tursodatabase/database-wasm-common@0.5.0': + resolution: {integrity: sha512-l7Tf7m52sS0mRWD584WBpFXACZtdcd2iGHXa5Jsjr3Ia3X9sRmDNy/SQkncLQIHXoQ+EPFUC951v9C79Hdgg/w==} + + '@tursodatabase/database-wasm@0.5.0': + resolution: {integrity: sha512-94ax/1oENl9wfOyQGIzWkcLE16FwPnOtM5zVTSRJ5NkOC/+FqbIGxWYL+jS4M8dd+U8FXSB2soQXLOWvCSHosg==} + + '@tybys/wasm-util@0.10.1': + resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} + '@types/bun@1.3.10': resolution: {integrity: sha512-0+rlrUrOrTSskibryHbvQkDOWRJwJZqZlxrUs1u4oOoTln8+WIXBPmAuCF35SWB2z4Zl3E84Nl/D0P7803nigQ==} @@ -1006,10 +1057,10 @@ packages: engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@use-gesture/core@10.3.1': - resolution: {integrity: sha512-WcINiDt8WjqBdUXye25anHiNxPc0VOrlT8F6LLkU6cycrOGUDyY/yyFmsg3k8i5OLvv25llc0QC45GhR/C8llw==, tarball: https://registry.npmjs.org/@use-gesture/core/-/core-10.3.1.tgz} + resolution: {integrity: sha512-WcINiDt8WjqBdUXye25anHiNxPc0VOrlT8F6LLkU6cycrOGUDyY/yyFmsg3k8i5OLvv25llc0QC45GhR/C8llw==} '@use-gesture/vanilla@10.3.1': - resolution: {integrity: sha512-lT4scGLu59ovA3zmtUonukAGcA0AdOOh+iwNDS05Bsu7Lq9aZToDHhI6D8Q2qvsVraovtsLLYwPrWdG/noMAKw==, tarball: https://registry.npmjs.org/@use-gesture/vanilla/-/vanilla-10.3.1.tgz} + resolution: {integrity: sha512-lT4scGLu59ovA3zmtUonukAGcA0AdOOh+iwNDS05Bsu7Lq9aZToDHhI6D8Q2qvsVraovtsLLYwPrWdG/noMAKw==} acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} @@ -1044,7 +1095,7 @@ packages: svelte: ^5.33.0 blake3-wasm@2.1.5: - resolution: {integrity: sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g==, tarball: https://registry.npmjs.org/blake3-wasm/-/blake3-wasm-2.1.5.tgz} + resolution: {integrity: sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g==} brace-expansion@5.0.4: resolution: {integrity: sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==} @@ -1066,11 +1117,11 @@ packages: engines: {node: '>= 0.6'} cookie@1.1.1: - resolution: {integrity: sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==, tarball: https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz} + resolution: {integrity: sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==} engines: {node: '>=18'} core-js@3.48.0: - resolution: {integrity: sha512-zpEHTy1fjTMZCKLHUZoVeylt9XrzaIN2rbPXEt0k+q7JE5CkCZdo6bNq55bn24a69CH7ErAVLKijxJja4fw+UQ==, tarball: https://registry.npmjs.org/core-js/-/core-js-3.48.0.tgz} + resolution: {integrity: sha512-zpEHTy1fjTMZCKLHUZoVeylt9XrzaIN2rbPXEt0k+q7JE5CkCZdo6bNq55bn24a69CH7ErAVLKijxJja4fw+UQ==} cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} @@ -1082,7 +1133,7 @@ packages: hasBin: true custom-event-polyfill@1.0.7: - resolution: {integrity: sha512-TDDkd5DkaZxZFM8p+1I3yAlvM3rSr1wbrOliG4yJiwinMZN8z/iGL7BTlDkrJcYTmgUSb4ywVCc3ZaUtOtC76w==, tarball: https://registry.npmjs.org/custom-event-polyfill/-/custom-event-polyfill-1.0.7.tgz} + resolution: {integrity: sha512-TDDkd5DkaZxZFM8p+1I3yAlvM3rSr1wbrOliG4yJiwinMZN8z/iGL7BTlDkrJcYTmgUSb4ywVCc3ZaUtOtC76w==} debug@4.4.3: resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} @@ -1111,24 +1162,21 @@ packages: devalue@5.6.3: resolution: {integrity: sha512-nc7XjUU/2Lb+SvEFVGcWLiKkzfw8+qHI7zn8WYXKkLMgfGSHbgCEaR6bJpev8Cm6Rmrb19Gfd/tZvGqx9is3wg==} - dexie@4.3.0: - resolution: {integrity: sha512-5EeoQpJvMKHe6zWt/FSIIuRa3CWlZeIl6zKXt+Lz7BU6RoRRLgX9dZEynRfXrkLcldKYCBiz7xekTEylnie1Ug==, tarball: https://registry.npmjs.org/dexie/-/dexie-4.3.0.tgz} - dompurify@3.3.1: - resolution: {integrity: sha512-qkdCKzLNtrgPFP1Vo+98FRzJnBRGe4ffyCea9IwHB1fyxPOeNTHpLKYGd4Uk9xvNoH0ZoOjwZxNptyMwqrId1Q==, tarball: https://registry.npmjs.org/dompurify/-/dompurify-3.3.1.tgz} + resolution: {integrity: sha512-qkdCKzLNtrgPFP1Vo+98FRzJnBRGe4ffyCea9IwHB1fyxPOeNTHpLKYGd4Uk9xvNoH0ZoOjwZxNptyMwqrId1Q==} emoji-picker-element@1.29.1: - resolution: {integrity: sha512-TOiHzu9Dqib3x4MwcAi3wi3RdyT4SoeB4b15AvH1ks4SBwTl7DeebhZ0d3x6dNi4XfNU7IGRZ7NBQllj0RqwrQ==, tarball: https://registry.npmjs.org/emoji-picker-element/-/emoji-picker-element-1.29.1.tgz} + resolution: {integrity: sha512-TOiHzu9Dqib3x4MwcAi3wi3RdyT4SoeB4b15AvH1ks4SBwTl7DeebhZ0d3x6dNi4XfNU7IGRZ7NBQllj0RqwrQ==} enhanced-resolve@5.20.0: resolution: {integrity: sha512-/ce7+jQ1PQ6rVXwe+jKEg5hW5ciicHwIQUagZkp6IufBoY3YDgdTTY1azVs0qoRgVmvsNB+rbjLJxDAeHHtwsQ==} engines: {node: '>=10.13.0'} error-stack-parser-es@1.0.5: - resolution: {integrity: sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA==, tarball: https://registry.npmjs.org/error-stack-parser-es/-/error-stack-parser-es-1.0.5.tgz} + resolution: {integrity: sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA==} esbuild@0.27.3: - resolution: {integrity: sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==, tarball: https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz} + resolution: {integrity: sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==} engines: {node: '>=18'} hasBin: true @@ -1245,11 +1293,8 @@ packages: flatted@3.3.4: resolution: {integrity: sha512-3+mMldrTAPdta5kjX2G2J7iX4zxtnwpdA8Tr2ZSjkyPSanvbZAcy6flmtnXbEybHrDcU9641lxrMfFuUxVz9vA==} - flexsearch@0.8.212: - resolution: {integrity: sha512-wSyJr1GUWoOOIISRu+X2IXiOcVfg9qqBRyCPRUdLMIGJqPzMo+jMRlvE83t14v1j0dRMEaBbER/adQjp6Du2pw==} - fsevents@2.3.3: - resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==, tarball: https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz} + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] @@ -1272,7 +1317,7 @@ packages: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} hls.js@1.6.15: - resolution: {integrity: sha512-E3a5VwgXimGHwpRGV+WxRTKeSp2DW5DI5MWv34ulL3t5UNmyJWCQ1KmLEHbYzcfThfXG8amBL+fCYPneGHC4VA==, tarball: https://registry.npmjs.org/hls.js/-/hls.js-1.6.15.tgz} + resolution: {integrity: sha512-E3a5VwgXimGHwpRGV+WxRTKeSp2DW5DI5MWv34ulL3t5UNmyJWCQ1KmLEHbYzcfThfXG8amBL+fCYPneGHC4VA==} ignore@5.3.2: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} @@ -1290,7 +1335,7 @@ packages: resolution: {integrity: sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==} is-emoji-supported@0.0.5: - resolution: {integrity: sha512-WOlXUhDDHxYqcSmFZis+xWhhqXiK2SU0iYiqmth5Ip0FHLZQAt9rKL5ahnilE8/86WH8tZ3bmNNNC+bTzamqlw==, tarball: https://registry.npmjs.org/is-emoji-supported/-/is-emoji-supported-0.0.5.tgz} + resolution: {integrity: sha512-WOlXUhDDHxYqcSmFZis+xWhhqXiK2SU0iYiqmth5Ip0FHLZQAt9rKL5ahnilE8/86WH8tZ3bmNNNC+bTzamqlw==} is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} @@ -1334,67 +1379,71 @@ packages: engines: {node: '>= 0.8.0'} lightningcss-android-arm64@1.31.1: - resolution: {integrity: sha512-HXJF3x8w9nQ4jbXRiNppBCqeZPIAfUo8zE/kOEGbW5NZvGc/K7nMxbhIr+YlFlHW5mpbg/YFPdbnCh1wAXCKFg==, tarball: https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.31.1.tgz} + resolution: {integrity: sha512-HXJF3x8w9nQ4jbXRiNppBCqeZPIAfUo8zE/kOEGbW5NZvGc/K7nMxbhIr+YlFlHW5mpbg/YFPdbnCh1wAXCKFg==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [android] lightningcss-darwin-arm64@1.31.1: - resolution: {integrity: sha512-02uTEqf3vIfNMq3h/z2cJfcOXnQ0GRwQrkmPafhueLb2h7mqEidiCzkE4gBMEH65abHRiQvhdcQ+aP0D0g67sg==, tarball: https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.31.1.tgz} + resolution: {integrity: sha512-02uTEqf3vIfNMq3h/z2cJfcOXnQ0GRwQrkmPafhueLb2h7mqEidiCzkE4gBMEH65abHRiQvhdcQ+aP0D0g67sg==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [darwin] lightningcss-darwin-x64@1.31.1: - resolution: {integrity: sha512-1ObhyoCY+tGxtsz1lSx5NXCj3nirk0Y0kB/g8B8DT+sSx4G9djitg9ejFnjb3gJNWo7qXH4DIy2SUHvpoFwfTA==, tarball: https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.31.1.tgz} + resolution: {integrity: sha512-1ObhyoCY+tGxtsz1lSx5NXCj3nirk0Y0kB/g8B8DT+sSx4G9djitg9ejFnjb3gJNWo7qXH4DIy2SUHvpoFwfTA==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [darwin] lightningcss-freebsd-x64@1.31.1: - resolution: {integrity: sha512-1RINmQKAItO6ISxYgPwszQE1BrsVU5aB45ho6O42mu96UiZBxEXsuQ7cJW4zs4CEodPUioj/QrXW1r9pLUM74A==, tarball: https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.31.1.tgz} + resolution: {integrity: sha512-1RINmQKAItO6ISxYgPwszQE1BrsVU5aB45ho6O42mu96UiZBxEXsuQ7cJW4zs4CEodPUioj/QrXW1r9pLUM74A==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [freebsd] lightningcss-linux-arm-gnueabihf@1.31.1: - 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} + resolution: {integrity: sha512-OOCm2//MZJ87CdDK62rZIu+aw9gBv4azMJuA8/KB74wmfS3lnC4yoPHm0uXZ/dvNNHmnZnB8XLAZzObeG0nS1g==} engines: {node: '>= 12.0.0'} cpu: [arm] os: [linux] lightningcss-linux-arm64-gnu@1.31.1: - resolution: {integrity: sha512-WKyLWztD71rTnou4xAD5kQT+982wvca7E6QoLpoawZ1gP9JM0GJj4Tp5jMUh9B3AitHbRZ2/H3W5xQmdEOUlLg==, tarball: https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.31.1.tgz} + resolution: {integrity: sha512-WKyLWztD71rTnou4xAD5kQT+982wvca7E6QoLpoawZ1gP9JM0GJj4Tp5jMUh9B3AitHbRZ2/H3W5xQmdEOUlLg==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] + libc: [glibc] lightningcss-linux-arm64-musl@1.31.1: - resolution: {integrity: sha512-mVZ7Pg2zIbe3XlNbZJdjs86YViQFoJSpc41CbVmKBPiGmC4YrfeOyz65ms2qpAobVd7WQsbW4PdsSJEMymyIMg==, tarball: https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.31.1.tgz} + resolution: {integrity: sha512-mVZ7Pg2zIbe3XlNbZJdjs86YViQFoJSpc41CbVmKBPiGmC4YrfeOyz65ms2qpAobVd7WQsbW4PdsSJEMymyIMg==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] + libc: [musl] lightningcss-linux-x64-gnu@1.31.1: - 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} + resolution: {integrity: sha512-xGlFWRMl+0KvUhgySdIaReQdB4FNudfUTARn7q0hh/V67PVGCs3ADFjw+6++kG1RNd0zdGRlEKa+T13/tQjPMA==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] + libc: [glibc] lightningcss-linux-x64-musl@1.31.1: - resolution: {integrity: sha512-eowF8PrKHw9LpoZii5tdZwnBcYDxRw2rRCyvAXLi34iyeYfqCQNA9rmUM0ce62NlPhCvof1+9ivRaTY6pSKDaA==, tarball: https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.31.1.tgz} + resolution: {integrity: sha512-eowF8PrKHw9LpoZii5tdZwnBcYDxRw2rRCyvAXLi34iyeYfqCQNA9rmUM0ce62NlPhCvof1+9ivRaTY6pSKDaA==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] + libc: [musl] lightningcss-win32-arm64-msvc@1.31.1: - resolution: {integrity: sha512-aJReEbSEQzx1uBlQizAOBSjcmr9dCdL3XuC/6HLXAxmtErsj2ICo5yYggg1qOODQMtnjNQv2UHb9NpOuFtYe4w==, tarball: https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.31.1.tgz} + resolution: {integrity: sha512-aJReEbSEQzx1uBlQizAOBSjcmr9dCdL3XuC/6HLXAxmtErsj2ICo5yYggg1qOODQMtnjNQv2UHb9NpOuFtYe4w==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [win32] lightningcss-win32-x64-msvc@1.31.1: - resolution: {integrity: sha512-I9aiFrbd7oYHwlnQDqr1Roz+fTz61oDDJX7n9tYF9FJymH1cIN1DtKw3iYt6b8WZgEjoNwVSncwF4wx/ZedMhw==, tarball: https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.31.1.tgz} + resolution: {integrity: sha512-I9aiFrbd7oYHwlnQDqr1Roz+fTz61oDDJX7n9tYF9FJymH1cIN1DtKw3iYt6b8WZgEjoNwVSncwF4wx/ZedMhw==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [win32] @@ -1408,7 +1457,7 @@ packages: engines: {node: '>=10'} loadjs@4.3.0: - resolution: {integrity: sha512-vNX4ZZLJBeDEOBvdr2v/F+0aN5oMuPu7JTqrMwp+DtgK+AryOlpy6Xtm2/HpNr+azEa828oQjOtWsB6iDtSfSQ==, tarball: https://registry.npmjs.org/loadjs/-/loadjs-4.3.0.tgz} + resolution: {integrity: sha512-vNX4ZZLJBeDEOBvdr2v/F+0aN5oMuPu7JTqrMwp+DtgK+AryOlpy6Xtm2/HpNr+azEa828oQjOtWsB6iDtSfSQ==} locate-character@3.0.0: resolution: {integrity: sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==} @@ -1429,7 +1478,7 @@ packages: hasBin: true miniflare@4.20260305.0: - resolution: {integrity: sha512-jVhtKJtiwaZa3rI+WgoLvSJmEazDsoUmAPYRUmEe2VO6VSbvkhbnDRm+dsPbYRatgNIExwrpqG1rv96jHiSb0w==, tarball: https://registry.npmjs.org/miniflare/-/miniflare-4.20260305.0.tgz} + resolution: {integrity: sha512-jVhtKJtiwaZa3rI+WgoLvSJmEazDsoUmAPYRUmEe2VO6VSbvkhbnDRm+dsPbYRatgNIExwrpqG1rv96jHiSb0w==} engines: {node: '>=18.0.0'} hasBin: true @@ -1438,7 +1487,7 @@ packages: engines: {node: 18 || 20 || >=22} mode-watcher@1.1.0: - resolution: {integrity: sha512-mUT9RRGPDYenk59qJauN1rhsIMKBmWA3xMF+uRwE8MW/tjhaDSCCARqkSuDTq8vr4/2KcAxIGVjACxTjdk5C3g==, tarball: https://registry.npmjs.org/mode-watcher/-/mode-watcher-1.1.0.tgz} + resolution: {integrity: sha512-mUT9RRGPDYenk59qJauN1rhsIMKBmWA3xMF+uRwE8MW/tjhaDSCCARqkSuDTq8vr4/2KcAxIGVjACxTjdk5C3g==} peerDependencies: svelte: ^5.27.0 @@ -1497,10 +1546,10 @@ packages: engines: {node: '>=8'} path-to-regexp@6.3.0: - resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==, tarball: https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz} + resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==} pathe@2.0.3: - resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==, tarball: https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz} + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -1510,7 +1559,7 @@ packages: engines: {node: '>=12'} plyr@3.8.4: - resolution: {integrity: sha512-DrzLbK9Wol3zeiuZCleD9aUOl0KAaBHR9H6WVVVYPZ4Ya+LYxUFTgSF1jooHcMQCv96Ws96wCaZzIoP3bES8pQ==, tarball: https://registry.npmjs.org/plyr/-/plyr-3.8.4.tgz} + resolution: {integrity: sha512-DrzLbK9Wol3zeiuZCleD9aUOl0KAaBHR9H6WVVVYPZ4Ya+LYxUFTgSF1jooHcMQCv96Ws96wCaZzIoP3bES8pQ==} postcss-load-config@3.1.4: resolution: {integrity: sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==} @@ -1619,7 +1668,7 @@ packages: engines: {node: '>=6'} rangetouch@2.0.1: - resolution: {integrity: sha512-sln+pNSc8NGaHoLzwNBssFSf/rSYkqeBXzX1AtJlkJiUaVSJSbRAWJk+4omsXkN+EJalzkZhWQ3th1m0FpR5xA==, tarball: https://registry.npmjs.org/rangetouch/-/rangetouch-2.0.1.tgz} + resolution: {integrity: sha512-sln+pNSc8NGaHoLzwNBssFSf/rSYkqeBXzX1AtJlkJiUaVSJSbRAWJk+4omsXkN+EJalzkZhWQ3th1m0FpR5xA==} readdirp@4.1.2: resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} @@ -1638,17 +1687,17 @@ packages: hasBin: true runed@0.23.4: - resolution: {integrity: sha512-9q8oUiBYeXIDLWNK5DfCWlkL0EW3oGbk845VdKlPeia28l751VpfesaB/+7pI6rnbx1I6rqoZ2fZxptOJLxILA==, tarball: https://registry.npmjs.org/runed/-/runed-0.23.4.tgz} + resolution: {integrity: sha512-9q8oUiBYeXIDLWNK5DfCWlkL0EW3oGbk845VdKlPeia28l751VpfesaB/+7pI6rnbx1I6rqoZ2fZxptOJLxILA==} peerDependencies: svelte: ^5.7.0 runed@0.25.0: - resolution: {integrity: sha512-7+ma4AG9FT2sWQEA0Egf6mb7PBT2vHyuHail1ie8ropfSjvZGtEAx8YTmUjv/APCsdRRxEVvArNjALk9zFSOrg==, tarball: https://registry.npmjs.org/runed/-/runed-0.25.0.tgz} + resolution: {integrity: sha512-7+ma4AG9FT2sWQEA0Egf6mb7PBT2vHyuHail1ie8ropfSjvZGtEAx8YTmUjv/APCsdRRxEVvArNjALk9zFSOrg==} peerDependencies: svelte: ^5.7.0 runed@0.28.0: - resolution: {integrity: sha512-k2xx7RuO9hWcdd9f+8JoBeqWtYrm5CALfgpkg2YDB80ds/QE4w0qqu34A7fqiAwiBBSBQOid7TLxwxVC27ymWQ==, tarball: https://registry.npmjs.org/runed/-/runed-0.28.0.tgz} + resolution: {integrity: sha512-k2xx7RuO9hWcdd9f+8JoBeqWtYrm5CALfgpkg2YDB80ds/QE4w0qqu34A7fqiAwiBBSBQOid7TLxwxVC27ymWQ==} peerDependencies: svelte: ^5.7.0 @@ -1674,7 +1723,7 @@ packages: resolution: {integrity: sha512-n7Z7dXZhJbwuAHhNzkTti6Aw9QDDjZtm3JTpTGATIdNzdQz5GuFs22w90BcvF4INfnrL5xrX3oGsuqO5Dx3A1Q==} sharp@0.34.5: - resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==, tarball: https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz} + resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} shebang-command@2.0.0: @@ -1697,7 +1746,7 @@ packages: resolution: {integrity: sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==} supports-color@10.2.2: - resolution: {integrity: sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==, tarball: https://registry.npmjs.org/supports-color/-/supports-color-10.2.2.tgz} + resolution: {integrity: sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==} engines: {node: '>=18'} svelte-check@4.4.4: @@ -1718,7 +1767,7 @@ packages: optional: true svelte-sonner@1.0.7: - resolution: {integrity: sha512-1EUFYmd7q/xfs2qCHwJzGPh9n5VJ3X6QjBN10fof2vxgy8fYE7kVfZ7uGnd7i6fQaWIr5KvXcwYXE/cmTEjk5A==, tarball: https://registry.npmjs.org/svelte-sonner/-/svelte-sonner-1.0.7.tgz} + resolution: {integrity: sha512-1EUFYmd7q/xfs2qCHwJzGPh9n5VJ3X6QjBN10fof2vxgy8fYE7kVfZ7uGnd7i6fQaWIr5KvXcwYXE/cmTEjk5A==} peerDependencies: svelte: ^5.0.0 @@ -1729,7 +1778,7 @@ packages: svelte: ^5.30.2 svelte-toolbelt@0.7.1: - resolution: {integrity: sha512-HcBOcR17Vx9bjaOceUvxkY3nGmbBmCBBbuWLLEWO6jtmWH8f/QoWmbyUfQZrpDINH39en1b8mptfPQT9VKQ1xQ==, tarball: https://registry.npmjs.org/svelte-toolbelt/-/svelte-toolbelt-0.7.1.tgz} + resolution: {integrity: sha512-HcBOcR17Vx9bjaOceUvxkY3nGmbBmCBBbuWLLEWO6jtmWH8f/QoWmbyUfQZrpDINH39en1b8mptfPQT9VKQ1xQ==} engines: {node: '>=18', pnpm: '>=8.7.0'} peerDependencies: svelte: ^5.0.0 @@ -1745,7 +1794,7 @@ packages: resolution: {integrity: sha512-I8K9wewnVDkL1NTGoqWmVEIlUcB9gFriAEkXkfCjX5ib8ezGxtR3xD7iZIxrfArjEsH7F1CHD4RFUtxefdqV/A==} tailwind-variants@3.2.2: - resolution: {integrity: sha512-Mi4kHeMTLvKlM98XPnK+7HoBPmf4gygdFmqQPaDivc3DpYS6aIY6KiG/PgThrGvii5YZJqRsPz0aPyhoFzmZgg==, tarball: https://registry.npmjs.org/tailwind-variants/-/tailwind-variants-3.2.2.tgz} + resolution: {integrity: sha512-Mi4kHeMTLvKlM98XPnK+7HoBPmf4gygdFmqQPaDivc3DpYS6aIY6KiG/PgThrGvii5YZJqRsPz0aPyhoFzmZgg==} engines: {node: '>=16.x', pnpm: '>=7.x'} peerDependencies: tailwind-merge: '>=3.0.0' @@ -1776,7 +1825,7 @@ packages: typescript: '>=4.8.4' tslib@2.8.1: - resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==, tarball: https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz} + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} tsx@4.21.0: resolution: {integrity: sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==} @@ -1803,11 +1852,11 @@ packages: resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==} undici@7.18.2: - resolution: {integrity: sha512-y+8YjDFzWdQlSE9N5nzKMT3g4a5UBX1HKowfdXh0uvAnTaqqwqB92Jt4UXBAeKekDs5IaDKyJFR4X1gYVCgXcw==, tarball: https://registry.npmjs.org/undici/-/undici-7.18.2.tgz} + resolution: {integrity: sha512-y+8YjDFzWdQlSE9N5nzKMT3g4a5UBX1HKowfdXh0uvAnTaqqwqB92Jt4UXBAeKekDs5IaDKyJFR4X1gYVCgXcw==} engines: {node: '>=20.18.1'} unenv@2.0.0-rc.24: - resolution: {integrity: sha512-i7qRCmY42zmCwnYlh9H2SvLEypEFGye5iRmEMKjcGi7zk9UquigRjFtTLz0TYqr0ZGLZhaMHl/foy1bZR+Cwlw==, tarball: https://registry.npmjs.org/unenv/-/unenv-2.0.0-rc.24.tgz} + resolution: {integrity: sha512-i7qRCmY42zmCwnYlh9H2SvLEypEFGye5iRmEMKjcGi7zk9UquigRjFtTLz0TYqr0ZGLZhaMHl/foy1bZR+Cwlw==} unicode-segmenter@0.14.5: resolution: {integrity: sha512-jHGmj2LUuqDcX3hqY12Ql+uhUTn8huuxNZGq7GvtF6bSybzH3aFgedYu/KTzQStEgt1Ra2F3HxadNXsNjb3m3g==} @@ -1816,7 +1865,7 @@ packages: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} url-polyfill@1.1.14: - resolution: {integrity: sha512-p4f3TTAG6ADVF3mwbXw7hGw+QJyw5CnNGvYh5fCuQQZIiuKUswqcznyV3pGDP9j0TSmC4UvRKm8kl1QsX1diiQ==, tarball: https://registry.npmjs.org/url-polyfill/-/url-polyfill-1.1.14.tgz} + resolution: {integrity: sha512-p4f3TTAG6ADVF3mwbXw7hGw+QJyw5CnNGvYh5fCuQQZIiuKUswqcznyV3pGDP9j0TSmC4UvRKm8kl1QsX1diiQ==} util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} @@ -1887,7 +1936,7 @@ packages: engines: {node: '>=0.10.0'} workerd@1.20260305.0: - resolution: {integrity: sha512-JkhfCLU+w+KbQmZ9k49IcDYc78GBo7eG8Mir8E2+KVjR7otQAmpcLlsous09YLh8WQ3Bt3Mi6/WMStvMAPukeA==, tarball: https://registry.npmjs.org/workerd/-/workerd-1.20260305.0.tgz} + resolution: {integrity: sha512-JkhfCLU+w+KbQmZ9k49IcDYc78GBo7eG8Mir8E2+KVjR7otQAmpcLlsous09YLh8WQ3Bt3Mi6/WMStvMAPukeA==} engines: {node: '>=16'} hasBin: true @@ -1896,7 +1945,7 @@ packages: engines: {node: '>=12'} wrangler@4.69.0: - resolution: {integrity: sha512-EmVfIM65I5b4ITHe3Y9R7zQyf4NUBQ1leStakMlWiVR9n6VlDwuEltyQI2l3i0JciDnWyR3uqe+T6C08ivniTQ==, tarball: https://registry.npmjs.org/wrangler/-/wrangler-4.69.0.tgz} + resolution: {integrity: sha512-EmVfIM65I5b4ITHe3Y9R7zQyf4NUBQ1leStakMlWiVR9n6VlDwuEltyQI2l3i0JciDnWyR3uqe+T6C08ivniTQ==} engines: {node: '>=20.0.0'} hasBin: true peerDependencies: @@ -1906,7 +1955,7 @@ packages: optional: true ws@8.18.0: - resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==, tarball: https://registry.npmjs.org/ws/-/ws-8.18.0.tgz} + resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} engines: {node: '>=10.0.0'} peerDependencies: bufferutil: ^4.0.1 @@ -1926,10 +1975,10 @@ packages: engines: {node: '>=10'} youch-core@0.3.3: - resolution: {integrity: sha512-ho7XuGjLaJ2hWHoK8yFnsUGy2Y5uDpqSTq1FkHLK4/oqKtyUU1AFbOOxY4IpC9f0fTLjwYbslUz0Po5BpD1wrA==, tarball: https://registry.npmjs.org/youch-core/-/youch-core-0.3.3.tgz} + resolution: {integrity: sha512-ho7XuGjLaJ2hWHoK8yFnsUGy2Y5uDpqSTq1FkHLK4/oqKtyUU1AFbOOxY4IpC9f0fTLjwYbslUz0Po5BpD1wrA==} youch@4.1.0-beta.10: - resolution: {integrity: sha512-rLfVLB4FgQneDr0dv1oddCVZmKjcJ6yX6mS4pU82Mq/Dt9a3cLZQ62pDBL4AUO+uVrCvtWz3ZFUL2HFAFJ/BXQ==, tarball: https://registry.npmjs.org/youch/-/youch-4.1.0-beta.10.tgz} + resolution: {integrity: sha512-rLfVLB4FgQneDr0dv1oddCVZmKjcJ6yX6mS4pU82Mq/Dt9a3cLZQ62pDBL4AUO+uVrCvtWz3ZFUL2HFAFJ/BXQ==} zimmerframe@1.1.4: resolution: {integrity: sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ==} @@ -2056,10 +2105,18 @@ snapshots: dependencies: '@jridgewell/trace-mapping': 0.3.9 + '@emnapi/core@1.8.1': + dependencies: + '@emnapi/wasi-threads': 1.1.0 + tslib: 2.8.1 + '@emnapi/runtime@1.8.1': dependencies: tslib: 2.8.1 - optional: true + + '@emnapi/wasi-threads@1.1.0': + dependencies: + tslib: 2.8.1 '@esbuild/aix-ppc64@0.27.3': optional: true @@ -2370,6 +2427,12 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 + '@napi-rs/wasm-runtime@1.1.1': + dependencies: + '@emnapi/core': 1.8.1 + '@emnapi/runtime': 1.8.1 + '@tybys/wasm-util': 0.10.1 + '@number-flow/svelte@0.3.13(svelte@5.53.6)': dependencies: esm-env: 1.2.2 @@ -2596,6 +2659,21 @@ snapshots: tailwindcss: 4.2.1 vite: 7.3.1(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0) + '@tursodatabase/database-common@0.5.0': {} + + '@tursodatabase/database-wasm-common@0.5.0': + dependencies: + '@napi-rs/wasm-runtime': 1.1.1 + + '@tursodatabase/database-wasm@0.5.0': + dependencies: + '@tursodatabase/database-common': 0.5.0 + '@tursodatabase/database-wasm-common': 0.5.0 + + '@tybys/wasm-util@0.10.1': + dependencies: + tslib: 2.8.1 + '@types/bun@1.3.10': dependencies: bun-types: 1.3.10 @@ -2789,8 +2867,6 @@ snapshots: devalue@5.6.3: {} - dexie@4.3.0: {} - dompurify@3.3.1: optionalDependencies: '@types/trusted-types': 2.0.7 @@ -2968,8 +3044,6 @@ snapshots: flatted@3.3.4: {} - flexsearch@0.8.212: {} - fsevents@2.3.3: optional: true diff --git a/src/hooks.server.ts b/src/hooks.server.ts index 71884c2..8289357 100644 --- a/src/hooks.server.ts +++ b/src/hooks.server.ts @@ -6,5 +6,12 @@ export const handle: Handle = async ({ event, resolve }) => { event.locals.session = session; event.locals.client = client; event.locals.did = did; - return resolve(event); + + const response = await resolve(event); + + // Required for @tursodatabase/database-wasm (OPFS + SharedArrayBuffer) + response.headers.set('Cross-Origin-Opener-Policy', 'same-origin'); + response.headers.set('Cross-Origin-Embedder-Policy', 'credentialless'); + + return response; }; diff --git a/src/lib/db.ts b/src/lib/db.ts index d75746c..ffc93ca 100644 --- a/src/lib/db.ts +++ b/src/lib/db.ts @@ -1,25 +1,655 @@ -import Dexie, { type EntityTable } from 'dexie'; +import type { Database } from '@tursodatabase/database-wasm/vite'; -export interface StoredPost { +export type SourceType = 'likes' | 'bookmarks' | 'posts' | 'reposts'; +export const ALL_SOURCES: SourceType[] = ['likes', 'bookmarks', 'posts', 'reposts']; + +let dbInstance: Database | null = null; +let dbReady: Promise | null = null; + +const SCHEMA_VERSION = 2; + +export async function getDb(): Promise { + if (dbInstance) return dbInstance; + if (dbReady) return dbReady; + dbReady = initDb(); + dbInstance = await dbReady; + return dbInstance; +} + +async function initDb(): Promise { + const { connect } = await import('@tursodatabase/database-wasm/vite'); + const db: Database = await connect('atmo.db'); + + await db.exec('PRAGMA journal_mode = WAL'); + await db.exec('PRAGMA foreign_keys = ON'); + + await db.exec(`CREATE TABLE IF NOT EXISTS schema_version (version INTEGER PRIMARY KEY)`); + const row = await (await db.prepare('SELECT version FROM schema_version ORDER BY version DESC LIMIT 1')).get() as + | { version: number } + | undefined; + const currentVersion = row?.version ?? 0; + + if (currentVersion < SCHEMA_VERSION) { + await applyMigrations(db, currentVersion); + } + + // Clean up legacy IndexedDB databases from Dexie + FlexSearch + if (typeof indexedDB !== 'undefined') { + try { + const dbs = await indexedDB.databases(); + for (const dbInfo of dbs) { + if (dbInfo.name) indexedDB.deleteDatabase(dbInfo.name); + } + } catch { + // databases() may not be supported in all browsers + } + } + + // Clean up legacy localStorage + for (const source of ALL_SOURCES) { + localStorage.removeItem(`${source}-ids`); + localStorage.removeItem(`${source}-cursor`); + } + + return db; +} + +async function applyMigrations(db: Database, fromVersion: number) { + if (fromVersion < 1) { + // Drop any partially-created tables from a previous failed migration + await db.exec(` + DROP TABLE IF EXISTS post_sources; + DROP TABLE IF EXISTS sync_cursors; + DROP TABLE IF EXISTS posts; + DROP TABLE IF EXISTS accounts; + `); + await db.exec(` + CREATE TABLE IF NOT EXISTS accounts ( + did TEXT PRIMARY KEY, + handle TEXT NOT NULL, + display_name TEXT, + avatar TEXT, + added_at INTEGER NOT NULL DEFAULT (unixepoch('now')) + ); + + CREATE TABLE IF NOT EXISTS posts ( + uri TEXT PRIMARY KEY, + author_did TEXT NOT NULL, + author_handle TEXT NOT NULL DEFAULT '', + author_display_name TEXT NOT NULL DEFAULT '', + text TEXT NOT NULL DEFAULT '', + created_at TEXT, + like_count INTEGER NOT NULL DEFAULT 0, + repost_count INTEGER NOT NULL DEFAULT 0, + reply_count INTEGER NOT NULL DEFAULT 0, + has_image INTEGER NOT NULL DEFAULT 0, + has_video INTEGER NOT NULL DEFAULT 0, + has_link INTEGER NOT NULL DEFAULT 0, + is_reply INTEGER NOT NULL DEFAULT 0, + embed_type TEXT, + embed_title TEXT NOT NULL DEFAULT '', + embed_description TEXT NOT NULL DEFAULT '', + quote_text TEXT NOT NULL DEFAULT '', + quote_author_handle TEXT NOT NULL DEFAULT '', + search_text TEXT NOT NULL DEFAULT '', + raw TEXT NOT NULL, + saved_at INTEGER NOT NULL, + fetched_at INTEGER NOT NULL + ); + + CREATE TABLE IF NOT EXISTS post_sources ( + uri TEXT NOT NULL REFERENCES posts(uri) ON DELETE CASCADE, + account_did TEXT NOT NULL REFERENCES accounts(did) ON DELETE CASCADE, + source TEXT NOT NULL CHECK(source IN ('likes','bookmarks','posts','reposts')), + PRIMARY KEY (uri, account_did, source) + ); + + CREATE TABLE IF NOT EXISTS sync_cursors ( + account_did TEXT NOT NULL, + source TEXT NOT NULL CHECK(source IN ('likes','bookmarks','posts','reposts')), + tail_cursor TEXT, + PRIMARY KEY (account_did, source) + ); + + -- Indexes for common query patterns + CREATE INDEX IF NOT EXISTS idx_posts_created_at ON posts(created_at DESC); + CREATE INDEX IF NOT EXISTS idx_posts_like_count ON posts(like_count); + CREATE INDEX IF NOT EXISTS idx_posts_repost_count ON posts(repost_count); + CREATE INDEX IF NOT EXISTS idx_posts_reply_count ON posts(reply_count); + CREATE INDEX IF NOT EXISTS idx_posts_author_handle ON posts(author_handle); + CREATE INDEX IF NOT EXISTS idx_posts_author_did ON posts(author_did); + CREATE INDEX IF NOT EXISTS idx_post_sources_account ON post_sources(account_did, source); + CREATE INDEX IF NOT EXISTS idx_post_sources_uri ON post_sources(uri); + `); + + } + + if (fromVersion >= 1 && fromVersion < 2) { + // v2: Added search_text column, removed FTS5. + // Easiest to drop and recreate since there's no user-created data to preserve. + await db.exec(` + DROP TABLE IF EXISTS post_sources; + DROP TABLE IF EXISTS sync_cursors; + DROP TABLE IF EXISTS posts; + DROP TABLE IF EXISTS accounts; + `); + // Re-create tables with the current v1 DDL (which includes search_text) + fromVersion = 0; + await applyMigrations(db, fromVersion); + } + + await (await db.prepare('INSERT OR REPLACE INTO schema_version (version) VALUES (?)')).run(SCHEMA_VERSION); +} + +// --- PostView field extraction --- + +export interface ExtractedPost { uri: string; - sources: string[]; - savedAt: number; - fetchedAt: number; - // PostView fields stored inline - [key: string]: any; + author_did: string; + author_handle: string; + author_display_name: string; + text: string; + created_at: string | null; + like_count: number; + repost_count: number; + reply_count: number; + has_image: number; + has_video: number; + has_link: number; + is_reply: number; + embed_type: string | null; + embed_title: string; + embed_description: string; + quote_text: string; + quote_author_handle: string; + search_text: string; + raw: string; + saved_at: number; + fetched_at: number; +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function extractPostFields(postView: any, savedAt: number, fetchedAt: number): ExtractedPost { + const embed = postView.embed; + const record = postView.record; + + let hasImage = 0, + hasVideo = 0, + hasLink = 0; + let embedType: string | null = null; + let embedTitle = '', + embedDescription = ''; + let quoteText = '', + quoteAuthorHandle = ''; + + if (embed) { + embedType = embed.$type; + + if (embed.$type === 'app.bsky.embed.images#view') { + hasImage = 1; + } else if (embed.$type === 'app.bsky.embed.video#view') { + hasVideo = 1; + } else if (embed.$type === 'app.bsky.embed.external#view') { + hasLink = 1; + embedTitle = embed.external?.title ?? ''; + embedDescription = embed.external?.description ?? ''; + } else if (embed.$type === 'app.bsky.embed.record#view') { + const embeddedRecord = embed.record; + if (embeddedRecord?.value?.text) { + quoteText = embeddedRecord.value.text; + quoteAuthorHandle = embeddedRecord.author?.handle ?? ''; + } + } else if (embed.$type === 'app.bsky.embed.recordWithMedia#view') { + const media = embed.media; + if (media?.$type === 'app.bsky.embed.images#view') hasImage = 1; + if (media?.$type === 'app.bsky.embed.video#view') hasVideo = 1; + if (media?.$type === 'app.bsky.embed.external#view') { + hasLink = 1; + embedTitle = media.external?.title ?? ''; + embedDescription = media.external?.description ?? ''; + } + const embeddedRecord = embed.record?.record; + if (embeddedRecord?.value?.text) { + quoteText = embeddedRecord.value.text; + quoteAuthorHandle = embeddedRecord.author?.handle ?? ''; + } + } + } + + // Check for links in facets + if (!hasLink && Array.isArray(record?.facets)) { + hasLink = record.facets.some( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (f: any) => f.features?.some((feat: any) => feat.$type === 'app.bsky.richtext.facet#link') + ) + ? 1 + : 0; + } + + const authorHandle = postView.author?.handle ?? ''; + const authorDisplayName = postView.author?.displayName ?? ''; + const postText = record?.text ?? ''; + + // Concatenate all searchable fields, lowercased, for LIKE-based search + const searchText = [authorHandle, authorDisplayName, postText, embedTitle, embedDescription, quoteText, quoteAuthorHandle] + .filter(Boolean) + .join(' ') + .toLowerCase(); + + return { + uri: postView.uri, + author_did: postView.author?.did ?? '', + author_handle: authorHandle, + author_display_name: authorDisplayName, + text: postText, + created_at: record?.createdAt ?? null, + like_count: postView.likeCount ?? 0, + repost_count: postView.repostCount ?? 0, + reply_count: postView.replyCount ?? 0, + has_image: hasImage, + has_video: hasVideo, + has_link: hasLink, + is_reply: record?.reply ? 1 : 0, + embed_type: embedType, + embed_title: embedTitle, + embed_description: embedDescription, + quote_text: quoteText, + quote_author_handle: quoteAuthorHandle, + search_text: searchText, + raw: JSON.stringify(postView), + saved_at: savedAt, + fetched_at: fetchedAt + }; +} + +// --- CRUD helpers --- + +export async function upsertAccount( + db: Database, + did: string, + handle: string, + displayName?: string | null, + avatar?: string | null +) { + await (await db.prepare( + `INSERT INTO accounts (did, handle, display_name, avatar) + VALUES (?, ?, ?, ?) + ON CONFLICT(did) DO UPDATE SET + handle = excluded.handle, + display_name = excluded.display_name, + avatar = excluded.avatar` + )).run(did, handle, displayName ?? null, avatar ?? null); +} + +export async function getAccounts(db: Database): Promise<{ did: string; handle: string; display_name: string | null }[]> { + return (await db.prepare('SELECT did, handle, display_name FROM accounts ORDER BY added_at')).all() as { + did: string; + handle: string; + display_name: string | null; + }[]; +} + +export async function postExistsForSource( + db: Database, + uri: string, + accountDid: string, + source: SourceType +): Promise { + const row = await (await db.prepare( + 'SELECT 1 FROM post_sources WHERE uri = ? AND account_did = ? AND source = ?' + )).get(uri, accountDid, source); + return !!row; } -export interface SourceMeta { - source: string; - tailCursor?: string; // deepest point reached paginating into the past +export async function postExists(db: Database, uri: string): Promise { + const row = await (await db.prepare('SELECT 1 FROM posts WHERE uri = ?')).get(uri); + return !!row; } -export const db = new Dexie('atmo') as Dexie & { - posts: EntityTable; - meta: EntityTable; +export async function getSourceCount(db: Database, accountDid: string, source: SourceType): Promise { + const row = await (await db.prepare( + 'SELECT COUNT(*) as count FROM post_sources WHERE account_did = ? AND source = ?' + )).get(accountDid, source) as { count: number } | undefined; + return row?.count ?? 0; +} + +export async function getCursor(db: Database, accountDid: string, source: SourceType): Promise { + const row = await (await db.prepare( + 'SELECT tail_cursor FROM sync_cursors WHERE account_did = ? AND source = ?' + )).get(accountDid, source) as { tail_cursor: string | null } | undefined; + return row?.tail_cursor ?? undefined; +} + +export async function setCursor(db: Database, accountDid: string, source: SourceType, cursor: string | undefined) { + await (await db.prepare( + `INSERT OR REPLACE INTO sync_cursors (account_did, source, tail_cursor) + VALUES (?, ?, ?)` + )).run(accountDid, source, cursor ?? null); +} + +// --- Batch operations --- + +export async function bulkUpsertPosts( + db: Database, + posts: ExtractedPost[], + accountDid: string, + source: SourceType +): Promise { + if (posts.length === 0) return 0; + + const insertPost = await db.prepare( + `INSERT INTO posts (uri, author_did, author_handle, author_display_name, text, + created_at, like_count, repost_count, reply_count, has_image, has_video, + has_link, is_reply, embed_type, embed_title, embed_description, + quote_text, quote_author_handle, search_text, raw, saved_at, fetched_at) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + ON CONFLICT(uri) DO UPDATE SET + like_count = excluded.like_count, + repost_count = excluded.repost_count, + reply_count = excluded.reply_count, + raw = excluded.raw, + fetched_at = excluded.fetched_at` + ); + + const insertSource = await db.prepare( + `INSERT OR IGNORE INTO post_sources (uri, account_did, source) VALUES (?, ?, ?)` + ); + + let count = 0; + await db.exec('BEGIN TRANSACTION'); + try { + for (const post of posts) { + await insertPost.run( + post.uri, + post.author_did, + post.author_handle, + post.author_display_name, + post.text, + post.created_at, + post.like_count, + post.repost_count, + post.reply_count, + post.has_image, + post.has_video, + post.has_link, + post.is_reply, + post.embed_type, + post.embed_title, + post.embed_description, + post.quote_text, + post.quote_author_handle, + post.search_text, + post.raw, + post.saved_at, + post.fetched_at + ); + await insertSource.run(post.uri, accountDid, source); + count++; + } + await db.exec('COMMIT'); + } catch (e) { + await db.exec('ROLLBACK'); + throw e; + } + return count; +} + +/** + * Tag existing posts with a source for this account (they already exist from another source). + * Returns number of posts that were newly tagged. + */ +export async function tagExistingPosts( + db: Database, + uris: string[], + accountDid: string, + source: SourceType +): Promise { + if (uris.length === 0) return 0; + + const insertSource = await db.prepare( + `INSERT OR IGNORE INTO post_sources (uri, account_did, source) VALUES (?, ?, ?)` + ); + + let count = 0; + await db.exec('BEGIN TRANSACTION'); + try { + for (const uri of uris) { + const result = await insertSource.run(uri, accountDid, source); + if (result.changes > 0) count++; + } + await db.exec('COMMIT'); + } catch (e) { + await db.exec('ROLLBACK'); + throw e; + } + return count; +} + +// --- Search --- + +export type SearchFilters = { + handles: string[]; + minLikes: number; + minReposts: number; + minReplies: number; + dateAfter: string; + dateBefore: string; + hasImage: boolean; + hasLink: boolean; + hasVideo: boolean; + showReplies: boolean; +}; + +export const DEFAULT_FILTERS: SearchFilters = { + handles: [], + minLikes: 0, + minReposts: 0, + minReplies: 0, + dateAfter: '', + dateBefore: '', + hasImage: false, + hasLink: false, + hasVideo: false, + showReplies: true }; -db.version(1).stores({ - posts: 'uri, *sources, savedAt, fetchedAt, likeCount, repostCount, replyCount', - meta: 'source' -}); +export function filtersActive(filters: SearchFilters): boolean { + return ( + filters.handles.length > 0 || + filters.minLikes > 0 || + filters.minReposts > 0 || + filters.minReplies > 0 || + !!filters.dateAfter || + !!filters.dateBefore || + filters.hasImage || + filters.hasLink || + filters.hasVideo || + !filters.showReplies + ); +} + +export interface SearchParams { + query: string; + filters: SearchFilters; + sources: SourceType[]; + accountDids: string[]; + limit: number; + offset: number; +} + +export interface SearchResult { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + doc: any; + isLiked: boolean; + isBookmarked: boolean; +} + +export async function searchPosts( + db: Database, + params: SearchParams +): Promise<{ results: SearchResult[]; hasMore: boolean }> { + const conditions: string[] = []; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const bindings: any[] = []; + + // Source filter + if (params.sources.length > 0) { + const placeholders = params.sources.map(() => '?').join(', '); + conditions.push(`ps.source IN (${placeholders})`); + bindings.push(...params.sources); + } + + // Account filter + if (params.accountDids.length > 0) { + const placeholders = params.accountDids.map(() => '?').join(', '); + conditions.push(`ps.account_did IN (${placeholders})`); + bindings.push(...params.accountDids); + } + + // Text search via LIKE on the pre-built search_text column + if (params.query.trim()) { + const terms = params.query.trim().toLowerCase().split(/\s+/).filter(Boolean); + for (const term of terms) { + conditions.push('p.search_text LIKE ?'); + bindings.push(`%${term}%`); + } + } + + // Structured filters + if (params.filters.handles.length > 0) { + const handleConditions = params.filters.handles.map(() => 'p.author_handle LIKE ?'); + conditions.push(`(${handleConditions.join(' OR ')})`); + bindings.push(...params.filters.handles.map((h) => `%${h.toLowerCase()}%`)); + } + if (params.filters.minLikes > 0) { + conditions.push('p.like_count >= ?'); + bindings.push(params.filters.minLikes); + } + if (params.filters.minReposts > 0) { + conditions.push('p.repost_count >= ?'); + bindings.push(params.filters.minReposts); + } + if (params.filters.minReplies > 0) { + conditions.push('p.reply_count >= ?'); + bindings.push(params.filters.minReplies); + } + if (params.filters.dateAfter) { + conditions.push('p.created_at >= ?'); + bindings.push(params.filters.dateAfter); + } + if (params.filters.dateBefore) { + const before = new Date(new Date(params.filters.dateBefore).getTime() + 86400000).toISOString(); + conditions.push('p.created_at <= ?'); + bindings.push(before); + } + if (params.filters.hasImage) { + conditions.push('p.has_image = 1'); + } + if (params.filters.hasLink) { + conditions.push('p.has_link = 1'); + } + if (params.filters.hasVideo) { + conditions.push('p.has_video = 1'); + } + if (!params.filters.showReplies) { + conditions.push('p.is_reply = 0'); + } + + const whereClause = conditions.length > 0 ? 'WHERE ' + conditions.join(' AND ') : ''; + + // Always sort by date descending (no relevance ranking without FTS5) + const orderClause = 'ORDER BY p.created_at DESC'; + + // We need the current user's DID for liked/bookmarked status. + const currentDid = params.accountDids[0] ?? ''; + + // Build final bindings in positional order: + // 1. currentDid x2 (for EXISTS subqueries in SELECT) + // 2. WHERE clause bindings + // 3. LIMIT, OFFSET + const finalBindings = [currentDid, currentDid, ...bindings, params.limit + 1, params.offset]; + + const sql = ` + SELECT DISTINCT p.raw, p.uri, + EXISTS(SELECT 1 FROM post_sources WHERE uri = p.uri AND account_did = ? AND source = 'likes') as is_liked, + EXISTS(SELECT 1 FROM post_sources WHERE uri = p.uri AND account_did = ? AND source = 'bookmarks') as is_bookmarked + FROM posts p + JOIN post_sources ps ON ps.uri = p.uri + ${whereClause} + ${orderClause} + LIMIT ? OFFSET ? + `; + + const rows = await (await db.prepare(sql)).all(...finalBindings) as { + raw: string; + uri: string; + is_liked: number; + is_bookmarked: number; + }[]; + + const hasMore = rows.length > params.limit; + const results: SearchResult[] = rows.slice(0, params.limit).map((row) => ({ + doc: JSON.parse(row.raw), + isLiked: row.is_liked === 1, + isBookmarked: row.is_bookmarked === 1 + })); + + return { results, hasMore }; +} + +// --- Clear operations --- + +export async function clearSource(db: Database, accountDid: string, source: SourceType) { + await db.exec('BEGIN TRANSACTION'); + try { + await (await db.prepare('DELETE FROM post_sources WHERE account_did = ? AND source = ?')).run( + accountDid, + source + ); + await (await db.prepare('DELETE FROM sync_cursors WHERE account_did = ? AND source = ?')).run( + accountDid, + source + ); + // Clean up orphaned posts + await db.exec('DELETE FROM posts WHERE uri NOT IN (SELECT DISTINCT uri FROM post_sources)'); + await db.exec('COMMIT'); + } catch (e) { + await db.exec('ROLLBACK'); + throw e; + } +} + +export async function clearAccountData(db: Database, accountDid: string) { + await db.exec('BEGIN TRANSACTION'); + try { + await (await db.prepare('DELETE FROM post_sources WHERE account_did = ?')).run(accountDid); + await (await db.prepare('DELETE FROM sync_cursors WHERE account_did = ?')).run(accountDid); + await (await db.prepare('DELETE FROM accounts WHERE did = ?')).run(accountDid); + await db.exec('DELETE FROM posts WHERE uri NOT IN (SELECT DISTINCT uri FROM post_sources)'); + await db.exec('COMMIT'); + } catch (e) { + await db.exec('ROLLBACK'); + throw e; + } +} + +export async function clearAllData(db: Database) { + await db.exec('BEGIN TRANSACTION'); + try { + await db.exec('DELETE FROM post_sources'); + await db.exec('DELETE FROM posts'); + await db.exec('DELETE FROM sync_cursors'); + await db.exec('DELETE FROM accounts'); + await db.exec('COMMIT'); + } catch (e) { + await db.exec('ROLLBACK'); + throw e; + } +} + +// --- Utilities --- + +export function getLink(uri: string, handle: string) { + const [, , rkey] = uri.replace('at://', '').split('/'); + return `https://bsky.app/profile/${handle}/post/${rkey}`; +} diff --git a/src/lib/search-state.svelte.ts b/src/lib/search-state.svelte.ts index 6929fb1..2895ffc 100644 --- a/src/lib/search-state.svelte.ts +++ b/src/lib/search-state.svelte.ts @@ -1,36 +1,30 @@ import { user } from '$lib/atproto'; import { toast } from '@foxui/core'; -import { Document, Charset, IndexedDB } from 'flexsearch'; import { Client, simpleFetchHandler } from '@atcute/client'; -import { db, type StoredPost } from '$lib/db'; - -export type SourceType = 'likes' | 'bookmarks' | 'posts' | 'reposts'; - -export type SearchFilters = { - handles: string[]; - minLikes: number; - minReposts: number; - minReplies: number; - dateAfter: string; - dateBefore: string; - hasImage: boolean; - hasLink: boolean; - hasVideo: boolean; - showReplies: boolean; -}; - -export const DEFAULT_FILTERS: SearchFilters = { - handles: [], - minLikes: 0, - minReposts: 0, - minReplies: 0, - dateAfter: '', - dateBefore: '', - hasImage: false, - hasLink: false, - hasVideo: false, - showReplies: true -}; +import { + getDb, + extractPostFields, + upsertAccount, + postExistsForSource, + postExists, + getSourceCount, + getCursor, + setCursor, + bulkUpsertPosts, + tagExistingPosts, + searchPosts, + clearSource as dbClearSource, + clearAllData as dbClearAllData, + getAccounts as dbGetAccounts, + clearAccountData as dbClearAccountData, + ALL_SOURCES, + DEFAULT_FILTERS, + type SourceType, + type SearchFilters, + type ExtractedPost +} from '$lib/db'; + +export { ALL_SOURCES, DEFAULT_FILTERS, type SourceType, type SearchFilters }; export const SOURCE_LABELS: Record = { likes: 'Likes', @@ -39,17 +33,23 @@ export const SOURCE_LABELS: Record = { reposts: 'Reposts' }; -export const PLACEHOLDERS: Record = { - likes: 'Search liked posts', - bookmarks: 'Search bookmarks', - posts: 'Search my posts', - reposts: 'Search reposted posts' -}; - -export const ALL_SOURCES: SourceType[] = ['likes', 'bookmarks', 'posts', 'reposts']; +export function getPlaceholder(sources: SourceType[]): string { + if (sources.length === ALL_SOURCES.length) return 'Search all posts'; + if (sources.length > 1) + return `Search ${sources.map((s) => SOURCE_LABELS[s].toLowerCase()).join(' & ')}`; + if (sources.length === 1) { + const labels: Record = { + likes: 'Search liked posts', + bookmarks: 'Search bookmarks', + posts: 'Search my posts', + reposts: 'Search reposted posts' + }; + return labels[sources[0]]; + } + return 'Select a source to search'; +} type SourceState = { - index: Document | null; count: number; indexed: number; totalToIndex: number; @@ -60,7 +60,6 @@ type SourceState = { function createSourceState(): SourceState { return { - index: null, count: 0, indexed: 0, totalToIndex: 0, @@ -70,32 +69,6 @@ function createSourceState(): SourceState { }; } -function createIndex() { - return new Document({ - document: { - id: 'uri', - store: false, - index: [ - { - field: 'author:handle', - tokenize: 'forward', - encoder: Charset.LatinBalance - }, - { - field: 'author:displayName', - tokenize: 'forward', - encoder: Charset.LatinBalance - }, - { - field: 'record:text', - tokenize: 'forward', - encoder: Charset.LatinBalance - } - ] - } - }); -} - const publicClient = new Client({ handler: simpleFetchHandler({ service: 'https://public.api.bsky.app' }) }); @@ -107,45 +80,66 @@ export const searchState = $state({ posts: createSourceState(), reposts: createSourceState() } as Record, - activeSource: 'likes' as SourceType + activeSources: ['likes'] as SourceType[] }); let generation = 0; export async function initSources() { - // Clean up legacy localStorage data + const db = await getDb(); + const accountDid = user.did; + if (!accountDid) return; + + // Register/update current account + await upsertAccount( + db, + accountDid, + user.profile?.handle ?? '', + user.profile?.displayName, + user.profile?.avatar + ); + + // Get counts for each source for (const source of ALL_SOURCES) { - localStorage.removeItem(`${source}-ids`); - localStorage.removeItem(`${source}-cursor`); + searchState.sources[source].count = await getSourceCount(db, accountDid, source); } - await Promise.all( - ALL_SOURCES.map(async (source) => { - const flexDb = new IndexedDB(`${source}-idx`); - searchState.sources[source].index = createIndex(); - await searchState.sources[source].index!.mount(flexDb); - - // Get count from Dexie - searchState.sources[source].count = await db.posts - .where('sources') - .equals(source) - .count(); - }) - ); + // Start loading active source first + startLoading(searchState.activeSources[0] ?? 'likes'); +} + +export function toggleSource(source: SourceType) { + const current = searchState.activeSources; + if (current.includes(source)) { + // Don't allow deselecting all + if (current.length <= 1) return; + searchState.activeSources = current.filter((s) => s !== source); + } else { + searchState.activeSources = [...current, source]; + } - // Start loading (active source first via loadNext) - startLoading(searchState.activeSource); + generation++; + // Start loading any sources that aren't done yet + for (const s of searchState.activeSources) { + if (searchState.sources[s].phase !== 'done') { + startLoading(s); + return; + } + } + loadNext(generation); } -export function switchSource(source: SourceType) { - if (source === searchState.activeSource) return; - searchState.activeSource = source; +export function setActiveSources(sources: SourceType[]) { + if (sources.length === 0) return; + searchState.activeSources = sources; generation++; - if (searchState.sources[source].phase !== 'done') { - startLoading(source); - } else { - loadNext(generation); + for (const s of sources) { + if (searchState.sources[s].phase !== 'done') { + startLoading(s); + return; + } } + loadNext(generation); } export function startLoading(source: SourceType) { @@ -162,9 +156,10 @@ export function startLoading(source: SourceType) { function loadNext(currentGen: number) { if (currentGen !== generation) return; + // Prioritize active sources, then others const sorted = [ - searchState.activeSource, - ...ALL_SOURCES.filter((s) => s !== searchState.activeSource) + ...searchState.activeSources, + ...ALL_SOURCES.filter((s) => !searchState.activeSources.includes(s)) ]; for (const source of sorted) { if (searchState.sources[source].phase !== 'done') { @@ -177,12 +172,17 @@ function loadNext(currentGen: number) { async function fetchRecordPage( source: SourceType, s: SourceState, + // eslint-disable-next-line @typescript-eslint/no-explicit-any fetchFn: Function, cursor: string | undefined, myGen: number ): Promise<{ cursor: string | undefined; done: boolean }> { if (myGen !== generation) return { cursor, done: true }; + const accountDid = user.did; + if (!accountDid) return { cursor, done: true }; + + // eslint-disable-next-line @typescript-eslint/no-explicit-any let data: any; try { data = await fetchFn({ limit: 100, cursor }); @@ -192,40 +192,42 @@ async function fetchRecordPage( return { cursor, done: true }; } - const uris = data.records.map((r: any) => - source === 'posts' ? r.uri : r.value.subject.uri - ); - const existingDocs = await db.posts.bulkGet(uris); - const toUpdate: any[] = []; - const now = Date.now(); + const db = await getDb(); + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const uris = data.records.map((r: any) => (source === 'posts' ? r.uri : r.value.subject.uri)); + + // Check which URIs already exist for this source + const toTag: string[] = []; for (let j = 0; j < uris.length; j++) { const subjectUri = uris[j]; - const existing = existingDocs[j]; - - if (existing) { - if (existing.sources.includes(source)) { - // Already indexed for this source — flush updates and stop. - // Use the last record's rkey as cursor so the tail resumes past this point. - const lastRecord = data.records[j]; - const stopCursor = lastRecord?.uri?.split('/').pop() ?? data.cursor; - if (toUpdate.length > 0) await db.posts.bulkPut(toUpdate); - return { cursor: stopCursor, done: true }; + + if (await postExistsForSource(db, subjectUri, accountDid, source)) { + // Already indexed for this source — flush tags and stop + if (toTag.length > 0) { + const tagged = await tagExistingPosts(db, toTag, accountDid, source); + s.count += tagged; } - // Post exists from another source — queue source tag update - toUpdate.push({ ...existing, sources: [...existing.sources, source], fetchedAt: now }); - s.index!.add(existing as any); - s.count++; - continue; + const lastRecord = data.records[j]; + const stopCursor = lastRecord?.uri?.split('/').pop() ?? data.cursor; + return { cursor: stopCursor, done: true }; } - s.pendingUris.push(subjectUri); + // Check if post exists from another source (already in posts table) + if (await postExists(db, subjectUri)) { + toTag.push(subjectUri); + } else { + s.pendingUris.push(subjectUri); + } } - if (toUpdate.length > 0) await db.posts.bulkPut(toUpdate); + if (toTag.length > 0) { + const tagged = await tagExistingPosts(db, toTag, accountDid, source); + s.count += tagged; + } const nextCursor = data.records.length > 0 ? data.cursor : undefined; - // Detect stalled cursor — if the API returns the same cursor we sent, stop. if (nextCursor && nextCursor === cursor) { return { cursor: nextCursor, done: true }; } @@ -233,7 +235,7 @@ async function fetchRecordPage( } async function loadRecords(source: SourceType, myGen: number) { - if (!user.did || !searchState.sources[source].index) return; + if (!user.did) return; const { listLikeRecords, listPostRecords, listRepostRecords } = await import( '$lib/atproto/server/search.remote' @@ -246,13 +248,15 @@ async function loadRecords(source: SourceType, myGen: number) { : listPostRecords; const s = searchState.sources[source]; + const db = await getDb(); + const accountDid = user.did; if (s.phase === 'idle' || s.phase === 'fetching') { s.phase = 'fetching'; - const meta = await db.meta.get(source); + const tailCursor = await getCursor(db, accountDid, source); - // Step 1: Fetch new posts from the top until we hit one we already have + // Step 1: Fetch new posts from the top let result = { cursor: undefined as string | undefined, done: false }; do { result = await fetchRecordPage(source, s, fetchFn, result.cursor, myGen); @@ -260,9 +264,9 @@ async function loadRecords(source: SourceType, myGen: number) { if (myGen !== generation) return; - // Step 2: Continue from where we left off last time (tail) - if (meta?.tailCursor) { - result = { cursor: meta.tailCursor, done: false }; + // Step 2: Continue from tail cursor + if (tailCursor) { + result = { cursor: tailCursor, done: false }; do { result = await fetchRecordPage(source, s, fetchFn, result.cursor, myGen); } while (!result.done && myGen === generation); @@ -270,13 +274,8 @@ async function loadRecords(source: SourceType, myGen: number) { if (myGen !== generation) return; } - // Save how far we got into the past - if (result.cursor) { - await db.meta.put({ source, tailCursor: result.cursor }); - } else { - // We've reached the very end — no tail cursor needed - await db.meta.put({ source, tailCursor: undefined }); - } + // Save cursor progress + await setCursor(db, accountDid, source, result.cursor); s.totalToIndex = s.pendingUris.length; s.phase = 'hydrating'; @@ -289,6 +288,9 @@ async function loadRecords(source: SourceType, myGen: number) { async function hydrateUris(source: SourceType, myGen: number) { const s = searchState.sources[source]; + const accountDid = user.did; + if (!accountDid) return; + const BATCH_SIZE = 25; const CONCURRENCY = 5; @@ -298,6 +300,8 @@ async function hydrateUris(source: SourceType, myGen: number) { batches.push(remaining.slice(i, i + BATCH_SIZE)); } + const db = await getDb(); + for (let i = 0; i < batches.length; i += CONCURRENCY) { if (myGen !== generation) return; @@ -305,6 +309,7 @@ async function hydrateUris(source: SourceType, myGen: number) { const results = await Promise.all( chunk.map((uris) => publicClient + // eslint-disable-next-line @typescript-eslint/no-explicit-any .get('app.bsky.feed.getPosts', { params: { uris: uris as any } }) .catch((e) => { console.error(`Failed to hydrate ${source} batch:`, e); @@ -314,42 +319,23 @@ async function hydrateUris(source: SourceType, myGen: number) { ); // Collect all posts from this chunk - const allPosts: any[] = []; + const now = Date.now(); + const extracted: ExtractedPost[] = []; for (const result of results) { if (result?.ok) { - allPosts.push(...result.data.posts); - } - } - - if (allPosts.length > 0) { - // Batch-check which already exist in Dexie - const uris = allPosts.map((p) => p.uri); - const existing = await db.posts.bulkGet(uris); - const existingMap = new Map(); - for (const doc of existing) { - if (doc) existingMap.set(doc.uri, doc); - } - - // Build batch for bulkPut - const now = Date.now(); - const toPut: any[] = []; - for (const post of allPosts) { - const ex = existingMap.get(post.uri); - if (ex) { - toPut.push({ ...post, sources: [...ex.sources, source], savedAt: ex.savedAt, fetchedAt: now }); - } else { - toPut.push({ ...post, sources: [source], savedAt: now, fetchedAt: now }); + for (const post of result.data.posts) { + extracted.push(extractPostFields(post, now, now)); } - s.index!.add(post as any); - s.indexed++; - s.count++; } + } - await db.posts.bulkPut(toPut); + if (extracted.length > 0) { + const count = await bulkUpsertPosts(db, extracted, accountDid, source); + s.indexed += count; + s.count += count; } s.pendingIndex += chunk.reduce((sum, b) => sum + b.length, 0); - await s.index!.commit(); } s.phase = 'done'; @@ -359,56 +345,74 @@ async function hydrateUris(source: SourceType, myGen: number) { async function fetchBookmarkPage( source: SourceType, s: SourceState, - getBookmarks: Function, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + getBookmarksFn: Function, cursor: string | undefined, myGen: number ): Promise<{ cursor: string | undefined; done: boolean }> { if (myGen !== generation) return { cursor, done: true }; + const accountDid = user.did; + if (!accountDid) return { cursor, done: true }; + + // eslint-disable-next-line @typescript-eslint/no-explicit-any let data: any; try { - data = await getBookmarks({ limit: 100, cursor }); - } catch (err: any) { + data = await getBookmarksFn({ limit: 100, cursor }); + } catch (err: unknown) { console.error('Failed to fetch bookmarks:', err); - toast.error(`Failed to fetch bookmarks: ${err?.body?.message ?? err?.message ?? err}`); + const message = + err && typeof err === 'object' && 'body' in err + ? (err as { body?: { message?: string } }).body?.message + : err instanceof Error + ? err.message + : String(err); + toast.error(`Failed to fetch bookmarks: ${message}`); return { cursor, done: true }; } + const db = await getDb(); + // eslint-disable-next-line @typescript-eslint/no-explicit-any const validBookmarks = data.bookmarks.filter((b: any) => b.item?.uri); - const uris = validBookmarks.map((b: any) => b.item.uri); - const existingDocs = await db.posts.bulkGet(uris); - const toPut: any[] = []; const now = Date.now(); - - for (let j = 0; j < validBookmarks.length; j++) { - const bookmark = validBookmarks[j]; - const postUri = uris[j]; - const existing = existingDocs[j]; - - if (existing) { - if (existing.sources.includes(source)) { - // Already indexed for this source — flush and stop - if (toPut.length > 0) await db.posts.bulkPut(toPut); - return { cursor: data.cursor ?? cursor, done: true }; + const toInsert: ExtractedPost[] = []; + const toTag: string[] = []; + + for (const bookmark of validBookmarks) { + const postUri = bookmark.item.uri; + + if (await postExistsForSource(db, postUri, accountDid, source)) { + // Already indexed — flush and stop + if (toInsert.length > 0) { + const count = await bulkUpsertPosts(db, toInsert, accountDid, source); + s.count += count; + s.indexed += count; + } + if (toTag.length > 0) { + s.count += await tagExistingPosts(db, toTag, accountDid, source); } - // Exists from another source — queue tag update - toPut.push({ ...existing, sources: [...existing.sources, source], fetchedAt: now }); - s.index!.add(existing as any); - s.count++; - continue; + return { cursor: data.cursor ?? cursor, done: true }; } - // Bookmarks come with full PostView — queue for batch write - toPut.push({ ...bookmark.item, sources: [source], savedAt: now, fetchedAt: now }); - s.index!.add(bookmark.item as any); - s.indexed++; - s.count++; + // Check if post exists from another source + if (await postExists(db, postUri)) { + toTag.push(postUri); + } else { + // Bookmarks come with full PostView + toInsert.push(extractPostFields(bookmark.item, now, now)); + } } - if (toPut.length > 0) await db.posts.bulkPut(toPut); + if (toInsert.length > 0) { + const count = await bulkUpsertPosts(db, toInsert, accountDid, source); + s.count += count; + s.indexed += count; + } + if (toTag.length > 0) { + s.count += await tagExistingPosts(db, toTag, accountDid, source); + } const nextCursor = data.bookmarks.length > 0 ? data.cursor : undefined; - // Detect stalled cursor — if the API returns the same cursor we sent, stop. if (nextCursor && nextCursor === cursor) { return { cursor: nextCursor, done: true }; } @@ -416,191 +420,82 @@ async function fetchBookmarkPage( } async function loadBookmarks(source: SourceType, myGen: number) { - if (!user.did || !searchState.sources[source].index) return; + if (!user.did) return; const { getBookmarks } = await import('$lib/atproto/server/search.remote'); const s = searchState.sources[source]; + const db = await getDb(); + const accountDid = user.did; s.phase = 'fetching'; - const meta = await db.meta.get(source); + const tailCursor = await getCursor(db, accountDid, source); - // Step 1: Fetch new bookmarks from the top until we hit one we already have + // Step 1: Fetch new bookmarks from the top let result = { cursor: undefined as string | undefined, done: false }; do { result = await fetchBookmarkPage(source, s, getBookmarks, result.cursor, myGen); - await s.index!.commit(); } while (!result.done && myGen === generation); if (myGen !== generation) return; - // Step 2: Continue from where we left off last time (tail) - if (meta?.tailCursor) { - result = { cursor: meta.tailCursor, done: false }; + // Step 2: Continue from tail cursor + if (tailCursor) { + result = { cursor: tailCursor, done: false }; do { result = await fetchBookmarkPage(source, s, getBookmarks, result.cursor, myGen); - await s.index!.commit(); } while (!result.done && myGen === generation); if (myGen !== generation) return; } - // Save how far we got into the past - if (result.cursor) { - await db.meta.put({ source, tailCursor: result.cursor }); - } else { - await db.meta.put({ source, tailCursor: undefined }); - } + await setCursor(db, accountDid, source, result.cursor); - await s.index!.commit(); s.phase = 'done'; loadNext(myGen); } -// --- Filter helpers --- +// --- Public search API --- -function hasEmbedType(doc: any, type: string): boolean { - const embed = doc.embed; - if (!embed) return false; - if (embed.$type === type) return true; - if (embed.$type === 'app.bsky.embed.recordWithMedia#view') { - return embed.media?.$type === type; - } - return false; -} - -function hasLinkInFacets(doc: any): boolean { - const facets = doc.record?.facets; - if (!Array.isArray(facets)) return false; - return facets.some( - (f: any) => f.features?.some((feat: any) => feat.$type === 'app.bsky.richtext.facet#link') - ); -} - -function applyFilters(results: any[], filters: SearchFilters): any[] { - let r = results; - if (filters.handles.length > 0) { - r = r.filter((item) => { - const h = item.doc.author?.handle?.toLowerCase(); - return h && filters.handles.some((fh) => h.includes(fh.toLowerCase())); - }); - } - if (filters.minLikes > 0) { - r = r.filter((item) => (item.doc.likeCount ?? 0) >= filters.minLikes); - } - if (filters.minReposts > 0) { - r = r.filter((item) => (item.doc.repostCount ?? 0) >= filters.minReposts); - } - if (filters.minReplies > 0) { - r = r.filter((item) => (item.doc.replyCount ?? 0) >= filters.minReplies); - } - if (filters.dateAfter) { - const after = new Date(filters.dateAfter).getTime(); - r = r.filter((item) => { - const created = item.doc.record?.createdAt; - return created && new Date(created).getTime() >= after; - }); - } - if (filters.dateBefore) { - const before = new Date(filters.dateBefore).getTime() + 86400000; - r = r.filter((item) => { - const created = item.doc.record?.createdAt; - return created && new Date(created).getTime() <= before; - }); - } - if (filters.hasImage) { - r = r.filter((item) => hasEmbedType(item.doc, 'app.bsky.embed.images#view')); - } - if (filters.hasLink) { - r = r.filter( - (item) => - hasEmbedType(item.doc, 'app.bsky.embed.external#view') || hasLinkInFacets(item.doc) - ); - } - if (filters.hasVideo) { - r = r.filter((item) => hasEmbedType(item.doc, 'app.bsky.embed.video#view')); - } - if (!filters.showReplies) { - r = r.filter((item) => !item.doc.record?.reply); - } - return r; -} - -export function filtersActive(filters: SearchFilters): boolean { - return ( - filters.handles.length > 0 || - filters.minLikes > 0 || - filters.minReposts > 0 || - filters.minReplies > 0 || - !!filters.dateAfter || - !!filters.dateBefore || - filters.hasImage || - filters.hasLink || - filters.hasVideo || - !filters.showReplies - ); -} - -function sortByDate(items: any[]): any[] { - return items.sort((a, b) => { - const dateA = a.doc.record?.createdAt ? new Date(a.doc.record.createdAt).getTime() : 0; - const dateB = b.doc.record?.createdAt ? new Date(b.doc.record.createdAt).getTime() : 0; - return dateB - dateA; - }); -} +export { filtersActive } from '$lib/db'; export async function searchIndex( query: string, filters: SearchFilters = DEFAULT_FILTERS, - limit: number = 50 -): Promise<{ results: any[]; hasMore: boolean }> { - const source = searchState.activeSource; - const hasFilters = filtersActive(filters); - - let docs: any[]; - - if (query) { - // Text search via FlexSearch → get URIs → lookup in Dexie - const idx = searchState.sources[source].index; - if (!idx) return { results: [], hasMore: false }; - - const raw = await idx.search({ - query, - merge: true, - limit: hasFilters ? 5000 : limit + 1 - }); - - const uris = raw.map((r: any) => r.id); - docs = (await db.posts.bulkGet(uris)).filter(Boolean) as any[]; - } else { - // No query: get all docs for this source from Dexie - docs = await db.posts.where('sources').equals(source).toArray(); - } - - let wrapped = docs.map((doc) => ({ doc })); - if (hasFilters) wrapped = applyFilters(wrapped, filters); - if (!query) wrapped = sortByDate(wrapped); - - const hasMore = wrapped.length > limit; - return { results: wrapped.slice(0, limit), hasMore }; + sources: SourceType[] = searchState.activeSources, + limit: number = 50, + offset: number = 0 +): Promise<{ + results: { doc: unknown; isLiked: boolean; isBookmarked: boolean }[]; + hasMore: boolean; +}> { + const accountDid = user.did; + if (!accountDid) return { results: [], hasMore: false }; + + const db = await getDb(); + + // Get all account DIDs for cross-account search (for now, just current account) + const accountDids = [accountDid]; + + return searchPosts(db, { + query, + filters, + sources, + accountDids, + limit, + offset + }); } +// --- Clear operations --- + export async function clearSource(source: SourceType) { - // Remove source tag from all posts, delete orphans - const posts = await db.posts.where('sources').equals(source).toArray(); - await db.transaction('rw', db.posts, async () => { - for (const post of posts) { - const newSources = post.sources.filter((s) => s !== source); - if (newSources.length === 0) { - await db.posts.delete(post.uri); - } else { - await db.posts.update(post.uri, { sources: newSources }); - } - } - }); - await db.meta.delete(source); + const accountDid = user.did; + if (!accountDid) return; + + const db = await getDb(); + await dbClearSource(db, accountDid, source); - searchState.sources[source].index?.clear(); searchState.sources[source].count = 0; searchState.sources[source].indexed = 0; searchState.sources[source].totalToIndex = 0; @@ -609,7 +504,40 @@ export async function clearSource(source: SourceType) { searchState.sources[source].pendingIndex = 0; } -export function getLink(uri: string, handle: string) { - const [, , rkey] = uri.replace('at://', '').split('/'); - return `https://bsky.app/profile/${handle}/post/${rkey}`; +export async function clearAll() { + const db = await getDb(); + await dbClearAllData(db); + + for (const source of ALL_SOURCES) { + searchState.sources[source].count = 0; + searchState.sources[source].indexed = 0; + searchState.sources[source].totalToIndex = 0; + searchState.sources[source].phase = 'idle'; + searchState.sources[source].pendingUris = []; + searchState.sources[source].pendingIndex = 0; + } +} + +export async function clearAccount(accountDid: string) { + const db = await getDb(); + await dbClearAccountData(db, accountDid); + + // If clearing current account, reset state + if (accountDid === user.did) { + for (const source of ALL_SOURCES) { + searchState.sources[source].count = 0; + searchState.sources[source].indexed = 0; + searchState.sources[source].totalToIndex = 0; + searchState.sources[source].phase = 'idle'; + searchState.sources[source].pendingUris = []; + searchState.sources[source].pendingIndex = 0; + } + } +} + +export async function listAccounts(): Promise< + { did: string; handle: string; display_name: string | null }[] +> { + const db = await getDb(); + return dbGetAccounts(db); } diff --git a/src/routes/search/Search.svelte b/src/routes/search/Search.svelte index 060c93d..9b5f6a7 100644 --- a/src/routes/search/Search.svelte +++ b/src/routes/search/Search.svelte @@ -1,26 +1,27 @@ {#each ALL_SOURCES as source} {/each} + @@ -130,7 +153,7 @@ bind:value={search} class="w-full" sizeVariant="lg" - placeholder={PLACEHOLDERS[searchState.activeSource]} + placeholder={getPlaceholder(searchState.activeSources)} /> + + {/each} + + {/if} +