From 9e6f9d005bf18aa54dbd6b83b6ef2ad55b5e9308 Mon Sep 17 00:00:00 2001 From: Paul Morris <10599524+1Cor125@users.noreply.github.com> Date: Thu, 27 Nov 2025 15:10:23 -0500 Subject: [PATCH 1/8] config: scaffold the plugin project - Brought in needed dependencies and moved to strict versioning. - Note: Rust uses "^" versioning by default. --- Cargo.lock | 232 +++++----- Cargo.toml | 20 +- crates/sqlx-sqlite-conn-mgr/Cargo.toml | 8 +- package-lock.json | 618 ++++++++++++++++++++++++- package.json | 26 +- rollup.config.js | 78 ++++ 6 files changed, 856 insertions(+), 126 deletions(-) create mode 100644 rollup.config.js diff --git a/Cargo.lock b/Cargo.lock index 4e1dbef..f8c6ac5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -198,9 +198,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" +checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" dependencies = [ "serde", ] @@ -274,9 +274,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.45" +version = "1.2.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35900b6c8d709fb1d854671ae27aeaa9eec2f8b01b364e1619a40da3e6fe2afe" +checksum = "cd405d82c84ff7f35739f175f67d8b9fb7687a0e84ccdc78bd3568839827cf07" dependencies = [ "find-msvc-tools", "shlex", @@ -425,9 +425,9 @@ dependencies = [ [[package]] name = "crc" -version = "3.3.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9710d3b3739c2e349eb44fe848ad0b7c8cb1e42bd87ee49371df2f7acaf3e675" +checksum = "5eb8a2a1cd12ab0d987a5d5e825195d372001a4094a0376319d5a0ad71c1ba0d" dependencies = [ "crc-catalog", ] @@ -473,9 +473,9 @@ checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crypto-common" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" dependencies = [ "generic-array", "typenum", @@ -505,7 +505,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" dependencies = [ "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -515,7 +515,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a2785755761f3ddc1492979ce1e48d2c00d09311c39e4466429188f3dd6501" dependencies = [ "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -539,7 +539,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -550,7 +550,7 @@ checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" dependencies = [ "darling_core", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -584,7 +584,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -644,7 +644,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -667,7 +667,7 @@ checksum = "788160fb30de9cdd857af31c6a2675904b16ece8fc2737b2c7127ba368c9d0f4" dependencies = [ "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -801,9 +801,9 @@ dependencies = [ [[package]] name = "find-msvc-tools" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127" +checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" [[package]] name = "flate2" @@ -856,7 +856,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -936,7 +936,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -1078,9 +1078,9 @@ dependencies = [ [[package]] name = "generic-array" -version = "0.14.9" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", @@ -1186,7 +1186,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -1265,7 +1265,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -1287,9 +1287,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.16.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" [[package]] name = "hashlink" @@ -1359,12 +1359,11 @@ dependencies = [ [[package]] name = "http" -version = "1.3.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" dependencies = [ "bytes", - "fnv", "itoa", ] @@ -1399,9 +1398,9 @@ checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] name = "hyper" -version = "1.7.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e" +checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" dependencies = [ "atomic-waker", "bytes", @@ -1420,9 +1419,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.17" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c6995591a8f1380fcb4ba966a252a4b29188d51d2b89e3a252f5305be65aea8" +checksum = "52e9a2a24dc5c6821e71a7030e1e14b7b632acac55c40e9d2e082c621261bb56" dependencies = [ "base64 0.22.1", "bytes", @@ -1597,12 +1596,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.12.0" +version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f" +checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" dependencies = [ "equivalent", - "hashbrown 0.16.0", + "hashbrown 0.16.1", "serde", "serde_core", ] @@ -1734,7 +1733,7 @@ checksum = "02cb977175687f33fa4afa0c95c112b987ea1443e5a51c8f8ff27dc618270cc2" dependencies = [ "cssparser", "html5ever", - "indexmap 2.12.0", + "indexmap 2.12.1", "selectors", ] @@ -1864,7 +1863,7 @@ checksum = "88a9689d8d44bf9964484516275f5cd4c9b59457a6940c1d5d0ecbb94510a36b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -1990,9 +1989,9 @@ checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" [[package]] name = "num-bigint-dig" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82c79c15c05d4bf82b6f5ef163104cc81a760d8e874d38ac50ab67c8877b647b" +checksum = "e661dda6640fad38e827a6d4a310ff4763082116fe217f279885c97f511bb0b7" dependencies = [ "lazy_static", "libm", @@ -2059,7 +2058,7 @@ dependencies = [ "proc-macro-crate 3.4.0", "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -2511,7 +2510,7 @@ dependencies = [ "phf_shared 0.11.3", "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -2587,7 +2586,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "740ebea15c5d1428f910cd1a5f52cebf8d25006245ed8ade92702f4943d91e07" dependencies = [ "base64 0.22.1", - "indexmap 2.12.0", + "indexmap 2.12.1", "quick-xml", "serde", "time", @@ -2706,9 +2705,9 @@ dependencies = [ [[package]] name = "quick-xml" -version = "0.38.3" +version = "0.38.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42a232e7487fc2ef313d96dde7948e7a3c05101870d8985e4fd8d26aedd27b89" +checksum = "b66c2058c55a409d601666cffe35f04333cf1013010882cec174a7467cd4e21c" dependencies = [ "memchr", ] @@ -2852,7 +2851,7 @@ checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -2921,9 +2920,9 @@ dependencies = [ [[package]] name = "rsa" -version = "0.9.8" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78928ac1ed176a5ca1d17e578a1825f3d81ca54cf41053a592584b020cfd691b" +checksum = "40a0376c50d0358279d9d643e4bf7b7be212f1f4ff1da9070a7b54d22ef75c88" dependencies = [ "const-oid", "digest", @@ -3017,7 +3016,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -3093,7 +3092,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -3104,7 +3103,7 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -3128,7 +3127,7 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -3163,15 +3162,15 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.15.1" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa66c845eee442168b2c8134fec70ac50dc20e760769c8ba0ad1319ca1959b04" +checksum = "10574371d41b0d9b2cff89418eda27da52bcaff2cc8741db26382a77c29131f1" dependencies = [ "base64 0.22.1", "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.12.0", + "indexmap 2.12.1", "schemars 0.9.0", "schemars 1.1.0", "serde_core", @@ -3182,14 +3181,14 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.15.1" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b91a903660542fced4e99881aa481bdbaec1634568ee02e0b8bd57c64cb38955" +checksum = "08a72d8216842fdd57820dc78d840bef99248e35fb2554ff923319e60f2d686b" dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -3211,7 +3210,7 @@ checksum = "772ee033c0916d670af7860b6e1ef7d658a4629a6d0b4c8c3e67f09b3765b75d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -3254,9 +3253,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook-registry" -version = "1.4.6" +version = "1.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" +checksum = "7664a098b8e616bdfcc2dc0e9ac44eb231eedf41db4e9fe95d8d32ec728dedad" dependencies = [ "libc", ] @@ -3412,7 +3411,7 @@ dependencies = [ "futures-util", "hashbrown 0.15.5", "hashlink", - "indexmap 2.12.0", + "indexmap 2.12.1", "log", "memchr", "once_cell", @@ -3422,6 +3421,7 @@ dependencies = [ "sha2", "smallvec", "thiserror 2.0.17", + "time", "tokio", "tokio-stream", "tracing", @@ -3438,7 +3438,7 @@ dependencies = [ "quote", "sqlx-core", "sqlx-macros-core", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -3461,7 +3461,7 @@ dependencies = [ "sqlx-mysql", "sqlx-postgres", "sqlx-sqlite", - "syn 2.0.109", + "syn 2.0.111", "tokio", "url", ] @@ -3504,6 +3504,7 @@ dependencies = [ "sqlx-core", "stringprep", "thiserror 2.0.17", + "time", "tracing", "whoami", ] @@ -3541,6 +3542,7 @@ dependencies = [ "sqlx-core", "stringprep", "thiserror 2.0.17", + "time", "tracing", "whoami", ] @@ -3565,6 +3567,7 @@ dependencies = [ "serde_urlencoded", "sqlx-core", "thiserror 2.0.17", + "time", "tracing", "url", ] @@ -3657,9 +3660,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.109" +version = "2.0.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f17c7e013e88258aa9543dcbe81aca68a667a9ac37cd69c9fbc07858bfe0e2f" +checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" dependencies = [ "proc-macro2", "quote", @@ -3683,7 +3686,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -3747,7 +3750,7 @@ checksum = "f4e16beb8b2ac17db28eab8bca40e62dbfbb34c0fcdc6d9826b11b7b5d047dfd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -3758,9 +3761,9 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tauri" -version = "2.9.2" +version = "2.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bceb52453e507c505b330afe3398510e87f428ea42b6e76ecb6bd63b15965b5" +checksum = "9e492485dd390b35f7497401f67694f46161a2a00ffd800938d5dd3c898fb9d8" dependencies = [ "anyhow", "bytes", @@ -3809,9 +3812,9 @@ dependencies = [ [[package]] name = "tauri-build" -version = "2.5.1" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a924b6c50fe83193f0f8b14072afa7c25b7a72752a2a73d9549b463f5fe91a38" +checksum = "87d6f8cafe6a75514ce5333f115b7b1866e8e68d9672bf4ca89fc0f35697ea9d" dependencies = [ "anyhow", "cargo_toml", @@ -3831,9 +3834,9 @@ dependencies = [ [[package]] name = "tauri-codegen" -version = "2.5.0" +version = "2.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c1fe64c74cc40f90848281a90058a6db931eb400b60205840e09801ee30f190" +checksum = "b7ef707148f0755110ca54377560ab891d722de4d53297595380a748026f139f" dependencies = [ "base64 0.22.1", "brotli", @@ -3847,7 +3850,7 @@ dependencies = [ "serde", "serde_json", "sha2", - "syn 2.0.109", + "syn 2.0.111", "tauri-utils", "thiserror 2.0.17", "time", @@ -3858,14 +3861,14 @@ dependencies = [ [[package]] name = "tauri-macros" -version = "2.5.0" +version = "2.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "260c5d2eb036b76206b9fca20b7be3614cfd21046c5396f7959e0e64a4b07f2f" +checksum = "71664fd715ee6e382c05345ad258d6d1d50f90cf1b58c0aa726638b33c2a075d" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", "tauri-codegen", "tauri-utils", ] @@ -3891,10 +3894,18 @@ dependencies = [ name = "tauri-plugin-sqlite" version = "0.1.0" dependencies = [ + "futures-core", + "indexmap 2.12.1", + "log", "serde", + "serde_json", + "sqlx", + "sqlx-sqlite-conn-mgr", "tauri", "tauri-plugin", "thiserror 2.0.17", + "time", + "tokio", ] [[package]] @@ -3989,10 +4000,11 @@ dependencies = [ [[package]] name = "tauri-winres" -version = "0.3.3" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd21509dd1fa9bd355dc29894a6ff10635880732396aa38c0066c1e6c1ab8074" +checksum = "1087b111fe2b005e42dbdc1990fc18593234238d47453b0c99b7de1c9ab2c1e0" dependencies = [ + "dunce", "embed-resource", "toml 0.9.8", ] @@ -4034,7 +4046,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -4045,7 +4057,7 @@ checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -4129,7 +4141,7 @@ checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -4174,13 +4186,13 @@ version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0dc8b1fb61449e27716ec0e1bdf0f6b8f3e8f6b05391e8497b8b6d7804ea6d8" dependencies = [ - "indexmap 2.12.0", + "indexmap 2.12.1", "serde_core", "serde_spanned 1.0.3", "toml_datetime 0.7.3", "toml_parser", "toml_writer", - "winnow 0.7.13", + "winnow 0.7.14", ] [[package]] @@ -4207,7 +4219,7 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.12.0", + "indexmap 2.12.1", "toml_datetime 0.6.3", "winnow 0.5.40", ] @@ -4218,7 +4230,7 @@ version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" dependencies = [ - "indexmap 2.12.0", + "indexmap 2.12.1", "serde", "serde_spanned 0.6.9", "toml_datetime 0.6.3", @@ -4231,10 +4243,10 @@ version = "0.23.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6485ef6d0d9b5d0ec17244ff7eb05310113c3f316f2d14200d4de56b3cb98f8d" dependencies = [ - "indexmap 2.12.0", + "indexmap 2.12.1", "toml_datetime 0.7.3", "toml_parser", - "winnow 0.7.13", + "winnow 0.7.14", ] [[package]] @@ -4243,7 +4255,7 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0cbe268d35bdb4bb5a56a2de88d0ad0eb70af5384a99d648cd4b3d04039800e" dependencies = [ - "winnow 0.7.13", + "winnow 0.7.14", ] [[package]] @@ -4269,9 +4281,9 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.6.6" +version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" +checksum = "9cf146f99d442e8e68e585f5d798ccd3cad9a7835b917e09728880a862706456" dependencies = [ "bitflags 2.10.0", "bytes", @@ -4311,20 +4323,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.30" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] name = "tracing-core" -version = "0.1.34" +version = "0.1.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" +checksum = "7a04e24fab5c89c6a36eb8558c9656f30d81de51dfa4d3b45f26b21d61fa0a6c" dependencies = [ "once_cell", ] @@ -4620,7 +4632,7 @@ dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", "wasm-bindgen-shared", ] @@ -4722,7 +4734,7 @@ checksum = "1d228f15bba3b9d56dde8bddbee66fa24545bd17b48d5128ccf4a8742b18e431" dependencies = [ "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -4859,7 +4871,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -4870,7 +4882,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -5248,9 +5260,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.7.13" +version = "0.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" +checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" dependencies = [ "memchr", ] @@ -5362,28 +5374,28 @@ checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", "synstructure", ] [[package]] name = "zerocopy" -version = "0.8.27" +version = "0.8.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" +checksum = "4ea879c944afe8a2b25fef16bb4ba234f47c694565e97383b36f3a878219065c" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.27" +version = "0.8.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" +checksum = "cf955aa904d6040f70dc8e9384444cb1030aed272ba3cb09bbc4ab9e7c1f34f5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -5403,7 +5415,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", "synstructure", ] @@ -5443,5 +5455,5 @@ checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] diff --git a/Cargo.toml b/Cargo.toml index 7df8899..4aa4fd0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,9 +14,21 @@ rust-version = "1.89" links = "tauri-plugin-sqlite" [dependencies] -tauri = "2.5.1" -serde = { version = "1.0.228", features = ["derive"] } -thiserror = "2.0.17" +tauri = "=2.9.3" +serde = { version = "=1.0.228", features = ["derive"] } +serde_json = "=1.0.145" +thiserror = "=2.0.17" +log = "=0.4.28" +futures-core = "=0.3.31" +time = "=0.3.44" +tokio = { version = "=1.48.0", features = ["sync"] } +indexmap = { version = "=2.12.1", features = ["serde"] } + +# SQLx for types and queries (time feature enables datetime type decoding) +sqlx = { version = "=0.8.6", features = ["sqlite", "json", "time", "runtime-tokio"] } + +# Connection manager +sqlx-sqlite-conn-mgr = { path = "crates/sqlx-sqlite-conn-mgr" } [build-dependencies] -tauri-plugin = { version = "2.5.1", features = ["build"] } +tauri-plugin = { version = "=2.5.1", features = ["build"] } diff --git a/crates/sqlx-sqlite-conn-mgr/Cargo.toml b/crates/sqlx-sqlite-conn-mgr/Cargo.toml index 49be472..6a9a9ac 100644 --- a/crates/sqlx-sqlite-conn-mgr/Cargo.toml +++ b/crates/sqlx-sqlite-conn-mgr/Cargo.toml @@ -7,7 +7,7 @@ edition = "2024" rust-version = "1.89" [dependencies] -sqlx = { version = "0.8.6", features = ["runtime-tokio", "sqlite"] } -thiserror = "2.0.17" -tokio = { version = "1.48.0", features = ["full"] } -tracing = { version = "0.1.41", default-features = false, features = ["std", "release_max_level_off"] } +sqlx = { version = "=0.8.6", features = ["runtime-tokio", "sqlite"] } +thiserror = "=2.0.17" +tokio = { version = "=1.48.0", features = ["full"] } +tracing = { version = "=0.1.41", default-features = false, features = ["std", "release_max_level_off"] } diff --git a/package-lock.json b/package-lock.json index 883f09b..19673b9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,16 +1,21 @@ { - "name": "tauri-plugin-sqlite", + "name": "@silvermine/tauri-plugin-sqlite", "version": "0.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "tauri-plugin-sqlite", + "name": "@silvermine/tauri-plugin-sqlite", "version": "0.1.0", "license": "MIT", "devDependencies": { + "@rollup/plugin-node-resolve": "16.0.3", + "@rollup/plugin-terser": "0.4.4", + "@rollup/plugin-typescript": "12.3.0", "@silvermine/standardization": "2.2.3", "@tauri-apps/api": "2.9.0", + "rollup": "4.53.3", + "tslib": "2.8.1", "typescript": "5.8.3" }, "peerDependencies": { @@ -557,6 +562,17 @@ "node": ">=6.0.0" } }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", + "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.5.5", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", @@ -613,6 +629,425 @@ "node": ">= 8" } }, + "node_modules/@rollup/plugin-node-resolve": { + "version": "16.0.3", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-16.0.3.tgz", + "integrity": "sha512-lUYM3UBGuM93CnMPG1YocWu7X802BrNF3jW2zny5gQyLQgRFJhV1Sq0Zi74+dh/6NBx1DxFC4b4GXg9wUCG5Qg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "@types/resolve": "1.20.2", + "deepmerge": "^4.2.2", + "is-module": "^1.0.0", + "resolve": "^1.22.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^2.78.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-terser": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/@rollup/plugin-terser/-/plugin-terser-0.4.4.tgz", + "integrity": "sha512-XHeJC5Bgvs8LfukDwWZp7yeqin6ns8RTl2B9avbejt6tZqsqvVoWI7ZTQrcNsfKEDWBTnTxM8nMDkO2IFFbd0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "serialize-javascript": "^6.0.1", + "smob": "^1.0.0", + "terser": "^5.17.4" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-typescript": { + "version": "12.3.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-12.3.0.tgz", + "integrity": "sha512-7DP0/p7y3t67+NabT9f8oTBFE6gGkto4SA6Np2oudYmZE/m1dt8RB0SjL1msMxFpLo631qjRCcBlAbq1ml/Big==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.1.0", + "resolve": "^1.22.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^2.14.0||^3.0.0||^4.0.0", + "tslib": "*", + "typescript": ">=3.7.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + }, + "tslib": { + "optional": true + } + } + }, + "node_modules/@rollup/pluginutils": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.3.0.tgz", + "integrity": "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/pluginutils/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.3.tgz", + "integrity": "sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.3.tgz", + "integrity": "sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.3.tgz", + "integrity": "sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.53.3.tgz", + "integrity": "sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.3.tgz", + "integrity": "sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.3.tgz", + "integrity": "sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.3.tgz", + "integrity": "sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.3.tgz", + "integrity": "sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.3.tgz", + "integrity": "sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.3.tgz", + "integrity": "sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.3.tgz", + "integrity": "sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.3.tgz", + "integrity": "sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.3.tgz", + "integrity": "sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.3.tgz", + "integrity": "sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.3.tgz", + "integrity": "sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.3.tgz", + "integrity": "sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.3.tgz", + "integrity": "sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.3.tgz", + "integrity": "sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.53.3.tgz", + "integrity": "sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.3.tgz", + "integrity": "sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.3.tgz", + "integrity": "sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.3.tgz", + "integrity": "sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@silvermine/markdownlint-rule-indent-alignment": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/@silvermine/markdownlint-rule-indent-alignment/-/markdownlint-rule-indent-alignment-0.1.1.tgz", @@ -688,6 +1123,13 @@ "url": "https://opencollective.com/tauri" } }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/mdast": { "version": "3.0.15", "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.15.tgz", @@ -719,6 +1161,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/resolve": { + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz", + "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/unist": { "version": "2.0.11", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", @@ -726,6 +1175,19 @@ "dev": true, "license": "MIT" }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/add-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/add-stream/-/add-stream-1.0.0.tgz", @@ -1594,6 +2056,16 @@ "node": ">=0.10.0" } }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -1729,6 +2201,13 @@ "node": ">=6" } }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true, + "license": "MIT" + }, "node_modules/execall": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/execall/-/execall-2.0.0.tgz", @@ -1912,6 +2391,21 @@ "dev": true, "license": "ISC" }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -2539,6 +3033,13 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/is-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", + "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==", + "dev": true, + "license": "MIT" + }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -3770,6 +4271,16 @@ "node": ">=8" } }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, "node_modules/read-pkg": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", @@ -4100,6 +4611,48 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/rollup": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.3.tgz", + "integrity": "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.53.3", + "@rollup/rollup-android-arm64": "4.53.3", + "@rollup/rollup-darwin-arm64": "4.53.3", + "@rollup/rollup-darwin-x64": "4.53.3", + "@rollup/rollup-freebsd-arm64": "4.53.3", + "@rollup/rollup-freebsd-x64": "4.53.3", + "@rollup/rollup-linux-arm-gnueabihf": "4.53.3", + "@rollup/rollup-linux-arm-musleabihf": "4.53.3", + "@rollup/rollup-linux-arm64-gnu": "4.53.3", + "@rollup/rollup-linux-arm64-musl": "4.53.3", + "@rollup/rollup-linux-loong64-gnu": "4.53.3", + "@rollup/rollup-linux-ppc64-gnu": "4.53.3", + "@rollup/rollup-linux-riscv64-gnu": "4.53.3", + "@rollup/rollup-linux-riscv64-musl": "4.53.3", + "@rollup/rollup-linux-s390x-gnu": "4.53.3", + "@rollup/rollup-linux-x64-gnu": "4.53.3", + "@rollup/rollup-linux-x64-musl": "4.53.3", + "@rollup/rollup-openharmony-arm64": "4.53.3", + "@rollup/rollup-win32-arm64-msvc": "4.53.3", + "@rollup/rollup-win32-ia32-msvc": "4.53.3", + "@rollup/rollup-win32-x64-gnu": "4.53.3", + "@rollup/rollup-win32-x64-msvc": "4.53.3", + "fsevents": "~2.3.2" + } + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -4161,6 +4714,16 @@ "node": ">=10" } }, + "node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "randombytes": "^2.1.0" + } + }, "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", @@ -4199,6 +4762,13 @@ "url": "https://github.com/chalk/slice-ansi?sponsor=1" } }, + "node_modules/smob": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/smob/-/smob-1.5.0.tgz", + "integrity": "sha512-g6T+p7QO8npa+/hNx9ohv1E5pVCmWrVCUzUXJyLdMmftX6ER0oiWY/w9knEonLpnOp6b6FenKnMfR8gqwWdwig==", + "dev": true, + "license": "MIT" + }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -4209,6 +4779,17 @@ "node": ">=0.10.0" } }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, "node_modules/spdx-correct": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", @@ -4726,6 +5307,32 @@ "node": ">=10.0.0" } }, + "node_modules/terser": { + "version": "5.44.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.44.1.tgz", + "integrity": "sha512-t/R3R/n0MSwnnazuPpPNVO60LX0SKL45pyl9YlvxIdkH0Of7D5qM2EVe+yASRIlY5pZ73nclYJfNANGWPwFDZw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.15.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true, + "license": "MIT" + }, "node_modules/text-extensions": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-1.9.0.tgz", @@ -4787,6 +5394,13 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, "node_modules/type-fest": { "version": "0.18.1", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", diff --git a/package.json b/package.json index 0fd9dad..198028b 100644 --- a/package.json +++ b/package.json @@ -1,15 +1,24 @@ { - "name": "tauri-plugin-sqlite", + "name": "@silvermine/tauri-plugin-sqlite", "version": "0.1.0", - "description": "A Tauri plugin for SQLite database access with connection pool management", - "main": "dist-js/index.js", - "types": "dist-js/index.d.ts", + "description": "SQLite database interface for Tauri applications", + "type": "module", + "types": "./dist-js/index.d.ts", + "main": "./dist-js/index.cjs", + "module": "./dist-js/index.js", + "exports": { + "types": "./dist-js/index.d.ts", + "import": "./dist-js/index.js", + "require": "./dist-js/index.cjs" + }, "files": [ "dist-js", - "permissions" + "permissions", + "README.md", + "LICENSE" ], "scripts": { - "build": "tsc -p guest-js/tsconfig.json", + "build": "rollup -c", "check-node-version": "check-node-version --npm 10.5.0", "commitlint": "commitlint --from ${COMMITLINT_FROM:-002bcc8} --to ${COMMITLINT_TO:-HEAD}", "markdownlint": "markdownlint-cli2", @@ -24,8 +33,13 @@ "url": "https://github.com/silvermine/tauri-plugin-sqlite.git" }, "devDependencies": { + "@rollup/plugin-node-resolve": "16.0.3", + "@rollup/plugin-terser": "0.4.4", + "@rollup/plugin-typescript": "12.3.0", "@silvermine/standardization": "2.2.3", "@tauri-apps/api": "2.9.0", + "rollup": "4.53.3", + "tslib": "2.8.1", "typescript": "5.8.3" }, "peerDependencies": { diff --git a/rollup.config.js b/rollup.config.js new file mode 100644 index 0000000..5b5a8c4 --- /dev/null +++ b/rollup.config.js @@ -0,0 +1,78 @@ +import { readFileSync } from 'node:fs' +import { dirname } from 'node:path' +import { nodeResolve } from '@rollup/plugin-node-resolve' +import typescript from '@rollup/plugin-typescript' +import terser from '@rollup/plugin-terser' + +const pkg = JSON.parse(readFileSync('./package.json', 'utf8')) + +// Convert package name to camelCase for IIFE variable +// @silvermine/tauri-plugin-sqlite -> sqlite +const pluginJsName = pkg.name + .replace('@silvermine/tauri-plugin-', '') + .replace(/-./g, (x) => x[1].toUpperCase()) + +// IIFE variable name: __TAURI_PLUGIN_SQLITE__ +const iifeVarName = `__TAURI_PLUGIN_${pkg.name + .replace('@silvermine/tauri-plugin-', '') + .replace('-', '_') + .toUpperCase()}__` + +export default [ + // ESM and CJS builds + { + input: 'guest-js/index.ts', + output: [ + { + file: pkg.exports.import, + format: 'esm' + }, + { + file: pkg.exports.require, + format: 'cjs' + } + ], + plugins: [ + typescript({ + tsconfig: './guest-js/tsconfig.json', + declaration: true, + declarationDir: dirname(pkg.exports.import) + }) + ], + external: [ + /^@tauri-apps\/api/, + ...Object.keys(pkg.dependencies || {}), + ...Object.keys(pkg.peerDependencies || {}) + ], + onwarn: (warning) => { + throw Object.assign(new Error(), warning) + } + }, + + // IIFE build for direct browser usage + { + input: 'guest-js/index.ts', + output: { + format: 'iife', + name: iifeVarName, + // IIFE is in the format `var ${iifeVarName} = (() => {})()` + // we check if __TAURI__ exists and inject the API object + banner: "if ('__TAURI__' in window) {", + // the last `}` closes the if in the banner + footer: `Object.defineProperty(window.__TAURI__, '${pluginJsName}', { value: ${iifeVarName} }) }`, + file: 'api-iife.js' + }, + plugins: [ + typescript({ + target: 'ES2020', + module: 'ES2020', + lib: ['ES2020'] + }), + terser(), + nodeResolve() + ], + onwarn: (warning) => { + throw Object.assign(new Error(), warning) + } + } +] From d0cf6ed4fbe9485eb17a90c4d8043d7e109a54c6 Mon Sep 17 00:00:00 2001 From: Paul Morris <10599524+1Cor125@users.noreply.github.com> Date: Thu, 27 Nov 2025 15:15:14 -0500 Subject: [PATCH 2/8] feat: add TS types and API shape - Following SQLx's fetch, fetchOne and execute semantics --- README.md | 501 +++++++++++++++++++++++++++++++++++++++++----- api-iife.js | 1 + guest-js/index.ts | 243 +++++++++++++++++++++- src/commands.rs | 8 +- src/error.rs | 1 + src/lib.rs | 10 +- 6 files changed, 700 insertions(+), 64 deletions(-) create mode 100644 api-iife.js diff --git a/README.md b/README.md index 83dcd70..1917a36 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,27 @@ -# Tauri SQLite Plugin +# Tauri Plugin SQLite [![CI][ci-badge]][ci-url] -A Tauri plugin for SQLite database access with connection management. This plugin -depends on [SQLx](https://github.com/launchbadge/sqlx) and enforces pragmatic policies -for connection management. +SQLite database interface for Tauri applications using +[sqlx](https://github.com/launchbadge/sqlx) and +[sqlx-sqlite-conn-mgr](https://github.com/silvermine/sqlx-sqlite-conn-mgr). + +This plugin provides a SQLite-focused database interface with optimized connection +pooling, write serialization, and proper resource management. [ci-badge]: https://github.com/silvermine/tauri-plugin-sqlite/actions/workflows/ci.yml/badge.svg [ci-url]: https://github.com/silvermine/tauri-plugin-sqlite/actions/workflows/ci.yml +## Features + + * **Optimized Connection Pooling**: Separate read and write pools for concurrent reads, + even while writing + * **Write Serialization**: Exclusive write access through connection manager + * **Migration Support**: Uses SQLx's database migration system (runs during preload) + * **Custom Configuration**: Configure read pool size and idle timeouts + * **Type Safety**: Full TypeScript bindings + * **Resource Management**: Proper cleanup on application exit + ## Project Structure This project is organized as a Cargo workspace with the following structure: @@ -16,23 +29,38 @@ This project is organized as a Cargo workspace with the following structure: ```text tauri-plugin-sqlite/ ├── crates/ -│ └── sqlx-sqlite-conn-mgr/ # SQLx SQLite connection pool manager +│ └── sqlx-sqlite-conn-mgr/ # SQLx SQLite connection manager │ ├── src/ -│ │ └── lib.rs -│ └── Cargo.toml +│ │ ├── config.rs # Database configuration types +│ │ ├── database.rs # Core database wrapper +│ │ ├── error.rs # Error types +│ │ ├── lib.rs # Public API +│ │ ├── registry.rs # Database registry +│ │ └── write_guard.rs # Write access guard +│ ├── Cargo.toml +│ └── README.md ├── src/ # Tauri plugin implementation -│ ├── commands.rs # Plugin commands +│ ├── commands.rs # Plugin commands (Phase 2) │ ├── error.rs # Error types -│ ├── lib.rs # Main plugin code -│ └── models.rs # Data models -├── guest-js/ # JavaScript/TypeScript bindings -│ ├── index.ts +│ └── lib.rs # Plugin initialization +├── guest-js/ # JavaScript/TypeScript API +│ ├── index.ts # Database class and types │ └── tsconfig.json -├── permissions/ # Permission definitions (mostly generated) -├── dist-js/ # Compiled JS (generated) +├── permissions/ # Tauri permission definitions +│ ├── autogenerated/ # Generated permission files +│ ├── schemas/ # Permission schemas +│ └── default.toml # Default permission set +├── dist-js/ # Built JavaScript (generated) +│ ├── index.js # ESM build +│ ├── index.cjs # CommonJS build +│ └── index.d.ts # TypeScript declarations +├── .github/ # CI/CD workflows ├── Cargo.toml # Workspace configuration ├── package.json # NPM package configuration -└── build.rs # Build script +├── rollup.config.js # JavaScript build configuration +├── api-iife.js # Browser IIFE bundle (generated) +├── build.rs # Build script +└── README.md # This file ``` ## Crates @@ -87,28 +115,430 @@ cargo test npm run standards ``` -## Usage +## Install + +_This plugin requires a Rust version of at least **1.77.2**_ + +### Rust -### In a Tauri Application +Add the plugin to your `Cargo.toml`: -Add the plugin to your Tauri application's `Cargo.toml`: +`src-tauri/Cargo.toml` ```toml [dependencies] -tauri-plugin-sqlite = { path = "../path/to/tauri-plugin-sqlite" } +tauri-plugin-sqlite = { git = "https://github.com/silvermine/tauri-plugin-sqlite" } +``` + +### JavaScript/TypeScript + +Install the JavaScript bindings: + +```sh +npm install @silvermine/tauri-plugin-sqlite ``` -Initialize the plugin in your Tauri app: +## Usage + +### Basic Setup + +Register the plugin in your Rust application: + +`src-tauri/src/lib.rs` ```rust fn main() { tauri::Builder::default() - .plugin(tauri_plugin_sqlite::init()) + .plugin(tauri_plugin_sqlite::Builder::new().build()) + .run(tauri::generate_context!()) + .expect("error while running tauri application"); +} +``` + +### Connecting to a Database + +```typescript +import Database from '@silvermine/tauri-plugin-sqlite' + +// Connect to a database (path is relative to app config directory) +const db = await Database.load('mydb.db') + +// Or with custom connection configuration +const db = await Database.load('mydb.db', { + maxReadConnections: 10, // Default: 6 + idleTimeoutSecs: 60 // Default: 30 +}) +``` + +> **Note:** Database paths are relative to the app's config directory. Unlike +> `tauri-plugin-sql`, no `sqlite:` prefix is needed. + +### Executing Write Operations + +Use `execute()` for INSERT, UPDATE, DELETE, or any query that modifies data: + +```typescript +// CREATE TABLE +await db.execute( + 'CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT, email TEXT)' +) + +// INSERT +const result = await db.execute( + 'INSERT INTO users (name, email) VALUES ($1, $2)', + ['Alice', 'alice@example.com'] +) +console.log(`Inserted ${result.rowsAffected} rows`) +console.log(`Last insert ID: ${result.lastInsertId}`) + +// UPDATE +const updateResult = await db.execute( + 'UPDATE users SET email = $1 WHERE name = $2', + ['alice.new@example.com', 'Alice'] +) +console.log(`Updated ${updateResult.rowsAffected} rows`) + +// DELETE +const deleteResult = await db.execute( + 'DELETE FROM users WHERE id = $1', + [1] +) +``` + +### Executing SELECT Queries + +Use `fetchAll()` for all read operations: + +```typescript +type User = {id: number, name: string, email: string} + +// SELECT all rows +const allUsers = await db.fetchAll( + 'SELECT * FROM users' +) + +// SELECT with parameters +const filtered = await db.fetchAll( + 'SELECT * FROM users WHERE name = $1 AND email LIKE $2', + ['Alice', '%@example.com'] +) + +// SELECT expecting single result (returns undefined if not found) +const user = await db.fetchOne( + 'SELECT * FROM users WHERE id = $1', + [42] +) + +if (user) { + console.log(`Found user: ${user.name}`) +} +``` + +### Closing Connections + +```typescript +// Close a specific database +await db.close() + +// Close all database connections +await Database.closeAll() +``` + +### Removing a Database + +Permanently delete a database and all its files (including WAL and SHM files): + +```typescript +// ⚠️ Warning: This permanently deletes the database file(s)! +await db.remove() +``` + +## Migrations + +Migrations run automatically during database preload at application startup. + +### Setting Up Migrations + +`src-tauri/src/lib.rs` + +```rust +use tauri_plugin_sqlite::{Builder, Migration, MigrationKind}; + +fn main() { + let migrations = vec![ + // Version 1: Create initial schema + Migration { + version: 1, + description: "create_users_table", + sql: vec!["CREATE TABLE users ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL, + email TEXT NOT NULL UNIQUE, + created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')) + )"], + kind: MigrationKind::Up, + }, + // Version 2: Add new column + Migration { + version: 2, + description: "add_users_role", + sql: vec!["ALTER TABLE users ADD COLUMN role TEXT DEFAULT 'user'"], + kind: MigrationKind::Up, + }, + // Version 3: Create index and add trigger (multiple statements) + Migration { + version: 3, + description: "create_email_index_and_trigger", + sql: vec![ + "CREATE INDEX idx_users_email ON users(email)", + "CREATE TRIGGER update_timestamp AFTER UPDATE ON users + BEGIN + UPDATE users SET created_at = strftime('%s', 'now') WHERE id = NEW.id; + END", + ], + kind: MigrationKind::Up, + }, + ]; + + tauri::Builder::default() + .plugin( + Builder::new() + .add_migrations("mydb.db", migrations) + .build(), + ) .run(tauri::generate_context!()) .expect("error while running tauri application"); } ``` +### Preloading Databases + +Add databases to `tauri.conf.json` to connect and run migrations at startup: + +```json +{ + "plugins": { + "sqlite": { + "preload": ["mydb.db", "cache.db"] + } + } +} +``` + +When preloaded: + +1. The plugin connects to each database +2. Runs any pending migrations defined in `add_migrations()` +3. Keeps the connection open and ready for use + +> **Important:** Migrations only run during preload. If you load a database +> dynamically with `Database.load()`, migrations will not run unless it was +> preloaded. + +## Query Parameter Binding + +SQLite uses the `$1`, `$2`, etc. syntax for parameter binding: + +```typescript +type User = {id: number, name: string, email: string, role: string, created_at: number} + +// Multiple parameters +const result = await db.execute( + 'INSERT INTO users (name, email, role) VALUES ($1, $2, $3)', + ['Bob', 'bob@example.com', 'admin'] +) + +// Parameters in WHERE clause +const filtered = await db.fetchAll( + 'SELECT * FROM users WHERE role = $1 AND created_at > $2', + ['admin', 1609459200] +) +``` + +> **Important:** Do NOT use `execute()` for read-only queries. It will return +> an error. Always use `fetchAll()` or `fetchOne()` for reads. + +## Configuration + +### Connection Pool Options + +Configure the connection pool when loading a database: + +```typescript +const db = await Database.load('mydb.db', { + maxReadConnections: 10, // Max concurrent read connections (default: 6) + idleTimeoutSecs: 60 // Idle timeout in seconds (default: 30) +}) +``` + +### Architecture + +The plugin uses `sqlx-sqlite-conn-mgr` for optimized connection management: + + * **Read Pool**: Multiple concurrent read-only connections (configurable, default: 6) + * **Write Connection**: Single exclusive write connection + * **WAL Mode**: Enabled automatically on first write operation + * **Connection Caching**: Databases are cached by path + * **Idle Timeout**: Connections close after inactivity (configurable, default: 30s) + +### Read vs Write Operations + +| Operation Type | Method | Pool Used | Concurrency | +| -------------------- | --------------- | ---------------- | ------------------ | +| SELECT (multiple) | `fetchAll()` | Read pool | Multiple concurrent| +| SELECT (single) | `fetchOne()` | Read pool | Multiple concurrent| +| INSERT/UPDATE/DELETE | `execute()` | Write connection | Serialized | +| CREATE TABLE | `execute()` | Write connection | Serialized | +| CREATE INDEX | `execute()` | Write connection | Serialized | + +## API Reference + +### Database Class + +#### Static Methods + +##### `Database.load(path: string, customConfig?: CustomConfig): Promise` + +Connect to a database and return a Database instance. + + * `path`: Relative path to database file (from app config directory) + * `customConfig`: Optional connection pool configuration + * `Returns`: Promise resolving to Database instance + +```typescript +const db = await Database.load('mydb.db', { + maxReadConnections: 10, + idleTimeoutSecs: 60 +}) +``` + +##### `Database.get(path: string): Database` + +Get a Database instance without connecting (lazy initialization). + +```typescript +const db = Database.get('mydb.db') +// Connection happens on first query +``` + +##### `Database.closeAll(): Promise` + +Close all database connections. + +```typescript +await Database.closeAll() +``` + +#### Instance Methods + +##### `execute(query: string, bindValues?: unknown[]): Promise` + +Execute a write query (INSERT, UPDATE, DELETE, CREATE, etc.). + +```typescript +const result = await db.execute( + 'INSERT INTO users (name) VALUES ($1)', + ['Alice'] +) +console.log(result.rowsAffected, result.lastInsertId) +``` + +##### `fetchAll(query: string, bindValues?: unknown[]): Promise` + +Execute a SELECT query and return all matching rows. + +```typescript +const users = await db.fetchAll( + 'SELECT * FROM users WHERE role = $1', + ['admin'] +) +``` + +##### `fetchOne(query: string, bindValues?: unknown[]): Promise` + +Execute a SELECT query expecting zero or one result. Returns `undefined` if no rows match. + +```typescript +const user = await db.fetchOne( + 'SELECT * FROM users WHERE id = $1', + [42] +) + +if (user) { + console.log(user.name) +} else { + console.log('User not found') +} +``` + +##### `close(): Promise` + +Close this database connection. + +```typescript +await db.close() +``` + +##### `remove(): Promise` + +Close the connection and permanently delete database file(s). + +> ⚠️ **Warning:** This cannot be undone! + +```typescript +await db.remove() +``` + +### TypeScript Interfaces + +```typescript +interface QueryResult { + rowsAffected: number // Number of rows modified + lastInsertId: number // ROWID of last inserted row +} + +interface CustomConfig { + maxReadConnections?: number // Default: 6 + idleTimeoutSecs?: number // Default: 30 +} +``` + +## Thread Safety + +All operations are async and thread-safe. The connection manager ensures: + + * ✓ Multiple concurrent readers + * ✓ Only one writer at a time + * ✓ No write conflicts + * ✓ Automatic WAL mode for writers + +## Permissions + +By default, the plugin has restrictive permissions. Add permissions in +`src-tauri/capabilities/default.json`: + +```json +{ + "permissions": [ + "sqlite:allow-load", + "sqlite:allow-select", + "sqlite:allow-select-one", + "sqlite:allow-execute-write", + "sqlite:allow-close", + "sqlite:allow-close-all", + "sqlite:allow-remove" + ] +} +``` + +Or use the `default` permission set: + +```json +{ + "permissions": ["sqlite:default"] +} +``` + ### Tracing and Logging This plugin and its connection manager crate use the @@ -154,35 +584,6 @@ With this setup, `tauri dev` shows all plugin and app logs, while `tauri build` a release binary that contains no logging from this plugin or your app-level `tracing` calls. -### JavaScript/TypeScript API - -Install the JavaScript package in your frontend: - -```bash -npm install @silvermine/tauri-plugin-sqlite -``` - -Use the plugin from JavaScript: - -Add the plugin permission to your capabilities file `src-tauri/capabilities/default.json`: - -```json -{ - "permissions": [ - "core:default", - "sqlite:default" - ] -} -``` - -```typescript -// TODO: Add real examples once we have decided on the plugin API -import { hello } from '@silvermine/tauri-plugin-sqlite'; - -// Call the hello command -const greeting = await hello('World'); -console.log(greeting); // "Hello, World! This is the SQLite plugin." -``` ## Development Standards diff --git a/api-iife.js b/api-iife.js new file mode 100644 index 0000000..c5bbcc8 --- /dev/null +++ b/api-iife.js @@ -0,0 +1 @@ +if("__TAURI__"in window){var __TAURI_PLUGIN_SQLITE__=function(){"use strict";async function t(t,e={},s){return window.__TAURI_INTERNALS__.invoke(t,e,s)}"function"==typeof SuppressedError&&SuppressedError;class e{constructor(t){this.path=t}static async load(s,n){const i=await t("plugin:sqlite|load",{db:s,customConfig:n});return new e(i)}static get(t){return new e(t)}async execute(e,s){const[n,i]=await t("plugin:sqlite|execute",{db:this.path,query:e,values:s??[]});return{lastInsertId:i,rowsAffected:n}}async fetchAll(e,s){return await t("plugin:sqlite|fetch_all",{db:this.path,query:e,values:s??[]})}async fetchOne(e,s){return await t("plugin:sqlite|select_one",{db:this.path,query:e,values:s??[]})}async close(){return await t("plugin:sqlite|close",{db:this.path})}static async closeAll(){return await t("plugin:sqlite|close_all")}async remove(){return await t("plugin:sqlite|remove",{db:this.path})}}return e}();Object.defineProperty(window.__TAURI__,"sqlite",{value:__TAURI_PLUGIN_SQLITE__})} diff --git a/guest-js/index.ts b/guest-js/index.ts index f5ddb5c..5b9bbed 100644 --- a/guest-js/index.ts +++ b/guest-js/index.ts @@ -1,10 +1,241 @@ -import { invoke } from '@tauri-apps/api/core'; +import { invoke } from '@tauri-apps/api/core' + +export interface QueryResult { + /** The number of rows affected by the query. */ + rowsAffected: number + /** The last inserted row ID (SQLite ROWID). */ + lastInsertId: number +} /** - * Says hello from the SQLite plugin - * @param name - The name to greet - * @returns A greeting message + * Custom configuration for SQLite database connection */ -export async function hello(name: string): Promise { - return await invoke('plugin:sqlite|hello', { name }); +export interface CustomConfig { + /** Maximum number of concurrent read connections. Default: 6 */ + maxReadConnections?: number + /** Idle timeout in seconds for connections. Default: 30 */ + idleTimeoutSecs?: number +} + +/** + * **Database** + * + * The `Database` class serves as the primary interface for + * communicating with SQLite databases through the plugin. + */ +export default class Database { + path: string + constructor(path: string) { + this.path = path + } + + /** + * **load** + * + * A static initializer which connects to the underlying SQLite database and + * returns a `Database` instance once a connection is established. + * + * The path is relative to `tauri::path::BaseDirectory::AppConfig`. + * + * @param path - Database file path (relative to AppConfig directory) + * @param customConfig - Optional custom configuration for connection pools + * + * @example + * ```ts + * // Use default configuration + * const db = await Database.load("test.db"); + * + * // Use custom configuration + * const db = await Database.load("test.db", { + * maxReadConnections: 10, + * idleTimeoutSecs: 60 + * }); + * ``` + */ + static async load(path: string, customConfig?: CustomConfig): Promise { + const _path = await invoke('plugin:sqlite|load', { + db: path, + customConfig + }) + + return new Database(_path) + } + + /** + * **get** + * + * A static initializer which synchronously returns an instance of + * the Database class while deferring the actual database connection + * until the first invocation or selection on the database. + * + * The path is relative to `tauri::path::BaseDirectory::AppConfig`. + * + * @example + * ```ts + * const db = Database.get("test.db"); + * ``` + */ + static get(path: string): Database { + return new Database(path) + } + + /** + * **execute** + * + * Executes a write query against the database (INSERT, UPDATE, DELETE, etc.). + * This method is specifically for mutations that modify data. + * + * **Important:** Do NOT use this for SELECT queries. Use `fetchX()` instead. + * Using `execute()` for read queries will trigger an error to prevent unnecessary + * write mode initialization. + * + * SQLite uses `$1`, `$2`, etc. for parameter binding. + * + * @example + * ```ts + * // INSERT example + * const result = await db.execute( + * "INSERT INTO todos (id, title, status) VALUES ($1, $2, $3)", + * [todos.id, todos.title, todos.status] + * ); + * console.log(`Inserted ${result.rowsAffected} rows`); + * console.log(`Last insert ID: ${result.lastInsertId}`); + * + * // UPDATE example + * const result = await db.execute( + * "UPDATE todos SET title = $1, status = $2 WHERE id = $3", + * [todos.title, todos.status, todos.id] + * ); + * ``` + */ + async execute(query: string, bindValues?: unknown[]): Promise { + const [rowsAffected, lastInsertId] = await invoke<[number, number]>( + 'plugin:sqlite|execute', + { + db: this.path, + query, + values: bindValues ?? [] + } + ) + return { + lastInsertId, + rowsAffected + } + } + + /** + * **fetchAll** + * + * Passes in a SELECT query to the database for execution. + * Returns all matching rows as an array. + * + * SQLite uses `$1`, `$2`, etc. for parameter binding. + * + * @example + * ```ts + * const todos = await db.fetchAll( + * "SELECT * FROM todos WHERE id = $1", + * [id] + * ); + * + * // Multiple parameters + * const result = await db.fetchAll( + * "SELECT * FROM todos WHERE status = $1 AND priority > $2", + * ["active", 5] + * ); + * ``` + */ + async fetchAll(query: string, bindValues?: unknown[]): Promise { + const result = await invoke('plugin:sqlite|fetch_all', { + db: this.path, + query, + values: bindValues ?? [] + }) + + return result + } + + /** + * **fetchOne** + * + * Passes in a SELECT query expecting zero or one result. + * Returns `undefined` if no rows match the query. + * + * SQLite uses `$1`, `$2`, etc. for parameter binding. + * + * @example + * ```ts + * const todo = await db.fetchOne( + * "SELECT * FROM todos WHERE id = $1", + * [id] + * ); + * + * if (todo) { + * console.log(todo.title); + * } else { + * console.log("Todo not found"); + * } + * ``` + */ + async fetchOne(query: string, bindValues?: unknown[]): Promise { + const result = await invoke('plugin:sqlite|select_one', { + db: this.path, + query, + values: bindValues ?? [] + }) + + return result + } + + /** + * **close** + * + * Closes the database connection pool(s) for this specific database. + * + * @example + * ```ts + * const success = await db.close() + * ``` + */ + async close(): Promise { + const success = await invoke('plugin:sqlite|close', { + db: this.path + }) + return success + } + + /** + * **closeAll** + * + * Closes connection pools for all databases. + * + * @example + * ```ts + * const success = await Database.closeAll() + * ``` + */ + static async closeAll(): Promise { + const success = await invoke('plugin:sqlite|close_all') + return success + } + + /** + * **remove** + * + * Closes the database connection pool and deletes all database files + * (including the main database file, and any WAL/SHM files). + * + * **Warning:** This permanently deletes the database files from disk. Use with caution! + * + * @example + * ```ts + * const success = await db.remove() + * ``` + */ + async remove(): Promise { + const success = await invoke('plugin:sqlite|remove', { + db: this.path + }) + return success + } } diff --git a/src/commands.rs b/src/commands.rs index 0747d9c..b54c829 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -1,8 +1,2 @@ -use tauri::command; +//! SQLite plugin commands -use crate::Result; - -#[command] -pub(crate) async fn hello(name: String) -> Result { - Ok(format!("Hello, {}!", name)) -} diff --git a/src/error.rs b/src/error.rs index 5ec6002..607281f 100644 --- a/src/error.rs +++ b/src/error.rs @@ -4,6 +4,7 @@ pub type Result = std::result::Result; #[derive(Debug, thiserror::Error)] pub enum Error { + // TODO: Add specific error variants for different failure cases #[error("{0}")] Custom(String), } diff --git a/src/lib.rs b/src/lib.rs index 3b4ce3f..e2e9e38 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,6 +8,14 @@ pub use error::{Error, Result}; /// Initializes the plugin. pub fn init() -> TauriPlugin { tauri::plugin::Builder::new("sqlite") - .invoke_handler(tauri::generate_handler![commands::hello]) + // .invoke_handler(tauri::generate_handler![ + // commands::load, + // commands::execute, + // commands::fetch_all, + // commands::fetch_one, + // commands::close, + // commands::close_all, + // commands::remove + // ]) .build() } From 000e14012fbeae3ff82459f02a7185a8c2a2e4cd Mon Sep 17 00:00:00 2001 From: Paul Morris <10599524+1Cor125@users.noreply.github.com> Date: Thu, 27 Nov 2025 15:33:43 -0500 Subject: [PATCH 3/8] chore: clean up clippy errors - Fixed clippy warnings: nested ifs, explicit deref, and len() > 0 checks. --- crates/sqlx-sqlite-conn-mgr/src/database.rs | 30 +++++++++---------- crates/sqlx-sqlite-conn-mgr/src/registry.rs | 18 +++++------ .../sqlx-sqlite-conn-mgr/src/write_guard.rs | 4 +-- src/commands.rs | 1 - 4 files changed, 26 insertions(+), 27 deletions(-) diff --git a/crates/sqlx-sqlite-conn-mgr/src/database.rs b/crates/sqlx-sqlite-conn-mgr/src/database.rs index a992323..293e94f 100644 --- a/crates/sqlx-sqlite-conn-mgr/src/database.rs +++ b/crates/sqlx-sqlite-conn-mgr/src/database.rs @@ -288,12 +288,12 @@ impl SqliteDatabase { // Checkpoint WAL before closing the write connection to flush changes and truncate WAL file // Only attempt if WAL was initialized (write connection was used) - if self.wal_initialized.load(Ordering::SeqCst) { - if let Ok(mut conn) = self.write_conn.acquire().await { - let _ = sqlx::query("PRAGMA wal_checkpoint(TRUNCATE)") - .execute(&mut *conn) - .await; - } + if self.wal_initialized.load(Ordering::SeqCst) + && let Ok(mut conn) = self.write_conn.acquire().await + { + let _ = sqlx::query("PRAGMA wal_checkpoint(TRUNCATE)") + .execute(&mut *conn) + .await; } self.write_conn.close().await; @@ -334,17 +334,17 @@ impl SqliteDatabase { // Remove WAL and SHM files - ignore "not found" but propagate other errors // (these files may not exist if WAL was never initialized) let wal_path = path.with_extension("db-wal"); - if let Err(e) = std::fs::remove_file(&wal_path) { - if e.kind() != std::io::ErrorKind::NotFound { - return Err(Error::Io(e)); - } + if let Err(e) = std::fs::remove_file(&wal_path) + && e.kind() != std::io::ErrorKind::NotFound + { + return Err(Error::Io(e)); } let shm_path = path.with_extension("db-shm"); - if let Err(e) = std::fs::remove_file(&shm_path) { - if e.kind() != std::io::ErrorKind::NotFound { - return Err(Error::Io(e)); - } + if let Err(e) = std::fs::remove_file(&shm_path) + && e.kind() != std::io::ErrorKind::NotFound + { + return Err(Error::Io(e)); } Ok(()) @@ -692,7 +692,7 @@ mod tests { .await .unwrap(); - assert!(rows.len() > 0); + assert!(!rows.is_empty()); })); } diff --git a/crates/sqlx-sqlite-conn-mgr/src/registry.rs b/crates/sqlx-sqlite-conn-mgr/src/registry.rs index dd1e66f..fb98009 100644 --- a/crates/sqlx-sqlite-conn-mgr/src/registry.rs +++ b/crates/sqlx-sqlite-conn-mgr/src/registry.rs @@ -50,22 +50,22 @@ where { let registry = registry().read().await; - if let Some(weak) = registry.get(&canonical_path) { - if let Some(db) = weak.upgrade() { - return Ok(db); - } - // Weak reference exists but dead - will be cleaned up in write phase + if let Some(weak) = registry.get(&canonical_path) + && let Some(db) = weak.upgrade() + { + return Ok(db); } + // Weak reference exists but dead - will be cleaned up in write phase } // Phase 2: Database not found, acquire write lock let mut registry = registry().write().await; // Double-check: another thread might have created it while we waited for write lock - if let Some(weak) = registry.get(&canonical_path) { - if let Some(db) = weak.upgrade() { - return Ok(db); - } + if let Some(weak) = registry.get(&canonical_path) + && let Some(db) = weak.upgrade() + { + return Ok(db); } // Clean up dead weak references while we have the write lock diff --git a/crates/sqlx-sqlite-conn-mgr/src/write_guard.rs b/crates/sqlx-sqlite-conn-mgr/src/write_guard.rs index afb4368..6b437d0 100644 --- a/crates/sqlx-sqlite-conn-mgr/src/write_guard.rs +++ b/crates/sqlx-sqlite-conn-mgr/src/write_guard.rs @@ -47,13 +47,13 @@ impl Deref for WriteGuard { type Target = SqliteConnection; fn deref(&self) -> &Self::Target { - &*self.conn + &self.conn } } impl DerefMut for WriteGuard { fn deref_mut(&mut self) -> &mut Self::Target { - &mut *self.conn + &mut self.conn } } diff --git a/src/commands.rs b/src/commands.rs index b54c829..ae13605 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -1,2 +1 @@ //! SQLite plugin commands - From 6d85a49eb2f42902b6a8f66f30299a5d070a4415 Mon Sep 17 00:00:00 2001 From: Paul Morris <10599524+1Cor125@users.noreply.github.com> Date: Thu, 27 Nov 2025 15:41:24 -0500 Subject: [PATCH 4/8] ci: rename commitlint ext to .cjs --- commitlint.config.js => commitlint.config.cjs | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename commitlint.config.js => commitlint.config.cjs (100%) diff --git a/commitlint.config.js b/commitlint.config.cjs similarity index 100% rename from commitlint.config.js rename to commitlint.config.cjs From a60d8f1d40a0a0bbee2b0fc0ce965e61be008741 Mon Sep 17 00:00:00 2001 From: Paul Morris <10599524+1Cor125@users.noreply.github.com> Date: Thu, 27 Nov 2025 16:59:45 -0500 Subject: [PATCH 5/8] chore: acknowledge Tauri SQL plugin in license --- LICENSE | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/LICENSE b/LICENSE index b294102..fa48ad9 100644 --- a/LICENSE +++ b/LICENSE @@ -19,3 +19,8 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Parts of the code are based on Tauri's "Plugin-SQL" +Copyright 2019-2023 Tauri Programme within The Commons Conservancy +SPDX-License-Identifier: Apache-2.0 +SPDX-License-Identifier: MIT From 7895e29f9a064d48abca75f09a5e2a23fb7a352a Mon Sep 17 00:00:00 2001 From: Paul Morris <10599524+1Cor125@users.noreply.github.com> Date: Fri, 28 Nov 2025 11:10:02 -0500 Subject: [PATCH 6/8] config: use caret versioning - Until we can discuss and decide on a strategy for Rust, strict versioning seems too restrictive --- Cargo.toml | 22 +++++++++++----------- crates/sqlx-sqlite-conn-mgr/Cargo.toml | 8 ++++---- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4aa4fd0..7fd6279 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,21 +14,21 @@ rust-version = "1.89" links = "tauri-plugin-sqlite" [dependencies] -tauri = "=2.9.3" -serde = { version = "=1.0.228", features = ["derive"] } -serde_json = "=1.0.145" -thiserror = "=2.0.17" -log = "=0.4.28" -futures-core = "=0.3.31" -time = "=0.3.44" -tokio = { version = "=1.48.0", features = ["sync"] } -indexmap = { version = "=2.12.1", features = ["serde"] } +tauri = "2.9.3" +serde = { version = "1.0.228", features = ["derive"] } +serde_json = "1.0.145" +thiserror = "2.0.17" +log = "0.4.28" +futures-core = "0.3.31" +time = "0.3.44" +tokio = { version = "1.48.0", features = ["sync"] } +indexmap = { version = "2.12.1", features = ["serde"] } # SQLx for types and queries (time feature enables datetime type decoding) -sqlx = { version = "=0.8.6", features = ["sqlite", "json", "time", "runtime-tokio"] } +sqlx = { version = "0.8.6", features = ["sqlite", "json", "time", "runtime-tokio"] } # Connection manager sqlx-sqlite-conn-mgr = { path = "crates/sqlx-sqlite-conn-mgr" } [build-dependencies] -tauri-plugin = { version = "=2.5.1", features = ["build"] } +tauri-plugin = { version = "2.5.1", features = ["build"] } diff --git a/crates/sqlx-sqlite-conn-mgr/Cargo.toml b/crates/sqlx-sqlite-conn-mgr/Cargo.toml index 6a9a9ac..49be472 100644 --- a/crates/sqlx-sqlite-conn-mgr/Cargo.toml +++ b/crates/sqlx-sqlite-conn-mgr/Cargo.toml @@ -7,7 +7,7 @@ edition = "2024" rust-version = "1.89" [dependencies] -sqlx = { version = "=0.8.6", features = ["runtime-tokio", "sqlite"] } -thiserror = "=2.0.17" -tokio = { version = "=1.48.0", features = ["full"] } -tracing = { version = "=0.1.41", default-features = false, features = ["std", "release_max_level_off"] } +sqlx = { version = "0.8.6", features = ["runtime-tokio", "sqlite"] } +thiserror = "2.0.17" +tokio = { version = "1.48.0", features = ["full"] } +tracing = { version = "0.1.41", default-features = false, features = ["std", "release_max_level_off"] } From 3c4a346ed9d9ebe8d6dca8d345b0ce4b3316a36e Mon Sep 17 00:00:00 2001 From: Paul Morris <10599524+1Cor125@users.noreply.github.com> Date: Fri, 28 Nov 2025 11:16:38 -0500 Subject: [PATCH 7/8] feat: clarify known errors and allowed sql types --- README.md | 70 +++++++++++++++++++++++++++++++++++++++++- api-iife.js | 2 +- guest-js/index.ts | 32 ++++++++++++++++--- src/error.rs | 78 ++++++++++++++++++++++++++++++++++++++++++++--- 4 files changed, 172 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 1917a36..147d062 100644 --- a/README.md +++ b/README.md @@ -173,6 +173,32 @@ const db = await Database.load('mydb.db', { > **Note:** Database paths are relative to the app's config directory. Unlike > `tauri-plugin-sql`, no `sqlite:` prefix is needed. +### Parameter Binding and Types + +All query methods (`execute`, `fetchAll`, `fetchOne`) support parameter binding using +the `$1`, `$2`, etc. syntax. Values must be of type `SqlValue`: + +```typescript +type SqlValue = string | number | boolean | null | Uint8Array +``` + +Supported SQLite types: + + * **TEXT** - `string` values (also used for DATE, TIME, DATETIME) + * **INTEGER** - `number` values (integers) + * **REAL** - `number` values (floating point) + * **BOOLEAN** - `boolean` values + * **NULL** - `null` value + * **BLOB** - `Uint8Array` for binary data + +```typescript +// Example with different types +await db.execute( + 'INSERT INTO data (text, int, real, bool, blob) VALUES ($1, $2, $3, $4, $5)', + ['hello', 42, 3.14, true, new Uint8Array([1, 2, 3])] +) +``` + ### Executing Write Operations Use `execute()` for INSERT, UPDATE, DELETE, or any query that modifies data: @@ -205,9 +231,46 @@ const deleteResult = await db.execute( ) ``` +### Handling Errors + +Handle database errors gracefully using structured error responses: + +```typescript +import type { SqliteError } from '@silvermine/tauri-plugin-sqlite'; + +try { + await db.execute( + 'INSERT INTO users (id, name) VALUES ($1, $2)', + [1, 'Alice'] + ); +} catch (err) { + const error = err as SqliteError; + + // Check error code for specific handling + if (error.code.startsWith('SQLITE_CONSTRAINT')) { + console.error('Constraint violation:', error.message); + } else if (error.code === 'DATABASE_NOT_LOADED') { + console.error('Database not initialized'); + } else { + console.error('Database error:', error.code, error.message); + } +} +``` + +Common error codes include: + + * `SQLITE_CONSTRAINT` - Constraint violation (unique, foreign key, etc.) + * `SQLITE_NOTFOUND` - Table or column not found + * `DATABASE_NOT_LOADED` - Database hasn't been loaded yet + * `INVALID_PATH` - Invalid database path + * `IO_ERROR` - File system error + * `MIGRATION_ERROR` - Migration failed + * `READ_ONLY_QUERY_IN_EXECUTE` - Attempted to use execute() for a read-only query + * `MULTIPLE_ROWS_RETURNED` - `fetchOne()` query returned multiple rows + ### Executing SELECT Queries -Use `fetchAll()` for all read operations: +Use `fetchAll()` or `fetchOne()` for all read operations: ```typescript type User = {id: number, name: string, email: string} @@ -234,6 +297,11 @@ if (user) { } ``` +> **Note:** `fetchOne()` validates that the query returns exactly 0 or 1 rows. If your +> query returns multiple rows, it will throw a `MULTIPLE_ROWS_RETURNED` error. This helps +> catch bugs where a query unexpectedly returns multiple results. Use `fetchAll()` if you +> expect multiple rows. + ### Closing Connections ```typescript diff --git a/api-iife.js b/api-iife.js index c5bbcc8..baaecfc 100644 --- a/api-iife.js +++ b/api-iife.js @@ -1 +1 @@ -if("__TAURI__"in window){var __TAURI_PLUGIN_SQLITE__=function(){"use strict";async function t(t,e={},s){return window.__TAURI_INTERNALS__.invoke(t,e,s)}"function"==typeof SuppressedError&&SuppressedError;class e{constructor(t){this.path=t}static async load(s,n){const i=await t("plugin:sqlite|load",{db:s,customConfig:n});return new e(i)}static get(t){return new e(t)}async execute(e,s){const[n,i]=await t("plugin:sqlite|execute",{db:this.path,query:e,values:s??[]});return{lastInsertId:i,rowsAffected:n}}async fetchAll(e,s){return await t("plugin:sqlite|fetch_all",{db:this.path,query:e,values:s??[]})}async fetchOne(e,s){return await t("plugin:sqlite|select_one",{db:this.path,query:e,values:s??[]})}async close(){return await t("plugin:sqlite|close",{db:this.path})}static async closeAll(){return await t("plugin:sqlite|close_all")}async remove(){return await t("plugin:sqlite|remove",{db:this.path})}}return e}();Object.defineProperty(window.__TAURI__,"sqlite",{value:__TAURI_PLUGIN_SQLITE__})} +if("__TAURI__"in window){var __TAURI_PLUGIN_SQLITE__=function(){"use strict";async function t(t,e={},s){return window.__TAURI_INTERNALS__.invoke(t,e,s)}"function"==typeof SuppressedError&&SuppressedError;class e{constructor(t){this.path=t}static async load(s,n){const i=await t("plugin:sqlite|load",{db:s,customConfig:n});return new e(i)}static get(t){return new e(t)}async execute(e,s){const[n,i]=await t("plugin:sqlite|execute",{db:this.path,query:e,values:s??[]});return{lastInsertId:i,rowsAffected:n}}async fetchAll(e,s){return await t("plugin:sqlite|fetch_all",{db:this.path,query:e,values:s??[]})}async fetchOne(e,s){return await t("plugin:sqlite|fetch_one",{db:this.path,query:e,values:s??[]})}async close(){return await t("plugin:sqlite|close",{db:this.path})}static async closeAll(){return await t("plugin:sqlite|close_all")}async remove(){return await t("plugin:sqlite|remove",{db:this.path})}}return e}();Object.defineProperty(window.__TAURI__,"sqlite",{value:__TAURI_PLUGIN_SQLITE__})} diff --git a/guest-js/index.ts b/guest-js/index.ts index 5b9bbed..c04b72e 100644 --- a/guest-js/index.ts +++ b/guest-js/index.ts @@ -1,5 +1,17 @@ import { invoke } from '@tauri-apps/api/core' +/** + * Valid SQLite parameter binding value types. + * + * SQLite supports a limited set of types for parameter binding: + * - `string` - TEXT, DATE, TIME, DATETIME + * - `number` - INTEGER, REAL + * - `boolean` - BOOLEAN + * - `null` - NULL + * - `Uint8Array` - BLOB (binary data) + */ +export type SqlValue = string | number | boolean | null | Uint8Array + export interface QueryResult { /** The number of rows affected by the query. */ rowsAffected: number @@ -7,6 +19,18 @@ export interface QueryResult { lastInsertId: number } +/** + * Structured error returned from SQLite operations. + * + * All errors thrown by the plugin will have this structure. + */ +export interface SqliteError { + /** Machine-readable error code (e.g., "SQLITE_CONSTRAINT", "DATABASE_NOT_LOADED") */ + code: string + /** Human-readable error message */ + message: string +} + /** * Custom configuration for SQLite database connection */ @@ -108,7 +132,7 @@ export default class Database { * ); * ``` */ - async execute(query: string, bindValues?: unknown[]): Promise { + async execute(query: string, bindValues?: SqlValue[]): Promise { const [rowsAffected, lastInsertId] = await invoke<[number, number]>( 'plugin:sqlite|execute', { @@ -145,7 +169,7 @@ export default class Database { * ); * ``` */ - async fetchAll(query: string, bindValues?: unknown[]): Promise { + async fetchAll(query: string, bindValues?: SqlValue[]): Promise { const result = await invoke('plugin:sqlite|fetch_all', { db: this.path, query, @@ -177,8 +201,8 @@ export default class Database { * } * ``` */ - async fetchOne(query: string, bindValues?: unknown[]): Promise { - const result = await invoke('plugin:sqlite|select_one', { + async fetchOne(query: string, bindValues?: SqlValue[]): Promise { + const result = await invoke('plugin:sqlite|fetch_one', { db: this.path, query, values: bindValues ?? [] diff --git a/src/error.rs b/src/error.rs index 607281f..3741b35 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,12 +1,78 @@ use serde::{Serialize, Serializer}; +/// Result type alias for plugin operations. pub type Result = std::result::Result; +/// Structured error response for frontend. +#[derive(Serialize)] +struct ErrorResponse { + code: String, + message: String, +} + +/// Error types for the SQLite plugin. #[derive(Debug, thiserror::Error)] pub enum Error { - // TODO: Add specific error variants for different failure cases - #[error("{0}")] - Custom(String), + /// Error from SQLx operations. + #[error(transparent)] + Sqlx(#[from] sqlx::Error), + + /// Error from the connection manager. + #[error(transparent)] + ConnectionManager(#[from] sqlx_sqlite_conn_mgr::Error), + + /// Error from database migrations. + #[error(transparent)] + Migration(#[from] sqlx::migrate::MigrateError), + + /// Invalid database path provided. + #[error("invalid database path: {0}")] + InvalidPath(String), + + /// Attempted to access a database that hasn't been loaded. + #[error("database {0} not loaded")] + DatabaseNotLoaded(String), + + /// SQLite type that cannot be mapped to JSON. + #[error("unsupported datatype: {0}")] + UnsupportedDatatype(String), + + /// I/O error when accessing database files. + #[error("io error: {0}")] + Io(#[from] std::io::Error), + + /// Read-only query executed with execute command. + #[error("execute() should not be used for read-only queries. Use fetchX() instead.")] + ReadOnlyQueryInExecute, + + /// Multiple rows returned from fetchOne query. + #[error("fetchOne() query returned {0} rows, expected 0 or 1")] + MultipleRowsReturned(usize), +} + +impl Error { + /// Extract a structured error code from the error type. + /// + /// This provides machine-readable error codes for frontend error handling. + fn error_code(&self) -> String { + match self { + Error::Sqlx(e) => { + // Extract SQLite error codes from sqlx errors + if let Some(code) = e.as_database_error().and_then(|db_err| db_err.code()) { + return format!("SQLITE_{}", code); + } + "SQLX_ERROR".to_string() + } + Error::ConnectionManager(_) => "CONNECTION_ERROR".to_string(), + Error::Migration(_) => "MIGRATION_ERROR".to_string(), + Error::InvalidPath(_) => "INVALID_PATH".to_string(), + Error::DatabaseNotLoaded(_) => "DATABASE_NOT_LOADED".to_string(), + Error::UnsupportedDatatype(_) => "UNSUPPORTED_DATATYPE".to_string(), + Error::Io(_) => "IO_ERROR".to_string(), + Error::ReadOnlyQueryInExecute => "READ_ONLY_QUERY_IN_EXECUTE".to_string(), + Error::MultipleRowsReturned(_) => "MULTIPLE_ROWS_RETURNED".to_string(), + } + } } impl Serialize for Error { @@ -14,6 +80,10 @@ impl Serialize for Error { where S: Serializer, { - serializer.serialize_str(self.to_string().as_ref()) + let response = ErrorResponse { + code: self.error_code(), + message: self.to_string(), + }; + response.serialize(serializer) } } From 173e847c20870ac772d84a9ebeb61e05fe76418a Mon Sep 17 00:00:00 2001 From: Paul Morris <10599524+1Cor125@users.noreply.github.com> Date: Fri, 28 Nov 2025 11:23:34 -0500 Subject: [PATCH 8/8] feat: support transactions on the API --- README.md | 42 +++++++++++++++++++++++++++ api-iife.js | 2 +- guest-js/index.ts | 72 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 115 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 147d062..2d9596b 100644 --- a/README.md +++ b/README.md @@ -302,6 +302,48 @@ if (user) { > catch bugs where a query unexpectedly returns multiple results. Use `fetchAll()` if you > expect multiple rows. +### Using Transactions + +Transactions ensure that multiple operations either all succeed or all fail together, +maintaining data consistency: + +```typescript +// Begin a transaction +await db.beginTransaction(); + +try { + // Execute multiple operations atomically + await db.execute( + 'INSERT INTO users (name, email) VALUES ($1, $2)', + ['Alice', 'alice@example.com'] + ); + + await db.execute( + 'INSERT INTO audit_log (action, user) VALUES ($1, $2)', + ['user_created', 'Alice'] + ); + + // Commit if all operations succeed + await db.commitTransaction(); + console.log('Transaction completed successfully'); + +} catch (error) { + // Rollback if any operation fails + await db.rollbackTransaction(); + console.error('Transaction failed, rolled back:', error); + throw error; +} +``` + +**Important Notes:** + + * All operations between `beginTransaction()` and + `commitTransaction()`/`rollbackTransaction()` are executed as a single atomic unit + * If an error occurs, call `rollbackTransaction()` to discard all changes + * Nested transactions are not supported + * Always ensure transactions are either committed or rolled back to avoid locking + issues + ### Closing Connections ```typescript diff --git a/api-iife.js b/api-iife.js index baaecfc..b82593c 100644 --- a/api-iife.js +++ b/api-iife.js @@ -1 +1 @@ -if("__TAURI__"in window){var __TAURI_PLUGIN_SQLITE__=function(){"use strict";async function t(t,e={},s){return window.__TAURI_INTERNALS__.invoke(t,e,s)}"function"==typeof SuppressedError&&SuppressedError;class e{constructor(t){this.path=t}static async load(s,n){const i=await t("plugin:sqlite|load",{db:s,customConfig:n});return new e(i)}static get(t){return new e(t)}async execute(e,s){const[n,i]=await t("plugin:sqlite|execute",{db:this.path,query:e,values:s??[]});return{lastInsertId:i,rowsAffected:n}}async fetchAll(e,s){return await t("plugin:sqlite|fetch_all",{db:this.path,query:e,values:s??[]})}async fetchOne(e,s){return await t("plugin:sqlite|fetch_one",{db:this.path,query:e,values:s??[]})}async close(){return await t("plugin:sqlite|close",{db:this.path})}static async closeAll(){return await t("plugin:sqlite|close_all")}async remove(){return await t("plugin:sqlite|remove",{db:this.path})}}return e}();Object.defineProperty(window.__TAURI__,"sqlite",{value:__TAURI_PLUGIN_SQLITE__})} +if("__TAURI__"in window){var __TAURI_PLUGIN_SQLITE__=function(){"use strict";async function t(t,a={},e){return window.__TAURI_INTERNALS__.invoke(t,a,e)}"function"==typeof SuppressedError&&SuppressedError;class a{constructor(t){this.path=t}static async load(e,n){const i=await t("plugin:sqlite|load",{db:e,customConfig:n});return new a(i)}static get(t){return new a(t)}async execute(a,e){const[n,i]=await t("plugin:sqlite|execute",{db:this.path,query:a,values:e??[]});return{lastInsertId:i,rowsAffected:n}}async fetchAll(a,e){return await t("plugin:sqlite|fetch_all",{db:this.path,query:a,values:e??[]})}async fetchOne(a,e){return await t("plugin:sqlite|fetch_one",{db:this.path,query:a,values:e??[]})}async beginTransaction(){await t("plugin:sqlite|begin_transaction",{db:this.path})}async commitTransaction(){await t("plugin:sqlite|commit_transaction",{db:this.path})}async rollbackTransaction(){await t("plugin:sqlite|rollback_transaction",{db:this.path})}async close(){return await t("plugin:sqlite|close",{db:this.path})}static async closeAll(){return await t("plugin:sqlite|close_all")}async remove(){return await t("plugin:sqlite|remove",{db:this.path})}}return a}();Object.defineProperty(window.__TAURI__,"sqlite",{value:__TAURI_PLUGIN_SQLITE__})} diff --git a/guest-js/index.ts b/guest-js/index.ts index c04b72e..a31642f 100644 --- a/guest-js/index.ts +++ b/guest-js/index.ts @@ -211,6 +211,78 @@ export default class Database { return result } + /** + * **beginTransaction** + * + * Begins a new database transaction. All subsequent operations will be + * part of this transaction until `commitTransaction()` or `rollbackTransaction()` + * is called. + * + * Transactions provide atomicity - either all operations succeed or all are rolled back. + * + * @example + * ```ts + * await db.beginTransaction(); + * try { + * await db.execute('INSERT INTO users (name) VALUES ($1)', ['Alice']); + * await db.execute('INSERT INTO logs (action) VALUES ($1)', ['user_created']); + * await db.commitTransaction(); + * } catch (error) { + * await db.rollbackTransaction(); + * throw error; + * } + * ``` + */ + async beginTransaction(): Promise { + await invoke('plugin:sqlite|begin_transaction', { + db: this.path + }) + } + + /** + * **commitTransaction** + * + * Commits the current transaction, making all changes permanent. + * + * @example + * ```ts + * await db.beginTransaction(); + * await db.execute('INSERT INTO users (name) VALUES ($1)', ['Alice']); + * await db.execute('INSERT INTO logs (action) VALUES ($1)', ['user_created']); + * await db.commitTransaction(); + * ``` + */ + async commitTransaction(): Promise { + await invoke('plugin:sqlite|commit_transaction', { + db: this.path + }) + } + + /** + * **rollbackTransaction** + * + * Rolls back the current transaction, discarding all changes made since + * `beginTransaction()` was called. + * + * @example + * ```ts + * await db.beginTransaction(); + * try { + * await db.execute('INSERT INTO users (name) VALUES ($1)', ['Alice']); + * await db.execute('INSERT INTO logs (action) VALUES ($1)', ['user_created']); + * await db.commitTransaction(); + * } catch (error) { + * await db.rollbackTransaction(); + * throw error; + * } + * ``` + */ + async rollbackTransaction(): Promise { + await invoke('plugin:sqlite|rollback_transaction', { + db: this.path + }) + } + /** * **close** *