From 58a803b0ce339ea2b2199f55375019fc65deab8b Mon Sep 17 00:00:00 2001 From: Samuel Date: Tue, 31 Dec 2024 16:16:18 +0100 Subject: [PATCH] feat: add built-in backup decryption --- .gitmodules | 3 + .husky/pre-commit | 6 - biome.json | 8 +- package.json | 15 +- pnpm-lock.yaml | 503 +++++++++++++++++---------------- signal-decrypt-backup-wasm | 1 + src/App.tsx | 2 +- src/components/ui/progress.tsx | 34 +++ src/db-queries.ts | 92 ++++-- src/db.ts | 62 ++-- src/index.tsx | 26 +- src/lib/db-cache.ts | 88 ++---- src/lib/decryptor.ts | 47 +++ src/lib/hash.ts | 13 + src/pages/home.tsx | 87 ++++-- src/pages/overview/index.tsx | 78 +++-- vite.config.ts | 13 +- 17 files changed, 641 insertions(+), 437 deletions(-) create mode 100644 .gitmodules create mode 160000 signal-decrypt-backup-wasm create mode 100644 src/components/ui/progress.tsx create mode 100644 src/lib/decryptor.ts create mode 100644 src/lib/hash.ts diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..e420e16 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "signal-decrypt-backup-wasm"] + path = signal-decrypt-backup-wasm + url = https://git.duskflower.dev/duskflower/signal-decrypt-backup-wasm diff --git a/.husky/pre-commit b/.husky/pre-commit index b793c42..0d4e3a9 100644 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,7 +1 @@ -# pnpm -export PNPM_HOME="/home/samuel/.local/share/pnpm" -case ":$PATH:" in - *":$PNPM_HOME:"*) ;; - *) export PATH="$PNPM_HOME:$PATH" ;; -esac pnpm dlx lint-staged \ No newline at end of file diff --git a/biome.json b/biome.json index 1c4089f..46a94d3 100644 --- a/biome.json +++ b/biome.json @@ -108,7 +108,13 @@ } } }, - "ignore": ["dist/**/*.ts", "dist/**", "**/*.mjs", "eslint.config.js", "**/*.js"] + "ignore": [ + "dist/**/*.ts", + "dist/**", + "**/*.mjs", + "eslint.config.js", + "**/*.js" + ] }, "javascript": { "formatter": { diff --git a/package.json b/package.json index f89bbb3..47d5729 100644 --- a/package.json +++ b/package.json @@ -8,8 +8,6 @@ "dev": "vite", "build": "vite build", "serve": "vite preview", - "postinstall": "cp ./node_modules/sql.js/dist/sql-wasm.wasm ./src/assets/sql-wasm.wasm", - "generate-db-types": "kysely-codegen --dialect=sqlite --url=./src/assets/database.sqlite", "prepare": "husky" }, "license": "MIT", @@ -27,16 +25,20 @@ "postcss": "^8.4.49", "tailwindcss": "^3.4.17", "typescript": "^5.7.2", - "vite": "^6.0.4", - "vite-plugin-solid": "^2.11.0" + "vite": "^6.0.6", + "vite-plugin-solid": "^2.11.0", + "vite-plugin-wasm": "^3.4.1" }, "dependencies": { + "@duskflower/signal-decrypt-backup-wasm": "^0.2.0", "@kobalte/core": "^0.13.7", "@kobalte/tailwindcss": "^0.9.0", "@solid-primitives/refs": "^1.0.8", + "@solid-primitives/storage": "^4.2.1", "@solid-primitives/workers": "^0.3.0", "@solidjs/meta": "^0.29.4", "@solidjs/router": "^0.15.2", + "@sqlite.org/sqlite-wasm": "3.47.2-build1", "@tanstack/solid-table": "^8.20.5", "chart.js": "^4.4.7", "chartjs-chart-wordcloud": "^4.4.4", @@ -47,11 +49,10 @@ "date-fns": "^4.1.0", "kysely": "^0.27.5", "kysely-wasm": "^0.7.0", - "lucide-solid": "^0.468.0", + "lucide-solid": "^0.469.0", "seroval": "^1.1.1", "solid-js": "^1.9.3", - "sql.js": "^1.12.0", - "tailwind-merge": "^2.5.5", + "tailwind-merge": "^2.6.0", "tailwindcss-animate": "^1.0.7" }, "lint-staged": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 37a3b43..74284cb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,6 +8,9 @@ importers: .: dependencies: + '@duskflower/signal-decrypt-backup-wasm': + specifier: ^0.2.0 + version: 0.2.0 '@kobalte/core': specifier: ^0.13.7 version: 0.13.7(solid-js@1.9.3) @@ -17,6 +20,9 @@ importers: '@solid-primitives/refs': specifier: ^1.0.8 version: 1.0.8(solid-js@1.9.3) + '@solid-primitives/storage': + specifier: ^4.2.1 + version: 4.2.1(solid-js@1.9.3) '@solid-primitives/workers': specifier: ^0.3.0 version: 0.3.0(solid-js@1.9.3) @@ -26,6 +32,9 @@ importers: '@solidjs/router': specifier: ^0.15.2 version: 0.15.2(solid-js@1.9.3) + '@sqlite.org/sqlite-wasm': + specifier: 3.47.2-build1 + version: 3.47.2-build1 '@tanstack/solid-table': specifier: ^8.20.5 version: 8.20.5(solid-js@1.9.3) @@ -57,20 +66,17 @@ importers: specifier: ^0.7.0 version: 0.7.0(kysely@0.27.5) lucide-solid: - specifier: ^0.468.0 - version: 0.468.0(solid-js@1.9.3) + specifier: ^0.469.0 + version: 0.469.0(solid-js@1.9.3) seroval: specifier: ^1.1.1 version: 1.1.1 solid-js: specifier: ^1.9.3 version: 1.9.3 - sql.js: - specifier: ^1.12.0 - version: 1.12.0 tailwind-merge: - specifier: ^2.5.5 - version: 2.5.5 + specifier: ^2.6.0 + version: 2.6.0 tailwindcss-animate: specifier: ^1.0.7 version: 1.0.7(tailwindcss@3.4.17) @@ -115,11 +121,14 @@ importers: specifier: ^5.7.2 version: 5.7.2 vite: - specifier: ^6.0.4 - version: 6.0.4(@types/node@22.10.2)(jiti@2.4.2)(yaml@2.6.1) + specifier: ^6.0.6 + version: 6.0.6(@types/node@22.10.2)(jiti@2.4.2)(yaml@2.6.1) vite-plugin-solid: specifier: ^2.11.0 - version: 2.11.0(solid-js@1.9.3)(vite@6.0.4(@types/node@22.10.2)(jiti@2.4.2)(yaml@2.6.1)) + version: 2.11.0(solid-js@1.9.3)(vite@6.0.6(@types/node@22.10.2)(jiti@2.4.2)(yaml@2.6.1)) + vite-plugin-wasm: + specifier: ^3.4.1 + version: 3.4.1(vite@6.0.6(@types/node@22.10.2)(jiti@2.4.2)(yaml@2.6.1)) packages: @@ -335,146 +344,155 @@ packages: peerDependencies: solid-js: ^1.8 - '@esbuild/aix-ppc64@0.24.0': - resolution: {integrity: sha512-WtKdFM7ls47zkKHFVzMz8opM7LkcsIp9amDUBIAWirg70RM71WRSjdILPsY5Uv1D42ZpUfaPILDlfactHgsRkw==} + '@duskflower/signal-decrypt-backup-wasm@0.2.0': + resolution: {integrity: sha512-vVDdJZhVFSfEYTsKQsHO85p/zyx9TuVTj6jaPvH1MRYtvL5812VTSQ1MBeERP1XWyySrZhNyNtvy2dV1A0Z0kQ==, tarball: https://git.duskflower.dev/api/packages/duskflower/npm/%40duskflower%2Fsignal-decrypt-backup-wasm/-/0.2.0/signal-decrypt-backup-wasm-0.2.0.tgz} + + '@esbuild/aix-ppc64@0.24.2': + resolution: {integrity: sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] - '@esbuild/android-arm64@0.24.0': - resolution: {integrity: sha512-Vsm497xFM7tTIPYK9bNTYJyF/lsP590Qc1WxJdlB6ljCbdZKU9SY8i7+Iin4kyhV/KV5J2rOKsBQbB77Ab7L/w==} + '@esbuild/android-arm64@0.24.2': + resolution: {integrity: sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==} engines: {node: '>=18'} cpu: [arm64] os: [android] - '@esbuild/android-arm@0.24.0': - resolution: {integrity: sha512-arAtTPo76fJ/ICkXWetLCc9EwEHKaeya4vMrReVlEIUCAUncH7M4bhMQ+M9Vf+FFOZJdTNMXNBrWwW+OXWpSew==} + '@esbuild/android-arm@0.24.2': + resolution: {integrity: sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==} engines: {node: '>=18'} cpu: [arm] os: [android] - '@esbuild/android-x64@0.24.0': - resolution: {integrity: sha512-t8GrvnFkiIY7pa7mMgJd7p8p8qqYIz1NYiAoKc75Zyv73L3DZW++oYMSHPRarcotTKuSs6m3hTOa5CKHaS02TQ==} + '@esbuild/android-x64@0.24.2': + resolution: {integrity: sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==} engines: {node: '>=18'} cpu: [x64] os: [android] - '@esbuild/darwin-arm64@0.24.0': - resolution: {integrity: sha512-CKyDpRbK1hXwv79soeTJNHb5EiG6ct3efd/FTPdzOWdbZZfGhpbcqIpiD0+vwmpu0wTIL97ZRPZu8vUt46nBSw==} + '@esbuild/darwin-arm64@0.24.2': + resolution: {integrity: sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] - '@esbuild/darwin-x64@0.24.0': - resolution: {integrity: sha512-rgtz6flkVkh58od4PwTRqxbKH9cOjaXCMZgWD905JOzjFKW+7EiUObfd/Kav+A6Gyud6WZk9w+xu6QLytdi2OA==} + '@esbuild/darwin-x64@0.24.2': + resolution: {integrity: sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==} engines: {node: '>=18'} cpu: [x64] os: [darwin] - '@esbuild/freebsd-arm64@0.24.0': - resolution: {integrity: sha512-6Mtdq5nHggwfDNLAHkPlyLBpE5L6hwsuXZX8XNmHno9JuL2+bg2BX5tRkwjyfn6sKbxZTq68suOjgWqCicvPXA==} + '@esbuild/freebsd-arm64@0.24.2': + resolution: {integrity: sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-x64@0.24.0': - resolution: {integrity: sha512-D3H+xh3/zphoX8ck4S2RxKR6gHlHDXXzOf6f/9dbFt/NRBDIE33+cVa49Kil4WUjxMGW0ZIYBYtaGCa2+OsQwQ==} + '@esbuild/freebsd-x64@0.24.2': + resolution: {integrity: sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] - '@esbuild/linux-arm64@0.24.0': - resolution: {integrity: sha512-TDijPXTOeE3eaMkRYpcy3LarIg13dS9wWHRdwYRnzlwlA370rNdZqbcp0WTyyV/k2zSxfko52+C7jU5F9Tfj1g==} + '@esbuild/linux-arm64@0.24.2': + resolution: {integrity: sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==} engines: {node: '>=18'} cpu: [arm64] os: [linux] - '@esbuild/linux-arm@0.24.0': - resolution: {integrity: sha512-gJKIi2IjRo5G6Glxb8d3DzYXlxdEj2NlkixPsqePSZMhLudqPhtZ4BUrpIuTjJYXxvF9njql+vRjB2oaC9XpBw==} + '@esbuild/linux-arm@0.24.2': + resolution: {integrity: sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==} engines: {node: '>=18'} cpu: [arm] os: [linux] - '@esbuild/linux-ia32@0.24.0': - resolution: {integrity: sha512-K40ip1LAcA0byL05TbCQ4yJ4swvnbzHscRmUilrmP9Am7//0UjPreh4lpYzvThT2Quw66MhjG//20mrufm40mA==} + '@esbuild/linux-ia32@0.24.2': + resolution: {integrity: sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==} engines: {node: '>=18'} cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.24.0': - resolution: {integrity: sha512-0mswrYP/9ai+CU0BzBfPMZ8RVm3RGAN/lmOMgW4aFUSOQBjA31UP8Mr6DDhWSuMwj7jaWOT0p0WoZ6jeHhrD7g==} + '@esbuild/linux-loong64@0.24.2': + resolution: {integrity: sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==} engines: {node: '>=18'} cpu: [loong64] os: [linux] - '@esbuild/linux-mips64el@0.24.0': - resolution: {integrity: sha512-hIKvXm0/3w/5+RDtCJeXqMZGkI2s4oMUGj3/jM0QzhgIASWrGO5/RlzAzm5nNh/awHE0A19h/CvHQe6FaBNrRA==} + '@esbuild/linux-mips64el@0.24.2': + resolution: {integrity: sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] - '@esbuild/linux-ppc64@0.24.0': - resolution: {integrity: sha512-HcZh5BNq0aC52UoocJxaKORfFODWXZxtBaaZNuN3PUX3MoDsChsZqopzi5UupRhPHSEHotoiptqikjN/B77mYQ==} + '@esbuild/linux-ppc64@0.24.2': + resolution: {integrity: sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] - '@esbuild/linux-riscv64@0.24.0': - resolution: {integrity: sha512-bEh7dMn/h3QxeR2KTy1DUszQjUrIHPZKyO6aN1X4BCnhfYhuQqedHaa5MxSQA/06j3GpiIlFGSsy1c7Gf9padw==} + '@esbuild/linux-riscv64@0.24.2': + resolution: {integrity: sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] - '@esbuild/linux-s390x@0.24.0': - resolution: {integrity: sha512-ZcQ6+qRkw1UcZGPyrCiHHkmBaj9SiCD8Oqd556HldP+QlpUIe2Wgn3ehQGVoPOvZvtHm8HPx+bH20c9pvbkX3g==} + '@esbuild/linux-s390x@0.24.2': + resolution: {integrity: sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==} engines: {node: '>=18'} cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.24.0': - resolution: {integrity: sha512-vbutsFqQ+foy3wSSbmjBXXIJ6PL3scghJoM8zCL142cGaZKAdCZHyf+Bpu/MmX9zT9Q0zFBVKb36Ma5Fzfa8xA==} + '@esbuild/linux-x64@0.24.2': + resolution: {integrity: sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==} engines: {node: '>=18'} cpu: [x64] os: [linux] - '@esbuild/netbsd-x64@0.24.0': - resolution: {integrity: sha512-hjQ0R/ulkO8fCYFsG0FZoH+pWgTTDreqpqY7UnQntnaKv95uP5iW3+dChxnx7C3trQQU40S+OgWhUVwCjVFLvg==} + '@esbuild/netbsd-arm64@0.24.2': + resolution: {integrity: sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.24.2': + resolution: {integrity: sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] - '@esbuild/openbsd-arm64@0.24.0': - resolution: {integrity: sha512-MD9uzzkPQbYehwcN583yx3Tu5M8EIoTD+tUgKF982WYL9Pf5rKy9ltgD0eUgs8pvKnmizxjXZyLt0z6DC3rRXg==} + '@esbuild/openbsd-arm64@0.24.2': + resolution: {integrity: sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-x64@0.24.0': - resolution: {integrity: sha512-4ir0aY1NGUhIC1hdoCzr1+5b43mw99uNwVzhIq1OY3QcEwPDO3B7WNXBzaKY5Nsf1+N11i1eOfFcq+D/gOS15Q==} + '@esbuild/openbsd-x64@0.24.2': + resolution: {integrity: sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] - '@esbuild/sunos-x64@0.24.0': - resolution: {integrity: sha512-jVzdzsbM5xrotH+W5f1s+JtUy1UWgjU0Cf4wMvffTB8m6wP5/kx0KiaLHlbJO+dMgtxKV8RQ/JvtlFcdZ1zCPA==} + '@esbuild/sunos-x64@0.24.2': + resolution: {integrity: sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==} engines: {node: '>=18'} cpu: [x64] os: [sunos] - '@esbuild/win32-arm64@0.24.0': - resolution: {integrity: sha512-iKc8GAslzRpBytO2/aN3d2yb2z8XTVfNV0PjGlCxKo5SgWmNXx82I/Q3aG1tFfS+A2igVCY97TJ8tnYwpUWLCA==} + '@esbuild/win32-arm64@0.24.2': + resolution: {integrity: sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==} engines: {node: '>=18'} cpu: [arm64] os: [win32] - '@esbuild/win32-ia32@0.24.0': - resolution: {integrity: sha512-vQW36KZolfIudCcTnaTpmLQ24Ha1RjygBo39/aLkM2kmjkWmZGEJ5Gn9l5/7tzXA42QGIoWbICfg6KLLkIw6yw==} + '@esbuild/win32-ia32@0.24.2': + resolution: {integrity: sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==} engines: {node: '>=18'} cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.24.0': - resolution: {integrity: sha512-7IAFPrjSQIJrGsK6flwg7NFmwBoSTyF3rl7If0hNUFQU4ilTsEPL6GuMuU9BfIWVVGuRnuIidkSMC+c0Otu8IA==} + '@esbuild/win32-x64@0.24.2': + resolution: {integrity: sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==} engines: {node: '>=18'} cpu: [x64] os: [win32] @@ -498,10 +516,6 @@ packages: resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} - '@jridgewell/gen-mapping@0.3.5': - resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==} - engines: {node: '>=6.0.0'} - '@jridgewell/gen-mapping@0.3.8': resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==} engines: {node: '>=6.0.0'} @@ -554,98 +568,98 @@ packages: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} - '@rollup/rollup-android-arm-eabi@4.28.1': - resolution: {integrity: sha512-2aZp8AES04KI2dy3Ss6/MDjXbwBzj+i0GqKtWXgw2/Ma6E4jJvujryO6gJAghIRVz7Vwr9Gtl/8na3nDUKpraQ==} + '@rollup/rollup-android-arm-eabi@4.29.1': + resolution: {integrity: sha512-ssKhA8RNltTZLpG6/QNkCSge+7mBQGUqJRisZ2MDQcEGaK93QESEgWK2iOpIDZ7k9zPVkG5AS3ksvD5ZWxmItw==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.28.1': - resolution: {integrity: sha512-EbkK285O+1YMrg57xVA+Dp0tDBRB93/BZKph9XhMjezf6F4TpYjaUSuPt5J0fZXlSag0LmZAsTmdGGqPp4pQFA==} + '@rollup/rollup-android-arm64@4.29.1': + resolution: {integrity: sha512-CaRfrV0cd+NIIcVVN/jx+hVLN+VRqnuzLRmfmlzpOzB87ajixsN/+9L5xNmkaUUvEbI5BmIKS+XTwXsHEb65Ew==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.28.1': - resolution: {integrity: sha512-prduvrMKU6NzMq6nxzQw445zXgaDBbMQvmKSJaxpaZ5R1QDM8w+eGxo6Y/jhT/cLoCvnZI42oEqf9KQNYz1fqQ==} + '@rollup/rollup-darwin-arm64@4.29.1': + resolution: {integrity: sha512-2ORr7T31Y0Mnk6qNuwtyNmy14MunTAMx06VAPI6/Ju52W10zk1i7i5U3vlDRWjhOI5quBcrvhkCHyF76bI7kEw==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.28.1': - resolution: {integrity: sha512-WsvbOunsUk0wccO/TV4o7IKgloJ942hVFK1CLatwv6TJspcCZb9umQkPdvB7FihmdxgaKR5JyxDjWpCOp4uZlQ==} + '@rollup/rollup-darwin-x64@4.29.1': + resolution: {integrity: sha512-j/Ej1oanzPjmN0tirRd5K2/nncAhS9W6ICzgxV+9Y5ZsP0hiGhHJXZ2JQ53iSSjj8m6cRY6oB1GMzNn2EUt6Ng==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.28.1': - resolution: {integrity: sha512-HTDPdY1caUcU4qK23FeeGxCdJF64cKkqajU0iBnTVxS8F7H/7BewvYoG+va1KPSL63kQ1PGNyiwKOfReavzvNA==} + '@rollup/rollup-freebsd-arm64@4.29.1': + resolution: {integrity: sha512-91C//G6Dm/cv724tpt7nTyP+JdN12iqeXGFM1SqnljCmi5yTXriH7B1r8AD9dAZByHpKAumqP1Qy2vVNIdLZqw==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.28.1': - resolution: {integrity: sha512-m/uYasxkUevcFTeRSM9TeLyPe2QDuqtjkeoTpP9SW0XxUWfcYrGDMkO/m2tTw+4NMAF9P2fU3Mw4ahNvo7QmsQ==} + '@rollup/rollup-freebsd-x64@4.29.1': + resolution: {integrity: sha512-hEioiEQ9Dec2nIRoeHUP6hr1PSkXzQaCUyqBDQ9I9ik4gCXQZjJMIVzoNLBRGet+hIUb3CISMh9KXuCcWVW/8w==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.28.1': - resolution: {integrity: sha512-QAg11ZIt6mcmzpNE6JZBpKfJaKkqTm1A9+y9O+frdZJEuhQxiugM05gnCWiANHj4RmbgeVJpTdmKRmH/a+0QbA==} + '@rollup/rollup-linux-arm-gnueabihf@4.29.1': + resolution: {integrity: sha512-Py5vFd5HWYN9zxBv3WMrLAXY3yYJ6Q/aVERoeUFwiDGiMOWsMs7FokXihSOaT/PMWUty/Pj60XDQndK3eAfE6A==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.28.1': - resolution: {integrity: sha512-dRP9PEBfolq1dmMcFqbEPSd9VlRuVWEGSmbxVEfiq2cs2jlZAl0YNxFzAQS2OrQmsLBLAATDMb3Z6MFv5vOcXg==} + '@rollup/rollup-linux-arm-musleabihf@4.29.1': + resolution: {integrity: sha512-RiWpGgbayf7LUcuSNIbahr0ys2YnEERD4gYdISA06wa0i8RALrnzflh9Wxii7zQJEB2/Eh74dX4y/sHKLWp5uQ==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.28.1': - resolution: {integrity: sha512-uGr8khxO+CKT4XU8ZUH1TTEUtlktK6Kgtv0+6bIFSeiSlnGJHG1tSFSjm41uQ9sAO/5ULx9mWOz70jYLyv1QkA==} + '@rollup/rollup-linux-arm64-gnu@4.29.1': + resolution: {integrity: sha512-Z80O+taYxTQITWMjm/YqNoe9d10OX6kDh8X5/rFCMuPqsKsSyDilvfg+vd3iXIqtfmp+cnfL1UrYirkaF8SBZA==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.28.1': - resolution: {integrity: sha512-QF54q8MYGAqMLrX2t7tNpi01nvq5RI59UBNx+3+37zoKX5KViPo/gk2QLhsuqok05sSCRluj0D00LzCwBikb0A==} + '@rollup/rollup-linux-arm64-musl@4.29.1': + resolution: {integrity: sha512-fOHRtF9gahwJk3QVp01a/GqS4hBEZCV1oKglVVq13kcK3NeVlS4BwIFzOHDbmKzt3i0OuHG4zfRP0YoG5OF/rA==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-loongarch64-gnu@4.28.1': - resolution: {integrity: sha512-vPul4uodvWvLhRco2w0GcyZcdyBfpfDRgNKU+p35AWEbJ/HPs1tOUrkSueVbBS0RQHAf/A+nNtDpvw95PeVKOA==} + '@rollup/rollup-linux-loongarch64-gnu@4.29.1': + resolution: {integrity: sha512-5a7q3tnlbcg0OodyxcAdrrCxFi0DgXJSoOuidFUzHZ2GixZXQs6Tc3CHmlvqKAmOs5eRde+JJxeIf9DonkmYkw==} cpu: [loong64] os: [linux] - '@rollup/rollup-linux-powerpc64le-gnu@4.28.1': - resolution: {integrity: sha512-pTnTdBuC2+pt1Rmm2SV7JWRqzhYpEILML4PKODqLz+C7Ou2apEV52h19CR7es+u04KlqplggmN9sqZlekg3R1A==} + '@rollup/rollup-linux-powerpc64le-gnu@4.29.1': + resolution: {integrity: sha512-9b4Mg5Yfz6mRnlSPIdROcfw1BU22FQxmfjlp/CShWwO3LilKQuMISMTtAu/bxmmrE6A902W2cZJuzx8+gJ8e9w==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.28.1': - resolution: {integrity: sha512-vWXy1Nfg7TPBSuAncfInmAI/WZDd5vOklyLJDdIRKABcZWojNDY0NJwruY2AcnCLnRJKSaBgf/GiJfauu8cQZA==} + '@rollup/rollup-linux-riscv64-gnu@4.29.1': + resolution: {integrity: sha512-G5pn0NChlbRM8OJWpJFMX4/i8OEU538uiSv0P6roZcbpe/WfhEO+AT8SHVKfp8qhDQzaz7Q+1/ixMy7hBRidnQ==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.28.1': - resolution: {integrity: sha512-/yqC2Y53oZjb0yz8PVuGOQQNOTwxcizudunl/tFs1aLvObTclTwZ0JhXF2XcPT/zuaymemCDSuuUPXJJyqeDOg==} + '@rollup/rollup-linux-s390x-gnu@4.29.1': + resolution: {integrity: sha512-WM9lIkNdkhVwiArmLxFXpWndFGuOka4oJOZh8EP3Vb8q5lzdSCBuhjavJsw68Q9AKDGeOOIHYzYm4ZFvmWez5g==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.28.1': - resolution: {integrity: sha512-fzgeABz7rrAlKYB0y2kSEiURrI0691CSL0+KXwKwhxvj92VULEDQLpBYLHpF49MSiPG4sq5CK3qHMnb9tlCjBw==} + '@rollup/rollup-linux-x64-gnu@4.29.1': + resolution: {integrity: sha512-87xYCwb0cPGZFoGiErT1eDcssByaLX4fc0z2nRM6eMtV9njAfEE6OW3UniAoDhX4Iq5xQVpE6qO9aJbCFumKYQ==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.28.1': - resolution: {integrity: sha512-xQTDVzSGiMlSshpJCtudbWyRfLaNiVPXt1WgdWTwWz9n0U12cI2ZVtWe/Jgwyv/6wjL7b66uu61Vg0POWVfz4g==} + '@rollup/rollup-linux-x64-musl@4.29.1': + resolution: {integrity: sha512-xufkSNppNOdVRCEC4WKvlR1FBDyqCSCpQeMMgv9ZyXqqtKBfkw1yfGMTUTs9Qsl6WQbJnsGboWCp7pJGkeMhKA==} cpu: [x64] os: [linux] - '@rollup/rollup-win32-arm64-msvc@4.28.1': - resolution: {integrity: sha512-wSXmDRVupJstFP7elGMgv+2HqXelQhuNf+IS4V+nUpNVi/GUiBgDmfwD0UGN3pcAnWsgKG3I52wMOBnk1VHr/A==} + '@rollup/rollup-win32-arm64-msvc@4.29.1': + resolution: {integrity: sha512-F2OiJ42m77lSkizZQLuC+jiZ2cgueWQL5YC9tjo3AgaEw+KJmVxHGSyQfDUoYR9cci0lAywv2Clmckzulcq6ig==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.28.1': - resolution: {integrity: sha512-ZkyTJ/9vkgrE/Rk9vhMXhf8l9D+eAhbAVbsGsXKy2ohmJaWg0LPQLnIxRdRp/bKyr8tXuPlXhIoGlEB5XpJnGA==} + '@rollup/rollup-win32-ia32-msvc@4.29.1': + resolution: {integrity: sha512-rYRe5S0FcjlOBZQHgbTKNrqxCBUmgDJem/VQTCcTnA2KCabYSWQDrytOzX7avb79cAAweNmMUb/Zw18RNd4mng==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.28.1': - resolution: {integrity: sha512-ZvK2jBafvttJjoIdKm/Q/Bh7IJ1Ose9IBOwpOXcOvW3ikGTQGmKDgxTC6oCAzW6PynbkKP8+um1du81XJHZ0JA==} + '@rollup/rollup-win32-x64-msvc@4.29.1': + resolution: {integrity: sha512-+10CMg9vt1MoHj6x1pxyjPSMjHTIlqs8/tBztXvPAx24SKs9jwVnKqHJumlH/IzhaPUaj3T6T6wfZr8okdXaIg==} cpu: [x64] os: [win32] @@ -694,6 +708,18 @@ packages: peerDependencies: solid-js: ^1.6.12 + '@solid-primitives/storage@4.2.1': + resolution: {integrity: sha512-1XUJeaSlizH9Eam/+IbIpslHEnggJMNZXzfsr06AlbG6tJtQENMu0+94ZIvooxt4Cyw46wPzcnHYbSK7LzoQAA==} + peerDependencies: + '@tauri-apps/plugin-store': '*' + solid-js: ^1.6.12 + solid-start: '*' + peerDependenciesMeta: + '@tauri-apps/plugin-store': + optional: true + solid-start: + optional: true + '@solid-primitives/trigger@1.1.0': resolution: {integrity: sha512-00BbAiXV66WwjHuKZc3wr0+GLb9C24mMUmi3JdTpNFgHBbrQGrIHubmZDg36c5/7wH+E0GQtOOanwQS063PO+A==} peerDependencies: @@ -719,6 +745,10 @@ packages: peerDependencies: solid-js: ^1.8.6 + '@sqlite.org/sqlite-wasm@3.47.2-build1': + resolution: {integrity: sha512-jDRWfcPYmOsJGN1GplXbw5ZlB+PSzihw1EDElzqkenSI+yK7QYOfwhzYnBR9H8Bv5MHi6EUMb4t3CnLfXCSA8A==} + hasBin: true + '@swc/helpers@0.5.15': resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} @@ -1101,8 +1131,8 @@ packages: error-ex@1.3.2: resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} - esbuild@0.24.0: - resolution: {integrity: sha512-FuLPevChGDshgSicjisSooU0cemp/sGXR841D5LHMB7mTVOmsEHcAxaH3irL53+8YDIeVNQEySh4DaYU/iuPqQ==} + esbuild@0.24.2: + resolution: {integrity: sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==} engines: {node: '>=18'} hasBin: true @@ -1285,10 +1315,6 @@ packages: resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} engines: {node: '>=8'} - is-core-module@2.15.1: - resolution: {integrity: sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==} - engines: {node: '>= 0.4'} - is-core-module@2.16.0: resolution: {integrity: sha512-urTSINYfAYgcbLb0yDQ6egFm6h3Mo1DcF9EkyXSRjjzdHbsulg01qhwWuXdOoUBuTkbQ80KDboXa0vFJ+BDH+g==} engines: {node: '>= 0.4'} @@ -1478,8 +1504,8 @@ packages: lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} - lucide-solid@0.468.0: - resolution: {integrity: sha512-saTgTS9QvkDdWMl2mHxxubz1A8+3hfbdKeZ2KbluONqBhxwncFDyYQCqfsYMzyrGn3JPRy8O6ha0N9qM0TsSSA==} + lucide-solid@0.469.0: + resolution: {integrity: sha512-kBZl5AFg02g/wcwaapTwOwjHw0VvyyFmZm3BE6McKjs0GjiauWtjYbrhf2bCtDpScEtcinhIG/LpRExBlIV3fA==} peerDependencies: solid-js: ^1.4.7 @@ -1739,10 +1765,6 @@ packages: resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} engines: {node: '>=8'} - resolve@1.22.8: - resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} - hasBin: true - resolve@1.22.9: resolution: {integrity: sha512-QxrmX1DzraFIi9PxdG5VkRfRwIgjwyud+z/iBwfRRrVmHc+P9Q7u2lSSpQ6bjr2gy5lrqIiU9vb6iAeGf2400A==} hasBin: true @@ -1758,8 +1780,8 @@ packages: rfdc@1.4.1: resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} - rollup@4.28.1: - resolution: {integrity: sha512-61fXYl/qNVinKmGSTHAZ6Yy8I3YIJC/r2m9feHo6SwVAVcLT5MPwOUFe7EuURA/4m0NR8lXG4BBXuo/IZEsjMg==} + rollup@4.29.1: + resolution: {integrity: sha512-RaJ45M/kmJUzSWDs1Nnd5DdV4eerC98idtUOVr6FfKcgxqvjwHmxc5upLF9qZU9EpsVzzhleFahrT3shLuJzIw==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -1849,9 +1871,6 @@ packages: resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} engines: {node: '>= 10.x'} - sql.js@1.12.0: - resolution: {integrity: sha512-Bi+43yMx/tUFZVYD4AUscmdL6NHn3gYQ+CM+YheFWLftOmrEC/Mz6Yh7E96Y2WDHYz3COSqT+LP6Z79zgrwJlA==} - string-argv@0.3.2: resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} engines: {node: '>=0.6.19'} @@ -1904,8 +1923,8 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} - tailwind-merge@2.5.5: - resolution: {integrity: sha512-0LXunzzAZzo0tEPxV3I297ffKZPlKDrjj7NXphC8V5ak9yHC5zRmxnOe2m/Rd/7ivsOMJe3JZ2JVocoDdQTRBA==} + tailwind-merge@2.6.0: + resolution: {integrity: sha512-P+Vu1qXfzediirmHOC3xKGAYeZtPcV9g76X+xg2FD4tYgR71ewMA35Y3sCz3zhiN/dwefRpJX0yBcgwi1fXNQA==} tailwindcss-animate@1.0.7: resolution: {integrity: sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==} @@ -1988,8 +2007,13 @@ packages: '@testing-library/jest-dom': optional: true - vite@6.0.4: - resolution: {integrity: sha512-zwlH6ar+6o6b4Wp+ydhtIKLrGM/LoqZzcdVmkGAFun0KHTzIzjh+h0kungEx7KJg/PYnC80I4TII9WkjciSR6Q==} + vite-plugin-wasm@3.4.1: + resolution: {integrity: sha512-ja3nSo2UCkVeitltJGkS3pfQHAanHv/DqGatdI39ja6McgABlpsZ5hVgl6wuR8Qx5etY3T5qgDQhOWzc5RReZA==} + peerDependencies: + vite: ^2 || ^3 || ^4 || ^5 || ^6 + + vite@6.0.6: + resolution: {integrity: sha512-NSjmUuckPmDU18bHz7QZ+bTYhRR0iA72cs2QAxCqDpafJ0S6qetco0LB3WW2OxlMHS0JmAv+yZ/R3uPmMyGTjQ==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true peerDependencies: @@ -2086,7 +2110,7 @@ snapshots: '@ampproject/remapping@2.3.0': dependencies: - '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/gen-mapping': 0.3.8 '@jridgewell/trace-mapping': 0.3.25 '@babel/code-frame@7.26.2': @@ -2121,7 +2145,7 @@ snapshots: dependencies: '@babel/parser': 7.26.3 '@babel/types': 7.26.3 - '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/gen-mapping': 0.3.8 '@jridgewell/trace-mapping': 0.3.25 jsesc: 3.0.2 @@ -2348,76 +2372,81 @@ snapshots: '@floating-ui/dom': 1.6.12 solid-js: 1.9.3 - '@esbuild/aix-ppc64@0.24.0': + '@duskflower/signal-decrypt-backup-wasm@0.2.0': {} + + '@esbuild/aix-ppc64@0.24.2': optional: true - '@esbuild/android-arm64@0.24.0': + '@esbuild/android-arm64@0.24.2': optional: true - '@esbuild/android-arm@0.24.0': + '@esbuild/android-arm@0.24.2': optional: true - '@esbuild/android-x64@0.24.0': + '@esbuild/android-x64@0.24.2': optional: true - '@esbuild/darwin-arm64@0.24.0': + '@esbuild/darwin-arm64@0.24.2': optional: true - '@esbuild/darwin-x64@0.24.0': + '@esbuild/darwin-x64@0.24.2': optional: true - '@esbuild/freebsd-arm64@0.24.0': + '@esbuild/freebsd-arm64@0.24.2': optional: true - '@esbuild/freebsd-x64@0.24.0': + '@esbuild/freebsd-x64@0.24.2': optional: true - '@esbuild/linux-arm64@0.24.0': + '@esbuild/linux-arm64@0.24.2': optional: true - '@esbuild/linux-arm@0.24.0': + '@esbuild/linux-arm@0.24.2': optional: true - '@esbuild/linux-ia32@0.24.0': + '@esbuild/linux-ia32@0.24.2': optional: true - '@esbuild/linux-loong64@0.24.0': + '@esbuild/linux-loong64@0.24.2': optional: true - '@esbuild/linux-mips64el@0.24.0': + '@esbuild/linux-mips64el@0.24.2': optional: true - '@esbuild/linux-ppc64@0.24.0': + '@esbuild/linux-ppc64@0.24.2': optional: true - '@esbuild/linux-riscv64@0.24.0': + '@esbuild/linux-riscv64@0.24.2': optional: true - '@esbuild/linux-s390x@0.24.0': + '@esbuild/linux-s390x@0.24.2': optional: true - '@esbuild/linux-x64@0.24.0': + '@esbuild/linux-x64@0.24.2': optional: true - '@esbuild/netbsd-x64@0.24.0': + '@esbuild/netbsd-arm64@0.24.2': optional: true - '@esbuild/openbsd-arm64@0.24.0': + '@esbuild/netbsd-x64@0.24.2': optional: true - '@esbuild/openbsd-x64@0.24.0': + '@esbuild/openbsd-arm64@0.24.2': optional: true - '@esbuild/sunos-x64@0.24.0': + '@esbuild/openbsd-x64@0.24.2': optional: true - '@esbuild/win32-arm64@0.24.0': + '@esbuild/sunos-x64@0.24.2': optional: true - '@esbuild/win32-ia32@0.24.0': + '@esbuild/win32-arm64@0.24.2': optional: true - '@esbuild/win32-x64@0.24.0': + '@esbuild/win32-ia32@0.24.2': + optional: true + + '@esbuild/win32-x64@0.24.2': optional: true '@floating-ui/core@1.6.8': @@ -2448,12 +2477,6 @@ snapshots: wrap-ansi: 8.1.0 wrap-ansi-cjs: wrap-ansi@7.0.0 - '@jridgewell/gen-mapping@0.3.5': - dependencies: - '@jridgewell/set-array': 1.2.1 - '@jridgewell/sourcemap-codec': 1.5.0 - '@jridgewell/trace-mapping': 0.3.25 - '@jridgewell/gen-mapping@0.3.8': dependencies: '@jridgewell/set-array': 1.2.1 @@ -2515,61 +2538,61 @@ snapshots: '@pkgjs/parseargs@0.11.0': optional: true - '@rollup/rollup-android-arm-eabi@4.28.1': + '@rollup/rollup-android-arm-eabi@4.29.1': optional: true - '@rollup/rollup-android-arm64@4.28.1': + '@rollup/rollup-android-arm64@4.29.1': optional: true - '@rollup/rollup-darwin-arm64@4.28.1': + '@rollup/rollup-darwin-arm64@4.29.1': optional: true - '@rollup/rollup-darwin-x64@4.28.1': + '@rollup/rollup-darwin-x64@4.29.1': optional: true - '@rollup/rollup-freebsd-arm64@4.28.1': + '@rollup/rollup-freebsd-arm64@4.29.1': optional: true - '@rollup/rollup-freebsd-x64@4.28.1': + '@rollup/rollup-freebsd-x64@4.29.1': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.28.1': + '@rollup/rollup-linux-arm-gnueabihf@4.29.1': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.28.1': + '@rollup/rollup-linux-arm-musleabihf@4.29.1': optional: true - '@rollup/rollup-linux-arm64-gnu@4.28.1': + '@rollup/rollup-linux-arm64-gnu@4.29.1': optional: true - '@rollup/rollup-linux-arm64-musl@4.28.1': + '@rollup/rollup-linux-arm64-musl@4.29.1': optional: true - '@rollup/rollup-linux-loongarch64-gnu@4.28.1': + '@rollup/rollup-linux-loongarch64-gnu@4.29.1': optional: true - '@rollup/rollup-linux-powerpc64le-gnu@4.28.1': + '@rollup/rollup-linux-powerpc64le-gnu@4.29.1': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.28.1': + '@rollup/rollup-linux-riscv64-gnu@4.29.1': optional: true - '@rollup/rollup-linux-s390x-gnu@4.28.1': + '@rollup/rollup-linux-s390x-gnu@4.29.1': optional: true - '@rollup/rollup-linux-x64-gnu@4.28.1': + '@rollup/rollup-linux-x64-gnu@4.29.1': optional: true - '@rollup/rollup-linux-x64-musl@4.28.1': + '@rollup/rollup-linux-x64-musl@4.29.1': optional: true - '@rollup/rollup-win32-arm64-msvc@4.28.1': + '@rollup/rollup-win32-arm64-msvc@4.29.1': optional: true - '@rollup/rollup-win32-ia32-msvc@4.28.1': + '@rollup/rollup-win32-ia32-msvc@4.29.1': optional: true - '@rollup/rollup-win32-x64-msvc@4.28.1': + '@rollup/rollup-win32-x64-msvc@4.29.1': optional: true '@solid-primitives/event-listener@2.3.3(solid-js@1.9.3)': @@ -2622,6 +2645,11 @@ snapshots: '@solid-primitives/utils': 6.2.3(solid-js@1.9.3) solid-js: 1.9.3 + '@solid-primitives/storage@4.2.1(solid-js@1.9.3)': + dependencies: + '@solid-primitives/utils': 6.2.3(solid-js@1.9.3) + solid-js: 1.9.3 + '@solid-primitives/trigger@1.1.0(solid-js@1.9.3)': dependencies: '@solid-primitives/utils': 6.2.3(solid-js@1.9.3) @@ -2643,6 +2671,8 @@ snapshots: dependencies: solid-js: 1.9.3 + '@sqlite.org/sqlite-wasm@3.47.2-build1': {} + '@swc/helpers@0.5.15': dependencies: tslib: 2.8.1 @@ -3018,32 +3048,33 @@ snapshots: dependencies: is-arrayish: 0.2.1 - esbuild@0.24.0: + esbuild@0.24.2: optionalDependencies: - '@esbuild/aix-ppc64': 0.24.0 - '@esbuild/android-arm': 0.24.0 - '@esbuild/android-arm64': 0.24.0 - '@esbuild/android-x64': 0.24.0 - '@esbuild/darwin-arm64': 0.24.0 - '@esbuild/darwin-x64': 0.24.0 - '@esbuild/freebsd-arm64': 0.24.0 - '@esbuild/freebsd-x64': 0.24.0 - '@esbuild/linux-arm': 0.24.0 - '@esbuild/linux-arm64': 0.24.0 - '@esbuild/linux-ia32': 0.24.0 - '@esbuild/linux-loong64': 0.24.0 - '@esbuild/linux-mips64el': 0.24.0 - '@esbuild/linux-ppc64': 0.24.0 - '@esbuild/linux-riscv64': 0.24.0 - '@esbuild/linux-s390x': 0.24.0 - '@esbuild/linux-x64': 0.24.0 - '@esbuild/netbsd-x64': 0.24.0 - '@esbuild/openbsd-arm64': 0.24.0 - '@esbuild/openbsd-x64': 0.24.0 - '@esbuild/sunos-x64': 0.24.0 - '@esbuild/win32-arm64': 0.24.0 - '@esbuild/win32-ia32': 0.24.0 - '@esbuild/win32-x64': 0.24.0 + '@esbuild/aix-ppc64': 0.24.2 + '@esbuild/android-arm': 0.24.2 + '@esbuild/android-arm64': 0.24.2 + '@esbuild/android-x64': 0.24.2 + '@esbuild/darwin-arm64': 0.24.2 + '@esbuild/darwin-x64': 0.24.2 + '@esbuild/freebsd-arm64': 0.24.2 + '@esbuild/freebsd-x64': 0.24.2 + '@esbuild/linux-arm': 0.24.2 + '@esbuild/linux-arm64': 0.24.2 + '@esbuild/linux-ia32': 0.24.2 + '@esbuild/linux-loong64': 0.24.2 + '@esbuild/linux-mips64el': 0.24.2 + '@esbuild/linux-ppc64': 0.24.2 + '@esbuild/linux-riscv64': 0.24.2 + '@esbuild/linux-s390x': 0.24.2 + '@esbuild/linux-x64': 0.24.2 + '@esbuild/netbsd-arm64': 0.24.2 + '@esbuild/netbsd-x64': 0.24.2 + '@esbuild/openbsd-arm64': 0.24.2 + '@esbuild/openbsd-x64': 0.24.2 + '@esbuild/sunos-x64': 0.24.2 + '@esbuild/win32-arm64': 0.24.2 + '@esbuild/win32-ia32': 0.24.2 + '@esbuild/win32-x64': 0.24.2 escalade@3.2.0: {} @@ -3209,10 +3240,6 @@ snapshots: dependencies: binary-extensions: 2.3.0 - is-core-module@2.15.1: - dependencies: - hasown: 2.0.2 - is-core-module@2.16.0: dependencies: hasown: 2.0.2 @@ -3356,7 +3383,7 @@ snapshots: dependencies: yallist: 3.1.1 - lucide-solid@0.468.0(solid-js@1.9.3): + lucide-solid@0.469.0(solid-js@1.9.3): dependencies: solid-js: 1.9.3 @@ -3571,7 +3598,7 @@ snapshots: rechoir@0.6.2: dependencies: - resolve: 1.22.8 + resolve: 1.22.9 require-directory@2.1.1: {} @@ -3581,12 +3608,6 @@ snapshots: resolve-from@5.0.0: {} - resolve@1.22.8: - dependencies: - is-core-module: 2.15.1 - path-parse: 1.0.7 - supports-preserve-symlinks-flag: 1.0.0 - resolve@1.22.9: dependencies: is-core-module: 2.16.0 @@ -3602,29 +3623,29 @@ snapshots: rfdc@1.4.1: {} - rollup@4.28.1: + rollup@4.29.1: dependencies: '@types/estree': 1.0.6 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.28.1 - '@rollup/rollup-android-arm64': 4.28.1 - '@rollup/rollup-darwin-arm64': 4.28.1 - '@rollup/rollup-darwin-x64': 4.28.1 - '@rollup/rollup-freebsd-arm64': 4.28.1 - '@rollup/rollup-freebsd-x64': 4.28.1 - '@rollup/rollup-linux-arm-gnueabihf': 4.28.1 - '@rollup/rollup-linux-arm-musleabihf': 4.28.1 - '@rollup/rollup-linux-arm64-gnu': 4.28.1 - '@rollup/rollup-linux-arm64-musl': 4.28.1 - '@rollup/rollup-linux-loongarch64-gnu': 4.28.1 - '@rollup/rollup-linux-powerpc64le-gnu': 4.28.1 - '@rollup/rollup-linux-riscv64-gnu': 4.28.1 - '@rollup/rollup-linux-s390x-gnu': 4.28.1 - '@rollup/rollup-linux-x64-gnu': 4.28.1 - '@rollup/rollup-linux-x64-musl': 4.28.1 - '@rollup/rollup-win32-arm64-msvc': 4.28.1 - '@rollup/rollup-win32-ia32-msvc': 4.28.1 - '@rollup/rollup-win32-x64-msvc': 4.28.1 + '@rollup/rollup-android-arm-eabi': 4.29.1 + '@rollup/rollup-android-arm64': 4.29.1 + '@rollup/rollup-darwin-arm64': 4.29.1 + '@rollup/rollup-darwin-x64': 4.29.1 + '@rollup/rollup-freebsd-arm64': 4.29.1 + '@rollup/rollup-freebsd-x64': 4.29.1 + '@rollup/rollup-linux-arm-gnueabihf': 4.29.1 + '@rollup/rollup-linux-arm-musleabihf': 4.29.1 + '@rollup/rollup-linux-arm64-gnu': 4.29.1 + '@rollup/rollup-linux-arm64-musl': 4.29.1 + '@rollup/rollup-linux-loongarch64-gnu': 4.29.1 + '@rollup/rollup-linux-powerpc64le-gnu': 4.29.1 + '@rollup/rollup-linux-riscv64-gnu': 4.29.1 + '@rollup/rollup-linux-s390x-gnu': 4.29.1 + '@rollup/rollup-linux-x64-gnu': 4.29.1 + '@rollup/rollup-linux-x64-musl': 4.29.1 + '@rollup/rollup-win32-arm64-msvc': 4.29.1 + '@rollup/rollup-win32-ia32-msvc': 4.29.1 + '@rollup/rollup-win32-x64-msvc': 4.29.1 fsevents: 2.3.3 run-parallel@1.2.0: @@ -3706,8 +3727,6 @@ snapshots: split2@4.2.0: {} - sql.js@1.12.0: {} - string-argv@0.3.2: {} string-width@4.2.3: @@ -3764,7 +3783,7 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} - tailwind-merge@2.5.5: {} + tailwind-merge@2.6.0: {} tailwindcss-animate@1.0.7(tailwindcss@3.4.17): dependencies: @@ -3854,7 +3873,7 @@ snapshots: validate-html-nesting@1.2.2: {} - vite-plugin-solid@2.11.0(solid-js@1.9.3)(vite@6.0.4(@types/node@22.10.2)(jiti@2.4.2)(yaml@2.6.1)): + vite-plugin-solid@2.11.0(solid-js@1.9.3)(vite@6.0.6(@types/node@22.10.2)(jiti@2.4.2)(yaml@2.6.1)): dependencies: '@babel/core': 7.26.0 '@types/babel__core': 7.20.5 @@ -3862,25 +3881,29 @@ snapshots: merge-anything: 5.1.7 solid-js: 1.9.3 solid-refresh: 0.6.3(solid-js@1.9.3) - vite: 6.0.4(@types/node@22.10.2)(jiti@2.4.2)(yaml@2.6.1) - vitefu: 1.0.4(vite@6.0.4(@types/node@22.10.2)(jiti@2.4.2)(yaml@2.6.1)) + vite: 6.0.6(@types/node@22.10.2)(jiti@2.4.2)(yaml@2.6.1) + vitefu: 1.0.4(vite@6.0.6(@types/node@22.10.2)(jiti@2.4.2)(yaml@2.6.1)) transitivePeerDependencies: - supports-color - vite@6.0.4(@types/node@22.10.2)(jiti@2.4.2)(yaml@2.6.1): + vite-plugin-wasm@3.4.1(vite@6.0.6(@types/node@22.10.2)(jiti@2.4.2)(yaml@2.6.1)): dependencies: - esbuild: 0.24.0 + vite: 6.0.6(@types/node@22.10.2)(jiti@2.4.2)(yaml@2.6.1) + + vite@6.0.6(@types/node@22.10.2)(jiti@2.4.2)(yaml@2.6.1): + dependencies: + esbuild: 0.24.2 postcss: 8.4.49 - rollup: 4.28.1 + rollup: 4.29.1 optionalDependencies: '@types/node': 22.10.2 fsevents: 2.3.3 jiti: 2.4.2 yaml: 2.6.1 - vitefu@1.0.4(vite@6.0.4(@types/node@22.10.2)(jiti@2.4.2)(yaml@2.6.1)): + vitefu@1.0.4(vite@6.0.6(@types/node@22.10.2)(jiti@2.4.2)(yaml@2.6.1)): optionalDependencies: - vite: 6.0.4(@types/node@22.10.2)(jiti@2.4.2)(yaml@2.6.1) + vite: 6.0.6(@types/node@22.10.2)(jiti@2.4.2)(yaml@2.6.1) which@2.0.2: dependencies: diff --git a/signal-decrypt-backup-wasm b/signal-decrypt-backup-wasm new file mode 160000 index 0000000..6559df9 --- /dev/null +++ b/signal-decrypt-backup-wasm @@ -0,0 +1 @@ +Subproject commit 6559df90b63429fcd24cf42fb4ebd91ecdf4d38e diff --git a/src/App.tsx b/src/App.tsx index 716b084..7680828 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,5 +1,5 @@ -import { type Component } from "solid-js"; import { Route } from "@solidjs/router"; +import { type Component } from "solid-js"; import { DmId, GroupId, Home, Overview, preloadDmId } from "./pages"; import "./app.css"; diff --git a/src/components/ui/progress.tsx b/src/components/ui/progress.tsx new file mode 100644 index 0000000..762c65d --- /dev/null +++ b/src/components/ui/progress.tsx @@ -0,0 +1,34 @@ +import type { Component, JSX, ValidComponent } from "solid-js" +import { splitProps } from "solid-js" + +import type { PolymorphicProps } from "@kobalte/core/polymorphic" +import * as ProgressPrimitive from "@kobalte/core/progress" + +import { Label } from "~/components/ui/label" + +type ProgressRootProps = + ProgressPrimitive.ProgressRootProps & { children?: JSX.Element } + +const Progress = ( + props: PolymorphicProps> +) => { + const [local, others] = splitProps(props as ProgressRootProps, ["children"]) + return ( + + {local.children} + + + + + ) +} + +const ProgressLabel: Component = (props) => { + return +} + +const ProgressValueLabel: Component = (props) => { + return +} + +export { Progress, ProgressLabel, ProgressValueLabel } diff --git a/src/db-queries.ts b/src/db-queries.ts index d725215..c8fc830 100644 --- a/src/db-queries.ts +++ b/src/db-queries.ts @@ -1,17 +1,52 @@ import { sql, type NotNull } from "kysely"; +import { db, kyselyDb, SELF_ID, setDbHash } from "./db"; import { cached } from "./lib/db-cache"; -import { kyselyDb, SELF_ID } from "./db"; +import { hashString } from "./lib/hash"; + +export const loadDb = ( + statements: string[], + progressCallback?: (percentage: number) => void, +) => { + const length = statements.length; + let percentage = 0; + + for (let i = 0; i < length; i++) { + const statement = statements[i]; + const newPercentage = Math.round((i / length) * 100); + + try { + db.exec(statement); + + if (newPercentage !== percentage) { + progressCallback?.(newPercentage); + percentage = newPercentage; + } + } catch (e) { + throw new Error(`statement failed: ${statement}`, { + cause: e, + }); + } + } + + setDbHash(hashString(statements.join())); +}; const allThreadsOverviewQueryRaw = () => - kyselyDb() - ?.selectFrom("thread") + kyselyDb + .selectFrom("thread") .innerJoin( (eb) => eb .selectFrom("message") - .select((eb) => ["message.thread_id", eb.fn.countAll().as("message_count")]) + .select((eb) => [ + "message.thread_id", + eb.fn.countAll().as("message_count"), + ]) .where((eb) => { - return eb.and([eb("message.body", "is not", null), eb("message.body", "is not", "")]); + return eb.and([ + eb("message.body", "is not", null), + eb("message.body", "is not", ""), + ]); }) .groupBy("message.thread_id") .as("message"), @@ -41,8 +76,8 @@ const allThreadsOverviewQueryRaw = () => export const allThreadsOverviewQuery = cached(allThreadsOverviewQueryRaw); const overallSentMessagesQueryRaw = (recipientId: number) => - kyselyDb() - ?.selectFrom("message") + kyselyDb + .selectFrom("message") .select((eb) => eb.fn.countAll().as("messageCount")) .where((eb) => eb.and([ @@ -56,8 +91,8 @@ const overallSentMessagesQueryRaw = (recipientId: number) => export const overallSentMessagesQuery = cached(overallSentMessagesQueryRaw); const dmPartnerRecipientQueryRaw = (dmId: number) => - kyselyDb() - ?.selectFrom("recipient") + kyselyDb + .selectFrom("recipient") .select([ "recipient._id", "recipient.system_joined_name", @@ -65,7 +100,9 @@ const dmPartnerRecipientQueryRaw = (dmId: number) => "recipient.nickname_joined_name", ]) .innerJoin("thread", "recipient._id", "thread.recipient_id") - .where((eb) => eb.and([eb("thread._id", "=", dmId), eb("recipient._id", "!=", SELF_ID)])) + .where((eb) => + eb.and([eb("thread._id", "=", dmId), eb("recipient._id", "!=", SELF_ID)]), + ) .$narrowType<{ _id: number; }>() @@ -74,30 +111,47 @@ const dmPartnerRecipientQueryRaw = (dmId: number) => export const dmPartnerRecipientQuery = cached(dmPartnerRecipientQueryRaw); const threadSentMessagesOverviewQueryRaw = (threadId: number) => - kyselyDb() - ?.selectFrom("message") - .select(["from_recipient_id", sql`datetime(date_sent / 1000, 'unixepoch')`.as("message_datetime")]) + kyselyDb + .selectFrom("message") + .select([ + "from_recipient_id", + sql`datetime(date_sent / 1000, 'unixepoch')`.as( + "message_datetime", + ), + ]) .orderBy(["message_datetime"]) - .where((eb) => eb.and([eb("body", "is not", null), eb("body", "!=", ""), eb("thread_id", "=", threadId)])) + .where((eb) => + eb.and([ + eb("body", "is not", null), + eb("body", "!=", ""), + eb("thread_id", "=", threadId), + ]), + ) .execute(); -export const threadSentMessagesOverviewQuery = cached(threadSentMessagesOverviewQueryRaw); +export const threadSentMessagesOverviewQuery = cached( + threadSentMessagesOverviewQueryRaw, +); const threadMostUsedWordsQueryRaw = (threadId: number, limit = 10) => - kyselyDb() - ?.withRecursive("words", (eb) => { + kyselyDb + .withRecursive("words", (eb) => { return eb .selectFrom("message") .select([ sql`LOWER(substr(body, 1, instr(body || " ", " ") - 1))`.as("word"), sql`(substr(body, instr(body || " ", " ") + 1))`.as("rest"), ]) - .where((eb) => eb.and([eb("body", "is not", null), eb("thread_id", "=", threadId)])) + .where((eb) => + eb.and([eb("body", "is not", null), eb("thread_id", "=", threadId)]), + ) .unionAll((ebInner) => { return ebInner .selectFrom("words") .select([ - sql`LOWER(substr(rest, 1, instr(rest || " ", " ") - 1))`.as("word"), + sql`LOWER(substr(rest, 1, instr(rest || " ", " ") - 1))`.as( + "word", + ), sql`(substr(rest, instr(rest || " ", " ") + 1))`.as("rest"), ]) .where("rest", "<>", ""); diff --git a/src/db.ts b/src/db.ts index 043302d..cf9a429 100644 --- a/src/db.ts +++ b/src/db.ts @@ -1,50 +1,30 @@ -import { createEffect, createMemo, createRoot, createSignal } from "solid-js"; - +import { makePersisted } from "@solid-primitives/storage"; +import sqlite3InitModule from "@sqlite.org/sqlite-wasm"; import { Kysely } from "kysely"; import type { DB } from "kysely-codegen"; -import { SqlJsDialect } from "kysely-wasm"; -import initSqlJS, { type Database } from "sql.js"; - -import wasmURL from "./assets/sql-wasm.wasm?url"; +import { OfficialWasmDialect } from "kysely-wasm"; +import { createSignal } from "solid-js"; +import workerUrl from "./lib/kysely-official-wasm-worker/worker?url"; export const SELF_ID = 2; -export const SQL = await initSqlJS({ - locateFile: () => wasmURL, +const sqlite3 = await sqlite3InitModule({ + print: console.log, + printErr: console.error, }); -export const [db, setDb] = createSignal(); +export const db = new sqlite3.oo1.DB("signal"); -const sqlJsDialect = () => { - const currentDb = db(); - - if (currentDb) { - return new SqlJsDialect({ - database: currentDb, - }); - } -}; - -export const kyselyDb = createRoot(() => { - createEffect(() => { - const currentDb = db(); - - if (currentDb) { - currentDb.create_function("is_not_empty", (str: string | null) => { - return str !== null && str !== ""; - }); - } - }); - - return createMemo(() => { - const currentSqlJsDialect = sqlJsDialect(); - - if (!currentSqlJsDialect) { - return; - } - - return new Kysely({ - dialect: currentSqlJsDialect, - }); - }); +export const worker = new Worker(workerUrl, { + type: "module", }); + +const dialect = new OfficialWasmDialect({ + database: db, +}); + +export const kyselyDb = new Kysely({ + dialect, +}); + +export const [dbHash, setDbHash] = makePersisted(createSignal()); diff --git a/src/index.tsx b/src/index.tsx index dbf7f34..76190b7 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,11 +1,9 @@ /* @refresh reload */ -import { render } from "solid-js/web"; -import { Router, useNavigate } from "@solidjs/router"; import { MetaProvider } from "@solidjs/meta"; +import { Router } from "@solidjs/router"; +import { render } from "solid-js/web"; import App from "./App"; -import { createEffect } from "solid-js"; -import { db } from "./db"; const root = document.getElementById("root"); @@ -21,18 +19,18 @@ if (root) {
{ - const navigate = useNavigate(); - const { pathname } = props.location; + // root={(props) => { + // const navigate = useNavigate(); + // const { pathname } = props.location; - createEffect(() => { - if (!db() && pathname !== "/") { - navigate("/"); - } - }); + // createEffect(() => { + // if (!db() && pathname !== "/") { + // navigate("/"); + // } + // }); - return props.children; - }} + // return props.children; + // }} > diff --git a/src/lib/db-cache.ts b/src/lib/db-cache.ts index d42e206..3b34ca6 100644 --- a/src/lib/db-cache.ts +++ b/src/lib/db-cache.ts @@ -1,9 +1,9 @@ -import { on, createSignal, createEffect, createRoot, createMemo } from "solid-js"; -import { serialize, deserialize } from "seroval"; -import { createSignaledWorker } from "@solid-primitives/workers"; -import { db } from "~/db"; +import { deserialize, serialize } from "seroval"; +import { createEffect, createMemo, createRoot, on } from "solid-js"; +import { dbHash } from "~/db"; +import { hashString } from "./hash"; -const DATABASE_HASH_PREFIX = "database"; +export const DATABASE_HASH_PREFIX = "database"; // clear the cache on new session so that selecting a different database does not result in wrong cache entries const clearDbCache = () => { @@ -16,64 +16,32 @@ const clearDbCache = () => { } }; -// https://stackoverflow.com/a/7616484 -const hashString = (str: string) => { - let hash = 0, - i, - chr; - if (str.length === 0) return hash; - for (i = 0; i < str.length; i++) { - chr = str.charCodeAt(i); - hash = (hash << 5) - hash + chr; - hash |= 0; // Convert to 32bit integer - } - return hash; -}; - -const HASH_STORE_KEY = `${DATABASE_HASH_PREFIX}_hash`; +let prevDbHash = dbHash(); createRoot(() => { - const [dbHash, setDbHash] = createSignal(localStorage.getItem(HASH_STORE_KEY)); - - // offloaded because this can take a long time (>1s easily) and would block the mainthread - createSignaledWorker({ - input: db, - output: setDbHash, - func: function hashDb(currentDb: ReturnType) { - const hashString = (str: string) => { - let hash = 0, - i, - chr; - if (str.length === 0) return hash; - for (i = 0; i < str.length; i++) { - chr = str.charCodeAt(i); - hash = (hash << 5) - hash + chr; - hash |= 0; // Convert to 32bit integer - } - return hash; - }; - - if (currentDb?.export) { - return hashString(new TextDecoder().decode(currentDb.export())).toString(); - } - }, - }); - createEffect(() => { - on(dbHash, (currentDbHash) => { - if (currentDbHash) { - clearDbCache(); - - localStorage.setItem(HASH_STORE_KEY, currentDbHash); - } - }); + on( + dbHash, + (currentDbHash) => { + if (currentDbHash && currentDbHash !== prevDbHash) { + prevDbHash = currentDbHash; + clearDbCache(); + } + }, + { + defer: true, + }, + ); }); }); class LocalStorageCacheAdapter { - keys = new Set(Object.keys(localStorage).filter((key) => key.startsWith(this.prefix))); + keys = new Set( + Object.keys(localStorage).filter((key) => key.startsWith(this.prefix)), + ); prefix = "database"; - #dbLoaded = createMemo(() => !!db()); + // TODO: real way of detecting if the db is loaded, on loading the db and opfs (if persisted db?) + #dbLoaded = createMemo(() => !!dbHash()); #createKey(cacheName: string, key: string): string { return `${this.prefix}-${cacheName}-${key}`; @@ -86,7 +54,10 @@ class LocalStorageCacheAdapter { try { localStorage.setItem(fullKey, serialize(value)); } catch (error: unknown) { - if (error instanceof DOMException && error.name === "QUOTA_EXCEEDED_ERR") { + if ( + error instanceof DOMException && + error.name === "QUOTA_EXCEEDED_ERR" + ) { console.error("Storage quota exceeded, not caching new function calls"); } else { console.error(error); @@ -146,7 +117,10 @@ const createHashKey = (...args: unknown[]) => { return hashString(stringToHash); }; -export const cached = (fn: (...args: T) => R, self?: ThisType): ((...args: T) => R) => { +export const cached = ( + fn: (...args: T) => R, + self?: ThisType, +): ((...args: T) => R) => { const cacheName = hashString(fn.toString()).toString(); // important to return a promise on follow-up calls even if the data is immediately available diff --git a/src/lib/decryptor.ts b/src/lib/decryptor.ts new file mode 100644 index 0000000..1ad2d8f --- /dev/null +++ b/src/lib/decryptor.ts @@ -0,0 +1,47 @@ +import { + BackupDecryptor, + type DecryptionResult, +} from "@duskflower/signal-decrypt-backup-wasm"; + +const CHUNK_SIZE = 1024 * 1024 * 40; // 40MB chunks + +export async function decryptBackup( + file: File, + passphrase: string, + progressCallback: (progress: number) => void, +): Promise { + const fileSize = file.size; + const decryptor = new BackupDecryptor(); + decryptor.set_progress_callback(fileSize, progressCallback); + + let offset = 0; + + try { + while (offset < file.size) { + const chunk = file.slice(offset, offset + CHUNK_SIZE); + const arrayBuffer = await chunk.arrayBuffer(); + const uint8Array = new Uint8Array(arrayBuffer); + + decryptor.feed_data(uint8Array); + + let done = false; + while (!done) { + try { + done = decryptor.process_chunk(passphrase); + } catch (e) { + console.error("Error processing chunk:", e); + throw e; + } + } + + offset += CHUNK_SIZE; + } + + const result = decryptor.finish(); + + return result; + } catch (e) { + console.error("Decryption failed:", e); + throw e; + } +} diff --git a/src/lib/hash.ts b/src/lib/hash.ts new file mode 100644 index 0000000..6794f4a --- /dev/null +++ b/src/lib/hash.ts @@ -0,0 +1,13 @@ +// https://stackoverflow.com/a/7616484 +export const hashString = (str: string) => { + let hash = 0, + i, + chr; + if (str.length === 0) return hash; + for (i = 0; i < str.length; i++) { + chr = str.charCodeAt(i); + hash = (hash << 5) - hash + chr; + hash |= 0; // Convert to 32bit integer + } + return hash; +}; diff --git a/src/pages/home.tsx b/src/pages/home.tsx index 3c5b066..2ac4777 100644 --- a/src/pages/home.tsx +++ b/src/pages/home.tsx @@ -1,47 +1,90 @@ +import { useNavigate, type RouteSectionProps } from "@solidjs/router"; import { createSignal, Show, type Component, type JSX } from "solid-js"; -import { type RouteSectionProps, useNavigate } from "@solidjs/router"; +import { Title } from "@solidjs/meta"; import { Portal } from "solid-js/web"; import { Flex } from "~/components/ui/flex"; -import { Title } from "@solidjs/meta"; -import { setDb, SQL } from "~/db"; + +import { + Progress, + ProgressLabel, + ProgressValueLabel, +} from "~/components/ui/progress"; +// import { db } from "~/db"; +import { loadDb } from "~/db-queries"; +import { decryptBackup } from "~/lib/decryptor"; export const Home: Component = () => { - const [isLoadingDb, setIsLoadingDb] = createSignal(false); + const [decryptionProgress, setDecryptionProgress] = createSignal(); + const [isLoadingDatabase, setIsLoadingDatabase] = createSignal(false); + const [passphrase, setPassphrase] = createSignal(""); const navigate = useNavigate(); - const onFileChange: JSX.ChangeEventHandler = (event) => { + const onFileChange: JSX.ChangeEventHandler = ( + event, + ) => { const file = event.currentTarget.files?.[0]; - if (file) { - const reader = new FileReader(); + const currentPassphrase = passphrase(); - reader.addEventListener("load", () => { - setIsLoadingDb(true); + if (file && currentPassphrase) { + decryptBackup(file, currentPassphrase, setDecryptionProgress) + .then((result) => { + setDecryptionProgress(undefined); + setIsLoadingDatabase(true); - setTimeout(() => { - const Uints = new Uint8Array(reader.result as ArrayBuffer); - setDb(new SQL.Database(Uints)); - setIsLoadingDb(false); - navigate("/overview"); - }, 10); - }); + setTimeout(() => { + loadDb(result.database_statements); - reader.readAsArrayBuffer(file); + setIsLoadingDatabase(false); + + navigate("/overview"); + }, 0); + }) + .catch((error) => { + console.error("Decryption failed:", error); + }); } }; return ( <> - - + + +

Decrypting database

+ `${value}%`} + class="w-[300px] space-y-1" + > +
+ Processing... + +
+
+
+

Loading database

-
-
+ +
Signal stats
- + setPassphrase(event.currentTarget.value)} + /> +
); diff --git a/src/pages/overview/index.tsx b/src/pages/overview/index.tsx index b117891..1ca5834 100644 --- a/src/pages/overview/index.tsx +++ b/src/pages/overview/index.tsx @@ -1,48 +1,70 @@ -import { type Component, createResource, Show } from "solid-js"; import type { RouteSectionProps } from "@solidjs/router"; +import { type Component, createResource, Show } from "solid-js"; -import { allThreadsOverviewQuery, overallSentMessagesQuery } from "~/db-queries"; +import { + allThreadsOverviewQuery, + overallSentMessagesQuery, +} from "~/db-queries"; -import { OverviewTable, type RoomOverview } from "./overview-table"; -import { getNameFromRecipient } from "~/lib/get-name-from-recipient"; import { Title } from "@solidjs/meta"; import { SELF_ID } from "~/db"; +import { getNameFromRecipient } from "~/lib/get-name-from-recipient"; +import { OverviewTable, type RoomOverview } from "./overview-table"; export const Overview: Component = () => { - const [allSelfSentMessagesCount] = createResource(() => overallSentMessagesQuery(SELF_ID)); + console.log(overallSentMessagesQuery(SELF_ID)); - const [roomOverview] = createResource(async () => { - return (await allThreadsOverviewQuery())?.map((row) => { - const isGroup = row.title !== null; + const [allSelfSentMessagesCount] = createResource(() => + overallSentMessagesQuery(SELF_ID), + ); - let name = ""; + const [roomOverview] = createResource( + async () => { + return (await allThreadsOverviewQuery())?.map((row) => { + const isGroup = row.title !== null; - if (row.title !== null) { - name = row.title; - } else { - name = getNameFromRecipient(row.nickname_joined_name, row.system_joined_name, row.profile_joined_name); - } + let name = ""; - return { - threadId: row.thread_id, - recipientId: row.recipient_id, - archived: Boolean(row.archived), - messageCount: row.message_count, - lastMessageDate: row.last_message_date ? new Date(row.last_message_date) : undefined, - name, - isGroup, - }; - }); - }); + if (row.title !== null) { + name = row.title; + } else { + name = getNameFromRecipient( + row.nickname_joined_name, + row.system_joined_name, + row.profile_joined_name, + ); + } + + return { + threadId: row.thread_id, + recipientId: row.recipient_id, + archived: Boolean(row.archived), + messageCount: row.message_count, + lastMessageDate: row.last_message_date + ? new Date(row.last_message_date) + : undefined, + name, + isGroup, + }; + }); + }, + ); return ( <> Signal statistics overview
-

All messages: {allSelfSentMessagesCount()?.messageCount as number}

- - {(currentRoomOverview) => } +

+ All messages: {allSelfSentMessagesCount()?.messageCount as number} +

+ + {(currentRoomOverview) => ( + + )}
diff --git a/vite.config.ts b/vite.config.ts index bf84311..367d54f 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,12 +1,23 @@ import path from "path"; import { defineConfig } from "vite"; import solidPlugin from "vite-plugin-solid"; +import wasm from "vite-plugin-wasm"; export default defineConfig({ - plugins: [solidPlugin()], + plugins: [solidPlugin(), wasm()], server: { + headers: { + "Cross-Origin-Opener-Policy": "same-origin", + "Cross-Origin-Embedder-Policy": "require-corp", + }, port: 3000, }, + optimizeDeps: { + exclude: [ + "@duskflower/signal-decrypt-backup-wasm", + "@sqlite.org/sqlite-wasm", + ], + }, build: { target: "esnext", },