From 953669d9f1c170bbfaea0b7c08d4ef6a4b3fd93b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Thu, 11 Jun 2026 20:02:33 +0100 Subject: [PATCH 01/83] Configure mise --- .gitignore | 225 +++++++++++++++++++++++++++++++++++++++++++++++++---- AGENTS.md | 3 + mise.lock | 5 ++ mise.toml | 2 + 4 files changed, 222 insertions(+), 13 deletions(-) create mode 100644 AGENTS.md create mode 100644 mise.lock create mode 100644 mise.toml diff --git a/.gitignore b/.gitignore index 9863270..d677453 100644 --- a/.gitignore +++ b/.gitignore @@ -1,14 +1,213 @@ -/.vs +# +# https://github.com/github/gitignore/blob/main/Rust.gitignore +# + +# Generated by Cargo +# will have compiled files and executables +debug +target + +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb + +# Generated by cargo mutants +# Contains mutation testing data +**/mutants.out*/ + +# rustc will dump stack traces when hitting an internal compiler error to PWD +rustc-ice-*.txt + +# +# https://github.com/github/gitignore/blob/main/Global/mise.gitignore +# + +# https://mise.jdx.dev/configuration.html +# https://mise.jdx.dev/configuration/environments.html +.mise.*.local.toml +.mise.local.toml +mise.*.local.toml +mise.local.toml +.mise/*.local.toml +mise/*.local.toml + +# +# https://github.com/github/gitignore/blob/main/Global/Agents.gitignore +# + +GEMINI.md +WARP.md +CRUSH.md +QWEN.md + +# OpenAI Codex +.codex/ + +# Aider +.aider.input.history +.aider.chat.history.md +.aider.llm.history +.aider.tags.cache.v* +.aiderignore + +# Claude Code +.claude/*.local.json +.claude/**/*.log +CLAUDE.local.md +.claude/ + +# Gemini CLI +gemini-debug.log +.gemini-clipboard/ +.gemini/ + +# Cursor AI +.cursorrules +.cursor/ +.cursor.json +.cursor-settings.yaml + +# Continue +.continue/ +.continuerc.json + +# Cline +.cline/ +.clinerules +cline.json + +# Other agent/editor project config +.warp/ +.crush/ +.codeium/ +.deepseek/ +.amazon-codewhisperer/ +.tabnineignore +.tabnine/ + +# GitHub Copilot +.github/copilot-instructions.md + +# Windsurf Editor +.windsurfrules +.windsurf/ + +# Replit AI Development +.replit +replit.nix + +# +# https://github.com/github/gitignore/blob/main/Global/Windows.gitignore +# + +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# +# https://github.com/github/gitignore/blob/main/Global/macOS.gitignore +# + +# General .DS_Store -.Thumbs.db -*.sublime* -.idea/ -debug.log -package-lock.json -.vscode/settings.json -yarn.lock - -/.tauri -/target -Cargo.lock -node_modules/ +.localized +__MACOSX/ +.AppleDouble +.LSOverride +Icon[ +] + +# Resource forks +._* + +# Files and directories that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent +.com.apple.timemachine.supported +.PKInstallSandboxManager +.PKInstallSandboxManager-SystemSoftware +.hotfiles.btree +.vol +.file +.disk_label* +lost+found +.HFS+ Private Directory Data[ +] + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# Mac OS 6 to 9 +Desktop DB +Desktop DF +TheFindByContentFolder +TheVolumeSettingsFolder +.FBCIndex +.FBCSemaphoreFile +.FBCLockFolder + +# Quota system +.quota.group +.quota.user +.quota.ops.group +.quota.ops.user + +# TimeMachine +Backups.backupdb +.MobileBackups +.MobileBackups.trash +MobileBackups.trash +tmbootpicker.efi + +# +# https://github.com/github/gitignore/blob/main/Global/Linux.gitignore +# + +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# Metadata left by Dolphin file manager, which comes with KDE Plasma +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +# Log files created by default by the nohup command +nohup.out diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..0c6811a --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,3 @@ +# Repository Guidelines + +If a command is unavailable, retry it as `mise exec -- `. diff --git a/mise.lock b/mise.lock new file mode 100644 index 0000000..1f598a7 --- /dev/null +++ b/mise.lock @@ -0,0 +1,5 @@ +# @generated - this file is auto-generated by `mise lock` https://mise.jdx.dev/dev-tools/mise-lock.html + +[[tools.rust]] +version = "nightly" +backend = "core:rust" diff --git a/mise.toml b/mise.toml new file mode 100644 index 0000000..bc64ee0 --- /dev/null +++ b/mise.toml @@ -0,0 +1,2 @@ +[tools] +rust = "nightly" From 89faa7f8078f400da73e1101c453c473157848a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Thu, 11 Jun 2026 20:04:02 +0100 Subject: [PATCH 02/83] Add lock file --- .gitattributes | 1 + Cargo.lock | 5123 ++++++++++++++++++++++++ permissions/autogenerated/reference.md | 1 - permissions/schemas/schema.json | 10 +- 4 files changed, 5130 insertions(+), 5 deletions(-) create mode 100644 .gitattributes create mode 100644 Cargo.lock diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..8f710ad --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.lock linguist-generated=true diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..c2f275f --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,5123 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "alloc-no-stdlib" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" + +[[package]] +name = "alloc-stdlib" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" +dependencies = [ + "alloc-no-stdlib", +] + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anyhow" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + +[[package]] +name = "atk" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "241b621213072e993be4f6f3a9e4b45f65b7e6faad43001be957184b7bb1824b" +dependencies = [ + "atk-sys", + "glib", + "libc", +] + +[[package]] +name = "atk-sys" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5e48b684b0ca77d2bbadeef17424c2ea3c897d44d566a1617e7e8f30614d086" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bit-set" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" +dependencies = [ + "serde_core", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block2" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5" +dependencies = [ + "objc2", +] + +[[package]] +name = "brotli" +version = "8.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8119e4516436f5708bbc474a9d395bf12f1b5395e93a92a56e647ac3388c8610" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5962523e1b92ce1b5e793d9169b9943eece10d39f62550bc04bb605d75b94924" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + +[[package]] +name = "bs58" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "bumpalo" +version = "3.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" + +[[package]] +name = "bytemuck" +version = "1.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" +dependencies = [ + "serde", +] + +[[package]] +name = "cairo-rs" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ca26ef0159422fb77631dc9d17b102f253b876fe1586b03b803e63a309b4ee2" +dependencies = [ + "bitflags 2.13.0", + "cairo-sys-rs", + "glib", + "libc", + "once_cell", + "thiserror 1.0.69", +] + +[[package]] +name = "cairo-sys-rs" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "685c9fa8e590b8b3d678873528d83411db17242a73fccaed827770ea0fedda51" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + +[[package]] +name = "camino" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e629a66d692cb9ff1a1c664e41771b3dcaf961985a9774c0eb0bd1b51cf60a48" +dependencies = [ + "serde_core", +] + +[[package]] +name = "cargo-platform" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e35af189006b9c0f00a064685c727031e3ed2d8020f7ba284d78cc2671bd36ea" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo_metadata" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd5eb614ed4c27c5d706420e4320fbe3216ab31fa1c33cd8246ac36dae4479ba" +dependencies = [ + "camino", + "cargo-platform", + "semver", + "serde", + "serde_json", + "thiserror 2.0.18", +] + +[[package]] +name = "cargo_toml" +version = "0.22.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "374b7c592d9c00c1f4972ea58390ac6b18cbb6ab79011f3bdc90a0b82ca06b77" +dependencies = [ + "serde", + "toml 0.9.12+spec-1.1.0", +] + +[[package]] +name = "cc" +version = "1.2.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "556e016178bb5662a08681bbe0f00f8e17631781a4dfc8c45e466e4b185ec27f" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + +[[package]] +name = "cfb" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d38f2da7a0a2c4ccf0065be06397cc26a81f4e528be095826eee9d4adbb8c60f" +dependencies = [ + "byteorder", + "fnv", + "uuid", +] + +[[package]] +name = "cfg-expr" +version = "0.15.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" +dependencies = [ + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "chrono" +version = "0.4.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" +dependencies = [ + "iana-time-zone", + "num-traits", + "serde", + "windows-link 0.2.1", +] + +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "cookie" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" +dependencies = [ + "time", + "version_check", +] + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "core-graphics" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "064badf302c3194842cf2c5d61f56cc88e54a759313879cdf03abdd27d0c3b97" +dependencies = [ + "bitflags 2.13.0", + "core-foundation 0.10.1", + "core-graphics-types", + "foreign-types 0.5.0", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" +dependencies = [ + "bitflags 2.13.0", + "core-foundation 0.10.1", + "libc", +] + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "cssparser" +version = "0.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dae61cf9c0abb83bd659dab65b7e4e38d8236824c85f0f804f173567bda257d2" +dependencies = [ + "cssparser-macros", + "dtoa-short", + "itoa", + "phf", + "smallvec", +] + +[[package]] +name = "cssparser-macros" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" +dependencies = [ + "quote", + "syn 2.0.117", +] + +[[package]] +name = "ctor" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "352d39c2f7bef1d6ad73db6f5160efcaed66d94ef8c6c573a8410c00bf909a98" +dependencies = [ + "ctor-proc-macro", + "dtor", +] + +[[package]] +name = "ctor-proc-macro" +version = "0.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52560adf09603e58c9a7ee1fe1dcb95a16927b17c127f0ac02d6e768a0e25bc1" + +[[package]] +name = "darling" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0" +dependencies = [ + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.117", +] + +[[package]] +name = "darling_macro" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "dbus" +version = "0.9.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b942602992bb7acfd1f51c49811c58a610ef9181b6e66f3e519d79b540a3bf73" +dependencies = [ + "libc", + "libdbus-sys", + "windows-sys 0.61.2", +] + +[[package]] +name = "deranged" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" +dependencies = [ + "powerfmt", + "serde_core", +] + +[[package]] +name = "derive_more" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" +dependencies = [ + "proc-macro2", + "quote", + "rustc_version", + "syn 2.0.117", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "dirs" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.61.2", +] + +[[package]] +name = "dispatch2" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" +dependencies = [ + "bitflags 2.13.0", + "block2", + "libc", + "objc2", +] + +[[package]] +name = "displaydoc" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "dlopen2" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e2c5bd4158e66d1e215c49b837e11d62f3267b30c92f1d171c4d3105e3dc4d4" +dependencies = [ + "dlopen2_derive", + "libc", + "once_cell", + "winapi", +] + +[[package]] +name = "dlopen2_derive" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fbbb781877580993a8707ec48672673ec7b81eeba04cfd2310bd28c08e47c8f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "dom_query" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521e380c0c8afb8d9a1e83a1822ee03556fc3e3e7dbc1fd30be14e37f9cb3f89" +dependencies = [ + "bit-set", + "cssparser", + "foldhash 0.2.0", + "html5ever", + "precomputed-hash", + "selectors", + "tendril", +] + +[[package]] +name = "dpi" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76" +dependencies = [ + "serde", +] + +[[package]] +name = "dtoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c3cf4824e2d5f025c7b531afcb2325364084a16806f6d47fbc1f5fbd9960590" + +[[package]] +name = "dtoa-short" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd1511a7b6a56299bd043a9c167a6d2bfb37bf84a6dfceaba651168adfb43c87" +dependencies = [ + "dtoa", +] + +[[package]] +name = "dtor" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1057d6c64987086ff8ed0fd3fbf377a6b7d205cc7715868cd401705f715cbe4" +dependencies = [ + "dtor-proc-macro", +] + +[[package]] +name = "dtor-proc-macro" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f678cf4a922c215c63e0de95eb1ff08a958a81d47e485cf9da1e27bf6305cfa5" + +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + +[[package]] +name = "dyn-clone" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" + +[[package]] +name = "embed-resource" +version = "3.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31a88c8d26de40ed18fe748c547845aa39de1db3afd958f8cb91579f3644bcb" +dependencies = [ + "cc", + "memchr", + "rustc_version", + "toml 1.1.2+spec-1.1.0", + "vswhom", + "winreg", +] + +[[package]] +name = "embed_plist" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ef6b89e5b37196644d8796de5268852ff179b44e96276cf4290264843743bb7" + +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "erased-serde" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2add8a07dd6a8d93ff627029c51de145e12686fbc36ecb298ac22e74cf02dec" +dependencies = [ + "serde", + "serde_core", + "typeid", +] + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "fastrand" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" + +[[package]] +name = "fdeflate" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "field-offset" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f" +dependencies = [ + "memoffset", + "rustc_version", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "flate2" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared 0.1.1", +] + +[[package]] +name = "foreign-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared 0.3.1", +] + +[[package]] +name = "foreign-types-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "foreign-types-shared" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-executor" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" + +[[package]] +name = "futures-macro" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "slab", +] + +[[package]] +name = "gdk" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9f245958c627ac99d8e529166f9823fb3b838d1d41fd2b297af3075093c2691" +dependencies = [ + "cairo-rs", + "gdk-pixbuf", + "gdk-sys", + "gio", + "glib", + "libc", + "pango", +] + +[[package]] +name = "gdk-pixbuf" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50e1f5f1b0bfb830d6ccc8066d18db35c487b1b2b1e8589b5dfe9f07e8defaec" +dependencies = [ + "gdk-pixbuf-sys", + "gio", + "glib", + "libc", + "once_cell", +] + +[[package]] +name = "gdk-pixbuf-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9839ea644ed9c97a34d129ad56d38a25e6756f99f3a88e15cd39c20629caf7" +dependencies = [ + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "gdk-sys" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c2d13f38594ac1e66619e188c6d5a1adb98d11b2fcf7894fc416ad76aa2f3f7" +dependencies = [ + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "pango-sys", + "pkg-config", + "system-deps", +] + +[[package]] +name = "gdkwayland-sys" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "140071d506d223f7572b9f09b5e155afbd77428cd5cc7af8f2694c41d98dfe69" +dependencies = [ + "gdk-sys", + "glib-sys", + "gobject-sys", + "libc", + "pkg-config", + "system-deps", +] + +[[package]] +name = "gdkx11" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3caa00e14351bebbc8183b3c36690327eb77c49abc2268dd4bd36b856db3fbfe" +dependencies = [ + "gdk", + "gdkx11-sys", + "gio", + "glib", + "libc", + "x11", +] + +[[package]] +name = "gdkx11-sys" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e7445fe01ac26f11601db260dd8608fe172514eb63b3b5e261ea6b0f4428d" +dependencies = [ + "gdk-sys", + "glib-sys", + "libc", + "system-deps", + "x11", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi 5.3.0", + "wasip2", +] + +[[package]] +name = "getrandom" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +dependencies = [ + "cfg-if", + "libc", + "r-efi 6.0.0", + "wasip2", + "wasip3", +] + +[[package]] +name = "gio" +version = "0.18.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fc8f532f87b79cbc51a79748f16a6828fb784be93145a322fa14d06d354c73" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "gio-sys", + "glib", + "libc", + "once_cell", + "pin-project-lite", + "smallvec", + "thiserror 1.0.69", +] + +[[package]] +name = "gio-sys" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37566df850baf5e4cb0dfb78af2e4b9898d817ed9263d1090a2df958c64737d2" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", + "winapi", +] + +[[package]] +name = "glib" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233daaf6e83ae6a12a52055f568f9d7cf4671dabb78ff9560ab6da230ce00ee5" +dependencies = [ + "bitflags 2.13.0", + "futures-channel", + "futures-core", + "futures-executor", + "futures-task", + "futures-util", + "gio-sys", + "glib-macros", + "glib-sys", + "gobject-sys", + "libc", + "memchr", + "once_cell", + "smallvec", + "thiserror 1.0.69", +] + +[[package]] +name = "glib-macros" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bb0228f477c0900c880fd78c8759b95c7636dbd7842707f49e132378aa2acdc" +dependencies = [ + "heck 0.4.1", + "proc-macro-crate 2.0.2", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "glib-sys" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "063ce2eb6a8d0ea93d2bf8ba1957e78dbab6be1c2220dd3daca57d5a9d869898" +dependencies = [ + "libc", + "system-deps", +] + +[[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + +[[package]] +name = "gobject-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0850127b514d1c4a4654ead6dedadb18198999985908e6ffe4436f53c785ce44" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + +[[package]] +name = "gtk" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd56fb197bfc42bd5d2751f4f017d44ff59fbb58140c6b49f9b3b2bdab08506a" +dependencies = [ + "atk", + "cairo-rs", + "field-offset", + "futures-channel", + "gdk", + "gdk-pixbuf", + "gio", + "glib", + "gtk-sys", + "gtk3-macros", + "libc", + "pango", + "pkg-config", +] + +[[package]] +name = "gtk-sys" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f29a1c21c59553eb7dd40e918be54dccd60c52b049b75119d5d96ce6b624414" +dependencies = [ + "atk-sys", + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gdk-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "pango-sys", + "system-deps", +] + +[[package]] +name = "gtk3-macros" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ff3c5b21f14f0736fed6dcfc0bfb4225ebf5725f3c0209edeec181e4d73e9d" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "h2" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "171fefbc92fe4a4de27e0698d6a5b392d6a0e333506bc49133760b3bcf948733" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap 2.14.0", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash 0.1.5", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "html5ever" +version = "0.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1054432bae2f14e0061e33d23402fbaa67a921d319d56adc6bcf887ddad1cbc2" +dependencies = [ + "log", + "markup5ever", +] + +[[package]] +name = "http" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8be7462df143984c4598a256ef469b251d7d7f9e271135073e78fc535414f3d0" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "hyper" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55281c53a1894c864990125767da440a4e630446785086f52523b20033b74498" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "h2", + "http", + "http-body", + "httparse", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f" +dependencies = [ + "http", + "hyper", + "hyper-util", + "rustls", + "tokio", + "tokio-rustls", + "tower-service", +] + +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "ipnet", + "libc", + "percent-encoding", + "pin-project-lite", + "socket2", + "system-configuration", + "tokio", + "tower-service", + "tracing", + "windows-registry", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core 0.62.2", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "ico" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e795dff5605e0f04bff85ca41b51a96b83e80b281e96231bcaaf1ac35103371" +dependencies = [ + "byteorder", + "png 0.17.16", +] + +[[package]] +name = "icu_collections" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" +dependencies = [ + "displaydoc", + "potential_utf", + "utf8_iter", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" + +[[package]] +name = "icu_properties" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" + +[[package]] +name = "icu_provider" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown 0.17.1", + "serde", + "serde_core", +] + +[[package]] +name = "infer" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a588916bfdfd92e71cacef98a63d9b1f0d74d6599980d11894290e7ddefffcf7" +dependencies = [ + "cfb", +] + +[[package]] +name = "ipnet" +version = "2.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "javascriptcore-rs" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca5671e9ffce8ffba57afc24070e906da7fc4b1ba66f2cabebf61bf2ea257fcc" +dependencies = [ + "bitflags 1.3.2", + "glib", + "javascriptcore-rs-sys", +] + +[[package]] +name = "javascriptcore-rs-sys" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af1be78d14ffa4b75b66df31840478fef72b51f8c2465d4ca7c194da9f7a5124" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys 0.3.1", + "log", + "thiserror 1.0.69", + "walkdir", + "windows-sys 0.45.0", +] + +[[package]] +name = "jni-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41a652e1f9b6e0275df1f15b32661cf0d4b78d4d87ddec5e0c3c20f097433258" +dependencies = [ + "jni-sys 0.4.1", +] + +[[package]] +name = "jni-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2" +dependencies = [ + "jni-sys-macros", +] + +[[package]] +name = "jni-sys-macros" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" +dependencies = [ + "quote", + "syn 2.0.117", +] + +[[package]] +name = "js-sys" +version = "0.3.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "142bc4740e452c1e57ade0cbc129f139c9093e354346f0872ef985f4f5cf5f11" +dependencies = [ + "cfg-if", + "futures-util", + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "json-patch" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "863726d7afb6bc2590eeff7135d923545e5e964f004c2ccf8716c25e70a86f08" +dependencies = [ + "jsonptr", + "serde", + "serde_json", + "thiserror 1.0.69", +] + +[[package]] +name = "jsonptr" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dea2b27dd239b2556ed7a25ba842fe47fd602e7fc7433c2a8d6106d4d9edd70" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "keyboard-types" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b750dcadc39a09dbadd74e118f6dd6598df77fa01df0cfcdc52c28dece74528a" +dependencies = [ + "bitflags 2.13.0", + "serde", + "unicode-segmentation", +] + +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + +[[package]] +name = "libappindicator" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03589b9607c868cc7ae54c0b2a22c8dc03dd41692d48f2d7df73615c6a95dc0a" +dependencies = [ + "glib", + "gtk", + "gtk-sys", + "libappindicator-sys", + "log", +] + +[[package]] +name = "libappindicator-sys" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e9ec52138abedcc58dc17a7c6c0c00a2bdb4f3427c7f63fa97fd0d859155caf" +dependencies = [ + "gtk-sys", + "libloading", + "once_cell", +] + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "libdbus-sys" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "328c4789d42200f1eeec05bd86c9c13c7f091d2ba9a6ea35acdf51f31bc0f043" +dependencies = [ + "pkg-config", +] + +[[package]] +name = "libloading" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +dependencies = [ + "cfg-if", + "winapi", +] + +[[package]] +name = "libredox" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f02ab6bace2054fb888a3c16f990117b579d14a3088e472d63c6011fa185c9d3" +dependencies = [ + "libc", +] + +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + +[[package]] +name = "litemap" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "953f07c43838f8e6f9758cab68bf5bed85465e7587ebe0b823f1bcd81978ad3a" + +[[package]] +name = "markup5ever" +version = "0.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8983d30f2915feeaaab2d6babdd6bc7e9ed1a00b66b5e6d74df19aa9c0e91862" +dependencies = [ + "log", + "tendril", + "web_atoms", +] + +[[package]] +name = "memchr" +version = "2.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8" + +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "mio" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "muda" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47a2e3dff89cd322c66647942668faee0a2b1f88ea6cbb4d374b4a8d7e92528c" +dependencies = [ + "crossbeam-channel", + "dpi", + "gtk", + "keyboard-types", + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation", + "once_cell", + "png 0.18.1", + "serde", + "thiserror 2.0.18", + "windows-sys 0.61.2", +] + +[[package]] +name = "native-tls" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "465500e14ea162429d264d44189adc38b199b62b1c21eea9f69e4b73cb03bbf2" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "ndk" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" +dependencies = [ + "bitflags 2.13.0", + "jni-sys 0.3.1", + "log", + "ndk-sys", + "num_enum", + "raw-window-handle", + "thiserror 1.0.69", +] + +[[package]] +name = "ndk-sys" +version = "0.6.0+11769913" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873" +dependencies = [ + "jni-sys 0.3.1", +] + +[[package]] +name = "new_debug_unreachable" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" + +[[package]] +name = "nix" +version = "0.31.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d" +dependencies = [ + "bitflags 2.13.0", + "cfg-if", + "cfg_aliases", + "libc", +] + +[[package]] +name = "num-conv" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_enum" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0bca838442ec211fa11de3a8b0e0e8f3a4522575b5c4c06ed722e005036f26" +dependencies = [ + "num_enum_derive", + "rustversion", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8" +dependencies = [ + "proc-macro-crate 3.5.0", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "objc2" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f" +dependencies = [ + "objc2-encode", + "objc2-exception-helper", +] + +[[package]] +name = "objc2-app-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c" +dependencies = [ + "bitflags 2.13.0", + "block2", + "objc2", + "objc2-core-foundation", + "objc2-foundation", +] + +[[package]] +name = "objc2-cloud-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ad74d880bb43877038da939b7427bba67e9dd42004a18b809ba7d87cee241c" +dependencies = [ + "bitflags 2.13.0", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-data" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b402a653efbb5e82ce4df10683b6b28027616a2715e90009947d50b8dd298fa" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" +dependencies = [ + "bitflags 2.13.0", + "dispatch2", + "objc2", +] + +[[package]] +name = "objc2-core-graphics" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807" +dependencies = [ + "bitflags 2.13.0", + "dispatch2", + "objc2", + "objc2-core-foundation", + "objc2-io-surface", +] + +[[package]] +name = "objc2-core-image" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d563b38d2b97209f8e861173de434bd0214cf020e3423a52624cd1d989f006" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-location" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca347214e24bc973fc025fd0d36ebb179ff30536ed1f80252706db19ee452009" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-text" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cde0dfb48d25d2b4862161a4d5fcc0e3c24367869ad306b0c9ec0073bfed92d" +dependencies = [ + "bitflags 2.13.0", + "objc2", + "objc2-core-foundation", + "objc2-core-graphics", +] + +[[package]] +name = "objc2-encode" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" + +[[package]] +name = "objc2-exception-helper" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7a1c5fbb72d7735b076bb47b578523aedc40f3c439bea6dfd595c089d79d98a" +dependencies = [ + "cc", +] + +[[package]] +name = "objc2-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" +dependencies = [ + "bitflags 2.13.0", + "block2", + "libc", + "objc2", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-io-surface" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d" +dependencies = [ + "bitflags 2.13.0", + "objc2", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-quartz-core" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c1358452b371bf9f104e21ec536d37a650eb10f7ee379fff67d2e08d537f1f" +dependencies = [ + "bitflags 2.13.0", + "objc2", + "objc2-core-foundation", + "objc2-foundation", +] + +[[package]] +name = "objc2-ui-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d87d638e33c06f577498cbcc50491496a3ed4246998a7fbba7ccb98b1e7eab22" +dependencies = [ + "bitflags 2.13.0", + "block2", + "objc2", + "objc2-cloud-kit", + "objc2-core-data", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-core-image", + "objc2-core-location", + "objc2-core-text", + "objc2-foundation", + "objc2-quartz-core", + "objc2-user-notifications", +] + +[[package]] +name = "objc2-user-notifications" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9df9128cbbfef73cda168416ccf7f837b62737d748333bfe9ab71c245d76613e" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-web-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2e5aaab980c433cf470df9d7af96a7b46a9d892d521a2cbbb2f8a4c16751e7f" +dependencies = [ + "bitflags 2.13.0", + "block2", + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "openssl" +version = "0.10.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a45fa2aa886c42762255da344f0a0d313e254066c46aad76f300c3d3da62d967" +dependencies = [ + "bitflags 2.13.0", + "cfg-if", + "foreign-types 0.3.2", + "libc", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "openssl-probe" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" + +[[package]] +name = "openssl-sys" +version = "0.9.116" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28a22dc7140cda5f096e5e7724a6962ca81a7f8bfd2979f9b18c11af56318c4" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[package]] +name = "os_info" +version = "3.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf20a545b305cf1da722b236b5155c9bb35f1d5ceb28c048bd96ca842f41b5b" +dependencies = [ + "android_system_properties", + "log", + "nix", + "objc2", + "objc2-foundation", + "objc2-ui-kit", + "serde", + "windows-sys 0.61.2", +] + +[[package]] +name = "pango" +version = "0.18.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ca27ec1eb0457ab26f3036ea52229edbdb74dee1edd29063f5b9b010e7ebee4" +dependencies = [ + "gio", + "glib", + "libc", + "once_cell", + "pango-sys", +] + +[[package]] +name = "pango-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436737e391a843e5933d6d9aa102cb126d501e815b83601365a948a518555dc5" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link 0.2.1", +] + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "phf" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf" +dependencies = [ + "phf_macros", + "phf_shared", + "serde", +] + +[[package]] +name = "phf_codegen" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49aa7f9d80421bca176ca8dbfebe668cc7a2684708594ec9f3c0db0805d5d6e1" +dependencies = [ + "phf_generator", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737" +dependencies = [ + "fastrand", + "phf_shared", +] + +[[package]] +name = "phf_macros" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812f032b54b1e759ccd5f8b6677695d5268c588701effba24601f6932f8269ef" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "phf_shared" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "pkg-config" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" + +[[package]] +name = "plist" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "092791278e026273c1b65bbdcfbba3a300f2994c896bd01ab01da613c29c46f1" +dependencies = [ + "base64 0.22.1", + "indexmap 2.14.0", + "quick-xml", + "serde", + "time", +] + +[[package]] +name = "png" +version = "0.17.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + +[[package]] +name = "png" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61" +dependencies = [ + "bitflags 2.13.0", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + +[[package]] +name = "potential_utf" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" +dependencies = [ + "zerovec", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "precomputed-hash" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn 2.0.117", +] + +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit 0.19.15", +] + +[[package]] +name = "proc-macro-crate" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b00f26d3400549137f92511a46ac1cd8ce37cb5598a96d382381458b992a5d24" +dependencies = [ + "toml_datetime 0.6.3", + "toml_edit 0.20.2", +] + +[[package]] +name = "proc-macro-crate" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" +dependencies = [ + "toml_edit 0.25.12+spec-1.1.0", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quick-xml" +version = "0.39.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdcc8dd4e2f670d309a5f0e83fe36dfdc05af317008fea29144da1a2ac858e5e" +dependencies = [ + "memchr", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "rand" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" +dependencies = [ + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom 0.3.4", +] + +[[package]] +name = "raw-window-handle" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags 2.13.0", +] + +[[package]] +name = "redox_users" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" +dependencies = [ + "getrandom 0.2.17", + "libredox", + "thiserror 2.0.18", +] + +[[package]] +name = "ref-cast" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "regex" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" + +[[package]] +name = "reqwest" +version = "0.12.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" +dependencies = [ + "base64 0.22.1", + "bytes", + "encoding_rs", + "futures-core", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-tls", + "hyper-util", + "js-sys", + "log", + "mime", + "native-tls", + "percent-encoding", + "pin-project-lite", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tokio-native-tls", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "reqwest" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "219c5811de6525e5416c7d5d53bb656d3afdbc6c5af816e0802bcfa42dbdc1c3" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures-core", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-util", + "js-sys", + "log", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "sync_wrapper", + "tokio", + "tokio-util", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", +] + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.17", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustc-hash" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags 2.13.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls" +version = "0.23.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b" +dependencies = [ + "once_cell", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pki-types" +version = "1.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9" +dependencies = [ + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "ryu" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schannel" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "schemars" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fbf2ae1b8bc8e02df939598064d22402220cd5bbcca1c76f7d6a310974d5615" +dependencies = [ + "dyn-clone", + "indexmap 1.9.3", + "schemars_derive", + "serde", + "serde_json", + "url", + "uuid", +] + +[[package]] +name = "schemars" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "schemars" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2b42f36aa1cd011945615b92222f6bf73c599a102a300334cd7f8dbeec726cc" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "schemars_derive" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32e265784ad618884abaea0600a9adf15393368d840e0222d101a072f3f7534d" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn 2.0.117", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "security-framework" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" +dependencies = [ + "bitflags 2.13.0", + "core-foundation 0.10.1", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "selectors" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5d9c0c92a92d33f08817311cf3f2c29a3538a8240e94a6a3c622ce652d7e00c" +dependencies = [ + "bitflags 2.13.0", + "cssparser", + "derive_more", + "log", + "new_debug_unreachable", + "phf", + "phf_codegen", + "precomputed-hash", + "rustc-hash", + "servo_arc", + "smallvec", +] + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" +dependencies = [ + "serde", + "serde_core", +] + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde-untagged" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9faf48a4a2d2693be24c6289dbe26552776eb7737074e6722891fadbe6c5058" +dependencies = [ + "erased-serde", + "serde", + "serde_core", + "typeid", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "serde_derive_internals" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "serde_json" +version = "1.0.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_repr" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "serde_spanned" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_spanned" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" +dependencies = [ + "serde_core", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_with" +version = "3.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a5c54c7310e7b8b9577c286d7e399ddd876c3e12b3ed917a8aabc4b96e9e8c" +dependencies = [ + "base64 0.22.1", + "bs58", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.14.0", + "schemars 0.9.0", + "schemars 1.2.1", + "serde_core", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84d57bc0c8b9a17920c178daa6bb924850d54a9c97ab45194bb8c17ad66bb660" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "serialize-to-javascript" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04f3666a07a197cdb77cdf306c32be9b7f598d7060d50cfd4d5aa04bfd92f6c5" +dependencies = [ + "serde", + "serde_json", + "serialize-to-javascript-impl", +] + +[[package]] +name = "serialize-to-javascript-impl" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "772ee033c0916d670af7860b6e1ef7d658a4629a6d0b4c8c3e67f09b3765b75d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "servo_arc" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "170fb83ab34de17dc69aa7c67482b22218ddb85da56546f9bd6b929e32a05930" +dependencies = [ + "stable_deref_trait", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + +[[package]] +name = "simd-adler32" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" + +[[package]] +name = "siphasher" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "socket2" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "softbuffer" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aac18da81ebbf05109ab275b157c22a653bb3c12cf884450179942f81bcbf6c3" +dependencies = [ + "bytemuck", + "js-sys", + "ndk", + "objc2", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-foundation", + "objc2-quartz-core", + "raw-window-handle", + "redox_syscall", + "tracing", + "wasm-bindgen", + "web-sys", + "windows-sys 0.61.2", +] + +[[package]] +name = "soup3" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "471f924a40f31251afc77450e781cb26d55c0b650842efafc9c6cbd2f7cc4f9f" +dependencies = [ + "futures-channel", + "gio", + "glib", + "libc", + "soup3-sys", +] + +[[package]] +name = "soup3-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ebe8950a680a12f24f15ebe1bf70db7af98ad242d9db43596ad3108aab86c27" +dependencies = [ + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "string_cache" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a18596f8c785a729f2819c0f6a7eae6ebeebdfffbfe4214ae6b087f690e31901" +dependencies = [ + "new_debug_unreachable", + "parking_lot", + "phf_shared", + "precomputed-hash", +] + +[[package]] +name = "string_cache_codegen" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "585635e46db231059f76c5849798146164652513eb9e8ab2685939dd90f29b69" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2", + "quote", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "swift-rs" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4057c98e2e852d51fdcfca832aac7b571f6b351ad159f9eda5db1655f8d0c4d7" +dependencies = [ + "base64 0.21.7", + "serde", + "serde_json", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "sys-locale" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eab9a99a024a169fe8a903cf9d4a3b3601109bcc13bd9e3c6fff259138626c4" +dependencies = [ + "libc", +] + +[[package]] +name = "system-configuration" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" +dependencies = [ + "bitflags 2.13.0", + "core-foundation 0.9.4", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "system-deps" +version = "6.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" +dependencies = [ + "cfg-expr", + "heck 0.5.0", + "pkg-config", + "toml 0.8.2", + "version-compare", +] + +[[package]] +name = "tao" +version = "0.35.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1c93047acf68669466a34690ac58cca7010bd1b201e1ec86f1fd0a75d3dd4a9" +dependencies = [ + "bitflags 2.13.0", + "block2", + "core-foundation 0.10.1", + "core-graphics", + "crossbeam-channel", + "dbus", + "dispatch2", + "dlopen2", + "dpi", + "gdkwayland-sys", + "gdkx11-sys", + "gtk", + "jni", + "libc", + "log", + "ndk", + "ndk-sys", + "objc2", + "objc2-app-kit", + "objc2-foundation", + "objc2-ui-kit", + "once_cell", + "parking_lot", + "percent-encoding", + "raw-window-handle", + "tao-macros", + "unicode-segmentation", + "url", + "windows", + "windows-core 0.61.2", + "windows-version", + "x11-dl", +] + +[[package]] +name = "tao-macros" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4e16beb8b2ac17db28eab8bca40e62dbfbb34c0fcdc6d9826b11b7b5d047dfd" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "target-lexicon" +version = "0.12.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" + +[[package]] +name = "tauri" +version = "2.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "437404997acf375d85f1177afa7e11bb971f274ed6a7b83a2a3e339015f4cc28" +dependencies = [ + "anyhow", + "bytes", + "cookie", + "dirs", + "dunce", + "embed_plist", + "getrandom 0.3.4", + "glob", + "gtk", + "heck 0.5.0", + "http", + "jni", + "libc", + "log", + "mime", + "muda", + "objc2", + "objc2-app-kit", + "objc2-foundation", + "objc2-ui-kit", + "objc2-web-kit", + "percent-encoding", + "plist", + "raw-window-handle", + "reqwest 0.13.4", + "serde", + "serde_json", + "serde_repr", + "serialize-to-javascript", + "swift-rs", + "tauri-build", + "tauri-macros", + "tauri-runtime", + "tauri-runtime-wry", + "tauri-utils", + "thiserror 2.0.18", + "tokio", + "tray-icon", + "url", + "webkit2gtk", + "webview2-com", + "window-vibrancy", + "windows", +] + +[[package]] +name = "tauri-build" +version = "2.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4aa1f9055fc23919a54e4e125052bed16ed04aef0487086e758fe01a67b451c7" +dependencies = [ + "anyhow", + "cargo_toml", + "dirs", + "glob", + "heck 0.5.0", + "json-patch", + "schemars 0.8.22", + "semver", + "serde", + "serde_json", + "tauri-utils", + "tauri-winres", + "walkdir", +] + +[[package]] +name = "tauri-codegen" +version = "2.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4a0319528a025a38c4078e7dae2c446f4e63620ddb0659a643ede1cb38f90e9" +dependencies = [ + "base64 0.22.1", + "brotli", + "ico", + "json-patch", + "plist", + "png 0.17.16", + "proc-macro2", + "quote", + "semver", + "serde", + "serde_json", + "sha2", + "syn 2.0.117", + "tauri-utils", + "thiserror 2.0.18", + "time", + "url", + "uuid", + "walkdir", +] + +[[package]] +name = "tauri-macros" +version = "2.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae6cb4e3896c21d2f6da5b31251d2faea0153bba56ed0e970f918115dbee4924" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.117", + "tauri-codegen", + "tauri-utils", +] + +[[package]] +name = "tauri-plugin" +version = "2.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e126abc9e84e35cdfd01596140a73a1850cdb0df0a23acf0185776c30b469a6e" +dependencies = [ + "anyhow", + "glob", + "plist", + "schemars 0.8.22", + "serde", + "serde_json", + "tauri-utils", + "walkdir", +] + +[[package]] +name = "tauri-plugin-aptabase" +version = "1.0.0" +dependencies = [ + "futures", + "log", + "os_info", + "rand", + "reqwest 0.12.28", + "serde", + "serde_json", + "sys-locale", + "tauri", + "tauri-plugin", + "time", + "tokio", +] + +[[package]] +name = "tauri-runtime" +version = "2.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48222d7116c8807eaa6fe2f372e023fae125084e61e6eca6d70b7961cdf129ef" +dependencies = [ + "cookie", + "dpi", + "gtk", + "http", + "jni", + "objc2", + "objc2-ui-kit", + "objc2-web-kit", + "raw-window-handle", + "serde", + "serde_json", + "tauri-utils", + "thiserror 2.0.18", + "url", + "webkit2gtk", + "webview2-com", + "windows", +] + +[[package]] +name = "tauri-runtime-wry" +version = "2.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b83849ee63ecb27a8e8d0fe51915ca215076914aca43f96db1179f0f415f6cd9" +dependencies = [ + "gtk", + "http", + "jni", + "log", + "objc2", + "objc2-app-kit", + "once_cell", + "percent-encoding", + "raw-window-handle", + "softbuffer", + "tao", + "tauri-runtime", + "tauri-utils", + "url", + "webkit2gtk", + "webview2-com", + "windows", + "wry", +] + +[[package]] +name = "tauri-utils" +version = "2.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "092379df9a707631978e6c56b1bc2401d387f01e2d4a3c123360d167bbb9aa95" +dependencies = [ + "anyhow", + "brotli", + "cargo_metadata", + "ctor", + "dom_query", + "dunce", + "glob", + "http", + "infer", + "json-patch", + "log", + "memchr", + "phf", + "plist", + "proc-macro2", + "quote", + "regex", + "schemars 0.8.22", + "semver", + "serde", + "serde-untagged", + "serde_json", + "serde_with", + "swift-rs", + "thiserror 2.0.18", + "toml 1.1.2+spec-1.1.0", + "url", + "urlpattern", + "uuid", + "walkdir", +] + +[[package]] +name = "tauri-winres" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc65d45c68858bfe420dd29e834b5d15dbecf8a07a8a16cf4d532c7b1f69d4b6" +dependencies = [ + "dunce", + "embed-resource", + "toml 1.1.2+spec-1.1.0", +] + +[[package]] +name = "tempfile" +version = "3.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" +dependencies = [ + "fastrand", + "getrandom 0.4.2", + "once_cell", + "rustix", + "windows-sys 0.61.2", +] + +[[package]] +name = "tendril" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4790fc369d5a530f4b544b094e31388b9b3a37c0f4652ade4505945f5660d24" +dependencies = [ + "new_debug_unreachable", + "utf-8", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl 2.0.18", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "time" +version = "0.3.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde_core", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" + +[[package]] +name = "time-macros" +version = "0.2.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinystr" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tinyvec" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" +dependencies = [ + "bytes", + "libc", + "mio", + "pin-project-lite", + "socket2", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "185d8ab0dfbb35cf1399a6344d8484209c088f75f8f68230da55d48d95d43e3d" +dependencies = [ + "serde", + "serde_spanned 0.6.9", + "toml_datetime 0.6.3", + "toml_edit 0.20.2", +] + +[[package]] +name = "toml" +version = "0.9.12+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863" +dependencies = [ + "indexmap 2.14.0", + "serde_core", + "serde_spanned 1.1.1", + "toml_datetime 0.7.5+spec-1.1.0", + "toml_parser", + "toml_writer", + "winnow 0.7.15", +] + +[[package]] +name = "toml" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee" +dependencies = [ + "indexmap 2.14.0", + "serde_core", + "serde_spanned 1.1.1", + "toml_datetime 1.1.1+spec-1.1.0", + "toml_parser", + "toml_writer", + "winnow 1.0.3", +] + +[[package]] +name = "toml_datetime" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_datetime" +version = "0.7.5+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_datetime" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap 2.14.0", + "toml_datetime 0.6.3", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" +dependencies = [ + "indexmap 2.14.0", + "serde", + "serde_spanned 0.6.9", + "toml_datetime 0.6.3", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.25.12+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2153edc6955a6c354fad8f5efd38b6a8769bdccf9fe50f8e1329f81b0baa5d7" +dependencies = [ + "indexmap 2.14.0", + "toml_datetime 1.1.1+spec-1.1.0", + "toml_parser", + "winnow 1.0.3", +] + +[[package]] +name = "toml_parser" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" +dependencies = [ + "winnow 1.0.3", +] + +[[package]] +name = "toml_writer" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" + +[[package]] +name = "tower" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-http" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" +dependencies = [ + "bitflags 2.13.0", + "bytes", + "futures-util", + "http", + "http-body", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", + "url", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "tray-icon" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15edbb0d80583e85ee8df283410038e17314df5cba30da2087a54a85216c0773" +dependencies = [ + "crossbeam-channel", + "dirs", + "libappindicator", + "muda", + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-foundation", + "once_cell", + "png 0.18.1", + "serde", + "thiserror 2.0.18", + "windows-sys 0.61.2", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "typeid" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" + +[[package]] +name = "typenum" +version = "1.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" + +[[package]] +name = "unic-char-property" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" +dependencies = [ + "unic-char-range", +] + +[[package]] +name = "unic-char-range" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" + +[[package]] +name = "unic-common" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" + +[[package]] +name = "unic-ucd-ident" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e230a37c0381caa9219d67cf063aa3a375ffed5bf541a452db16e744bdab6987" +dependencies = [ + "unic-char-property", + "unic-char-range", + "unic-ucd-version", +] + +[[package]] +name = "unic-ucd-version" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" +dependencies = [ + "unic-common", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-segmentation" +version = "1.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", + "serde_derive", +] + +[[package]] +name = "urlpattern" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70acd30e3aa1450bc2eece896ce2ad0d178e9c079493819301573dae3c37ba6d" +dependencies = [ + "regex", + "serde", + "unic-ucd-ident", + "url", +] + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "uuid" +version = "1.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d258b83ceec21034727ecee8c382cfa6c3e133699b0742c64571814fb420c9f7" +dependencies = [ + "getrandom 0.4.2", + "js-sys", + "serde_core", + "wasm-bindgen", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version-compare" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c2856837ef78f57382f06b2b8563a2f512f7185d732608fd9176cb3b8edf0e" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "vswhom" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be979b7f07507105799e854203b470ff7c78a1639e330a58f183b5fea574608b" +dependencies = [ + "libc", + "vswhom-sys", +] + +[[package]] +name = "vswhom-sys" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb067e4cbd1ff067d1df46c9194b5de0e98efd2810bbc95c5d5e5f25a3231150" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.3+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" +dependencies = [ + "wit-bindgen 0.57.1", +] + +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +dependencies = [ + "wit-bindgen 0.51.0", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.122" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed04576f974d2b2fba0f38c51dbc5518011e38c36bf1143164be765528fd409" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9473dbd2991ae90b6291c3c32c30c6187ac49aa32f9905d1cce280ec1e110b0f" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.122" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "916151b09da36bd82f6615cbf3a419e2f0ba23a03c6160e8e92eb6bd4aa1dec6" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.122" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "299047362ccbfce148b67ab7e73349f77748e00c8296f9542adfad2ad82c5c5e" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn 2.0.117", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.122" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a929b2c61f11ba3e9bc35b50c1f25cb38e0e892c0c231ae2b8cf78d5dad4437" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap 2.14.0", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasm-streams" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1ec4f6517c9e11ae630e200b2b65d193279042e28edd4a2cda233e46670bbb" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags 2.13.0", + "hashbrown 0.15.5", + "indexmap 2.14.0", + "semver", +] + +[[package]] +name = "web-sys" +version = "0.3.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621441cfc37b84979402712047321980c178f299193a3589d05b99e8763436" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web_atoms" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7cff6eef815df1834fd250e3a2ff436044d82a9f1bc1980ca1dbdf07effc538" +dependencies = [ + "phf", + "phf_codegen", + "string_cache", + "string_cache_codegen", +] + +[[package]] +name = "webkit2gtk" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1027150013530fb2eaf806408df88461ae4815a45c541c8975e61d6f2fc4793" +dependencies = [ + "bitflags 1.3.2", + "cairo-rs", + "gdk", + "gdk-sys", + "gio", + "gio-sys", + "glib", + "glib-sys", + "gobject-sys", + "gtk", + "gtk-sys", + "javascriptcore-rs", + "libc", + "once_cell", + "soup3", + "webkit2gtk-sys", +] + +[[package]] +name = "webkit2gtk-sys" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "916a5f65c2ef0dfe12fff695960a2ec3d4565359fdbb2e9943c974e06c734ea5" +dependencies = [ + "bitflags 1.3.2", + "cairo-sys-rs", + "gdk-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "gtk-sys", + "javascriptcore-rs-sys", + "libc", + "pkg-config", + "soup3-sys", + "system-deps", +] + +[[package]] +name = "webview2-com" +version = "0.38.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7130243a7a5b33c54a444e54842e6a9e133de08b5ad7b5861cd8ed9a6a5bc96a" +dependencies = [ + "webview2-com-macros", + "webview2-com-sys", + "windows", + "windows-core 0.61.2", + "windows-implement", + "windows-interface", +] + +[[package]] +name = "webview2-com-macros" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a921c1b6914c367b2b823cd4cde6f96beec77d30a939c8199bb377cf9b9b54" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "webview2-com-sys" +version = "0.38.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "381336cfffd772377d291702245447a5251a2ffa5bad679c99e61bc48bacbf9c" +dependencies = [ + "thiserror 2.0.18", + "windows", + "windows-core 0.61.2", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "window-vibrancy" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9bec5a31f3f9362f2258fd0e9c9dd61a9ca432e7306cc78c444258f0dce9a9c" +dependencies = [ + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation", + "raw-window-handle", + "windows-sys 0.59.0", + "windows-version", +] + +[[package]] +name = "windows" +version = "0.61.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" +dependencies = [ + "windows-collections", + "windows-core 0.61.2", + "windows-future", + "windows-link 0.1.3", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" +dependencies = [ + "windows-core 0.61.2", +] + +[[package]] +name = "windows-core" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link 0.1.3", + "windows-result 0.3.4", + "windows-strings 0.4.2", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link 0.2.1", + "windows-result 0.4.1", + "windows-strings 0.5.1", +] + +[[package]] +name = "windows-future" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" +dependencies = [ + "windows-core 0.61.2", + "windows-link 0.1.3", + "windows-threading", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-numerics" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" +dependencies = [ + "windows-core 0.61.2", + "windows-link 0.1.3", +] + +[[package]] +name = "windows-registry" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" +dependencies = [ + "windows-link 0.2.1", + "windows-result 0.4.1", + "windows-strings 0.5.1", +] + +[[package]] +name = "windows-result" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +dependencies = [ + "windows-link 0.1.3", +] + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link 0.2.1", +] + +[[package]] +name = "windows-strings" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +dependencies = [ + "windows-link 0.1.3", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link 0.2.1", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link 0.2.1", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-threading" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" +dependencies = [ + "windows-link 0.1.3", +] + +[[package]] +name = "windows-version" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4060a1da109b9d0326b7262c8e12c84df67cc0dbc9e33cf49e01ccc2eb63631" +dependencies = [ + "windows-link 0.2.1", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + +[[package]] +name = "winnow" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" + +[[package]] +name = "winnow" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" +dependencies = [ + "memchr", +] + +[[package]] +name = "winreg" +version = "0.55.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb5a765337c50e9ec252c2069be9bf91c7df47afb103b642ba3a53bf8101be97" +dependencies = [ + "cfg-if", + "windows-sys 0.59.0", +] + +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck 0.5.0", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck 0.5.0", + "indexmap 2.14.0", + "prettyplease", + "syn 2.0.117", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn 2.0.117", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags 2.13.0", + "indexmap 2.14.0", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap 2.14.0", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] + +[[package]] +name = "writeable" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" + +[[package]] +name = "wry" +version = "0.55.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "186f9871daa55fd9c016578b810d149de58367113db7fb72b462d2323ce19514" +dependencies = [ + "base64 0.22.1", + "block2", + "cookie", + "crossbeam-channel", + "dirs", + "dom_query", + "dpi", + "dunce", + "gdkx11", + "gtk", + "http", + "javascriptcore-rs", + "jni", + "libc", + "ndk", + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation", + "objc2-ui-kit", + "objc2-web-kit", + "once_cell", + "percent-encoding", + "raw-window-handle", + "sha2", + "soup3", + "tao-macros", + "thiserror 2.0.18", + "url", + "webkit2gtk", + "webkit2gtk-sys", + "webview2-com", + "windows", + "windows-core 0.61.2", + "windows-version", + "x11-dl", +] + +[[package]] +name = "x11" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "502da5464ccd04011667b11c435cb992822c2c0dbde1770c988480d312a0db2e" +dependencies = [ + "libc", + "pkg-config", +] + +[[package]] +name = "x11-dl" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f" +dependencies = [ + "libc", + "once_cell", + "pkg-config", +] + +[[package]] +name = "yoke" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "709fe23a0424b6a435d82152b1bd3fdfb0833487d5fa90d05d42762a9891fef5" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.8.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b065d4f0e55f82fae73202e189638116a87c55ab6b8e6c2721e13dd9d854ad1" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b631b19d36a892ab55420c92dbc83ccd79274f25be714855d3074aa71cab639" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "zerofrom" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" + +[[package]] +name = "zerotrie" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/permissions/autogenerated/reference.md b/permissions/autogenerated/reference.md index 3c0486b..652ec58 100644 --- a/permissions/autogenerated/reference.md +++ b/permissions/autogenerated/reference.md @@ -1,4 +1,3 @@ - ## Permission Table diff --git a/permissions/schemas/schema.json b/permissions/schemas/schema.json index f13b11b..4c0ac2b 100644 --- a/permissions/schemas/schema.json +++ b/permissions/schemas/schema.json @@ -49,7 +49,7 @@ "minimum": 1.0 }, "description": { - "description": "Human-readable description of what the permission does. Tauri convention is to use

headings in markdown content for Tauri documentation generation purposes.", + "description": "Human-readable description of what the permission does. Tauri convention is to use `

` headings in markdown content for Tauri documentation generation purposes.", "type": [ "string", "null" @@ -111,7 +111,7 @@ "type": "string" }, "description": { - "description": "Human-readable description of what the permission does. Tauri internal convention is to use

headings in markdown content for Tauri documentation generation purposes.", + "description": "Human-readable description of what the permission does. Tauri internal convention is to use `

` headings in markdown content for Tauri documentation generation purposes.", "type": [ "string", "null" @@ -297,12 +297,14 @@ { "description": "Enables the track_event command without any pre-configured scope.", "type": "string", - "const": "allow-track-event" + "const": "allow-track-event", + "markdownDescription": "Enables the track_event command without any pre-configured scope." }, { "description": "Denies the track_event command without any pre-configured scope.", "type": "string", - "const": "deny-track-event" + "const": "deny-track-event", + "markdownDescription": "Denies the track_event command without any pre-configured scope." } ] } From 8426cd09b02b0a10fd3e3cad13210f03dee79a00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Thu, 11 Jun 2026 20:16:05 +0100 Subject: [PATCH 03/83] Configure openspec --- .gitattributes | 1 + mise.lock | 36 ++++++++++++++++++++++++++++++++++++ mise.toml | 2 ++ openspec/config.yaml | 1 + 4 files changed, 40 insertions(+) create mode 100644 openspec/config.yaml diff --git a/.gitattributes b/.gitattributes index 8f710ad..423ae39 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1,2 @@ +openspec/** linguist-generated=true *.lock linguist-generated=true diff --git a/mise.lock b/mise.lock index 1f598a7..166d2ae 100644 --- a/mise.lock +++ b/mise.lock @@ -1,5 +1,41 @@ # @generated - this file is auto-generated by `mise lock` https://mise.jdx.dev/dev-tools/mise-lock.html +[[tools.node]] +version = "26.2.0" +backend = "core:node" + +[tools.node."platforms.linux-arm64"] +checksum = "sha256:bf52461d25017479cdc549642c4e7a2303ac5c5395d7c3d026d5d8b3fa283c05" +url = "https://nodejs.org/dist/v26.2.0/node-v26.2.0-linux-arm64.tar.gz" + +[tools.node."platforms.linux-arm64-musl"] +checksum = "sha256:bf52461d25017479cdc549642c4e7a2303ac5c5395d7c3d026d5d8b3fa283c05" +url = "https://nodejs.org/dist/v26.2.0/node-v26.2.0-linux-arm64.tar.gz" + +[tools.node."platforms.linux-x64"] +checksum = "sha256:8e5b4b969bb2414aca2ff84d2b99608511aedca32bf8a8427e6187dbdd703bbe" +url = "https://nodejs.org/dist/v26.2.0/node-v26.2.0-linux-x64.tar.gz" + +[tools.node."platforms.linux-x64-musl"] +checksum = "sha256:8e5b4b969bb2414aca2ff84d2b99608511aedca32bf8a8427e6187dbdd703bbe" +url = "https://nodejs.org/dist/v26.2.0/node-v26.2.0-linux-x64.tar.gz" + +[tools.node."platforms.macos-arm64"] +checksum = "sha256:2d8575b6a749c0bc66cc6ecd22a952f7d9bbf3b0ea76fcb0ed3ce858018afff7" +url = "https://nodejs.org/dist/v26.2.0/node-v26.2.0-darwin-arm64.tar.gz" + +[tools.node."platforms.macos-x64"] +checksum = "sha256:26a5480d0dd952be86beae83e6636dacd7a3ffd17ef51d0b26bc9b260f2b42e9" +url = "https://nodejs.org/dist/v26.2.0/node-v26.2.0-darwin-x64.tar.gz" + +[tools.node."platforms.windows-x64"] +checksum = "sha256:371071a4f7e2c8a5dd280730049c685911feecc59f50ebc488d675dc1087c69c" +url = "https://nodejs.org/dist/v26.2.0/node-v26.2.0-win-x64.zip" + +[[tools."npm:@fission-ai/openspec"]] +version = "1.3.1" +backend = "npm:@fission-ai/openspec" + [[tools.rust]] version = "nightly" backend = "core:rust" diff --git a/mise.toml b/mise.toml index bc64ee0..6f46233 100644 --- a/mise.toml +++ b/mise.toml @@ -1,2 +1,4 @@ [tools] +node = "latest" +"npm:@fission-ai/openspec" = "latest" rust = "nightly" diff --git a/openspec/config.yaml b/openspec/config.yaml new file mode 100644 index 0000000..b4bbeb9 --- /dev/null +++ b/openspec/config.yaml @@ -0,0 +1 @@ +schema: spec-driven From 3b3c8bf6de5fb8082ef7212d67f6b58e845705b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Thu, 11 Jun 2026 20:46:31 +0100 Subject: [PATCH 04/83] Implement generic client --- CHANGELOG.md | 6 + Cargo.lock | 4625 +++-------------- Cargo.toml | 21 +- README.md | 155 +- build.rs | 5 - examples/cli.rs | 12 + examples/helloworld/.gitignore | 24 - examples/helloworld/index.html | 14 - examples/helloworld/jsconfig.json | 34 - examples/helloworld/package.json | 24 - examples/helloworld/public/svelte.svg | 1 - examples/helloworld/public/tauri.svg | 6 - examples/helloworld/public/vite.svg | 1 - examples/helloworld/src-tauri/.gitignore | 4 - examples/helloworld/src-tauri/Cargo.toml | 29 - examples/helloworld/src-tauri/build.rs | 3 - .../src-tauri/capabilities/desktop.json | 8 - .../src-tauri/gen/schemas/capabilities.json | 1 - .../src-tauri/gen/schemas/desktop-schema.json | 1 - .../src-tauri/gen/schemas/macOS-schema.json | 1 - .../gen/schemas/plugin-manifests.json | 1 - .../helloworld/src-tauri/icons/128x128.png | Bin 11059 -> 0 bytes .../helloworld/src-tauri/icons/128x128@2x.png | Bin 23137 -> 0 bytes examples/helloworld/src-tauri/icons/32x32.png | Bin 2225 -> 0 bytes examples/helloworld/src-tauri/icons/icon.icns | Bin 277003 -> 0 bytes examples/helloworld/src-tauri/icons/icon.ico | Bin 37710 -> 0 bytes examples/helloworld/src-tauri/icons/icon.png | Bin 49979 -> 0 bytes examples/helloworld/src-tauri/src/lib.rs | 26 - examples/helloworld/src-tauri/src/main.rs | 6 - examples/helloworld/src-tauri/tauri.conf.json | 37 - examples/helloworld/src/App.svelte | 47 - examples/helloworld/src/lib/Greet.svelte | 24 - examples/helloworld/src/main.js | 8 - examples/helloworld/src/style.css | 102 - examples/helloworld/src/vite-env.d.ts | 2 - examples/helloworld/vite.config.js | 25 - llms.txt | 167 +- .../.openspec.yaml | 2 + .../design.md | 107 + .../proposal.md | 32 + .../specs/generic-rust-client/spec.md | 119 + .../tasks.md | 45 + openspec/specs/generic-rust-client/spec.md | 123 + package.json | 42 - .../autogenerated/commands/track_event.toml | 13 - permissions/autogenerated/reference.md | 35 - permissions/schemas/schema.json | 312 -- src/client.rs | 170 +- src/commands.rs | 14 - src/config.rs | 118 +- src/dispatcher.rs | 190 +- src/lib.rs | 143 +- src/sys.rs | 56 +- webview-dist/index.d.ts | 5 - webview-dist/index.js | 1 - webview-src/index.ts | 9 - webview-src/rollup.config.js | 21 - webview-src/tsconfig.json | 16 - 58 files changed, 1768 insertions(+), 5225 deletions(-) delete mode 100644 build.rs create mode 100644 examples/cli.rs delete mode 100644 examples/helloworld/.gitignore delete mode 100644 examples/helloworld/index.html delete mode 100644 examples/helloworld/jsconfig.json delete mode 100644 examples/helloworld/package.json delete mode 100644 examples/helloworld/public/svelte.svg delete mode 100644 examples/helloworld/public/tauri.svg delete mode 100644 examples/helloworld/public/vite.svg delete mode 100644 examples/helloworld/src-tauri/.gitignore delete mode 100644 examples/helloworld/src-tauri/Cargo.toml delete mode 100644 examples/helloworld/src-tauri/build.rs delete mode 100644 examples/helloworld/src-tauri/capabilities/desktop.json delete mode 100644 examples/helloworld/src-tauri/gen/schemas/capabilities.json delete mode 100644 examples/helloworld/src-tauri/gen/schemas/desktop-schema.json delete mode 100644 examples/helloworld/src-tauri/gen/schemas/macOS-schema.json delete mode 100644 examples/helloworld/src-tauri/gen/schemas/plugin-manifests.json delete mode 100644 examples/helloworld/src-tauri/icons/128x128.png delete mode 100644 examples/helloworld/src-tauri/icons/128x128@2x.png delete mode 100644 examples/helloworld/src-tauri/icons/32x32.png delete mode 100644 examples/helloworld/src-tauri/icons/icon.icns delete mode 100644 examples/helloworld/src-tauri/icons/icon.ico delete mode 100644 examples/helloworld/src-tauri/icons/icon.png delete mode 100644 examples/helloworld/src-tauri/src/lib.rs delete mode 100644 examples/helloworld/src-tauri/src/main.rs delete mode 100644 examples/helloworld/src-tauri/tauri.conf.json delete mode 100644 examples/helloworld/src/App.svelte delete mode 100644 examples/helloworld/src/lib/Greet.svelte delete mode 100644 examples/helloworld/src/main.js delete mode 100644 examples/helloworld/src/style.css delete mode 100644 examples/helloworld/src/vite-env.d.ts delete mode 100644 examples/helloworld/vite.config.js create mode 100644 openspec/changes/archive/2026-06-12-create-generic-aptabase-rust-client/.openspec.yaml create mode 100644 openspec/changes/archive/2026-06-12-create-generic-aptabase-rust-client/design.md create mode 100644 openspec/changes/archive/2026-06-12-create-generic-aptabase-rust-client/proposal.md create mode 100644 openspec/changes/archive/2026-06-12-create-generic-aptabase-rust-client/specs/generic-rust-client/spec.md create mode 100644 openspec/changes/archive/2026-06-12-create-generic-aptabase-rust-client/tasks.md create mode 100644 openspec/specs/generic-rust-client/spec.md delete mode 100644 package.json delete mode 100644 permissions/autogenerated/commands/track_event.toml delete mode 100644 permissions/autogenerated/reference.md delete mode 100644 permissions/schemas/schema.json delete mode 100644 src/commands.rs delete mode 100644 webview-dist/index.d.ts delete mode 100644 webview-dist/index.js delete mode 100644 webview-src/index.ts delete mode 100644 webview-src/rollup.config.js delete mode 100644 webview-src/tsconfig.json diff --git a/CHANGELOG.md b/CHANGELOG.md index 09b5710..ebda304 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## Unreleased + +* **Breaking:** replace the Tauri plugin in place with the framework-independent `aptabase-rs` crate. +* Add synchronous event enqueueing, asynchronous explicit flushing, and caller-managed periodic flushing for Tokio applications. +* Remove all Tauri dependencies, APIs, build scripts, permissions, WebView bindings, JavaScript metadata, and Tauri examples. + ## 1.0.0 * Update Tauri v2 diff --git a/Cargo.lock b/Cargo.lock index c2f275f..c42ff27 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,36 +2,6 @@ # It is not intended for manual editing. version = 3 -[[package]] -name = "adler2" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" - -[[package]] -name = "aho-corasick" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" -dependencies = [ - "memchr", -] - -[[package]] -name = "alloc-no-stdlib" -version = "2.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" - -[[package]] -name = "alloc-stdlib" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" -dependencies = [ - "alloc-no-stdlib", -] - [[package]] name = "android_system_properties" version = "0.1.5" @@ -48,26 +18,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" [[package]] -name = "atk" -version = "0.18.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "241b621213072e993be4f6f3a9e4b45f65b7e6faad43001be957184b7bb1824b" -dependencies = [ - "atk-sys", - "glib", - "libc", -] - -[[package]] -name = "atk-sys" -version = "0.18.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5e48b684b0ca77d2bbadeef17424c2ea3c897d44d566a1617e7e8f30614d086" +name = "aptabase-rs" +version = "1.0.0" dependencies = [ - "glib-sys", - "gobject-sys", - "libc", - "system-deps", + "log", + "os_info", + "rand", + "reqwest", + "serde_json", + "sys-locale", + "time", + "tokio", ] [[package]] @@ -76,62 +37,17 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" -[[package]] -name = "autocfg" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" - -[[package]] -name = "base64" -version = "0.21.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" - [[package]] name = "base64" version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" -[[package]] -name = "bit-set" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" -dependencies = [ - "bit-vec", -] - -[[package]] -name = "bit-vec" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - [[package]] name = "bitflags" version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" -dependencies = [ - "serde_core", -] - -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array", -] [[package]] name = "block2" @@ -142,129 +58,17 @@ dependencies = [ "objc2", ] -[[package]] -name = "brotli" -version = "8.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8119e4516436f5708bbc474a9d395bf12f1b5395e93a92a56e647ac3388c8610" -dependencies = [ - "alloc-no-stdlib", - "alloc-stdlib", - "brotli-decompressor", -] - -[[package]] -name = "brotli-decompressor" -version = "5.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5962523e1b92ce1b5e793d9169b9943eece10d39f62550bc04bb605d75b94924" -dependencies = [ - "alloc-no-stdlib", - "alloc-stdlib", -] - -[[package]] -name = "bs58" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" -dependencies = [ - "tinyvec", -] - [[package]] name = "bumpalo" version = "3.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" -[[package]] -name = "bytemuck" -version = "1.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" - -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - [[package]] name = "bytes" version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" -dependencies = [ - "serde", -] - -[[package]] -name = "cairo-rs" -version = "0.18.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ca26ef0159422fb77631dc9d17b102f253b876fe1586b03b803e63a309b4ee2" -dependencies = [ - "bitflags 2.13.0", - "cairo-sys-rs", - "glib", - "libc", - "once_cell", - "thiserror 1.0.69", -] - -[[package]] -name = "cairo-sys-rs" -version = "0.18.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "685c9fa8e590b8b3d678873528d83411db17242a73fccaed827770ea0fedda51" -dependencies = [ - "glib-sys", - "libc", - "system-deps", -] - -[[package]] -name = "camino" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e629a66d692cb9ff1a1c664e41771b3dcaf961985a9774c0eb0bd1b51cf60a48" -dependencies = [ - "serde_core", -] - -[[package]] -name = "cargo-platform" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e35af189006b9c0f00a064685c727031e3ed2d8020f7ba284d78cc2671bd36ea" -dependencies = [ - "serde", -] - -[[package]] -name = "cargo_metadata" -version = "0.19.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd5eb614ed4c27c5d706420e4320fbe3216ab31fa1c33cd8246ac36dae4479ba" -dependencies = [ - "camino", - "cargo-platform", - "semver", - "serde", - "serde_json", - "thiserror 2.0.18", -] - -[[package]] -name = "cargo_toml" -version = "0.22.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "374b7c592d9c00c1f4972ea58390ac6b18cbb6ab79011f3bdc90a0b82ca06b77" -dependencies = [ - "serde", - "toml 0.9.12+spec-1.1.0", -] [[package]] name = "cc" @@ -276,33 +80,6 @@ dependencies = [ "shlex", ] -[[package]] -name = "cesu8" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" - -[[package]] -name = "cfb" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d38f2da7a0a2c4ccf0065be06397cc26a81f4e528be095826eee9d4adbb8c60f" -dependencies = [ - "byteorder", - "fnv", - "uuid", -] - -[[package]] -name = "cfg-expr" -version = "0.15.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" -dependencies = [ - "smallvec", - "target-lexicon", -] - [[package]] name = "cfg-if" version = "1.0.4" @@ -315,38 +92,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" -[[package]] -name = "chrono" -version = "0.4.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" -dependencies = [ - "iana-time-zone", - "num-traits", - "serde", - "windows-link 0.2.1", -] - -[[package]] -name = "combine" -version = "4.6.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" -dependencies = [ - "bytes", - "memchr", -] - -[[package]] -name = "cookie" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" -dependencies = [ - "time", - "version_check", -] - [[package]] name = "core-foundation" version = "0.9.4" @@ -374,3347 +119,1224 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] -name = "core-graphics" -version = "0.25.0" +name = "deranged" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "064badf302c3194842cf2c5d61f56cc88e54a759313879cdf03abdd27d0c3b97" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" dependencies = [ - "bitflags 2.13.0", - "core-foundation 0.10.1", - "core-graphics-types", - "foreign-types 0.5.0", - "libc", + "powerfmt", ] [[package]] -name = "core-graphics-types" -version = "0.2.0" +name = "dispatch2" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" +checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" dependencies = [ - "bitflags 2.13.0", - "core-foundation 0.10.1", - "libc", + "bitflags", + "objc2", ] [[package]] -name = "cpufeatures" -version = "0.2.17" +name = "displaydoc" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f" dependencies = [ - "libc", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "crc32fast" -version = "1.5.0" +name = "encoding_rs" +version = "0.8.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" dependencies = [ "cfg-if", ] [[package]] -name = "crossbeam-channel" -version = "0.5.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.21" +name = "equivalent" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] -name = "crypto-common" -version = "0.1.7" +name = "errno" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ - "generic-array", - "typenum", + "libc", + "windows-sys 0.61.2", ] [[package]] -name = "cssparser" -version = "0.36.0" +name = "fastrand" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dae61cf9c0abb83bd659dab65b7e4e38d8236824c85f0f804f173567bda257d2" -dependencies = [ - "cssparser-macros", - "dtoa-short", - "itoa", - "phf", - "smallvec", -] +checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" [[package]] -name = "cssparser-macros" -version = "0.6.1" +name = "find-msvc-tools" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" -dependencies = [ - "quote", - "syn 2.0.117", -] +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" [[package]] -name = "ctor" -version = "0.8.0" +name = "fnv" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "352d39c2f7bef1d6ad73db6f5160efcaed66d94ef8c6c573a8410c00bf909a98" -dependencies = [ - "ctor-proc-macro", - "dtor", -] +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] -name = "ctor-proc-macro" -version = "0.0.7" +name = "foldhash" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52560adf09603e58c9a7ee1fe1dcb95a16927b17c127f0ac02d6e768a0e25bc1" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" [[package]] -name = "darling" -version = "0.23.0" +name = "foreign-types" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" dependencies = [ - "darling_core", - "darling_macro", + "foreign-types-shared", ] [[package]] -name = "darling_core" -version = "0.23.0" +name = "foreign-types-shared" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0" -dependencies = [ - "ident_case", - "proc-macro2", - "quote", - "strsim", - "syn 2.0.117", -] +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] -name = "darling_macro" -version = "0.23.0" +name = "form_urlencoded" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" dependencies = [ - "darling_core", - "quote", - "syn 2.0.117", + "percent-encoding", ] [[package]] -name = "dbus" -version = "0.9.11" +name = "futures-channel" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b942602992bb7acfd1f51c49811c58a610ef9181b6e66f3e519d79b540a3bf73" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" dependencies = [ - "libc", - "libdbus-sys", - "windows-sys 0.61.2", + "futures-core", ] [[package]] -name = "deranged" -version = "0.5.8" +name = "futures-core" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" -dependencies = [ - "powerfmt", - "serde_core", -] +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" [[package]] -name = "derive_more" -version = "2.1.1" +name = "futures-sink" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" -dependencies = [ - "derive_more-impl", -] +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" [[package]] -name = "derive_more-impl" -version = "2.1.1" +name = "futures-task" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" -dependencies = [ - "proc-macro2", - "quote", - "rustc_version", - "syn 2.0.117", -] +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" [[package]] -name = "digest" -version = "0.10.7" +name = "futures-util" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" dependencies = [ - "block-buffer", - "crypto-common", + "futures-core", + "futures-task", + "pin-project-lite", + "slab", ] [[package]] -name = "dirs" -version = "6.0.0" +name = "getrandom" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" dependencies = [ - "dirs-sys", + "cfg-if", + "libc", + "wasi", ] [[package]] -name = "dirs-sys" -version = "0.5.0" +name = "getrandom" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ + "cfg-if", "libc", - "option-ext", - "redox_users", - "windows-sys 0.61.2", + "r-efi 5.3.0", + "wasip2", ] [[package]] -name = "dispatch2" -version = "0.3.1" +name = "getrandom" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" dependencies = [ - "bitflags 2.13.0", - "block2", + "cfg-if", "libc", - "objc2", + "r-efi 6.0.0", + "wasip2", + "wasip3", ] [[package]] -name = "displaydoc" -version = "0.2.6" +name = "h2" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f" +checksum = "171fefbc92fe4a4de27e0698d6a5b392d6a0e333506bc49133760b3bcf948733" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", ] [[package]] -name = "dlopen2" -version = "0.8.2" +name = "hashbrown" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e2c5bd4158e66d1e215c49b837e11d62f3267b30c92f1d171c4d3105e3dc4d4" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ - "dlopen2_derive", - "libc", - "once_cell", - "winapi", + "foldhash", ] [[package]] -name = "dlopen2_derive" -version = "0.4.3" +name = "hashbrown" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fbbb781877580993a8707ec48672673ec7b81eeba04cfd2310bd28c08e47c8f" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" [[package]] -name = "dom_query" -version = "0.27.0" +name = "heck" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "521e380c0c8afb8d9a1e83a1822ee03556fc3e3e7dbc1fd30be14e37f9cb3f89" -dependencies = [ - "bit-set", - "cssparser", - "foldhash 0.2.0", - "html5ever", - "precomputed-hash", - "selectors", - "tendril", -] +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] -name = "dpi" -version = "0.1.2" +name = "http" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76" +checksum = "8be7462df143984c4598a256ef469b251d7d7f9e271135073e78fc535414f3d0" dependencies = [ - "serde", + "bytes", + "itoa", ] [[package]] -name = "dtoa" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c3cf4824e2d5f025c7b531afcb2325364084a16806f6d47fbc1f5fbd9960590" - -[[package]] -name = "dtoa-short" -version = "0.3.5" +name = "http-body" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd1511a7b6a56299bd043a9c167a6d2bfb37bf84a6dfceaba651168adfb43c87" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ - "dtoa", + "bytes", + "http", ] [[package]] -name = "dtor" -version = "0.3.0" +name = "http-body-util" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1057d6c64987086ff8ed0fd3fbf377a6b7d205cc7715868cd401705f715cbe4" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" dependencies = [ - "dtor-proc-macro", + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", ] [[package]] -name = "dtor-proc-macro" -version = "0.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f678cf4a922c215c63e0de95eb1ff08a958a81d47e485cf9da1e27bf6305cfa5" - -[[package]] -name = "dunce" -version = "1.0.5" +name = "httparse" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] -name = "dyn-clone" -version = "1.0.20" +name = "hyper" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" +checksum = "55281c53a1894c864990125767da440a4e630446785086f52523b20033b74498" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "h2", + "http", + "http-body", + "httparse", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] [[package]] -name = "embed-resource" -version = "3.0.9" +name = "hyper-rustls" +version = "0.27.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c31a88c8d26de40ed18fe748c547845aa39de1db3afd958f8cb91579f3644bcb" +checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f" dependencies = [ - "cc", - "memchr", - "rustc_version", - "toml 1.1.2+spec-1.1.0", - "vswhom", - "winreg", + "http", + "hyper", + "hyper-util", + "rustls", + "tokio", + "tokio-rustls", + "tower-service", ] [[package]] -name = "embed_plist" -version = "1.2.2" +name = "hyper-tls" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ef6b89e5b37196644d8796de5268852ff179b44e96276cf4290264843743bb7" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] [[package]] -name = "encoding_rs" -version = "0.8.35" +name = "hyper-util" +version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" dependencies = [ - "cfg-if", + "base64", + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "ipnet", + "libc", + "percent-encoding", + "pin-project-lite", + "socket2", + "system-configuration", + "tokio", + "tower-service", + "tracing", + "windows-registry", ] [[package]] -name = "equivalent" -version = "1.0.2" +name = "icu_collections" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" +checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" +dependencies = [ + "displaydoc", + "potential_utf", + "utf8_iter", + "yoke", + "zerofrom", + "zerovec", +] [[package]] -name = "erased-serde" -version = "0.4.10" +name = "icu_locale_core" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2add8a07dd6a8d93ff627029c51de145e12686fbc36ecb298ac22e74cf02dec" +checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" dependencies = [ - "serde", - "serde_core", - "typeid", + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", ] [[package]] -name = "errno" -version = "0.3.14" +name = "icu_normalizer" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" dependencies = [ - "libc", - "windows-sys 0.61.2", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", ] [[package]] -name = "fastrand" -version = "2.4.1" +name = "icu_normalizer_data" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" +checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" [[package]] -name = "fdeflate" -version = "0.3.7" +name = "icu_properties" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" +checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" dependencies = [ - "simd-adler32", + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", ] [[package]] -name = "field-offset" -version = "0.3.6" +name = "icu_properties_data" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" + +[[package]] +name = "icu_provider" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f" +checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" dependencies = [ - "memoffset", - "rustc_version", + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", ] [[package]] -name = "find-msvc-tools" -version = "0.1.9" +name = "id-arena" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" [[package]] -name = "flate2" -version = "1.1.9" +name = "idna" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" dependencies = [ - "crc32fast", - "miniz_oxide", + "idna_adapter", + "smallvec", + "utf8_iter", ] [[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "foldhash" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" - -[[package]] -name = "foldhash" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" - -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared 0.1.1", -] - -[[package]] -name = "foreign-types" -version = "0.5.0" +name = "idna_adapter" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" dependencies = [ - "foreign-types-macros", - "foreign-types-shared 0.3.1", + "icu_normalizer", + "icu_properties", ] [[package]] -name = "foreign-types-macros" -version = "0.2.3" +name = "indexmap" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", + "equivalent", + "hashbrown 0.17.1", + "serde", + "serde_core", ] [[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - -[[package]] -name = "foreign-types-shared" -version = "0.3.1" +name = "ipnet" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" +checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" [[package]] -name = "form_urlencoded" -version = "1.2.2" +name = "itoa" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" -dependencies = [ - "percent-encoding", -] +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] -name = "futures" -version = "0.3.32" +name = "js-sys" +version = "0.3.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +checksum = "142bc4740e452c1e57ade0cbc129f139c9093e354346f0872ef985f4f5cf5f11" dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", + "cfg-if", "futures-util", + "once_cell", + "wasm-bindgen", ] [[package]] -name = "futures-channel" -version = "0.3.32" +name = "leb128fmt" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" -dependencies = [ - "futures-core", - "futures-sink", -] +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" [[package]] -name = "futures-core" -version = "0.3.32" +name = "libc" +version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" [[package]] -name = "futures-executor" -version = "0.3.32" +name = "linux-raw-sys" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" [[package]] -name = "futures-io" -version = "0.3.32" +name = "litemap" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" +checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" [[package]] -name = "futures-macro" -version = "0.3.32" +name = "log" +version = "0.4.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] +checksum = "953f07c43838f8e6f9758cab68bf5bed85465e7587ebe0b823f1bcd81978ad3a" [[package]] -name = "futures-sink" -version = "0.3.32" +name = "memchr" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" +checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8" [[package]] -name = "futures-task" -version = "0.3.32" +name = "mime" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] -name = "futures-util" -version = "0.3.32" +name = "mio" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda" dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "slab", + "libc", + "wasi", + "windows-sys 0.61.2", ] [[package]] -name = "gdk" -version = "0.18.2" +name = "native-tls" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9f245958c627ac99d8e529166f9823fb3b838d1d41fd2b297af3075093c2691" +checksum = "465500e14ea162429d264d44189adc38b199b62b1c21eea9f69e4b73cb03bbf2" dependencies = [ - "cairo-rs", - "gdk-pixbuf", - "gdk-sys", - "gio", - "glib", "libc", - "pango", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", ] [[package]] -name = "gdk-pixbuf" -version = "0.18.5" +name = "nix" +version = "0.31.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50e1f5f1b0bfb830d6ccc8066d18db35c487b1b2b1e8589b5dfe9f07e8defaec" +checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d" dependencies = [ - "gdk-pixbuf-sys", - "gio", - "glib", + "bitflags", + "cfg-if", + "cfg_aliases", "libc", - "once_cell", ] [[package]] -name = "gdk-pixbuf-sys" -version = "0.18.0" +name = "num-conv" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9839ea644ed9c97a34d129ad56d38a25e6756f99f3a88e15cd39c20629caf7" -dependencies = [ - "gio-sys", - "glib-sys", - "gobject-sys", - "libc", - "system-deps", -] +checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" [[package]] -name = "gdk-sys" -version = "0.18.2" +name = "objc2" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c2d13f38594ac1e66619e188c6d5a1adb98d11b2fcf7894fc416ad76aa2f3f7" +checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f" dependencies = [ - "cairo-sys-rs", - "gdk-pixbuf-sys", - "gio-sys", - "glib-sys", - "gobject-sys", - "libc", - "pango-sys", - "pkg-config", - "system-deps", + "objc2-encode", ] [[package]] -name = "gdkwayland-sys" -version = "0.18.2" +name = "objc2-cloud-kit" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "140071d506d223f7572b9f09b5e155afbd77428cd5cc7af8f2694c41d98dfe69" +checksum = "73ad74d880bb43877038da939b7427bba67e9dd42004a18b809ba7d87cee241c" dependencies = [ - "gdk-sys", - "glib-sys", - "gobject-sys", - "libc", - "pkg-config", - "system-deps", + "bitflags", + "objc2", + "objc2-foundation", ] [[package]] -name = "gdkx11" -version = "0.18.2" +name = "objc2-core-data" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3caa00e14351bebbc8183b3c36690327eb77c49abc2268dd4bd36b856db3fbfe" +checksum = "0b402a653efbb5e82ce4df10683b6b28027616a2715e90009947d50b8dd298fa" dependencies = [ - "gdk", - "gdkx11-sys", - "gio", - "glib", - "libc", - "x11", + "objc2", + "objc2-foundation", ] [[package]] -name = "gdkx11-sys" -version = "0.18.2" +name = "objc2-core-foundation" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e2e7445fe01ac26f11601db260dd8608fe172514eb63b3b5e261ea6b0f4428d" +checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" dependencies = [ - "gdk-sys", - "glib-sys", - "libc", - "system-deps", - "x11", + "bitflags", + "dispatch2", + "objc2", ] [[package]] -name = "generic-array" -version = "0.14.7" +name = "objc2-core-graphics" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807" dependencies = [ - "typenum", - "version_check", + "bitflags", + "dispatch2", + "objc2", + "objc2-core-foundation", + "objc2-io-surface", ] [[package]] -name = "getrandom" -version = "0.2.17" +name = "objc2-core-image" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +checksum = "e5d563b38d2b97209f8e861173de434bd0214cf020e3423a52624cd1d989f006" dependencies = [ - "cfg-if", - "libc", - "wasi", + "objc2", + "objc2-foundation", ] [[package]] -name = "getrandom" -version = "0.3.4" +name = "objc2-core-location" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +checksum = "ca347214e24bc973fc025fd0d36ebb179ff30536ed1f80252706db19ee452009" dependencies = [ - "cfg-if", - "libc", - "r-efi 5.3.0", - "wasip2", + "objc2", + "objc2-foundation", ] [[package]] -name = "getrandom" -version = "0.4.2" +name = "objc2-core-text" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +checksum = "0cde0dfb48d25d2b4862161a4d5fcc0e3c24367869ad306b0c9ec0073bfed92d" dependencies = [ - "cfg-if", - "libc", - "r-efi 6.0.0", - "wasip2", - "wasip3", + "bitflags", + "objc2", + "objc2-core-foundation", + "objc2-core-graphics", ] [[package]] -name = "gio" -version = "0.18.4" +name = "objc2-encode" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" + +[[package]] +name = "objc2-foundation" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fc8f532f87b79cbc51a79748f16a6828fb784be93145a322fa14d06d354c73" +checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-util", - "gio-sys", - "glib", + "bitflags", + "block2", "libc", - "once_cell", - "pin-project-lite", - "smallvec", - "thiserror 1.0.69", + "objc2", + "objc2-core-foundation", ] [[package]] -name = "gio-sys" -version = "0.18.1" +name = "objc2-io-surface" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37566df850baf5e4cb0dfb78af2e4b9898d817ed9263d1090a2df958c64737d2" +checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d" dependencies = [ - "glib-sys", - "gobject-sys", - "libc", - "system-deps", - "winapi", + "bitflags", + "objc2", + "objc2-core-foundation", ] [[package]] -name = "glib" -version = "0.18.5" +name = "objc2-quartz-core" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233daaf6e83ae6a12a52055f568f9d7cf4671dabb78ff9560ab6da230ce00ee5" +checksum = "96c1358452b371bf9f104e21ec536d37a650eb10f7ee379fff67d2e08d537f1f" dependencies = [ - "bitflags 2.13.0", - "futures-channel", - "futures-core", - "futures-executor", - "futures-task", - "futures-util", - "gio-sys", - "glib-macros", - "glib-sys", - "gobject-sys", - "libc", - "memchr", - "once_cell", - "smallvec", - "thiserror 1.0.69", -] - -[[package]] -name = "glib-macros" -version = "0.18.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bb0228f477c0900c880fd78c8759b95c7636dbd7842707f49e132378aa2acdc" -dependencies = [ - "heck 0.4.1", - "proc-macro-crate 2.0.2", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 2.0.117", + "bitflags", + "objc2", + "objc2-core-foundation", + "objc2-foundation", ] [[package]] -name = "glib-sys" -version = "0.18.1" +name = "objc2-ui-kit" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "063ce2eb6a8d0ea93d2bf8ba1957e78dbab6be1c2220dd3daca57d5a9d869898" +checksum = "d87d638e33c06f577498cbcc50491496a3ed4246998a7fbba7ccb98b1e7eab22" dependencies = [ - "libc", - "system-deps", + "bitflags", + "block2", + "objc2", + "objc2-cloud-kit", + "objc2-core-data", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-core-image", + "objc2-core-location", + "objc2-core-text", + "objc2-foundation", + "objc2-quartz-core", + "objc2-user-notifications", ] [[package]] -name = "glob" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" - -[[package]] -name = "gobject-sys" -version = "0.18.0" +name = "objc2-user-notifications" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0850127b514d1c4a4654ead6dedadb18198999985908e6ffe4436f53c785ce44" +checksum = "9df9128cbbfef73cda168416ccf7f837b62737d748333bfe9ab71c245d76613e" dependencies = [ - "glib-sys", - "libc", - "system-deps", + "objc2", + "objc2-foundation", ] [[package]] -name = "gtk" -version = "0.18.2" +name = "once_cell" +version = "1.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd56fb197bfc42bd5d2751f4f017d44ff59fbb58140c6b49f9b3b2bdab08506a" -dependencies = [ - "atk", - "cairo-rs", - "field-offset", - "futures-channel", - "gdk", - "gdk-pixbuf", - "gio", - "glib", - "gtk-sys", - "gtk3-macros", - "libc", - "pango", - "pkg-config", -] +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" [[package]] -name = "gtk-sys" -version = "0.18.2" +name = "openssl" +version = "0.10.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f29a1c21c59553eb7dd40e918be54dccd60c52b049b75119d5d96ce6b624414" +checksum = "a45fa2aa886c42762255da344f0a0d313e254066c46aad76f300c3d3da62d967" dependencies = [ - "atk-sys", - "cairo-sys-rs", - "gdk-pixbuf-sys", - "gdk-sys", - "gio-sys", - "glib-sys", - "gobject-sys", + "bitflags", + "cfg-if", + "foreign-types", "libc", - "pango-sys", - "system-deps", + "openssl-macros", + "openssl-sys", ] [[package]] -name = "gtk3-macros" -version = "0.18.2" +name = "openssl-macros" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ff3c5b21f14f0736fed6dcfc0bfb4225ebf5725f3c0209edeec181e4d73e9d" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ - "proc-macro-crate 1.3.1", - "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.117", -] - -[[package]] -name = "h2" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "171fefbc92fe4a4de27e0698d6a5b392d6a0e333506bc49133760b3bcf948733" -dependencies = [ - "atomic-waker", - "bytes", - "fnv", - "futures-core", - "futures-sink", - "http", - "indexmap 2.14.0", - "slab", - "tokio", - "tokio-util", - "tracing", + "syn", ] [[package]] -name = "hashbrown" -version = "0.12.3" +name = "openssl-probe" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" [[package]] -name = "hashbrown" -version = "0.15.5" +name = "openssl-sys" +version = "0.9.116" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +checksum = "f28a22dc7140cda5f096e5e7724a6962ca81a7f8bfd2979f9b18c11af56318c4" dependencies = [ - "foldhash 0.1.5", + "cc", + "libc", + "pkg-config", + "vcpkg", ] [[package]] -name = "hashbrown" -version = "0.17.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" - -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - -[[package]] -name = "heck" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" - -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - -[[package]] -name = "html5ever" -version = "0.38.0" +name = "os_info" +version = "3.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1054432bae2f14e0061e33d23402fbaa67a921d319d56adc6bcf887ddad1cbc2" +checksum = "9cf20a545b305cf1da722b236b5155c9bb35f1d5ceb28c048bd96ca842f41b5b" dependencies = [ + "android_system_properties", "log", - "markup5ever", -] - -[[package]] -name = "http" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8be7462df143984c4598a256ef469b251d7d7f9e271135073e78fc535414f3d0" -dependencies = [ - "bytes", - "itoa", + "nix", + "objc2", + "objc2-foundation", + "objc2-ui-kit", + "serde", + "windows-sys 0.61.2", ] [[package]] -name = "http-body" -version = "1.0.1" +name = "percent-encoding" +version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" -dependencies = [ - "bytes", - "http", -] +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] -name = "http-body-util" -version = "0.1.3" +name = "pin-project-lite" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" -dependencies = [ - "bytes", - "futures-core", - "http", - "http-body", - "pin-project-lite", -] +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" [[package]] -name = "httparse" -version = "1.10.1" +name = "pkg-config" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" +checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" [[package]] -name = "hyper" -version = "1.10.1" +name = "potential_utf" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55281c53a1894c864990125767da440a4e630446785086f52523b20033b74498" +checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" dependencies = [ - "atomic-waker", - "bytes", - "futures-channel", - "futures-core", - "h2", - "http", - "http-body", - "httparse", - "itoa", - "pin-project-lite", - "smallvec", - "tokio", - "want", + "zerovec", ] [[package]] -name = "hyper-rustls" -version = "0.27.9" +name = "powerfmt" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f" -dependencies = [ - "http", - "hyper", - "hyper-util", - "rustls", - "tokio", - "tokio-rustls", - "tower-service", -] +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] -name = "hyper-tls" -version = "0.6.0" +name = "ppv-lite86" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ - "bytes", - "http-body-util", - "hyper", - "hyper-util", - "native-tls", - "tokio", - "tokio-native-tls", - "tower-service", + "zerocopy", ] [[package]] -name = "hyper-util" -version = "0.1.20" +name = "prettyplease" +version = "0.2.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ - "base64 0.22.1", - "bytes", - "futures-channel", - "futures-util", - "http", - "http-body", - "hyper", - "ipnet", - "libc", - "percent-encoding", - "pin-project-lite", - "socket2", - "system-configuration", - "tokio", - "tower-service", - "tracing", - "windows-registry", + "proc-macro2", + "syn", ] [[package]] -name = "iana-time-zone" -version = "0.1.65" +name = "proc-macro2" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "log", - "wasm-bindgen", - "windows-core 0.62.2", + "unicode-ident", ] [[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" +name = "quote" +version = "1.0.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" dependencies = [ - "cc", + "proc-macro2", ] [[package]] -name = "ico" -version = "0.5.0" +name = "r-efi" +version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e795dff5605e0f04bff85ca41b51a96b83e80b281e96231bcaaf1ac35103371" -dependencies = [ - "byteorder", - "png 0.17.16", -] +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" [[package]] -name = "icu_collections" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" -dependencies = [ - "displaydoc", - "potential_utf", - "utf8_iter", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_locale_core" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" -dependencies = [ - "displaydoc", - "litemap", - "tinystr", - "writeable", - "zerovec", -] - -[[package]] -name = "icu_normalizer" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" -dependencies = [ - "icu_collections", - "icu_normalizer_data", - "icu_properties", - "icu_provider", - "smallvec", - "zerovec", -] - -[[package]] -name = "icu_normalizer_data" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" - -[[package]] -name = "icu_properties" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" -dependencies = [ - "icu_collections", - "icu_locale_core", - "icu_properties_data", - "icu_provider", - "zerotrie", - "zerovec", -] - -[[package]] -name = "icu_properties_data" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" - -[[package]] -name = "icu_provider" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" -dependencies = [ - "displaydoc", - "icu_locale_core", - "writeable", - "yoke", - "zerofrom", - "zerotrie", - "zerovec", -] - -[[package]] -name = "id-arena" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" - -[[package]] -name = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - -[[package]] -name = "idna" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" -dependencies = [ - "idna_adapter", - "smallvec", - "utf8_iter", -] - -[[package]] -name = "idna_adapter" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" -dependencies = [ - "icu_normalizer", - "icu_properties", -] - -[[package]] -name = "indexmap" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg", - "hashbrown 0.12.3", - "serde", -] - -[[package]] -name = "indexmap" -version = "2.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" -dependencies = [ - "equivalent", - "hashbrown 0.17.1", - "serde", - "serde_core", -] - -[[package]] -name = "infer" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a588916bfdfd92e71cacef98a63d9b1f0d74d6599980d11894290e7ddefffcf7" -dependencies = [ - "cfb", -] - -[[package]] -name = "ipnet" -version = "2.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" - -[[package]] -name = "itoa" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" - -[[package]] -name = "javascriptcore-rs" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca5671e9ffce8ffba57afc24070e906da7fc4b1ba66f2cabebf61bf2ea257fcc" -dependencies = [ - "bitflags 1.3.2", - "glib", - "javascriptcore-rs-sys", -] - -[[package]] -name = "javascriptcore-rs-sys" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af1be78d14ffa4b75b66df31840478fef72b51f8c2465d4ca7c194da9f7a5124" -dependencies = [ - "glib-sys", - "gobject-sys", - "libc", - "system-deps", -] - -[[package]] -name = "jni" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" -dependencies = [ - "cesu8", - "cfg-if", - "combine", - "jni-sys 0.3.1", - "log", - "thiserror 1.0.69", - "walkdir", - "windows-sys 0.45.0", -] - -[[package]] -name = "jni-sys" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41a652e1f9b6e0275df1f15b32661cf0d4b78d4d87ddec5e0c3c20f097433258" -dependencies = [ - "jni-sys 0.4.1", -] - -[[package]] -name = "jni-sys" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2" -dependencies = [ - "jni-sys-macros", -] - -[[package]] -name = "jni-sys-macros" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" -dependencies = [ - "quote", - "syn 2.0.117", -] - -[[package]] -name = "js-sys" -version = "0.3.99" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "142bc4740e452c1e57ade0cbc129f139c9093e354346f0872ef985f4f5cf5f11" -dependencies = [ - "cfg-if", - "futures-util", - "once_cell", - "wasm-bindgen", -] - -[[package]] -name = "json-patch" -version = "3.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "863726d7afb6bc2590eeff7135d923545e5e964f004c2ccf8716c25e70a86f08" -dependencies = [ - "jsonptr", - "serde", - "serde_json", - "thiserror 1.0.69", -] - -[[package]] -name = "jsonptr" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dea2b27dd239b2556ed7a25ba842fe47fd602e7fc7433c2a8d6106d4d9edd70" -dependencies = [ - "serde", - "serde_json", -] - -[[package]] -name = "keyboard-types" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b750dcadc39a09dbadd74e118f6dd6598df77fa01df0cfcdc52c28dece74528a" -dependencies = [ - "bitflags 2.13.0", - "serde", - "unicode-segmentation", -] - -[[package]] -name = "leb128fmt" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" - -[[package]] -name = "libappindicator" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03589b9607c868cc7ae54c0b2a22c8dc03dd41692d48f2d7df73615c6a95dc0a" -dependencies = [ - "glib", - "gtk", - "gtk-sys", - "libappindicator-sys", - "log", -] - -[[package]] -name = "libappindicator-sys" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e9ec52138abedcc58dc17a7c6c0c00a2bdb4f3427c7f63fa97fd0d859155caf" -dependencies = [ - "gtk-sys", - "libloading", - "once_cell", -] - -[[package]] -name = "libc" -version = "0.2.186" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" - -[[package]] -name = "libdbus-sys" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "328c4789d42200f1eeec05bd86c9c13c7f091d2ba9a6ea35acdf51f31bc0f043" -dependencies = [ - "pkg-config", -] - -[[package]] -name = "libloading" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" -dependencies = [ - "cfg-if", - "winapi", -] - -[[package]] -name = "libredox" -version = "0.1.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f02ab6bace2054fb888a3c16f990117b579d14a3088e472d63c6011fa185c9d3" -dependencies = [ - "libc", -] - -[[package]] -name = "linux-raw-sys" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" - -[[package]] -name = "litemap" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" - -[[package]] -name = "lock_api" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" -dependencies = [ - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "953f07c43838f8e6f9758cab68bf5bed85465e7587ebe0b823f1bcd81978ad3a" - -[[package]] -name = "markup5ever" -version = "0.38.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8983d30f2915feeaaab2d6babdd6bc7e9ed1a00b66b5e6d74df19aa9c0e91862" -dependencies = [ - "log", - "tendril", - "web_atoms", -] - -[[package]] -name = "memchr" -version = "2.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8" - -[[package]] -name = "memoffset" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" -dependencies = [ - "autocfg", -] - -[[package]] -name = "mime" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" - -[[package]] -name = "miniz_oxide" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" -dependencies = [ - "adler2", - "simd-adler32", -] - -[[package]] -name = "mio" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda" -dependencies = [ - "libc", - "wasi", - "windows-sys 0.61.2", -] - -[[package]] -name = "muda" -version = "0.19.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47a2e3dff89cd322c66647942668faee0a2b1f88ea6cbb4d374b4a8d7e92528c" -dependencies = [ - "crossbeam-channel", - "dpi", - "gtk", - "keyboard-types", - "objc2", - "objc2-app-kit", - "objc2-core-foundation", - "objc2-foundation", - "once_cell", - "png 0.18.1", - "serde", - "thiserror 2.0.18", - "windows-sys 0.61.2", -] - -[[package]] -name = "native-tls" -version = "0.2.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "465500e14ea162429d264d44189adc38b199b62b1c21eea9f69e4b73cb03bbf2" -dependencies = [ - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", -] - -[[package]] -name = "ndk" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" -dependencies = [ - "bitflags 2.13.0", - "jni-sys 0.3.1", - "log", - "ndk-sys", - "num_enum", - "raw-window-handle", - "thiserror 1.0.69", -] - -[[package]] -name = "ndk-sys" -version = "0.6.0+11769913" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873" -dependencies = [ - "jni-sys 0.3.1", -] - -[[package]] -name = "new_debug_unreachable" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" - -[[package]] -name = "nix" -version = "0.31.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d" -dependencies = [ - "bitflags 2.13.0", - "cfg-if", - "cfg_aliases", - "libc", -] - -[[package]] -name = "num-conv" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" - -[[package]] -name = "num-traits" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" -dependencies = [ - "autocfg", -] - -[[package]] -name = "num_enum" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d0bca838442ec211fa11de3a8b0e0e8f3a4522575b5c4c06ed722e005036f26" -dependencies = [ - "num_enum_derive", - "rustversion", -] - -[[package]] -name = "num_enum_derive" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8" -dependencies = [ - "proc-macro-crate 3.5.0", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "objc2" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f" -dependencies = [ - "objc2-encode", - "objc2-exception-helper", -] - -[[package]] -name = "objc2-app-kit" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c" -dependencies = [ - "bitflags 2.13.0", - "block2", - "objc2", - "objc2-core-foundation", - "objc2-foundation", -] - -[[package]] -name = "objc2-cloud-kit" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73ad74d880bb43877038da939b7427bba67e9dd42004a18b809ba7d87cee241c" -dependencies = [ - "bitflags 2.13.0", - "objc2", - "objc2-foundation", -] - -[[package]] -name = "objc2-core-data" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b402a653efbb5e82ce4df10683b6b28027616a2715e90009947d50b8dd298fa" -dependencies = [ - "objc2", - "objc2-foundation", -] - -[[package]] -name = "objc2-core-foundation" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" -dependencies = [ - "bitflags 2.13.0", - "dispatch2", - "objc2", -] - -[[package]] -name = "objc2-core-graphics" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807" -dependencies = [ - "bitflags 2.13.0", - "dispatch2", - "objc2", - "objc2-core-foundation", - "objc2-io-surface", -] - -[[package]] -name = "objc2-core-image" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5d563b38d2b97209f8e861173de434bd0214cf020e3423a52624cd1d989f006" -dependencies = [ - "objc2", - "objc2-foundation", -] - -[[package]] -name = "objc2-core-location" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca347214e24bc973fc025fd0d36ebb179ff30536ed1f80252706db19ee452009" -dependencies = [ - "objc2", - "objc2-foundation", -] - -[[package]] -name = "objc2-core-text" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cde0dfb48d25d2b4862161a4d5fcc0e3c24367869ad306b0c9ec0073bfed92d" -dependencies = [ - "bitflags 2.13.0", - "objc2", - "objc2-core-foundation", - "objc2-core-graphics", -] - -[[package]] -name = "objc2-encode" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" - -[[package]] -name = "objc2-exception-helper" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7a1c5fbb72d7735b076bb47b578523aedc40f3c439bea6dfd595c089d79d98a" -dependencies = [ - "cc", -] - -[[package]] -name = "objc2-foundation" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" -dependencies = [ - "bitflags 2.13.0", - "block2", - "libc", - "objc2", - "objc2-core-foundation", -] - -[[package]] -name = "objc2-io-surface" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d" -dependencies = [ - "bitflags 2.13.0", - "objc2", - "objc2-core-foundation", -] - -[[package]] -name = "objc2-quartz-core" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96c1358452b371bf9f104e21ec536d37a650eb10f7ee379fff67d2e08d537f1f" -dependencies = [ - "bitflags 2.13.0", - "objc2", - "objc2-core-foundation", - "objc2-foundation", -] - -[[package]] -name = "objc2-ui-kit" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d87d638e33c06f577498cbcc50491496a3ed4246998a7fbba7ccb98b1e7eab22" -dependencies = [ - "bitflags 2.13.0", - "block2", - "objc2", - "objc2-cloud-kit", - "objc2-core-data", - "objc2-core-foundation", - "objc2-core-graphics", - "objc2-core-image", - "objc2-core-location", - "objc2-core-text", - "objc2-foundation", - "objc2-quartz-core", - "objc2-user-notifications", -] - -[[package]] -name = "objc2-user-notifications" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9df9128cbbfef73cda168416ccf7f837b62737d748333bfe9ab71c245d76613e" -dependencies = [ - "objc2", - "objc2-foundation", -] - -[[package]] -name = "objc2-web-kit" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2e5aaab980c433cf470df9d7af96a7b46a9d892d521a2cbbb2f8a4c16751e7f" -dependencies = [ - "bitflags 2.13.0", - "block2", - "objc2", - "objc2-app-kit", - "objc2-core-foundation", - "objc2-foundation", -] - -[[package]] -name = "once_cell" -version = "1.21.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" - -[[package]] -name = "openssl" -version = "0.10.80" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a45fa2aa886c42762255da344f0a0d313e254066c46aad76f300c3d3da62d967" -dependencies = [ - "bitflags 2.13.0", - "cfg-if", - "foreign-types 0.3.2", - "libc", - "openssl-macros", - "openssl-sys", -] - -[[package]] -name = "openssl-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "openssl-probe" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" - -[[package]] -name = "openssl-sys" -version = "0.9.116" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f28a22dc7140cda5f096e5e7724a6962ca81a7f8bfd2979f9b18c11af56318c4" -dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "option-ext" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" - -[[package]] -name = "os_info" -version = "3.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf20a545b305cf1da722b236b5155c9bb35f1d5ceb28c048bd96ca842f41b5b" -dependencies = [ - "android_system_properties", - "log", - "nix", - "objc2", - "objc2-foundation", - "objc2-ui-kit", - "serde", - "windows-sys 0.61.2", -] - -[[package]] -name = "pango" -version = "0.18.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ca27ec1eb0457ab26f3036ea52229edbdb74dee1edd29063f5b9b010e7ebee4" -dependencies = [ - "gio", - "glib", - "libc", - "once_cell", - "pango-sys", -] - -[[package]] -name = "pango-sys" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "436737e391a843e5933d6d9aa102cb126d501e815b83601365a948a518555dc5" -dependencies = [ - "glib-sys", - "gobject-sys", - "libc", - "system-deps", -] - -[[package]] -name = "parking_lot" -version = "0.12.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-link 0.2.1", -] - -[[package]] -name = "percent-encoding" -version = "2.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" - -[[package]] -name = "phf" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf" -dependencies = [ - "phf_macros", - "phf_shared", - "serde", -] - -[[package]] -name = "phf_codegen" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49aa7f9d80421bca176ca8dbfebe668cc7a2684708594ec9f3c0db0805d5d6e1" -dependencies = [ - "phf_generator", - "phf_shared", -] - -[[package]] -name = "phf_generator" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737" -dependencies = [ - "fastrand", - "phf_shared", -] - -[[package]] -name = "phf_macros" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "812f032b54b1e759ccd5f8b6677695d5268c588701effba24601f6932f8269ef" -dependencies = [ - "phf_generator", - "phf_shared", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "phf_shared" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266" -dependencies = [ - "siphasher", -] - -[[package]] -name = "pin-project-lite" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" - -[[package]] -name = "pkg-config" -version = "0.3.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" - -[[package]] -name = "plist" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "092791278e026273c1b65bbdcfbba3a300f2994c896bd01ab01da613c29c46f1" -dependencies = [ - "base64 0.22.1", - "indexmap 2.14.0", - "quick-xml", - "serde", - "time", -] - -[[package]] -name = "png" -version = "0.17.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" -dependencies = [ - "bitflags 1.3.2", - "crc32fast", - "fdeflate", - "flate2", - "miniz_oxide", -] - -[[package]] -name = "png" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61" -dependencies = [ - "bitflags 2.13.0", - "crc32fast", - "fdeflate", - "flate2", - "miniz_oxide", -] - -[[package]] -name = "potential_utf" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" -dependencies = [ - "zerovec", -] - -[[package]] -name = "powerfmt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" - -[[package]] -name = "ppv-lite86" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" -dependencies = [ - "zerocopy", -] - -[[package]] -name = "precomputed-hash" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" - -[[package]] -name = "prettyplease" -version = "0.2.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" -dependencies = [ - "proc-macro2", - "syn 2.0.117", -] - -[[package]] -name = "proc-macro-crate" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" -dependencies = [ - "once_cell", - "toml_edit 0.19.15", -] - -[[package]] -name = "proc-macro-crate" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b00f26d3400549137f92511a46ac1cd8ce37cb5598a96d382381458b992a5d24" -dependencies = [ - "toml_datetime 0.6.3", - "toml_edit 0.20.2", -] - -[[package]] -name = "proc-macro-crate" -version = "3.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" -dependencies = [ - "toml_edit 0.25.12+spec-1.1.0", -] - -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn 1.0.109", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - -[[package]] -name = "proc-macro2" -version = "1.0.106" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quick-xml" -version = "0.39.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdcc8dd4e2f670d309a5f0e83fe36dfdc05af317008fea29144da1a2ac858e5e" -dependencies = [ - "memchr", -] - -[[package]] -name = "quote" -version = "1.0.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "r-efi" -version = "5.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" - -[[package]] -name = "r-efi" -version = "6.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" - -[[package]] -name = "rand" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" -dependencies = [ - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" -dependencies = [ - "getrandom 0.3.4", -] - -[[package]] -name = "raw-window-handle" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" - -[[package]] -name = "redox_syscall" -version = "0.5.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" -dependencies = [ - "bitflags 2.13.0", -] - -[[package]] -name = "redox_users" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" -dependencies = [ - "getrandom 0.2.17", - "libredox", - "thiserror 2.0.18", -] - -[[package]] -name = "ref-cast" -version = "1.0.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" -dependencies = [ - "ref-cast-impl", -] - -[[package]] -name = "ref-cast-impl" -version = "1.0.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "regex" -version = "1.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.8.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" - -[[package]] -name = "reqwest" -version = "0.12.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" -dependencies = [ - "base64 0.22.1", - "bytes", - "encoding_rs", - "futures-core", - "h2", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-rustls", - "hyper-tls", - "hyper-util", - "js-sys", - "log", - "mime", - "native-tls", - "percent-encoding", - "pin-project-lite", - "rustls-pki-types", - "serde", - "serde_json", - "serde_urlencoded", - "sync_wrapper", - "tokio", - "tokio-native-tls", - "tower", - "tower-http", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "reqwest" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "219c5811de6525e5416c7d5d53bb656d3afdbc6c5af816e0802bcfa42dbdc1c3" -dependencies = [ - "base64 0.22.1", - "bytes", - "futures-core", - "futures-util", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-util", - "js-sys", - "log", - "percent-encoding", - "pin-project-lite", - "serde", - "serde_json", - "sync_wrapper", - "tokio", - "tokio-util", - "tower", - "tower-http", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "wasm-streams", - "web-sys", -] - -[[package]] -name = "ring" -version = "0.17.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" -dependencies = [ - "cc", - "cfg-if", - "getrandom 0.2.17", - "libc", - "untrusted", - "windows-sys 0.52.0", -] - -[[package]] -name = "rustc-hash" -version = "2.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" - -[[package]] -name = "rustc_version" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" -dependencies = [ - "semver", -] - -[[package]] -name = "rustix" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" -dependencies = [ - "bitflags 2.13.0", - "errno", - "libc", - "linux-raw-sys", - "windows-sys 0.61.2", -] - -[[package]] -name = "rustls" -version = "0.23.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b" -dependencies = [ - "once_cell", - "rustls-pki-types", - "rustls-webpki", - "subtle", - "zeroize", -] - -[[package]] -name = "rustls-pki-types" -version = "1.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9" -dependencies = [ - "zeroize", -] - -[[package]] -name = "rustls-webpki" -version = "0.103.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" -dependencies = [ - "ring", - "rustls-pki-types", - "untrusted", -] - -[[package]] -name = "rustversion" -version = "1.0.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" - -[[package]] -name = "ryu" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" - -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "schannel" -version = "0.1.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" -dependencies = [ - "windows-sys 0.61.2", -] - -[[package]] -name = "schemars" -version = "0.8.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fbf2ae1b8bc8e02df939598064d22402220cd5bbcca1c76f7d6a310974d5615" -dependencies = [ - "dyn-clone", - "indexmap 1.9.3", - "schemars_derive", - "serde", - "serde_json", - "url", - "uuid", -] - -[[package]] -name = "schemars" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" -dependencies = [ - "dyn-clone", - "ref-cast", - "serde", - "serde_json", -] - -[[package]] -name = "schemars" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2b42f36aa1cd011945615b92222f6bf73c599a102a300334cd7f8dbeec726cc" -dependencies = [ - "dyn-clone", - "ref-cast", - "serde", - "serde_json", -] - -[[package]] -name = "schemars_derive" -version = "0.8.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32e265784ad618884abaea0600a9adf15393368d840e0222d101a072f3f7534d" -dependencies = [ - "proc-macro2", - "quote", - "serde_derive_internals", - "syn 2.0.117", -] - -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - -[[package]] -name = "security-framework" -version = "3.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" -dependencies = [ - "bitflags 2.13.0", - "core-foundation 0.10.1", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "selectors" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5d9c0c92a92d33f08817311cf3f2c29a3538a8240e94a6a3c622ce652d7e00c" -dependencies = [ - "bitflags 2.13.0", - "cssparser", - "derive_more", - "log", - "new_debug_unreachable", - "phf", - "phf_codegen", - "precomputed-hash", - "rustc-hash", - "servo_arc", - "smallvec", -] - -[[package]] -name = "semver" -version = "1.0.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" -dependencies = [ - "serde", - "serde_core", -] - -[[package]] -name = "serde" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" -dependencies = [ - "serde_core", - "serde_derive", -] - -[[package]] -name = "serde-untagged" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9faf48a4a2d2693be24c6289dbe26552776eb7737074e6722891fadbe6c5058" -dependencies = [ - "erased-serde", - "serde", - "serde_core", - "typeid", -] - -[[package]] -name = "serde_core" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "serde_derive_internals" -version = "0.29.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "serde_json" -version = "1.0.150" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" -dependencies = [ - "itoa", - "memchr", - "serde", - "serde_core", - "zmij", -] - -[[package]] -name = "serde_repr" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "serde_spanned" -version = "0.6.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" -dependencies = [ - "serde", -] - -[[package]] -name = "serde_spanned" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" -dependencies = [ - "serde_core", -] - -[[package]] -name = "serde_urlencoded" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" -dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "serde_with" -version = "3.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76a5c54c7310e7b8b9577c286d7e399ddd876c3e12b3ed917a8aabc4b96e9e8c" -dependencies = [ - "base64 0.22.1", - "bs58", - "chrono", - "hex", - "indexmap 1.9.3", - "indexmap 2.14.0", - "schemars 0.9.0", - "schemars 1.2.1", - "serde_core", - "serde_json", - "serde_with_macros", - "time", -] - -[[package]] -name = "serde_with_macros" -version = "3.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84d57bc0c8b9a17920c178daa6bb924850d54a9c97ab45194bb8c17ad66bb660" -dependencies = [ - "darling", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "serialize-to-javascript" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04f3666a07a197cdb77cdf306c32be9b7f598d7060d50cfd4d5aa04bfd92f6c5" -dependencies = [ - "serde", - "serde_json", - "serialize-to-javascript-impl", -] - -[[package]] -name = "serialize-to-javascript-impl" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "772ee033c0916d670af7860b6e1ef7d658a4629a6d0b4c8c3e67f09b3765b75d" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "servo_arc" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "170fb83ab34de17dc69aa7c67482b22218ddb85da56546f9bd6b929e32a05930" -dependencies = [ - "stable_deref_trait", -] - -[[package]] -name = "sha2" -version = "0.10.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "shlex" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" - -[[package]] -name = "simd-adler32" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" - -[[package]] -name = "siphasher" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649" - -[[package]] -name = "slab" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" - -[[package]] -name = "smallvec" -version = "1.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" - -[[package]] -name = "socket2" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51" -dependencies = [ - "libc", - "windows-sys 0.61.2", -] - -[[package]] -name = "softbuffer" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aac18da81ebbf05109ab275b157c22a653bb3c12cf884450179942f81bcbf6c3" -dependencies = [ - "bytemuck", - "js-sys", - "ndk", - "objc2", - "objc2-core-foundation", - "objc2-core-graphics", - "objc2-foundation", - "objc2-quartz-core", - "raw-window-handle", - "redox_syscall", - "tracing", - "wasm-bindgen", - "web-sys", - "windows-sys 0.61.2", -] - -[[package]] -name = "soup3" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "471f924a40f31251afc77450e781cb26d55c0b650842efafc9c6cbd2f7cc4f9f" -dependencies = [ - "futures-channel", - "gio", - "glib", - "libc", - "soup3-sys", -] - -[[package]] -name = "soup3-sys" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ebe8950a680a12f24f15ebe1bf70db7af98ad242d9db43596ad3108aab86c27" -dependencies = [ - "gio-sys", - "glib-sys", - "gobject-sys", - "libc", - "system-deps", -] - -[[package]] -name = "stable_deref_trait" -version = "1.2.1" +name = "r-efi" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" [[package]] -name = "string_cache" -version = "0.9.0" +name = "rand" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a18596f8c785a729f2819c0f6a7eae6ebeebdfffbfe4214ae6b087f690e31901" +checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" dependencies = [ - "new_debug_unreachable", - "parking_lot", - "phf_shared", - "precomputed-hash", + "rand_chacha", + "rand_core", ] [[package]] -name = "string_cache_codegen" -version = "0.6.1" +name = "rand_chacha" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "585635e46db231059f76c5849798146164652513eb9e8ab2685939dd90f29b69" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ - "phf_generator", - "phf_shared", - "proc-macro2", - "quote", + "ppv-lite86", + "rand_core", ] [[package]] -name = "strsim" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" - -[[package]] -name = "subtle" -version = "2.6.1" +name = "rand_core" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom 0.3.4", +] [[package]] -name = "swift-rs" -version = "1.0.7" +name = "reqwest" +version = "0.12.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4057c98e2e852d51fdcfca832aac7b571f6b351ad159f9eda5db1655f8d0c4d7" +checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" dependencies = [ - "base64 0.21.7", + "base64", + "bytes", + "encoding_rs", + "futures-core", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-tls", + "hyper-util", + "js-sys", + "log", + "mime", + "native-tls", + "percent-encoding", + "pin-project-lite", + "rustls-pki-types", "serde", "serde_json", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tokio-native-tls", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", ] [[package]] -name = "syn" -version = "1.0.109" +name = "ring" +version = "0.17.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ - "proc-macro2", - "unicode-ident", + "cc", + "cfg-if", + "getrandom 0.2.17", + "libc", + "untrusted", + "windows-sys 0.52.0", ] [[package]] -name = "syn" -version = "2.0.117" +name = "rustix" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.61.2", ] [[package]] -name = "sync_wrapper" -version = "1.0.2" +name = "rustls" +version = "0.23.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b" dependencies = [ - "futures-core", + "once_cell", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", ] [[package]] -name = "synstructure" -version = "0.13.2" +name = "rustls-pki-types" +version = "1.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", + "zeroize", ] [[package]] -name = "sys-locale" -version = "0.3.2" +name = "rustls-webpki" +version = "0.103.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eab9a99a024a169fe8a903cf9d4a3b3601109bcc13bd9e3c6fff259138626c4" +checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" dependencies = [ - "libc", + "ring", + "rustls-pki-types", + "untrusted", ] [[package]] -name = "system-configuration" -version = "0.7.0" +name = "rustversion" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" -dependencies = [ - "bitflags 2.13.0", - "core-foundation 0.9.4", - "system-configuration-sys", -] +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] -name = "system-configuration-sys" -version = "0.6.0" +name = "ryu" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" -dependencies = [ - "core-foundation-sys", - "libc", -] +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" [[package]] -name = "system-deps" -version = "6.2.2" +name = "schannel" +version = "0.1.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" +checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" dependencies = [ - "cfg-expr", - "heck 0.5.0", - "pkg-config", - "toml 0.8.2", - "version-compare", + "windows-sys 0.61.2", ] [[package]] -name = "tao" -version = "0.35.3" +name = "security-framework" +version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1c93047acf68669466a34690ac58cca7010bd1b201e1ec86f1fd0a75d3dd4a9" +checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" dependencies = [ - "bitflags 2.13.0", - "block2", + "bitflags", "core-foundation 0.10.1", - "core-graphics", - "crossbeam-channel", - "dbus", - "dispatch2", - "dlopen2", - "dpi", - "gdkwayland-sys", - "gdkx11-sys", - "gtk", - "jni", + "core-foundation-sys", "libc", - "log", - "ndk", - "ndk-sys", - "objc2", - "objc2-app-kit", - "objc2-foundation", - "objc2-ui-kit", - "once_cell", - "parking_lot", - "percent-encoding", - "raw-window-handle", - "tao-macros", - "unicode-segmentation", - "url", - "windows", - "windows-core 0.61.2", - "windows-version", - "x11-dl", + "security-framework-sys", ] [[package]] -name = "tao-macros" -version = "0.1.3" +name = "security-framework-sys" +version = "2.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4e16beb8b2ac17db28eab8bca40e62dbfbb34c0fcdc6d9826b11b7b5d047dfd" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", + "core-foundation-sys", + "libc", ] [[package]] -name = "target-lexicon" -version = "0.12.16" +name = "semver" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" [[package]] -name = "tauri" -version = "2.11.2" +name = "serde" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "437404997acf375d85f1177afa7e11bb971f274ed6a7b83a2a3e339015f4cc28" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" dependencies = [ - "anyhow", - "bytes", - "cookie", - "dirs", - "dunce", - "embed_plist", - "getrandom 0.3.4", - "glob", - "gtk", - "heck 0.5.0", - "http", - "jni", - "libc", - "log", - "mime", - "muda", - "objc2", - "objc2-app-kit", - "objc2-foundation", - "objc2-ui-kit", - "objc2-web-kit", - "percent-encoding", - "plist", - "raw-window-handle", - "reqwest 0.13.4", - "serde", - "serde_json", - "serde_repr", - "serialize-to-javascript", - "swift-rs", - "tauri-build", - "tauri-macros", - "tauri-runtime", - "tauri-runtime-wry", - "tauri-utils", - "thiserror 2.0.18", - "tokio", - "tray-icon", - "url", - "webkit2gtk", - "webview2-com", - "window-vibrancy", - "windows", + "serde_core", + "serde_derive", ] [[package]] -name = "tauri-build" -version = "2.6.2" +name = "serde_core" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4aa1f9055fc23919a54e4e125052bed16ed04aef0487086e758fe01a67b451c7" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ - "anyhow", - "cargo_toml", - "dirs", - "glob", - "heck 0.5.0", - "json-patch", - "schemars 0.8.22", - "semver", - "serde", - "serde_json", - "tauri-utils", - "tauri-winres", - "walkdir", + "serde_derive", ] [[package]] -name = "tauri-codegen" -version = "2.6.2" +name = "serde_derive" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4a0319528a025a38c4078e7dae2c446f4e63620ddb0659a643ede1cb38f90e9" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ - "base64 0.22.1", - "brotli", - "ico", - "json-patch", - "plist", - "png 0.17.16", "proc-macro2", "quote", - "semver", - "serde", - "serde_json", - "sha2", - "syn 2.0.117", - "tauri-utils", - "thiserror 2.0.18", - "time", - "url", - "uuid", - "walkdir", + "syn", ] [[package]] -name = "tauri-macros" -version = "2.6.2" +name = "serde_json" +version = "1.0.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae6cb4e3896c21d2f6da5b31251d2faea0153bba56ed0e970f918115dbee4924" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" dependencies = [ - "heck 0.5.0", - "proc-macro2", - "quote", - "syn 2.0.117", - "tauri-codegen", - "tauri-utils", + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", ] [[package]] -name = "tauri-plugin" -version = "2.6.2" +name = "serde_urlencoded" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e126abc9e84e35cdfd01596140a73a1850cdb0df0a23acf0185776c30b469a6e" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" dependencies = [ - "anyhow", - "glob", - "plist", - "schemars 0.8.22", + "form_urlencoded", + "itoa", + "ryu", "serde", - "serde_json", - "tauri-utils", - "walkdir", ] [[package]] -name = "tauri-plugin-aptabase" -version = "1.0.0" -dependencies = [ - "futures", - "log", - "os_info", - "rand", - "reqwest 0.12.28", - "serde", - "serde_json", - "sys-locale", - "tauri", - "tauri-plugin", - "time", - "tokio", -] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" [[package]] -name = "tauri-runtime" -version = "2.11.2" +name = "slab" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48222d7116c8807eaa6fe2f372e023fae125084e61e6eca6d70b7961cdf129ef" -dependencies = [ - "cookie", - "dpi", - "gtk", - "http", - "jni", - "objc2", - "objc2-ui-kit", - "objc2-web-kit", - "raw-window-handle", - "serde", - "serde_json", - "tauri-utils", - "thiserror 2.0.18", - "url", - "webkit2gtk", - "webview2-com", - "windows", +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "socket2" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51" +dependencies = [ + "libc", + "windows-sys 0.61.2", ] [[package]] -name = "tauri-runtime-wry" -version = "2.11.2" +name = "stable_deref_trait" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b83849ee63ecb27a8e8d0fe51915ca215076914aca43f96db1179f0f415f6cd9" -dependencies = [ - "gtk", - "http", - "jni", - "log", - "objc2", - "objc2-app-kit", - "once_cell", - "percent-encoding", - "raw-window-handle", - "softbuffer", - "tao", - "tauri-runtime", - "tauri-utils", - "url", - "webkit2gtk", - "webview2-com", - "windows", - "wry", -] +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" [[package]] -name = "tauri-utils" -version = "2.9.2" +name = "subtle" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "092379df9a707631978e6c56b1bc2401d387f01e2d4a3c123360d167bbb9aa95" -dependencies = [ - "anyhow", - "brotli", - "cargo_metadata", - "ctor", - "dom_query", - "dunce", - "glob", - "http", - "infer", - "json-patch", - "log", - "memchr", - "phf", - "plist", - "proc-macro2", - "quote", - "regex", - "schemars 0.8.22", - "semver", - "serde", - "serde-untagged", - "serde_json", - "serde_with", - "swift-rs", - "thiserror 2.0.18", - "toml 1.1.2+spec-1.1.0", - "url", - "urlpattern", - "uuid", - "walkdir", -] +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] -name = "tauri-winres" -version = "0.3.6" +name = "syn" +version = "2.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc65d45c68858bfe420dd29e834b5d15dbecf8a07a8a16cf4d532c7b1f69d4b6" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" dependencies = [ - "dunce", - "embed-resource", - "toml 1.1.2+spec-1.1.0", + "proc-macro2", + "quote", + "unicode-ident", ] [[package]] -name = "tempfile" -version = "3.27.0" +name = "sync_wrapper" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" dependencies = [ - "fastrand", - "getrandom 0.4.2", - "once_cell", - "rustix", - "windows-sys 0.61.2", + "futures-core", ] [[package]] -name = "tendril" -version = "0.5.0" +name = "synstructure" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4790fc369d5a530f4b544b094e31388b9b3a37c0f4652ade4505945f5660d24" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ - "new_debug_unreachable", - "utf-8", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "thiserror" -version = "1.0.69" +name = "sys-locale" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +checksum = "8eab9a99a024a169fe8a903cf9d4a3b3601109bcc13bd9e3c6fff259138626c4" dependencies = [ - "thiserror-impl 1.0.69", + "libc", ] [[package]] -name = "thiserror" -version = "2.0.18" +name = "system-configuration" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" dependencies = [ - "thiserror-impl 2.0.18", + "bitflags", + "core-foundation 0.9.4", + "system-configuration-sys", ] [[package]] -name = "thiserror-impl" -version = "1.0.69" +name = "system-configuration-sys" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", + "core-foundation-sys", + "libc", ] [[package]] -name = "thiserror-impl" -version = "2.0.18" +name = "tempfile" +version = "3.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", + "fastrand", + "getrandom 0.4.2", + "once_cell", + "rustix", + "windows-sys 0.61.2", ] [[package]] @@ -3758,21 +1380,6 @@ dependencies = [ "zerovec", ] -[[package]] -name = "tinyvec" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - [[package]] name = "tokio" version = "1.52.3" @@ -3784,9 +1391,21 @@ dependencies = [ "mio", "pin-project-lite", "socket2", + "tokio-macros", "windows-sys 0.61.2", ] +[[package]] +name = "tokio-macros" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "tokio-native-tls" version = "0.3.1" @@ -3820,126 +1439,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "toml" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "185d8ab0dfbb35cf1399a6344d8484209c088f75f8f68230da55d48d95d43e3d" -dependencies = [ - "serde", - "serde_spanned 0.6.9", - "toml_datetime 0.6.3", - "toml_edit 0.20.2", -] - -[[package]] -name = "toml" -version = "0.9.12+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863" -dependencies = [ - "indexmap 2.14.0", - "serde_core", - "serde_spanned 1.1.1", - "toml_datetime 0.7.5+spec-1.1.0", - "toml_parser", - "toml_writer", - "winnow 0.7.15", -] - -[[package]] -name = "toml" -version = "1.1.2+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee" -dependencies = [ - "indexmap 2.14.0", - "serde_core", - "serde_spanned 1.1.1", - "toml_datetime 1.1.1+spec-1.1.0", - "toml_parser", - "toml_writer", - "winnow 1.0.3", -] - -[[package]] -name = "toml_datetime" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" -dependencies = [ - "serde", -] - -[[package]] -name = "toml_datetime" -version = "0.7.5+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" -dependencies = [ - "serde_core", -] - -[[package]] -name = "toml_datetime" -version = "1.1.1+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" -dependencies = [ - "serde_core", -] - -[[package]] -name = "toml_edit" -version = "0.19.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" -dependencies = [ - "indexmap 2.14.0", - "toml_datetime 0.6.3", - "winnow 0.5.40", -] - -[[package]] -name = "toml_edit" -version = "0.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" -dependencies = [ - "indexmap 2.14.0", - "serde", - "serde_spanned 0.6.9", - "toml_datetime 0.6.3", - "winnow 0.5.40", -] - -[[package]] -name = "toml_edit" -version = "0.25.12+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2153edc6955a6c354fad8f5efd38b6a8769bdccf9fe50f8e1329f81b0baa5d7" -dependencies = [ - "indexmap 2.14.0", - "toml_datetime 1.1.1+spec-1.1.0", - "toml_parser", - "winnow 1.0.3", -] - -[[package]] -name = "toml_parser" -version = "1.1.2+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" -dependencies = [ - "winnow 1.0.3", -] - -[[package]] -name = "toml_writer" -version = "1.1.1+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" - [[package]] name = "tower" version = "0.5.3" @@ -3961,7 +1460,7 @@ version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" dependencies = [ - "bitflags 2.13.0", + "bitflags", "bytes", "futures-util", "http", @@ -4004,99 +1503,18 @@ dependencies = [ "once_cell", ] -[[package]] -name = "tray-icon" -version = "0.23.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15edbb0d80583e85ee8df283410038e17314df5cba30da2087a54a85216c0773" -dependencies = [ - "crossbeam-channel", - "dirs", - "libappindicator", - "muda", - "objc2", - "objc2-app-kit", - "objc2-core-foundation", - "objc2-core-graphics", - "objc2-foundation", - "once_cell", - "png 0.18.1", - "serde", - "thiserror 2.0.18", - "windows-sys 0.61.2", -] - [[package]] name = "try-lock" version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" -[[package]] -name = "typeid" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" - -[[package]] -name = "typenum" -version = "1.20.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" - -[[package]] -name = "unic-char-property" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" -dependencies = [ - "unic-char-range", -] - -[[package]] -name = "unic-char-range" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" - -[[package]] -name = "unic-common" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" - -[[package]] -name = "unic-ucd-ident" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e230a37c0381caa9219d67cf063aa3a375ffed5bf541a452db16e744bdab6987" -dependencies = [ - "unic-char-property", - "unic-char-range", - "unic-ucd-version", -] - -[[package]] -name = "unic-ucd-version" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" -dependencies = [ - "unic-common", -] - [[package]] name = "unicode-ident" version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" -[[package]] -name = "unicode-segmentation" -version = "1.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" - [[package]] name = "unicode-xid" version = "0.2.6" @@ -4119,92 +1537,19 @@ dependencies = [ "idna", "percent-encoding", "serde", - "serde_derive", -] - -[[package]] -name = "urlpattern" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70acd30e3aa1450bc2eece896ce2ad0d178e9c079493819301573dae3c37ba6d" -dependencies = [ - "regex", - "serde", - "unic-ucd-ident", - "url", ] -[[package]] -name = "utf-8" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" - [[package]] name = "utf8_iter" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" - -[[package]] -name = "uuid" -version = "1.23.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d258b83ceec21034727ecee8c382cfa6c3e133699b0742c64571814fb420c9f7" -dependencies = [ - "getrandom 0.4.2", - "js-sys", - "serde_core", - "wasm-bindgen", -] - -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - -[[package]] -name = "version-compare" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03c2856837ef78f57382f06b2b8563a2f512f7185d732608fd9176cb3b8edf0e" - -[[package]] -name = "version_check" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" - -[[package]] -name = "vswhom" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be979b7f07507105799e854203b470ff7c78a1639e330a58f183b5fea574608b" -dependencies = [ - "libc", - "vswhom-sys", -] - -[[package]] -name = "vswhom-sys" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb067e4cbd1ff067d1df46c9194b5de0e98efd2810bbc95c5d5e5f25a3231150" -dependencies = [ - "cc", - "libc", -] +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] -name = "walkdir" -version = "2.5.0" +name = "vcpkg" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" -dependencies = [ - "same-file", - "winapi-util", -] +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "want" @@ -4281,7 +1626,7 @@ dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 2.0.117", + "syn", "wasm-bindgen-shared", ] @@ -4311,33 +1656,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" dependencies = [ "anyhow", - "indexmap 2.14.0", + "indexmap", "wasm-encoder", "wasmparser", ] -[[package]] -name = "wasm-streams" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d1ec4f6517c9e11ae630e200b2b65d193279042e28edd4a2cda233e46670bbb" -dependencies = [ - "futures-util", - "js-sys", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - [[package]] name = "wasmparser" version = "0.244.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" dependencies = [ - "bitflags 2.13.0", + "bitflags", "hashbrown 0.15.5", - "indexmap 2.14.0", + "indexmap", "semver", ] @@ -4351,265 +1683,21 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "web_atoms" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7cff6eef815df1834fd250e3a2ff436044d82a9f1bc1980ca1dbdf07effc538" -dependencies = [ - "phf", - "phf_codegen", - "string_cache", - "string_cache_codegen", -] - -[[package]] -name = "webkit2gtk" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1027150013530fb2eaf806408df88461ae4815a45c541c8975e61d6f2fc4793" -dependencies = [ - "bitflags 1.3.2", - "cairo-rs", - "gdk", - "gdk-sys", - "gio", - "gio-sys", - "glib", - "glib-sys", - "gobject-sys", - "gtk", - "gtk-sys", - "javascriptcore-rs", - "libc", - "once_cell", - "soup3", - "webkit2gtk-sys", -] - -[[package]] -name = "webkit2gtk-sys" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "916a5f65c2ef0dfe12fff695960a2ec3d4565359fdbb2e9943c974e06c734ea5" -dependencies = [ - "bitflags 1.3.2", - "cairo-sys-rs", - "gdk-sys", - "gio-sys", - "glib-sys", - "gobject-sys", - "gtk-sys", - "javascriptcore-rs-sys", - "libc", - "pkg-config", - "soup3-sys", - "system-deps", -] - -[[package]] -name = "webview2-com" -version = "0.38.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7130243a7a5b33c54a444e54842e6a9e133de08b5ad7b5861cd8ed9a6a5bc96a" -dependencies = [ - "webview2-com-macros", - "webview2-com-sys", - "windows", - "windows-core 0.61.2", - "windows-implement", - "windows-interface", -] - -[[package]] -name = "webview2-com-macros" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67a921c1b6914c367b2b823cd4cde6f96beec77d30a939c8199bb377cf9b9b54" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "webview2-com-sys" -version = "0.38.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "381336cfffd772377d291702245447a5251a2ffa5bad679c99e61bc48bacbf9c" -dependencies = [ - "thiserror 2.0.18", - "windows", - "windows-core 0.61.2", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" -dependencies = [ - "windows-sys 0.61.2", -] - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "window-vibrancy" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9bec5a31f3f9362f2258fd0e9c9dd61a9ca432e7306cc78c444258f0dce9a9c" -dependencies = [ - "objc2", - "objc2-app-kit", - "objc2-core-foundation", - "objc2-foundation", - "raw-window-handle", - "windows-sys 0.59.0", - "windows-version", -] - -[[package]] -name = "windows" -version = "0.61.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" -dependencies = [ - "windows-collections", - "windows-core 0.61.2", - "windows-future", - "windows-link 0.1.3", - "windows-numerics", -] - -[[package]] -name = "windows-collections" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" -dependencies = [ - "windows-core 0.61.2", -] - -[[package]] -name = "windows-core" -version = "0.61.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" -dependencies = [ - "windows-implement", - "windows-interface", - "windows-link 0.1.3", - "windows-result 0.3.4", - "windows-strings 0.4.2", -] - -[[package]] -name = "windows-core" -version = "0.62.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" -dependencies = [ - "windows-implement", - "windows-interface", - "windows-link 0.2.1", - "windows-result 0.4.1", - "windows-strings 0.5.1", -] - -[[package]] -name = "windows-future" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" -dependencies = [ - "windows-core 0.61.2", - "windows-link 0.1.3", - "windows-threading", -] - -[[package]] -name = "windows-implement" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "windows-interface" -version = "0.59.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "windows-link" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" - [[package]] name = "windows-link" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" -[[package]] -name = "windows-numerics" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" -dependencies = [ - "windows-core 0.61.2", - "windows-link 0.1.3", -] - [[package]] name = "windows-registry" version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" dependencies = [ - "windows-link 0.2.1", - "windows-result 0.4.1", - "windows-strings 0.5.1", -] - -[[package]] -name = "windows-result" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" -dependencies = [ - "windows-link 0.1.3", + "windows-link", + "windows-result", + "windows-strings", ] [[package]] @@ -4618,16 +1706,7 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" dependencies = [ - "windows-link 0.2.1", -] - -[[package]] -name = "windows-strings" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" -dependencies = [ - "windows-link 0.1.3", + "windows-link", ] [[package]] @@ -4636,16 +1715,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" dependencies = [ - "windows-link 0.2.1", -] - -[[package]] -name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets 0.42.2", + "windows-link", ] [[package]] @@ -4654,16 +1724,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-sys" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" -dependencies = [ - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -4672,22 +1733,7 @@ version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ - "windows-link 0.2.1", -] - -[[package]] -name = "windows-targets" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", + "windows-link", ] [[package]] @@ -4696,64 +1742,28 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", "windows_i686_gnullvm", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", -] - -[[package]] -name = "windows-threading" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" -dependencies = [ - "windows-link 0.1.3", -] - -[[package]] -name = "windows-version" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4060a1da109b9d0326b7262c8e12c84df67cc0dbc9e33cf49e01ccc2eb63631" -dependencies = [ - "windows-link 0.2.1", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" - [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" - [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -4766,88 +1776,30 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" -[[package]] -name = "windows_i686_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" - [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" - [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" -[[package]] -name = "winnow" -version = "0.5.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" -dependencies = [ - "memchr", -] - -[[package]] -name = "winnow" -version = "0.7.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" - -[[package]] -name = "winnow" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" -dependencies = [ - "memchr", -] - -[[package]] -name = "winreg" -version = "0.55.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb5a765337c50e9ec252c2069be9bf91c7df47afb103b642ba3a53bf8101be97" -dependencies = [ - "cfg-if", - "windows-sys 0.59.0", -] - [[package]] name = "wit-bindgen" version = "0.51.0" @@ -4870,7 +1822,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" dependencies = [ "anyhow", - "heck 0.5.0", + "heck", "wit-parser", ] @@ -4881,10 +1833,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" dependencies = [ "anyhow", - "heck 0.5.0", - "indexmap 2.14.0", + "heck", + "indexmap", "prettyplease", - "syn 2.0.117", + "syn", "wasm-metadata", "wit-bindgen-core", "wit-component", @@ -4900,7 +1852,7 @@ dependencies = [ "prettyplease", "proc-macro2", "quote", - "syn 2.0.117", + "syn", "wit-bindgen-core", "wit-bindgen-rust", ] @@ -4912,8 +1864,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" dependencies = [ "anyhow", - "bitflags 2.13.0", - "indexmap 2.14.0", + "bitflags", + "indexmap", "log", "serde", "serde_derive", @@ -4932,7 +1884,7 @@ checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" dependencies = [ "anyhow", "id-arena", - "indexmap 2.14.0", + "indexmap", "log", "semver", "serde", @@ -4948,71 +1900,6 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" -[[package]] -name = "wry" -version = "0.55.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "186f9871daa55fd9c016578b810d149de58367113db7fb72b462d2323ce19514" -dependencies = [ - "base64 0.22.1", - "block2", - "cookie", - "crossbeam-channel", - "dirs", - "dom_query", - "dpi", - "dunce", - "gdkx11", - "gtk", - "http", - "javascriptcore-rs", - "jni", - "libc", - "ndk", - "objc2", - "objc2-app-kit", - "objc2-core-foundation", - "objc2-foundation", - "objc2-ui-kit", - "objc2-web-kit", - "once_cell", - "percent-encoding", - "raw-window-handle", - "sha2", - "soup3", - "tao-macros", - "thiserror 2.0.18", - "url", - "webkit2gtk", - "webkit2gtk-sys", - "webview2-com", - "windows", - "windows-core 0.61.2", - "windows-version", - "x11-dl", -] - -[[package]] -name = "x11" -version = "2.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "502da5464ccd04011667b11c435cb992822c2c0dbde1770c988480d312a0db2e" -dependencies = [ - "libc", - "pkg-config", -] - -[[package]] -name = "x11-dl" -version = "2.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f" -dependencies = [ - "libc", - "once_cell", - "pkg-config", -] - [[package]] name = "yoke" version = "0.8.3" @@ -5032,7 +1919,7 @@ checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", "synstructure", ] @@ -5053,7 +1940,7 @@ checksum = "0b631b19d36a892ab55420c92dbc83ccd79274f25be714855d3074aa71cab639" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -5073,7 +1960,7 @@ checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", "synstructure", ] @@ -5113,7 +2000,7 @@ checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index be4338d..723d4e1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,21 +1,20 @@ [package] -name = "tauri-plugin-aptabase" +name = "aptabase-rs" version = "1.0.0" license = "MIT" -description = "Tauri Plugin for Aptabase: Open Source, Privacy-First and Simple Analytics for Mobile, Desktop and Web Apps" +description = "Aptabase analytics client for Rust applications" authors = ["Guilherme Oenning"] edition = "2021" rust-version = "1.70" readme = "README.md" -repository = "https://github.com/aptabase/tauri-plugin-aptabase" -exclude = ["/examples", "/webview-dist", "/webview-src", "/node_modules"] -links = "tauri-plugin-aptabase" +repository = "https://github.com/aptabase/aptabase-rs" +homepage = "https://aptabase.com" +keywords = ["analytics", "aptabase", "telemetry"] +categories = ["api-bindings"] +exclude = ["/openspec", "/AGENTS.md", "/mise.toml", "/mise.lock", "/llms.txt"] [dependencies] -tauri = "2.2.5" -tokio = "1.43.0" -futures = "0.3.31" -serde = "1.0.217" +tokio = { version = "1.43.0", features = ["rt", "time"] } serde_json = "1.0.138" reqwest = { version = "0.12.12", features = ["json"] } time = { version = "0.3.37", features = ["formatting"] } @@ -24,5 +23,5 @@ rand = "0.9.0" log = "0.4.25" sys-locale = "0.3.2" -[build-dependencies] -tauri-plugin = { version = "2.0.4", features = ["build"] } +[dev-dependencies] +tokio = { version = "1.43.0", features = ["macros", "rt-multi-thread", "time"] } diff --git a/README.md b/README.md index 42fd437..8ab4609 100644 --- a/README.md +++ b/README.md @@ -1,129 +1,88 @@ -![Aptabase](https://aptabase.com/og.png) +# aptabase-rs -# Tauri Plugin for Aptabase +`aptabase-rs` is a framework-independent Aptabase client for Rust applications. It synchronously queues events in memory and delivers them with an asynchronous Tokio/Reqwest transport. -This plugin allows you to instrument your app with events that can be analyzed in Aptabase, an Open Source, Privacy-First, and Simple Analytics for Mobile, Desktop, and Web Apps. - -## Install - -Install the Core plugin by adding the following to your `Cargo.toml` file: - -`src-tauri/Cargo.toml` +## Installation ```toml [dependencies] -tauri-plugin-aptabase = "1.0.0" +aptabase-rs = "1.0.0" +serde_json = "1" +tokio = { version = "1", features = ["macros", "rt-multi-thread"] } ``` -```toml -[dependencies] -tauri-plugin-aptabase = { git = "https://github.com/aptabase/tauri-plugin-aptabase" } -``` +## CLI usage -You can install the JavaScript Guest bindings using your preferred JavaScript package manager +Create the client with an Aptabase app key and your application version. Event tracking does not perform network I/O; explicitly await `flush` before a short-lived process exits. -```bash -npm add @aptabase/tauri -``` - -```bash -npm add https://github.com/aptabase/tauri-plugin-aptabase -``` - -## Usage - -First, you need to get your `App Key` from Aptabase, you can find it in the `Instructions` menu on the left side menu. +```rust +use aptabase_rs::AptabaseClient; +use serde_json::json; -Then register the plugin with Tauri: +#[tokio::main] +async fn main() -> Result<(), Box> { + let client = AptabaseClient::new("A-US-your-app-key", env!("CARGO_PKG_VERSION")); -`src-tauri/src/main.rs` + client.track_event("command_started", Some(json!({ "command": "sync" })))?; + client.flush().await; -```rust -#[tokio::main] -async fn main() { - tauri::Builder::default() - .plugin(tauri_plugin_aptabase::Builder::new("").build()) // ๐Ÿ‘ˆ this is where you enter your App Key - .run(tauri::generate_context!()) - .expect("error while running tauri application"); + Ok(()) } ``` -And finally add `aptabase:allow-track-event` to your list Access Control List. +Properties must be a `serde_json::Value::Object`. Invalid app keys disable tracking without making construction fail. -You can then start sending events from Rust by importing the `tauri_plugin_aptabase::EventTracker` trait and calling the `track_event` method on `App`, `AppHandle` or `Window`. +## Periodic flushing -As an example, you can add `app_started` and `app_exited` events like this: +Long-running applications can start a periodic worker on an active Tokio runtime. The interval defaults to 60 seconds in release builds and 2 seconds in debug builds. ```rust -use tauri_plugin_aptabase::EventTracker; +use aptabase_rs::AptabaseClient; -#[tokio::main] -async fn main() { - tauri::Builder::default() - .plugin(tauri_plugin_aptabase::init("".into())) - .setup(|app| { - app.track_event("app_started", None); - Ok(()) - }) - .build(tauri::generate_context!()) - .expect("error while running tauri application") - .run(|handler, event| match event { - tauri::RunEvent::Exit { .. } => { - handler.track_event("app_exited", None); - handler.flush_events_blocking(); - } - _ => {} - }) -} +let client = AptabaseClient::new("A-EU-your-app-key", "2.0.0"); +let worker = client.start_periodic_flush(); + +client.track_event("service_started", None)?; + +// Stop the worker during shutdown, then deliver anything still queued. +worker.abort(); +client.flush().await; +# Ok::<(), Box>(()) ``` -The `trackEvent` function is also available through the JavaScript guest bindings: +Start at most one periodic worker per client. Events are kept only in memory, so applications should explicitly flush during orderly shutdown. -```js -import { trackEvent } from "@aptabase/tauri"; +## Configuration -trackEvent("save_settings") // An event with no properties -trackEvent("screen_view", { name: "Settings" }) // An event with a custom property -``` +Use `InitOptions` for a self-hosted endpoint, a custom periodic interval, or runtime metadata overrides. -A few important notes: +```rust +use aptabase_rs::{AptabaseClient, InitOptions}; +use std::time::Duration; + +let client = AptabaseClient::with_options( + "A-SH-your-app-key", + "1.4.0", + InitOptions { + host: Some("https://analytics.example.com".into()), + flush_interval: Some(Duration::from_secs(30)), + engine_name: Some("my-cli-runtime".into()), + engine_version: Some("1.4.0".into()), + }, +); +``` -1. The plugin will automatically enhance the event with some useful information, like the OS, the app version, and other things. -2. You're in control of what gets sent to Aptabase. This plugin does not automatically track any events, you need to call `trackEvent` manually. - - Because of this, it's generally recommended to at least track an event at startup -3. You do not need to await for the `trackEvent` function, it'll run in the background. -3. Only strings and numbers values are allowed on custom properties +Hosted keys beginning with `A-US-` and `A-EU-` use Aptabase's regional endpoints. `A-DEV-` targets `http://localhost:3000`, and `A-SH-` requires `host`. -## Providing the APTABASE_KEY via .env +## Delivery behavior -It's possible to load the APTABASE_KEY from a .env file at compile time using the `dotenvy_macro` crate. The `.env` file needs to be -in the `src-tauri` directory for the `dotevny_macro` crate to find it properly. +- Requests contain at most 25 events and use Aptabase's `/api/v0/events` endpoint. +- Transport failures and HTTP 5xx responses are requeued in memory for a later flush. +- Other unsuccessful HTTP responses are discarded. +- This crate provides only the asynchronous Tokio transport; it does not provide a blocking client. -Add the `use` declaration to where you are building the tauri app (likely `lib.rs` for Tauri v2), and then call it where you would put the key. +## Privacy -```rust -use tauri_plugin_aptabase::EventTracker; -use dotenvy_macro::dotenv; - -#[cfg_attr(mobile, tauri::mobile_entry_point)] -/// This function sets up and runs a Rust application using the Tauri framework, with various plugins -/// and event handlers. -pub fn run() { - tauri::Builder::default() - .build(tauri::generate_context!()) - .plugin(tauri_plugin_aptabase::Builder::new(dotenv!("APTABASE_KEY")).build()) - .expect("Error when building tauri app") - .run(|handler, event| match event { - tauri::RunEvent::Exit { .. } => { - handler.track_event("app_exited", None); - handler.flush_events_blocking(); - } - tauri::RunEvent::Ready { .. } => { - handler.track_event("app_started", None); - } - _ => {} - }); -} -``` +The client never tracks events automatically. Applications decide which event names and properties to send. Each event includes the app version, SDK version, debug status, OS name and version, locale, and configured engine name and version. Avoid sending personal or sensitive data in event names or properties. -For AI/LLM integration instructions, see [llms.txt](./llms.txt) +See [Aptabase](https://aptabase.com) for service documentation and privacy guidance. diff --git a/build.rs b/build.rs deleted file mode 100644 index b6b7b1a..0000000 --- a/build.rs +++ /dev/null @@ -1,5 +0,0 @@ -const COMMANDS: &[&str] = &["track_event"]; - -fn main() { - tauri_plugin::Builder::new(COMMANDS).build(); -} diff --git a/examples/cli.rs b/examples/cli.rs new file mode 100644 index 0000000..36f8711 --- /dev/null +++ b/examples/cli.rs @@ -0,0 +1,12 @@ +use aptabase_rs::AptabaseClient; + +#[tokio::main] +async fn main() -> Result<(), Box> { + let app_key = std::env::var("APTABASE_APP_KEY").unwrap_or_default(); + let client = AptabaseClient::new(app_key, env!("CARGO_PKG_VERSION")); + + client.track_event("cli_started", None)?; + client.flush().await; + + Ok(()) +} diff --git a/examples/helloworld/.gitignore b/examples/helloworld/.gitignore deleted file mode 100644 index a547bf3..0000000 --- a/examples/helloworld/.gitignore +++ /dev/null @@ -1,24 +0,0 @@ -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -pnpm-debug.log* -lerna-debug.log* - -node_modules -dist -dist-ssr -*.local - -# Editor directories and files -.vscode/* -!.vscode/extensions.json -.idea -.DS_Store -*.suo -*.ntvs* -*.njsproj -*.sln -*.sw? diff --git a/examples/helloworld/index.html b/examples/helloworld/index.html deleted file mode 100644 index fad1c5d..0000000 --- a/examples/helloworld/index.html +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - Tauri + Svelte - - - -
- - - diff --git a/examples/helloworld/jsconfig.json b/examples/helloworld/jsconfig.json deleted file mode 100644 index ee5e92f..0000000 --- a/examples/helloworld/jsconfig.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "compilerOptions": { - "moduleResolution": "Node", - "target": "ESNext", - "module": "ESNext", - /** - * svelte-preprocess cannot figure out whether you have - * a value or a type, so tell TypeScript to enforce using - * `import type` instead of `import` for Types. - */ - "importsNotUsedAsValues": "error", - "isolatedModules": true, - "resolveJsonModule": true, - /** - * To have warnings / errors of the Svelte compiler at the - * correct position, enable source maps by default. - */ - "sourceMap": true, - "esModuleInterop": true, - "skipLibCheck": true, - "forceConsistentCasingInFileNames": true, - "baseUrl": ".", - /** - * Typecheck JS in `.svelte` and `.js` files by default. - * Disable this if you'd like to use dynamic types. - */ - "checkJs": true - }, - /** - * Use global.d.ts instead of compilerOptions.types - * to avoid limiting type declarations. - */ - "include": ["src/**/*.d.ts", "src/**/*.js", "src/**/*.svelte"] -} diff --git a/examples/helloworld/package.json b/examples/helloworld/package.json deleted file mode 100644 index 2c6fd20..0000000 --- a/examples/helloworld/package.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "name": "helloworld", - "private": true, - "version": "0.0.0", - "type": "module", - "scripts": { - "dev": "tauri dev", - "dev:vite": "vite", - "build": "tsc && vite build", - "preview": "vite preview", - "tauri": "tauri" - }, - "dependencies": { - "@tauri-apps/api": "^2.1.1", - "@aptabase/tauri": "file:../../" - }, - "devDependencies": { - "@sveltejs/vite-plugin-svelte": "^1.0.1", - "internal-ip": "^7.0.0", - "svelte": "^3.49.0", - "vite": "^3.0.2", - "@tauri-apps/cli": "^2.1.0" - } -} diff --git a/examples/helloworld/public/svelte.svg b/examples/helloworld/public/svelte.svg deleted file mode 100644 index c5e0848..0000000 --- a/examples/helloworld/public/svelte.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/examples/helloworld/public/tauri.svg b/examples/helloworld/public/tauri.svg deleted file mode 100644 index 31b62c9..0000000 --- a/examples/helloworld/public/tauri.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/examples/helloworld/public/vite.svg b/examples/helloworld/public/vite.svg deleted file mode 100644 index e7b8dfb..0000000 --- a/examples/helloworld/public/vite.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/examples/helloworld/src-tauri/.gitignore b/examples/helloworld/src-tauri/.gitignore deleted file mode 100644 index f4dfb82..0000000 --- a/examples/helloworld/src-tauri/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -# Generated by Cargo -# will have compiled files and executables -/target/ - diff --git a/examples/helloworld/src-tauri/Cargo.toml b/examples/helloworld/src-tauri/Cargo.toml deleted file mode 100644 index 619e34e..0000000 --- a/examples/helloworld/src-tauri/Cargo.toml +++ /dev/null @@ -1,29 +0,0 @@ -[package] -name = "tauri-app" -version = "0.0.0" -description = "A Tauri App" -authors = ["you"] -license = "" -repository = "" -edition = "2021" -rust-version = "1.70" - -[lib] -name = "tauri_app_lib" -crate-type = ["staticlib", "cdylib", "rlib"] - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[build-dependencies] -tauri-build = { version = "2.0", default-features = false, features = [] } - -[dependencies] -tauri = { version = "2.1", features = [] } -tauri-plugin-aptabase = { path = "../../../" } -serde_json = "1.0" -serde = { version = "1.0", features = ["derive"] } - -[features] -# this feature is used for production builds or when `devUrl` points to the filesystem -# DO NOT REMOVE!! -custom-protocol = ["tauri/custom-protocol"] diff --git a/examples/helloworld/src-tauri/build.rs b/examples/helloworld/src-tauri/build.rs deleted file mode 100644 index 795b9b7..0000000 --- a/examples/helloworld/src-tauri/build.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - tauri_build::build() -} diff --git a/examples/helloworld/src-tauri/capabilities/desktop.json b/examples/helloworld/src-tauri/capabilities/desktop.json deleted file mode 100644 index 8182109..0000000 --- a/examples/helloworld/src-tauri/capabilities/desktop.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "identifier": "main-capability", - "description": "Capability for the main window", - "windows": ["main"], - "permissions": [ - "aptabase:allow-track-event" - ] - } \ No newline at end of file diff --git a/examples/helloworld/src-tauri/gen/schemas/capabilities.json b/examples/helloworld/src-tauri/gen/schemas/capabilities.json deleted file mode 100644 index ab8512e..0000000 --- a/examples/helloworld/src-tauri/gen/schemas/capabilities.json +++ /dev/null @@ -1 +0,0 @@ -{"main-capability":{"identifier":"main-capability","description":"Capability for the main window","context":"local","windows":["main"],"permissions":["aptabase:allow-track-event"],"platforms":["linux","macOS","windows","android","iOS"]}} \ No newline at end of file diff --git a/examples/helloworld/src-tauri/gen/schemas/desktop-schema.json b/examples/helloworld/src-tauri/gen/schemas/desktop-schema.json deleted file mode 100644 index e42d396..0000000 --- a/examples/helloworld/src-tauri/gen/schemas/desktop-schema.json +++ /dev/null @@ -1 +0,0 @@ -{schema_str} \ No newline at end of file diff --git a/examples/helloworld/src-tauri/gen/schemas/macOS-schema.json b/examples/helloworld/src-tauri/gen/schemas/macOS-schema.json deleted file mode 100644 index e42d396..0000000 --- a/examples/helloworld/src-tauri/gen/schemas/macOS-schema.json +++ /dev/null @@ -1 +0,0 @@ -{schema_str} \ No newline at end of file diff --git a/examples/helloworld/src-tauri/gen/schemas/plugin-manifests.json b/examples/helloworld/src-tauri/gen/schemas/plugin-manifests.json deleted file mode 100644 index fc6adbe..0000000 --- a/examples/helloworld/src-tauri/gen/schemas/plugin-manifests.json +++ /dev/null @@ -1 +0,0 @@ -{"app":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","permissions":["allow-version","allow-name","allow-tauri-version"]},"permissions":{"allow-app-hide":{"version":null,"identifier":"allow-app-hide","description":"Enables the app_hide command without any pre-configured scope.","commands":{"allow":["app_hide"],"deny":[]},"scope":{}},"allow-app-show":{"version":null,"identifier":"allow-app-show","description":"Enables the app_show command without any pre-configured scope.","commands":{"allow":["app_show"],"deny":[]},"scope":{}},"allow-name":{"version":null,"identifier":"allow-name","description":"Enables the name command without any pre-configured scope.","commands":{"allow":["name"],"deny":[]},"scope":{}},"allow-tauri-version":{"version":null,"identifier":"allow-tauri-version","description":"Enables the tauri_version command without any pre-configured scope.","commands":{"allow":["tauri_version"],"deny":[]},"scope":{}},"allow-version":{"version":null,"identifier":"allow-version","description":"Enables the version command without any pre-configured scope.","commands":{"allow":["version"],"deny":[]},"scope":{}},"deny-app-hide":{"version":null,"identifier":"deny-app-hide","description":"Denies the app_hide command without any pre-configured scope.","commands":{"allow":[],"deny":["app_hide"]},"scope":{}},"deny-app-show":{"version":null,"identifier":"deny-app-show","description":"Denies the app_show command without any pre-configured scope.","commands":{"allow":[],"deny":["app_show"]},"scope":{}},"deny-name":{"version":null,"identifier":"deny-name","description":"Denies the name command without any pre-configured scope.","commands":{"allow":[],"deny":["name"]},"scope":{}},"deny-tauri-version":{"version":null,"identifier":"deny-tauri-version","description":"Denies the tauri_version command without any pre-configured scope.","commands":{"allow":[],"deny":["tauri_version"]},"scope":{}},"deny-version":{"version":null,"identifier":"deny-version","description":"Denies the version command without any pre-configured scope.","commands":{"allow":[],"deny":["version"]},"scope":{}}},"permission_sets":{},"global_scope_schema":null},"aptabase":{"default_permission":null,"permissions":{"allow-track-event":{"version":null,"identifier":"allow-track-event","description":"Enables the track_event command without any pre-configured scope.","commands":{"allow":["track_event"],"deny":[]},"scope":{}},"deny-track-event":{"version":null,"identifier":"deny-track-event","description":"Denies the track_event command without any pre-configured scope.","commands":{"allow":[],"deny":["track_event"]},"scope":{}}},"permission_sets":{},"global_scope_schema":null},"event":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","permissions":["allow-listen","allow-unlisten","allow-emit","allow-emit-to"]},"permissions":{"allow-emit":{"version":null,"identifier":"allow-emit","description":"Enables the emit command without any pre-configured scope.","commands":{"allow":["emit"],"deny":[]},"scope":{}},"allow-emit-to":{"version":null,"identifier":"allow-emit-to","description":"Enables the emit_to command without any pre-configured scope.","commands":{"allow":["emit_to"],"deny":[]},"scope":{}},"allow-listen":{"version":null,"identifier":"allow-listen","description":"Enables the listen command without any pre-configured scope.","commands":{"allow":["listen"],"deny":[]},"scope":{}},"allow-unlisten":{"version":null,"identifier":"allow-unlisten","description":"Enables the unlisten command without any pre-configured scope.","commands":{"allow":["unlisten"],"deny":[]},"scope":{}},"deny-emit":{"version":null,"identifier":"deny-emit","description":"Denies the emit command without any pre-configured scope.","commands":{"allow":[],"deny":["emit"]},"scope":{}},"deny-emit-to":{"version":null,"identifier":"deny-emit-to","description":"Denies the emit_to command without any pre-configured scope.","commands":{"allow":[],"deny":["emit_to"]},"scope":{}},"deny-listen":{"version":null,"identifier":"deny-listen","description":"Denies the listen command without any pre-configured scope.","commands":{"allow":[],"deny":["listen"]},"scope":{}},"deny-unlisten":{"version":null,"identifier":"deny-unlisten","description":"Denies the unlisten command without any pre-configured scope.","commands":{"allow":[],"deny":["unlisten"]},"scope":{}}},"permission_sets":{},"global_scope_schema":null},"menu":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","permissions":[]},"permissions":{"allow-append":{"version":null,"identifier":"allow-append","description":"Enables the append command without any pre-configured scope.","commands":{"allow":["append"],"deny":[]},"scope":{}},"allow-create-default":{"version":null,"identifier":"allow-create-default","description":"Enables the create_default command without any pre-configured scope.","commands":{"allow":["create_default"],"deny":[]},"scope":{}},"allow-get":{"version":null,"identifier":"allow-get","description":"Enables the get command without any pre-configured scope.","commands":{"allow":["get"],"deny":[]},"scope":{}},"allow-insert":{"version":null,"identifier":"allow-insert","description":"Enables the insert command without any pre-configured scope.","commands":{"allow":["insert"],"deny":[]},"scope":{}},"allow-is-checked":{"version":null,"identifier":"allow-is-checked","description":"Enables the is_checked command without any pre-configured scope.","commands":{"allow":["is_checked"],"deny":[]},"scope":{}},"allow-is-enabled":{"version":null,"identifier":"allow-is-enabled","description":"Enables the is_enabled command without any pre-configured scope.","commands":{"allow":["is_enabled"],"deny":[]},"scope":{}},"allow-items":{"version":null,"identifier":"allow-items","description":"Enables the items command without any pre-configured scope.","commands":{"allow":["items"],"deny":[]},"scope":{}},"allow-new":{"version":null,"identifier":"allow-new","description":"Enables the new command without any pre-configured scope.","commands":{"allow":["new"],"deny":[]},"scope":{}},"allow-popup":{"version":null,"identifier":"allow-popup","description":"Enables the popup command without any pre-configured scope.","commands":{"allow":["popup"],"deny":[]},"scope":{}},"allow-prepend":{"version":null,"identifier":"allow-prepend","description":"Enables the prepend command without any pre-configured scope.","commands":{"allow":["prepend"],"deny":[]},"scope":{}},"allow-remove":{"version":null,"identifier":"allow-remove","description":"Enables the remove command without any pre-configured scope.","commands":{"allow":["remove"],"deny":[]},"scope":{}},"allow-remove-at":{"version":null,"identifier":"allow-remove-at","description":"Enables the remove_at command without any pre-configured scope.","commands":{"allow":["remove_at"],"deny":[]},"scope":{}},"allow-set-accelerator":{"version":null,"identifier":"allow-set-accelerator","description":"Enables the set_accelerator command without any pre-configured scope.","commands":{"allow":["set_accelerator"],"deny":[]},"scope":{}},"allow-set-as-app-menu":{"version":null,"identifier":"allow-set-as-app-menu","description":"Enables the set_as_app_menu command without any pre-configured scope.","commands":{"allow":["set_as_app_menu"],"deny":[]},"scope":{}},"allow-set-as-help-menu-for-nsapp":{"version":null,"identifier":"allow-set-as-help-menu-for-nsapp","description":"Enables the set_as_help_menu_for_nsapp command without any pre-configured scope.","commands":{"allow":["set_as_help_menu_for_nsapp"],"deny":[]},"scope":{}},"allow-set-as-window-menu":{"version":null,"identifier":"allow-set-as-window-menu","description":"Enables the set_as_window_menu command without any pre-configured scope.","commands":{"allow":["set_as_window_menu"],"deny":[]},"scope":{}},"allow-set-as-windows-menu-for-nsapp":{"version":null,"identifier":"allow-set-as-windows-menu-for-nsapp","description":"Enables the set_as_windows_menu_for_nsapp command without any pre-configured scope.","commands":{"allow":["set_as_windows_menu_for_nsapp"],"deny":[]},"scope":{}},"allow-set-checked":{"version":null,"identifier":"allow-set-checked","description":"Enables the set_checked command without any pre-configured scope.","commands":{"allow":["set_checked"],"deny":[]},"scope":{}},"allow-set-enabled":{"version":null,"identifier":"allow-set-enabled","description":"Enables the set_enabled command without any pre-configured scope.","commands":{"allow":["set_enabled"],"deny":[]},"scope":{}},"allow-set-icon":{"version":null,"identifier":"allow-set-icon","description":"Enables the set_icon command without any pre-configured scope.","commands":{"allow":["set_icon"],"deny":[]},"scope":{}},"allow-set-text":{"version":null,"identifier":"allow-set-text","description":"Enables the set_text command without any pre-configured scope.","commands":{"allow":["set_text"],"deny":[]},"scope":{}},"allow-text":{"version":null,"identifier":"allow-text","description":"Enables the text command without any pre-configured scope.","commands":{"allow":["text"],"deny":[]},"scope":{}},"deny-append":{"version":null,"identifier":"deny-append","description":"Denies the append command without any pre-configured scope.","commands":{"allow":[],"deny":["append"]},"scope":{}},"deny-create-default":{"version":null,"identifier":"deny-create-default","description":"Denies the create_default command without any pre-configured scope.","commands":{"allow":[],"deny":["create_default"]},"scope":{}},"deny-get":{"version":null,"identifier":"deny-get","description":"Denies the get command without any pre-configured scope.","commands":{"allow":[],"deny":["get"]},"scope":{}},"deny-insert":{"version":null,"identifier":"deny-insert","description":"Denies the insert command without any pre-configured scope.","commands":{"allow":[],"deny":["insert"]},"scope":{}},"deny-is-checked":{"version":null,"identifier":"deny-is-checked","description":"Denies the is_checked command without any pre-configured scope.","commands":{"allow":[],"deny":["is_checked"]},"scope":{}},"deny-is-enabled":{"version":null,"identifier":"deny-is-enabled","description":"Denies the is_enabled command without any pre-configured scope.","commands":{"allow":[],"deny":["is_enabled"]},"scope":{}},"deny-items":{"version":null,"identifier":"deny-items","description":"Denies the items command without any pre-configured scope.","commands":{"allow":[],"deny":["items"]},"scope":{}},"deny-new":{"version":null,"identifier":"deny-new","description":"Denies the new command without any pre-configured scope.","commands":{"allow":[],"deny":["new"]},"scope":{}},"deny-popup":{"version":null,"identifier":"deny-popup","description":"Denies the popup command without any pre-configured scope.","commands":{"allow":[],"deny":["popup"]},"scope":{}},"deny-prepend":{"version":null,"identifier":"deny-prepend","description":"Denies the prepend command without any pre-configured scope.","commands":{"allow":[],"deny":["prepend"]},"scope":{}},"deny-remove":{"version":null,"identifier":"deny-remove","description":"Denies the remove command without any pre-configured scope.","commands":{"allow":[],"deny":["remove"]},"scope":{}},"deny-remove-at":{"version":null,"identifier":"deny-remove-at","description":"Denies the remove_at command without any pre-configured scope.","commands":{"allow":[],"deny":["remove_at"]},"scope":{}},"deny-set-accelerator":{"version":null,"identifier":"deny-set-accelerator","description":"Denies the set_accelerator command without any pre-configured scope.","commands":{"allow":[],"deny":["set_accelerator"]},"scope":{}},"deny-set-as-app-menu":{"version":null,"identifier":"deny-set-as-app-menu","description":"Denies the set_as_app_menu command without any pre-configured scope.","commands":{"allow":[],"deny":["set_as_app_menu"]},"scope":{}},"deny-set-as-help-menu-for-nsapp":{"version":null,"identifier":"deny-set-as-help-menu-for-nsapp","description":"Denies the set_as_help_menu_for_nsapp command without any pre-configured scope.","commands":{"allow":[],"deny":["set_as_help_menu_for_nsapp"]},"scope":{}},"deny-set-as-window-menu":{"version":null,"identifier":"deny-set-as-window-menu","description":"Denies the set_as_window_menu command without any pre-configured scope.","commands":{"allow":[],"deny":["set_as_window_menu"]},"scope":{}},"deny-set-as-windows-menu-for-nsapp":{"version":null,"identifier":"deny-set-as-windows-menu-for-nsapp","description":"Denies the set_as_windows_menu_for_nsapp command without any pre-configured scope.","commands":{"allow":[],"deny":["set_as_windows_menu_for_nsapp"]},"scope":{}},"deny-set-checked":{"version":null,"identifier":"deny-set-checked","description":"Denies the set_checked command without any pre-configured scope.","commands":{"allow":[],"deny":["set_checked"]},"scope":{}},"deny-set-enabled":{"version":null,"identifier":"deny-set-enabled","description":"Denies the set_enabled command without any pre-configured scope.","commands":{"allow":[],"deny":["set_enabled"]},"scope":{}},"deny-set-icon":{"version":null,"identifier":"deny-set-icon","description":"Denies the set_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_icon"]},"scope":{}},"deny-set-text":{"version":null,"identifier":"deny-set-text","description":"Denies the set_text command without any pre-configured scope.","commands":{"allow":[],"deny":["set_text"]},"scope":{}},"deny-text":{"version":null,"identifier":"deny-text","description":"Denies the text command without any pre-configured scope.","commands":{"allow":[],"deny":["text"]},"scope":{}}},"permission_sets":{},"global_scope_schema":null},"path":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","permissions":["allow-resolve-directory","allow-resolve","allow-normalize","allow-join","allow-dirname","allow-extname","allow-basename","allow-is-absolute"]},"permissions":{"allow-basename":{"version":null,"identifier":"allow-basename","description":"Enables the basename command without any pre-configured scope.","commands":{"allow":["basename"],"deny":[]},"scope":{}},"allow-dirname":{"version":null,"identifier":"allow-dirname","description":"Enables the dirname command without any pre-configured scope.","commands":{"allow":["dirname"],"deny":[]},"scope":{}},"allow-extname":{"version":null,"identifier":"allow-extname","description":"Enables the extname command without any pre-configured scope.","commands":{"allow":["extname"],"deny":[]},"scope":{}},"allow-is-absolute":{"version":null,"identifier":"allow-is-absolute","description":"Enables the is_absolute command without any pre-configured scope.","commands":{"allow":["is_absolute"],"deny":[]},"scope":{}},"allow-join":{"version":null,"identifier":"allow-join","description":"Enables the join command without any pre-configured scope.","commands":{"allow":["join"],"deny":[]},"scope":{}},"allow-normalize":{"version":null,"identifier":"allow-normalize","description":"Enables the normalize command without any pre-configured scope.","commands":{"allow":["normalize"],"deny":[]},"scope":{}},"allow-resolve":{"version":null,"identifier":"allow-resolve","description":"Enables the resolve command without any pre-configured scope.","commands":{"allow":["resolve"],"deny":[]},"scope":{}},"allow-resolve-directory":{"version":null,"identifier":"allow-resolve-directory","description":"Enables the resolve_directory command without any pre-configured scope.","commands":{"allow":["resolve_directory"],"deny":[]},"scope":{}},"deny-basename":{"version":null,"identifier":"deny-basename","description":"Denies the basename command without any pre-configured scope.","commands":{"allow":[],"deny":["basename"]},"scope":{}},"deny-dirname":{"version":null,"identifier":"deny-dirname","description":"Denies the dirname command without any pre-configured scope.","commands":{"allow":[],"deny":["dirname"]},"scope":{}},"deny-extname":{"version":null,"identifier":"deny-extname","description":"Denies the extname command without any pre-configured scope.","commands":{"allow":[],"deny":["extname"]},"scope":{}},"deny-is-absolute":{"version":null,"identifier":"deny-is-absolute","description":"Denies the is_absolute command without any pre-configured scope.","commands":{"allow":[],"deny":["is_absolute"]},"scope":{}},"deny-join":{"version":null,"identifier":"deny-join","description":"Denies the join command without any pre-configured scope.","commands":{"allow":[],"deny":["join"]},"scope":{}},"deny-normalize":{"version":null,"identifier":"deny-normalize","description":"Denies the normalize command without any pre-configured scope.","commands":{"allow":[],"deny":["normalize"]},"scope":{}},"deny-resolve":{"version":null,"identifier":"deny-resolve","description":"Denies the resolve command without any pre-configured scope.","commands":{"allow":[],"deny":["resolve"]},"scope":{}},"deny-resolve-directory":{"version":null,"identifier":"deny-resolve-directory","description":"Denies the resolve_directory command without any pre-configured scope.","commands":{"allow":[],"deny":["resolve_directory"]},"scope":{}}},"permission_sets":{},"global_scope_schema":null},"resources":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","permissions":["allow-close"]},"permissions":{"allow-close":{"version":null,"identifier":"allow-close","description":"Enables the close command without any pre-configured scope.","commands":{"allow":["close"],"deny":[]},"scope":{}},"deny-close":{"version":null,"identifier":"deny-close","description":"Denies the close command without any pre-configured scope.","commands":{"allow":[],"deny":["close"]},"scope":{}}},"permission_sets":{},"global_scope_schema":null},"tray":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","permissions":[]},"permissions":{"allow-new":{"version":null,"identifier":"allow-new","description":"Enables the new command without any pre-configured scope.","commands":{"allow":["new"],"deny":[]},"scope":{}},"allow-set-icon":{"version":null,"identifier":"allow-set-icon","description":"Enables the set_icon command without any pre-configured scope.","commands":{"allow":["set_icon"],"deny":[]},"scope":{}},"allow-set-icon-as-template":{"version":null,"identifier":"allow-set-icon-as-template","description":"Enables the set_icon_as_template command without any pre-configured scope.","commands":{"allow":["set_icon_as_template"],"deny":[]},"scope":{}},"allow-set-menu":{"version":null,"identifier":"allow-set-menu","description":"Enables the set_menu command without any pre-configured scope.","commands":{"allow":["set_menu"],"deny":[]},"scope":{}},"allow-set-show-menu-on-left-click":{"version":null,"identifier":"allow-set-show-menu-on-left-click","description":"Enables the set_show_menu_on_left_click command without any pre-configured scope.","commands":{"allow":["set_show_menu_on_left_click"],"deny":[]},"scope":{}},"allow-set-temp-dir-path":{"version":null,"identifier":"allow-set-temp-dir-path","description":"Enables the set_temp_dir_path command without any pre-configured scope.","commands":{"allow":["set_temp_dir_path"],"deny":[]},"scope":{}},"allow-set-title":{"version":null,"identifier":"allow-set-title","description":"Enables the set_title command without any pre-configured scope.","commands":{"allow":["set_title"],"deny":[]},"scope":{}},"allow-set-tooltip":{"version":null,"identifier":"allow-set-tooltip","description":"Enables the set_tooltip command without any pre-configured scope.","commands":{"allow":["set_tooltip"],"deny":[]},"scope":{}},"allow-set-visible":{"version":null,"identifier":"allow-set-visible","description":"Enables the set_visible command without any pre-configured scope.","commands":{"allow":["set_visible"],"deny":[]},"scope":{}},"deny-new":{"version":null,"identifier":"deny-new","description":"Denies the new command without any pre-configured scope.","commands":{"allow":[],"deny":["new"]},"scope":{}},"deny-set-icon":{"version":null,"identifier":"deny-set-icon","description":"Denies the set_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_icon"]},"scope":{}},"deny-set-icon-as-template":{"version":null,"identifier":"deny-set-icon-as-template","description":"Denies the set_icon_as_template command without any pre-configured scope.","commands":{"allow":[],"deny":["set_icon_as_template"]},"scope":{}},"deny-set-menu":{"version":null,"identifier":"deny-set-menu","description":"Denies the set_menu command without any pre-configured scope.","commands":{"allow":[],"deny":["set_menu"]},"scope":{}},"deny-set-show-menu-on-left-click":{"version":null,"identifier":"deny-set-show-menu-on-left-click","description":"Denies the set_show_menu_on_left_click command without any pre-configured scope.","commands":{"allow":[],"deny":["set_show_menu_on_left_click"]},"scope":{}},"deny-set-temp-dir-path":{"version":null,"identifier":"deny-set-temp-dir-path","description":"Denies the set_temp_dir_path command without any pre-configured scope.","commands":{"allow":[],"deny":["set_temp_dir_path"]},"scope":{}},"deny-set-title":{"version":null,"identifier":"deny-set-title","description":"Denies the set_title command without any pre-configured scope.","commands":{"allow":[],"deny":["set_title"]},"scope":{}},"deny-set-tooltip":{"version":null,"identifier":"deny-set-tooltip","description":"Denies the set_tooltip command without any pre-configured scope.","commands":{"allow":[],"deny":["set_tooltip"]},"scope":{}},"deny-set-visible":{"version":null,"identifier":"deny-set-visible","description":"Denies the set_visible command without any pre-configured scope.","commands":{"allow":[],"deny":["set_visible"]},"scope":{}}},"permission_sets":{},"global_scope_schema":null},"webview":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","permissions":["allow-webview-position","allow-webview-size","allow-internal-toggle-devtools"]},"permissions":{"allow-create-webview":{"version":null,"identifier":"allow-create-webview","description":"Enables the create_webview command without any pre-configured scope.","commands":{"allow":["create_webview"],"deny":[]},"scope":{}},"allow-create-webview-window":{"version":null,"identifier":"allow-create-webview-window","description":"Enables the create_webview_window command without any pre-configured scope.","commands":{"allow":["create_webview_window"],"deny":[]},"scope":{}},"allow-internal-toggle-devtools":{"version":null,"identifier":"allow-internal-toggle-devtools","description":"Enables the internal_toggle_devtools command without any pre-configured scope.","commands":{"allow":["internal_toggle_devtools"],"deny":[]},"scope":{}},"allow-print":{"version":null,"identifier":"allow-print","description":"Enables the print command without any pre-configured scope.","commands":{"allow":["print"],"deny":[]},"scope":{}},"allow-set-webview-focus":{"version":null,"identifier":"allow-set-webview-focus","description":"Enables the set_webview_focus command without any pre-configured scope.","commands":{"allow":["set_webview_focus"],"deny":[]},"scope":{}},"allow-set-webview-position":{"version":null,"identifier":"allow-set-webview-position","description":"Enables the set_webview_position command without any pre-configured scope.","commands":{"allow":["set_webview_position"],"deny":[]},"scope":{}},"allow-set-webview-size":{"version":null,"identifier":"allow-set-webview-size","description":"Enables the set_webview_size command without any pre-configured scope.","commands":{"allow":["set_webview_size"],"deny":[]},"scope":{}},"allow-webview-close":{"version":null,"identifier":"allow-webview-close","description":"Enables the webview_close command without any pre-configured scope.","commands":{"allow":["webview_close"],"deny":[]},"scope":{}},"allow-webview-position":{"version":null,"identifier":"allow-webview-position","description":"Enables the webview_position command without any pre-configured scope.","commands":{"allow":["webview_position"],"deny":[]},"scope":{}},"allow-webview-size":{"version":null,"identifier":"allow-webview-size","description":"Enables the webview_size command without any pre-configured scope.","commands":{"allow":["webview_size"],"deny":[]},"scope":{}},"deny-create-webview":{"version":null,"identifier":"deny-create-webview","description":"Denies the create_webview command without any pre-configured scope.","commands":{"allow":[],"deny":["create_webview"]},"scope":{}},"deny-create-webview-window":{"version":null,"identifier":"deny-create-webview-window","description":"Denies the create_webview_window command without any pre-configured scope.","commands":{"allow":[],"deny":["create_webview_window"]},"scope":{}},"deny-internal-toggle-devtools":{"version":null,"identifier":"deny-internal-toggle-devtools","description":"Denies the internal_toggle_devtools command without any pre-configured scope.","commands":{"allow":[],"deny":["internal_toggle_devtools"]},"scope":{}},"deny-print":{"version":null,"identifier":"deny-print","description":"Denies the print command without any pre-configured scope.","commands":{"allow":[],"deny":["print"]},"scope":{}},"deny-set-webview-focus":{"version":null,"identifier":"deny-set-webview-focus","description":"Denies the set_webview_focus command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_focus"]},"scope":{}},"deny-set-webview-position":{"version":null,"identifier":"deny-set-webview-position","description":"Denies the set_webview_position command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_position"]},"scope":{}},"deny-set-webview-size":{"version":null,"identifier":"deny-set-webview-size","description":"Denies the set_webview_size command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_size"]},"scope":{}},"deny-webview-close":{"version":null,"identifier":"deny-webview-close","description":"Denies the webview_close command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_close"]},"scope":{}},"deny-webview-position":{"version":null,"identifier":"deny-webview-position","description":"Denies the webview_position command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_position"]},"scope":{}},"deny-webview-size":{"version":null,"identifier":"deny-webview-size","description":"Denies the webview_size command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_size"]},"scope":{}}},"permission_sets":{},"global_scope_schema":null},"window":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","permissions":["allow-scale-factor","allow-inner-position","allow-outer-position","allow-inner-size","allow-outer-size","allow-is-fullscreen","allow-is-minimized","allow-is-maximized","allow-is-focused","allow-is-decorated","allow-is-resizable","allow-is-maximizable","allow-is-minimizable","allow-is-closable","allow-is-visible","allow-title","allow-current-monitor","allow-primary-monitor","allow-available-monitors","allow-theme","allow-internal-toggle-maximize","allow-internal-on-mousemove","allow-internal-on-mousedown"]},"permissions":{"allow-available-monitors":{"version":null,"identifier":"allow-available-monitors","description":"Enables the available_monitors command without any pre-configured scope.","commands":{"allow":["available_monitors"],"deny":[]},"scope":{}},"allow-center":{"version":null,"identifier":"allow-center","description":"Enables the center command without any pre-configured scope.","commands":{"allow":["center"],"deny":[]},"scope":{}},"allow-close":{"version":null,"identifier":"allow-close","description":"Enables the close command without any pre-configured scope.","commands":{"allow":["close"],"deny":[]},"scope":{}},"allow-create":{"version":null,"identifier":"allow-create","description":"Enables the create command without any pre-configured scope.","commands":{"allow":["create"],"deny":[]},"scope":{}},"allow-current-monitor":{"version":null,"identifier":"allow-current-monitor","description":"Enables the current_monitor command without any pre-configured scope.","commands":{"allow":["current_monitor"],"deny":[]},"scope":{}},"allow-destroy":{"version":null,"identifier":"allow-destroy","description":"Enables the destroy command without any pre-configured scope.","commands":{"allow":["destroy"],"deny":[]},"scope":{}},"allow-hide":{"version":null,"identifier":"allow-hide","description":"Enables the hide command without any pre-configured scope.","commands":{"allow":["hide"],"deny":[]},"scope":{}},"allow-inner-position":{"version":null,"identifier":"allow-inner-position","description":"Enables the inner_position command without any pre-configured scope.","commands":{"allow":["inner_position"],"deny":[]},"scope":{}},"allow-inner-size":{"version":null,"identifier":"allow-inner-size","description":"Enables the inner_size command without any pre-configured scope.","commands":{"allow":["inner_size"],"deny":[]},"scope":{}},"allow-internal-on-mousedown":{"version":null,"identifier":"allow-internal-on-mousedown","description":"Enables the internal_on_mousedown command without any pre-configured scope.","commands":{"allow":["internal_on_mousedown"],"deny":[]},"scope":{}},"allow-internal-on-mousemove":{"version":null,"identifier":"allow-internal-on-mousemove","description":"Enables the internal_on_mousemove command without any pre-configured scope.","commands":{"allow":["internal_on_mousemove"],"deny":[]},"scope":{}},"allow-internal-toggle-maximize":{"version":null,"identifier":"allow-internal-toggle-maximize","description":"Enables the internal_toggle_maximize command without any pre-configured scope.","commands":{"allow":["internal_toggle_maximize"],"deny":[]},"scope":{}},"allow-is-closable":{"version":null,"identifier":"allow-is-closable","description":"Enables the is_closable command without any pre-configured scope.","commands":{"allow":["is_closable"],"deny":[]},"scope":{}},"allow-is-decorated":{"version":null,"identifier":"allow-is-decorated","description":"Enables the is_decorated command without any pre-configured scope.","commands":{"allow":["is_decorated"],"deny":[]},"scope":{}},"allow-is-focused":{"version":null,"identifier":"allow-is-focused","description":"Enables the is_focused command without any pre-configured scope.","commands":{"allow":["is_focused"],"deny":[]},"scope":{}},"allow-is-fullscreen":{"version":null,"identifier":"allow-is-fullscreen","description":"Enables the is_fullscreen command without any pre-configured scope.","commands":{"allow":["is_fullscreen"],"deny":[]},"scope":{}},"allow-is-maximizable":{"version":null,"identifier":"allow-is-maximizable","description":"Enables the is_maximizable command without any pre-configured scope.","commands":{"allow":["is_maximizable"],"deny":[]},"scope":{}},"allow-is-maximized":{"version":null,"identifier":"allow-is-maximized","description":"Enables the is_maximized command without any pre-configured scope.","commands":{"allow":["is_maximized"],"deny":[]},"scope":{}},"allow-is-minimizable":{"version":null,"identifier":"allow-is-minimizable","description":"Enables the is_minimizable command without any pre-configured scope.","commands":{"allow":["is_minimizable"],"deny":[]},"scope":{}},"allow-is-minimized":{"version":null,"identifier":"allow-is-minimized","description":"Enables the is_minimized command without any pre-configured scope.","commands":{"allow":["is_minimized"],"deny":[]},"scope":{}},"allow-is-resizable":{"version":null,"identifier":"allow-is-resizable","description":"Enables the is_resizable command without any pre-configured scope.","commands":{"allow":["is_resizable"],"deny":[]},"scope":{}},"allow-is-visible":{"version":null,"identifier":"allow-is-visible","description":"Enables the is_visible command without any pre-configured scope.","commands":{"allow":["is_visible"],"deny":[]},"scope":{}},"allow-maximize":{"version":null,"identifier":"allow-maximize","description":"Enables the maximize command without any pre-configured scope.","commands":{"allow":["maximize"],"deny":[]},"scope":{}},"allow-minimize":{"version":null,"identifier":"allow-minimize","description":"Enables the minimize command without any pre-configured scope.","commands":{"allow":["minimize"],"deny":[]},"scope":{}},"allow-outer-position":{"version":null,"identifier":"allow-outer-position","description":"Enables the outer_position command without any pre-configured scope.","commands":{"allow":["outer_position"],"deny":[]},"scope":{}},"allow-outer-size":{"version":null,"identifier":"allow-outer-size","description":"Enables the outer_size command without any pre-configured scope.","commands":{"allow":["outer_size"],"deny":[]},"scope":{}},"allow-primary-monitor":{"version":null,"identifier":"allow-primary-monitor","description":"Enables the primary_monitor command without any pre-configured scope.","commands":{"allow":["primary_monitor"],"deny":[]},"scope":{}},"allow-request-user-attention":{"version":null,"identifier":"allow-request-user-attention","description":"Enables the request_user_attention command without any pre-configured scope.","commands":{"allow":["request_user_attention"],"deny":[]},"scope":{}},"allow-scale-factor":{"version":null,"identifier":"allow-scale-factor","description":"Enables the scale_factor command without any pre-configured scope.","commands":{"allow":["scale_factor"],"deny":[]},"scope":{}},"allow-set-always-on-bottom":{"version":null,"identifier":"allow-set-always-on-bottom","description":"Enables the set_always_on_bottom command without any pre-configured scope.","commands":{"allow":["set_always_on_bottom"],"deny":[]},"scope":{}},"allow-set-always-on-top":{"version":null,"identifier":"allow-set-always-on-top","description":"Enables the set_always_on_top command without any pre-configured scope.","commands":{"allow":["set_always_on_top"],"deny":[]},"scope":{}},"allow-set-closable":{"version":null,"identifier":"allow-set-closable","description":"Enables the set_closable command without any pre-configured scope.","commands":{"allow":["set_closable"],"deny":[]},"scope":{}},"allow-set-content-protected":{"version":null,"identifier":"allow-set-content-protected","description":"Enables the set_content_protected command without any pre-configured scope.","commands":{"allow":["set_content_protected"],"deny":[]},"scope":{}},"allow-set-cursor-grab":{"version":null,"identifier":"allow-set-cursor-grab","description":"Enables the set_cursor_grab command without any pre-configured scope.","commands":{"allow":["set_cursor_grab"],"deny":[]},"scope":{}},"allow-set-cursor-icon":{"version":null,"identifier":"allow-set-cursor-icon","description":"Enables the set_cursor_icon command without any pre-configured scope.","commands":{"allow":["set_cursor_icon"],"deny":[]},"scope":{}},"allow-set-cursor-position":{"version":null,"identifier":"allow-set-cursor-position","description":"Enables the set_cursor_position command without any pre-configured scope.","commands":{"allow":["set_cursor_position"],"deny":[]},"scope":{}},"allow-set-cursor-visible":{"version":null,"identifier":"allow-set-cursor-visible","description":"Enables the set_cursor_visible command without any pre-configured scope.","commands":{"allow":["set_cursor_visible"],"deny":[]},"scope":{}},"allow-set-decorations":{"version":null,"identifier":"allow-set-decorations","description":"Enables the set_decorations command without any pre-configured scope.","commands":{"allow":["set_decorations"],"deny":[]},"scope":{}},"allow-set-effects":{"version":null,"identifier":"allow-set-effects","description":"Enables the set_effects command without any pre-configured scope.","commands":{"allow":["set_effects"],"deny":[]},"scope":{}},"allow-set-focus":{"version":null,"identifier":"allow-set-focus","description":"Enables the set_focus command without any pre-configured scope.","commands":{"allow":["set_focus"],"deny":[]},"scope":{}},"allow-set-fullscreen":{"version":null,"identifier":"allow-set-fullscreen","description":"Enables the set_fullscreen command without any pre-configured scope.","commands":{"allow":["set_fullscreen"],"deny":[]},"scope":{}},"allow-set-icon":{"version":null,"identifier":"allow-set-icon","description":"Enables the set_icon command without any pre-configured scope.","commands":{"allow":["set_icon"],"deny":[]},"scope":{}},"allow-set-ignore-cursor-events":{"version":null,"identifier":"allow-set-ignore-cursor-events","description":"Enables the set_ignore_cursor_events command without any pre-configured scope.","commands":{"allow":["set_ignore_cursor_events"],"deny":[]},"scope":{}},"allow-set-max-size":{"version":null,"identifier":"allow-set-max-size","description":"Enables the set_max_size command without any pre-configured scope.","commands":{"allow":["set_max_size"],"deny":[]},"scope":{}},"allow-set-maximizable":{"version":null,"identifier":"allow-set-maximizable","description":"Enables the set_maximizable command without any pre-configured scope.","commands":{"allow":["set_maximizable"],"deny":[]},"scope":{}},"allow-set-min-size":{"version":null,"identifier":"allow-set-min-size","description":"Enables the set_min_size command without any pre-configured scope.","commands":{"allow":["set_min_size"],"deny":[]},"scope":{}},"allow-set-minimizable":{"version":null,"identifier":"allow-set-minimizable","description":"Enables the set_minimizable command without any pre-configured scope.","commands":{"allow":["set_minimizable"],"deny":[]},"scope":{}},"allow-set-position":{"version":null,"identifier":"allow-set-position","description":"Enables the set_position command without any pre-configured scope.","commands":{"allow":["set_position"],"deny":[]},"scope":{}},"allow-set-progress-bar":{"version":null,"identifier":"allow-set-progress-bar","description":"Enables the set_progress_bar command without any pre-configured scope.","commands":{"allow":["set_progress_bar"],"deny":[]},"scope":{}},"allow-set-resizable":{"version":null,"identifier":"allow-set-resizable","description":"Enables the set_resizable command without any pre-configured scope.","commands":{"allow":["set_resizable"],"deny":[]},"scope":{}},"allow-set-shadow":{"version":null,"identifier":"allow-set-shadow","description":"Enables the set_shadow command without any pre-configured scope.","commands":{"allow":["set_shadow"],"deny":[]},"scope":{}},"allow-set-size":{"version":null,"identifier":"allow-set-size","description":"Enables the set_size command without any pre-configured scope.","commands":{"allow":["set_size"],"deny":[]},"scope":{}},"allow-set-skip-taskbar":{"version":null,"identifier":"allow-set-skip-taskbar","description":"Enables the set_skip_taskbar command without any pre-configured scope.","commands":{"allow":["set_skip_taskbar"],"deny":[]},"scope":{}},"allow-set-title":{"version":null,"identifier":"allow-set-title","description":"Enables the set_title command without any pre-configured scope.","commands":{"allow":["set_title"],"deny":[]},"scope":{}},"allow-set-visible-on-all-workspaces":{"version":null,"identifier":"allow-set-visible-on-all-workspaces","description":"Enables the set_visible_on_all_workspaces command without any pre-configured scope.","commands":{"allow":["set_visible_on_all_workspaces"],"deny":[]},"scope":{}},"allow-show":{"version":null,"identifier":"allow-show","description":"Enables the show command without any pre-configured scope.","commands":{"allow":["show"],"deny":[]},"scope":{}},"allow-start-dragging":{"version":null,"identifier":"allow-start-dragging","description":"Enables the start_dragging command without any pre-configured scope.","commands":{"allow":["start_dragging"],"deny":[]},"scope":{}},"allow-theme":{"version":null,"identifier":"allow-theme","description":"Enables the theme command without any pre-configured scope.","commands":{"allow":["theme"],"deny":[]},"scope":{}},"allow-title":{"version":null,"identifier":"allow-title","description":"Enables the title command without any pre-configured scope.","commands":{"allow":["title"],"deny":[]},"scope":{}},"allow-toggle-maximize":{"version":null,"identifier":"allow-toggle-maximize","description":"Enables the toggle_maximize command without any pre-configured scope.","commands":{"allow":["toggle_maximize"],"deny":[]},"scope":{}},"allow-unmaximize":{"version":null,"identifier":"allow-unmaximize","description":"Enables the unmaximize command without any pre-configured scope.","commands":{"allow":["unmaximize"],"deny":[]},"scope":{}},"allow-unminimize":{"version":null,"identifier":"allow-unminimize","description":"Enables the unminimize command without any pre-configured scope.","commands":{"allow":["unminimize"],"deny":[]},"scope":{}},"deny-available-monitors":{"version":null,"identifier":"deny-available-monitors","description":"Denies the available_monitors command without any pre-configured scope.","commands":{"allow":[],"deny":["available_monitors"]},"scope":{}},"deny-center":{"version":null,"identifier":"deny-center","description":"Denies the center command without any pre-configured scope.","commands":{"allow":[],"deny":["center"]},"scope":{}},"deny-close":{"version":null,"identifier":"deny-close","description":"Denies the close command without any pre-configured scope.","commands":{"allow":[],"deny":["close"]},"scope":{}},"deny-create":{"version":null,"identifier":"deny-create","description":"Denies the create command without any pre-configured scope.","commands":{"allow":[],"deny":["create"]},"scope":{}},"deny-current-monitor":{"version":null,"identifier":"deny-current-monitor","description":"Denies the current_monitor command without any pre-configured scope.","commands":{"allow":[],"deny":["current_monitor"]},"scope":{}},"deny-destroy":{"version":null,"identifier":"deny-destroy","description":"Denies the destroy command without any pre-configured scope.","commands":{"allow":[],"deny":["destroy"]},"scope":{}},"deny-hide":{"version":null,"identifier":"deny-hide","description":"Denies the hide command without any pre-configured scope.","commands":{"allow":[],"deny":["hide"]},"scope":{}},"deny-inner-position":{"version":null,"identifier":"deny-inner-position","description":"Denies the inner_position command without any pre-configured scope.","commands":{"allow":[],"deny":["inner_position"]},"scope":{}},"deny-inner-size":{"version":null,"identifier":"deny-inner-size","description":"Denies the inner_size command without any pre-configured scope.","commands":{"allow":[],"deny":["inner_size"]},"scope":{}},"deny-internal-on-mousedown":{"version":null,"identifier":"deny-internal-on-mousedown","description":"Denies the internal_on_mousedown command without any pre-configured scope.","commands":{"allow":[],"deny":["internal_on_mousedown"]},"scope":{}},"deny-internal-on-mousemove":{"version":null,"identifier":"deny-internal-on-mousemove","description":"Denies the internal_on_mousemove command without any pre-configured scope.","commands":{"allow":[],"deny":["internal_on_mousemove"]},"scope":{}},"deny-internal-toggle-maximize":{"version":null,"identifier":"deny-internal-toggle-maximize","description":"Denies the internal_toggle_maximize command without any pre-configured scope.","commands":{"allow":[],"deny":["internal_toggle_maximize"]},"scope":{}},"deny-is-closable":{"version":null,"identifier":"deny-is-closable","description":"Denies the is_closable command without any pre-configured scope.","commands":{"allow":[],"deny":["is_closable"]},"scope":{}},"deny-is-decorated":{"version":null,"identifier":"deny-is-decorated","description":"Denies the is_decorated command without any pre-configured scope.","commands":{"allow":[],"deny":["is_decorated"]},"scope":{}},"deny-is-focused":{"version":null,"identifier":"deny-is-focused","description":"Denies the is_focused command without any pre-configured scope.","commands":{"allow":[],"deny":["is_focused"]},"scope":{}},"deny-is-fullscreen":{"version":null,"identifier":"deny-is-fullscreen","description":"Denies the is_fullscreen command without any pre-configured scope.","commands":{"allow":[],"deny":["is_fullscreen"]},"scope":{}},"deny-is-maximizable":{"version":null,"identifier":"deny-is-maximizable","description":"Denies the is_maximizable command without any pre-configured scope.","commands":{"allow":[],"deny":["is_maximizable"]},"scope":{}},"deny-is-maximized":{"version":null,"identifier":"deny-is-maximized","description":"Denies the is_maximized command without any pre-configured scope.","commands":{"allow":[],"deny":["is_maximized"]},"scope":{}},"deny-is-minimizable":{"version":null,"identifier":"deny-is-minimizable","description":"Denies the is_minimizable command without any pre-configured scope.","commands":{"allow":[],"deny":["is_minimizable"]},"scope":{}},"deny-is-minimized":{"version":null,"identifier":"deny-is-minimized","description":"Denies the is_minimized command without any pre-configured scope.","commands":{"allow":[],"deny":["is_minimized"]},"scope":{}},"deny-is-resizable":{"version":null,"identifier":"deny-is-resizable","description":"Denies the is_resizable command without any pre-configured scope.","commands":{"allow":[],"deny":["is_resizable"]},"scope":{}},"deny-is-visible":{"version":null,"identifier":"deny-is-visible","description":"Denies the is_visible command without any pre-configured scope.","commands":{"allow":[],"deny":["is_visible"]},"scope":{}},"deny-maximize":{"version":null,"identifier":"deny-maximize","description":"Denies the maximize command without any pre-configured scope.","commands":{"allow":[],"deny":["maximize"]},"scope":{}},"deny-minimize":{"version":null,"identifier":"deny-minimize","description":"Denies the minimize command without any pre-configured scope.","commands":{"allow":[],"deny":["minimize"]},"scope":{}},"deny-outer-position":{"version":null,"identifier":"deny-outer-position","description":"Denies the outer_position command without any pre-configured scope.","commands":{"allow":[],"deny":["outer_position"]},"scope":{}},"deny-outer-size":{"version":null,"identifier":"deny-outer-size","description":"Denies the outer_size command without any pre-configured scope.","commands":{"allow":[],"deny":["outer_size"]},"scope":{}},"deny-primary-monitor":{"version":null,"identifier":"deny-primary-monitor","description":"Denies the primary_monitor command without any pre-configured scope.","commands":{"allow":[],"deny":["primary_monitor"]},"scope":{}},"deny-request-user-attention":{"version":null,"identifier":"deny-request-user-attention","description":"Denies the request_user_attention command without any pre-configured scope.","commands":{"allow":[],"deny":["request_user_attention"]},"scope":{}},"deny-scale-factor":{"version":null,"identifier":"deny-scale-factor","description":"Denies the scale_factor command without any pre-configured scope.","commands":{"allow":[],"deny":["scale_factor"]},"scope":{}},"deny-set-always-on-bottom":{"version":null,"identifier":"deny-set-always-on-bottom","description":"Denies the set_always_on_bottom command without any pre-configured scope.","commands":{"allow":[],"deny":["set_always_on_bottom"]},"scope":{}},"deny-set-always-on-top":{"version":null,"identifier":"deny-set-always-on-top","description":"Denies the set_always_on_top command without any pre-configured scope.","commands":{"allow":[],"deny":["set_always_on_top"]},"scope":{}},"deny-set-closable":{"version":null,"identifier":"deny-set-closable","description":"Denies the set_closable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_closable"]},"scope":{}},"deny-set-content-protected":{"version":null,"identifier":"deny-set-content-protected","description":"Denies the set_content_protected command without any pre-configured scope.","commands":{"allow":[],"deny":["set_content_protected"]},"scope":{}},"deny-set-cursor-grab":{"version":null,"identifier":"deny-set-cursor-grab","description":"Denies the set_cursor_grab command without any pre-configured scope.","commands":{"allow":[],"deny":["set_cursor_grab"]},"scope":{}},"deny-set-cursor-icon":{"version":null,"identifier":"deny-set-cursor-icon","description":"Denies the set_cursor_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_cursor_icon"]},"scope":{}},"deny-set-cursor-position":{"version":null,"identifier":"deny-set-cursor-position","description":"Denies the set_cursor_position command without any pre-configured scope.","commands":{"allow":[],"deny":["set_cursor_position"]},"scope":{}},"deny-set-cursor-visible":{"version":null,"identifier":"deny-set-cursor-visible","description":"Denies the set_cursor_visible command without any pre-configured scope.","commands":{"allow":[],"deny":["set_cursor_visible"]},"scope":{}},"deny-set-decorations":{"version":null,"identifier":"deny-set-decorations","description":"Denies the set_decorations command without any pre-configured scope.","commands":{"allow":[],"deny":["set_decorations"]},"scope":{}},"deny-set-effects":{"version":null,"identifier":"deny-set-effects","description":"Denies the set_effects command without any pre-configured scope.","commands":{"allow":[],"deny":["set_effects"]},"scope":{}},"deny-set-focus":{"version":null,"identifier":"deny-set-focus","description":"Denies the set_focus command without any pre-configured scope.","commands":{"allow":[],"deny":["set_focus"]},"scope":{}},"deny-set-fullscreen":{"version":null,"identifier":"deny-set-fullscreen","description":"Denies the set_fullscreen command without any pre-configured scope.","commands":{"allow":[],"deny":["set_fullscreen"]},"scope":{}},"deny-set-icon":{"version":null,"identifier":"deny-set-icon","description":"Denies the set_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_icon"]},"scope":{}},"deny-set-ignore-cursor-events":{"version":null,"identifier":"deny-set-ignore-cursor-events","description":"Denies the set_ignore_cursor_events command without any pre-configured scope.","commands":{"allow":[],"deny":["set_ignore_cursor_events"]},"scope":{}},"deny-set-max-size":{"version":null,"identifier":"deny-set-max-size","description":"Denies the set_max_size command without any pre-configured scope.","commands":{"allow":[],"deny":["set_max_size"]},"scope":{}},"deny-set-maximizable":{"version":null,"identifier":"deny-set-maximizable","description":"Denies the set_maximizable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_maximizable"]},"scope":{}},"deny-set-min-size":{"version":null,"identifier":"deny-set-min-size","description":"Denies the set_min_size command without any pre-configured scope.","commands":{"allow":[],"deny":["set_min_size"]},"scope":{}},"deny-set-minimizable":{"version":null,"identifier":"deny-set-minimizable","description":"Denies the set_minimizable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_minimizable"]},"scope":{}},"deny-set-position":{"version":null,"identifier":"deny-set-position","description":"Denies the set_position command without any pre-configured scope.","commands":{"allow":[],"deny":["set_position"]},"scope":{}},"deny-set-progress-bar":{"version":null,"identifier":"deny-set-progress-bar","description":"Denies the set_progress_bar command without any pre-configured scope.","commands":{"allow":[],"deny":["set_progress_bar"]},"scope":{}},"deny-set-resizable":{"version":null,"identifier":"deny-set-resizable","description":"Denies the set_resizable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_resizable"]},"scope":{}},"deny-set-shadow":{"version":null,"identifier":"deny-set-shadow","description":"Denies the set_shadow command without any pre-configured scope.","commands":{"allow":[],"deny":["set_shadow"]},"scope":{}},"deny-set-size":{"version":null,"identifier":"deny-set-size","description":"Denies the set_size command without any pre-configured scope.","commands":{"allow":[],"deny":["set_size"]},"scope":{}},"deny-set-skip-taskbar":{"version":null,"identifier":"deny-set-skip-taskbar","description":"Denies the set_skip_taskbar command without any pre-configured scope.","commands":{"allow":[],"deny":["set_skip_taskbar"]},"scope":{}},"deny-set-title":{"version":null,"identifier":"deny-set-title","description":"Denies the set_title command without any pre-configured scope.","commands":{"allow":[],"deny":["set_title"]},"scope":{}},"deny-set-visible-on-all-workspaces":{"version":null,"identifier":"deny-set-visible-on-all-workspaces","description":"Denies the set_visible_on_all_workspaces command without any pre-configured scope.","commands":{"allow":[],"deny":["set_visible_on_all_workspaces"]},"scope":{}},"deny-show":{"version":null,"identifier":"deny-show","description":"Denies the show command without any pre-configured scope.","commands":{"allow":[],"deny":["show"]},"scope":{}},"deny-start-dragging":{"version":null,"identifier":"deny-start-dragging","description":"Denies the start_dragging command without any pre-configured scope.","commands":{"allow":[],"deny":["start_dragging"]},"scope":{}},"deny-theme":{"version":null,"identifier":"deny-theme","description":"Denies the theme command without any pre-configured scope.","commands":{"allow":[],"deny":["theme"]},"scope":{}},"deny-title":{"version":null,"identifier":"deny-title","description":"Denies the title command without any pre-configured scope.","commands":{"allow":[],"deny":["title"]},"scope":{}},"deny-toggle-maximize":{"version":null,"identifier":"deny-toggle-maximize","description":"Denies the toggle_maximize command without any pre-configured scope.","commands":{"allow":[],"deny":["toggle_maximize"]},"scope":{}},"deny-unmaximize":{"version":null,"identifier":"deny-unmaximize","description":"Denies the unmaximize command without any pre-configured scope.","commands":{"allow":[],"deny":["unmaximize"]},"scope":{}},"deny-unminimize":{"version":null,"identifier":"deny-unminimize","description":"Denies the unminimize command without any pre-configured scope.","commands":{"allow":[],"deny":["unminimize"]},"scope":{}}},"permission_sets":{},"global_scope_schema":null}} \ No newline at end of file diff --git a/examples/helloworld/src-tauri/icons/128x128.png b/examples/helloworld/src-tauri/icons/128x128.png deleted file mode 100644 index 77e7d2338e9d8ccffc731198dc584906627c903f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11059 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�{Kmc5tGtrHLwPd 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౉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
j#(n6`cTK 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 String { - format!("Hello, {}! You've been greeted from Rust!", name) -} - -#[tauri::command] -fn this_will_panic() { - panic!("I told you!"); -} - -#[cfg_attr(mobile, tauri::mobile_entry_point)] -pub fn run() { - tauri::Builder::default() - .invoke_handler(tauri::generate_handler![greet, this_will_panic]) - .plugin(tauri_plugin_aptabase::Builder::new("A-US-0928558097").with_panic_hook(Box::new(|client, info, msg| { - let location = info.location().map(|loc| format!("{}:{}:{}", loc.file(), loc.line(), loc.column())).unwrap_or_else(|| "".to_string()); - - client.track_event("panic", Some(json!({ - "info": format!("{} ({})", msg, location), - }))); - })).build()) - .run(tauri::generate_context!()) - .expect("error while running tauri application"); -} diff --git a/examples/helloworld/src-tauri/src/main.rs b/examples/helloworld/src-tauri/src/main.rs deleted file mode 100644 index 455963e..0000000 --- a/examples/helloworld/src-tauri/src/main.rs +++ /dev/null @@ -1,6 +0,0 @@ -// Prevents additional console window on Windows in release, DO NOT REMOVE!! -#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] - -fn main() { - tauri_app_lib::run(); -} diff --git a/examples/helloworld/src-tauri/tauri.conf.json b/examples/helloworld/src-tauri/tauri.conf.json deleted file mode 100644 index b028d19..0000000 --- a/examples/helloworld/src-tauri/tauri.conf.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "productName": "tauri-app", - "version": "0.0.0", - "identifier": "com.tauri.dev", - "build": { - "beforeDevCommand": "npm run dev:vite", - "beforeBuildCommand": "npm run build", - "devUrl": "http://localhost:1420", - "frontendDist": "../dist" - }, - "app": { - "withGlobalTauri": false, - "security": { - "csp": null - }, - "windows": [ - { - "fullscreen": false, - "height": 600, - "resizable": true, - "title": "tauri-app", - "width": 800 - } - ] - }, - "bundle": { - "active": true, - "targets": "all", - "icon": [ - "icons/32x32.png", - "icons/128x128.png", - "icons/128x128@2x.png", - "icons/icon.icns", - "icons/icon.ico" - ] - } -} diff --git a/examples/helloworld/src/App.svelte b/examples/helloworld/src/App.svelte deleted file mode 100644 index 126b4a4..0000000 --- a/examples/helloworld/src/App.svelte +++ /dev/null @@ -1,47 +0,0 @@ - - - - - diff --git a/examples/helloworld/src/lib/Greet.svelte b/examples/helloworld/src/lib/Greet.svelte deleted file mode 100644 index 91b9989..0000000 --- a/examples/helloworld/src/lib/Greet.svelte +++ /dev/null @@ -1,24 +0,0 @@ - - -
-
- - -
-

{greetMsg}

-
- diff --git a/examples/helloworld/src/main.js b/examples/helloworld/src/main.js deleted file mode 100644 index 6b4e1a9..0000000 --- a/examples/helloworld/src/main.js +++ /dev/null @@ -1,8 +0,0 @@ -import "./style.css"; -import App from "./App.svelte"; - -const app = new App({ - target: document.getElementById("app"), -}); - -export default app; diff --git a/examples/helloworld/src/style.css b/examples/helloworld/src/style.css deleted file mode 100644 index c0f9e3b..0000000 --- a/examples/helloworld/src/style.css +++ /dev/null @@ -1,102 +0,0 @@ -:root { - font-family: Inter, Avenir, Helvetica, Arial, sans-serif; - font-size: 16px; - line-height: 24px; - font-weight: 400; - - color: #0f0f0f; - background-color: #f6f6f6; - - font-synthesis: none; - text-rendering: optimizeLegibility; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - -webkit-text-size-adjust: 100%; -} - -.container { - margin: 0; - padding-top: 10vh; - display: flex; - flex-direction: column; - justify-content: center; - text-align: center; -} - -.logo { - height: 6em; - padding: 1.5em; - will-change: filter; - transition: 0.75s; -} - -.logo.tauri:hover { - filter: drop-shadow(0 0 2em #24c8db); -} - -.row { - display: flex; - justify-content: center; -} - -a { - font-weight: 500; - color: #646cff; - text-decoration: inherit; -} - -a:hover { - color: #535bf2; -} - -h1 { - text-align: center; -} - -input, -button { - border-radius: 8px; - border: 1px solid transparent; - padding: 0.6em 1.2em; - font-size: 1em; - font-weight: 500; - font-family: inherit; - color: #0f0f0f; - background-color: #ffffff; - transition: border-color 0.25s; - box-shadow: 0 2px 2px rgba(0, 0, 0, 0.2); -} - -button { - cursor: pointer; -} - -button:hover { - border-color: #396cd8; -} - -input, -button { - outline: none; -} - -#greet-input { - margin-right: 5px; -} - -@media (prefers-color-scheme: dark) { - :root { - color: #f6f6f6; - background-color: #2f2f2f; - } - - a:hover { - color: #24c8db; - } - - input, - button { - color: #ffffff; - background-color: #0f0f0f98; - } -} diff --git a/examples/helloworld/src/vite-env.d.ts b/examples/helloworld/src/vite-env.d.ts deleted file mode 100644 index 4078e74..0000000 --- a/examples/helloworld/src/vite-env.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -/// -/// diff --git a/examples/helloworld/vite.config.js b/examples/helloworld/vite.config.js deleted file mode 100644 index 4e1a260..0000000 --- a/examples/helloworld/vite.config.js +++ /dev/null @@ -1,25 +0,0 @@ -import { defineConfig } from "vite"; -import { svelte } from "@sveltejs/vite-plugin-svelte"; -import { internalIpV4Sync } from 'internal-ip' - -const mobile = !!/android|ios/.exec(process.env.TAURI_ENV_PLATFORM); - -// https://vitejs.dev/config/ -export default defineConfig({ - plugins: [svelte()], - - // Vite optons tailored for Tauri development and only applied in `tauri dev` or `tauri build` - // prevent vite from obscuring rust errors - clearScreen: false, - // tauri expects a fixed port, fail if that port is not available - server: { - host: mobile ? "0.0.0.0" : false, - port: 1420, - strictPort: true, - hmr: mobile ? { - protocol: 'ws', - host: internalIpV4Sync(), - port: 1421 - } : undefined, - }, -}) diff --git a/llms.txt b/llms.txt index 5003bae..584c07d 100644 --- a/llms.txt +++ b/llms.txt @@ -1,161 +1,18 @@ -# Aptabase โ€” Tauri Plugin +# Aptabase Rust Client -> Tauri plugin for Aptabase: open-source, privacy-first analytics for desktop and mobile apps built with Tauri v2. +Rust crate: `aptabase-rs` +Repository: https://github.com/aptabase/aptabase-rs -Rust Crate: `tauri-plugin-aptabase` -Install (Rust): `cargo add tauri-plugin-aptabase` -npm Package: `@aptabase/tauri` -Install (JS): `npm add @aptabase/tauri` -Repo: https://github.com/aptabase/tauri-plugin-aptabase +`aptabase-rs` is a framework-independent Aptabase analytics client for Tokio-based Rust applications. -## Overview +## API -Aptabase is an open-source, privacy-first analytics platform. This plugin integrates Aptabase into Tauri v2 apps. +- Construct with `AptabaseClient::new(app_key, app_version)`. +- Use `AptabaseClient::with_options(...)` for a self-hosted host, flush interval, or engine metadata overrides. +- Call `track_event(name, props)` to synchronously enqueue an event. Properties must be absent or a JSON object. +- Await `flush()` before a short-lived process exits. +- Call `start_periodic_flush()` in long-running applications and abort the returned Tokio task during shutdown. -- Get your App Key from the Aptabase dashboard (Settings > Instructions) -- App keys follow the format `A-EU-*` (European) or `A-US-*` (US) -- Only strings and numbers are allowed as custom property values -- All tracking is non-blocking and runs in the background -- No events are tracked automatically โ€” you must call `track_event`/`trackEvent` manually -- The plugin automatically captures OS, app version, locale, and other system properties +Hosted app keys use `A-US-*` or `A-EU-*`. Development keys use `A-DEV-*`. Self-hosted keys use `A-SH-*` and require `InitOptions::host`. -## Important: Dual API (Rust + JavaScript) - -This plugin provides **two APIs** โ€” use whichever fits your app architecture: - -- **Rust API** โ€” use `EventTracker` trait on `App`, `AppHandle`, or `Window` -- **JavaScript API** โ€” use `trackEvent()` from `@aptabase/tauri` in your webview frontend - -Both APIs require the Rust plugin to be registered in your Tauri builder. The JS API calls the Rust backend via Tauri's IPC. - -## Installation - -### 1. Add the Rust crate - -In `src-tauri/Cargo.toml`: - -```toml -[dependencies] -tauri-plugin-aptabase = "1.0.0" -``` - -### 2. Add the JS package (optional, for webview tracking) - -```bash -npm add @aptabase/tauri -``` - -### 3. Register the plugin - -In `src-tauri/src/main.rs` (or `lib.rs`): - -```rust -#[tokio::main] -async fn main() { - tauri::Builder::default() - .plugin(tauri_plugin_aptabase::Builder::new("").build()) - .run(tauri::generate_context!()) - .expect("error while running tauri application"); -} -``` - -### 4. Add ACL permission - -Add `aptabase:allow-track-event` to your Tauri capabilities to allow the JS API to send events. In your `src-tauri/capabilities/*.json`: - -```json -{ - "permissions": ["aptabase:allow-track-event"] -} -``` - -## Track Events (Rust) - -Import the `EventTracker` trait and call `track_event` on `App`, `AppHandle`, or `Window`: - -```rust -use tauri_plugin_aptabase::EventTracker; - -// Event with no properties -app.track_event("app_started", None); - -// Event with properties (serde_json::Value) -app.track_event("screen_view", Some(serde_json::json!({ "name": "Settings" }))); -``` - -Full example with startup and exit events: - -```rust -use tauri_plugin_aptabase::EventTracker; - -#[tokio::main] -async fn main() { - tauri::Builder::default() - .plugin(tauri_plugin_aptabase::Builder::new("").build()) - .setup(|app| { - app.track_event("app_started", None); - Ok(()) - }) - .build(tauri::generate_context!()) - .expect("error while running tauri application") - .run(|handler, event| match event { - tauri::RunEvent::Exit { .. } => { - handler.track_event("app_exited", None); - handler.flush_events_blocking(); - } - _ => {} - }); -} -``` - -## Track Events (JavaScript) - -```js -import { trackEvent } from "@aptabase/tauri"; - -trackEvent("app_started"); -``` - -## Track Events with Properties (JavaScript) - -```js -import { trackEvent } from "@aptabase/tauri"; - -trackEvent("screen_view", { name: "Settings" }); -trackEvent("purchase", { plan: "pro", price: 9.99 }); -``` - -## Configuration - -The `Builder` accepts optional `InitOptions`: - -```rust -use tauri_plugin_aptabase::{Builder, InitOptions}; -use std::time::Duration; - -tauri::Builder::default() - .plugin( - Builder::new("") - .with_options(InitOptions { - host: Some("https://your-self-hosted-instance.com".into()), // self-hosted only (A-SH-* keys) - flush_interval: Some(Duration::from_secs(30)), // custom flush interval - }) - .build() - ) -``` - -- `host` โ€” required only for self-hosted Aptabase instances (app keys starting with `A-SH-*`) -- `flush_interval` โ€” defaults to 60s in release, 2s in debug mode -- Events are batched and flushed periodically; `flush_events_blocking()` forces an immediate flush - -## Platform Notes - -- Requires Tauri v2 (2.x) -- Rust edition 2021, MSRV 1.70 -- The `EventTracker` trait is implemented for `App`, `AppHandle`, and `Window` -- The JS `trackEvent` is async but you don't need to await it -- A custom panic hook can be set via `Builder::with_panic_hook()` to track crashes - -## Cross-Discovery - -For all Aptabase SDKs and documentation, see: https://aptabase.com/llms.txt +The queue is in memory. Transport and HTTP 5xx failures are requeued; other unsuccessful HTTP responses are discarded. No events are tracked automatically. diff --git a/openspec/changes/archive/2026-06-12-create-generic-aptabase-rust-client/.openspec.yaml b/openspec/changes/archive/2026-06-12-create-generic-aptabase-rust-client/.openspec.yaml new file mode 100644 index 0000000..e0c0898 --- /dev/null +++ b/openspec/changes/archive/2026-06-12-create-generic-aptabase-rust-client/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-06-11 diff --git a/openspec/changes/archive/2026-06-12-create-generic-aptabase-rust-client/design.md b/openspec/changes/archive/2026-06-12-create-generic-aptabase-rust-client/design.md new file mode 100644 index 0000000..e634b3e --- /dev/null +++ b/openspec/changes/archive/2026-06-12-create-generic-aptabase-rust-client/design.md @@ -0,0 +1,107 @@ +## Context + +The current root package combines a mostly framework-independent Aptabase client with a Tauri-specific shell. `config.rs`, `client.rs`, and `dispatcher.rs` contain the reusable behavior, while `lib.rs`, `commands.rs`, `build.rs`, permissions, webview bindings, package metadata, and examples make this repository a Tauri plugin. The only direct Tauri dependency inside the otherwise reusable modules is WebView version collection in `sys.rs`. + +This change is an in-place rewrite of the existing source tree. The repository will not become a workspace and will not retain a Tauri adapter. The root package itself becomes the generic `aptabase-rs` crate, and all Tauri-specific code and assets are deleted. + +## Goals / Non-Goals + +**Goals:** + +- Convert the repository's root package into the `aptabase-rs` crate with no Tauri dependency. +- Retain and adapt the existing configuration, session, event construction, queue, batching, retry, and HTTP dispatch code with minimal behavioral changes. +- Provide explicit asynchronous flushing suitable for short-lived CLI applications and optional periodic flushing for long-running applications. +- Preserve the Aptabase ingestion endpoint, headers, batch size, event field names, session behavior, and retry policy. +- Remove every Tauri-specific source file, dependency, build artifact definition, permission, frontend binding, example, and documentation path from this repository. +- Add focused tests around the retained behavior. + +**Non-Goals:** + +- Preserving the Tauri plugin API or maintaining source compatibility for existing plugin consumers. +- Keeping a Tauri adapter, compatibility crate, workspace member, or deprecated shim in this repository. +- Adding automatic command, argument, crash, or usage tracking to CLI applications. +- Persisting unsent events to disk or bounding the in-memory queue in this change. +- Changing Aptabase's event schema, ingestion protocol, or retry semantics. +- Providing a synchronous HTTP implementation independent of Tokio. +- Publishing `aptabase-rs` to crates.io as part of the code change. +- Providing a runtime-independent blocking client or a `reqwest::blocking` transport. + +## Decisions + +### Rewrite the root package in place + +The existing root `Cargo.toml`, `src/`, README, and repository metadata will be rewritten for the `aptabase-rs` package. No Cargo workspace or secondary crate directory will be introduced. + +This directly matches the intended repository ownership and minimizes structural churn. A workspace containing both a generic core and the Tauri plugin was considered and rejected because the Tauri code is no longer wanted in this repository. + +### Delete Tauri integration rather than preserve an adapter + +The Tauri builder, managed state, command, `EventTracker` implementations, panic-hook integration, exit lifecycle integration, plugin build script, permissions, webview bindings, JavaScript package, and Tauri example will be removed. + +Retaining a compatibility adapter was considered, but it would preserve dependencies and maintenance responsibilities that this rewrite is explicitly intended to eliminate. This is therefore a breaking package replacement rather than a backward-compatible extraction. + +### Adapt existing modules instead of reimplementing the protocol + +The generic crate will be based directly on the existing `config`, `client`, `dispatcher`, and `sys` modules. Type and method names may be simplified at the public boundary, but event construction, session generation, batching, and retry code should remain structurally recognizable. + +A ground-up implementation was considered, but it would increase the risk of changing established payload details and offline behavior for little benefit. + +### Make the client own framework-independent lifecycle operations + +The root crate will expose a client that supports: + +- construction from an app key, app version, and options; +- synchronous event enqueueing; +- asynchronous `flush`; +- optional periodic flushing started from an active Tokio runtime. + +Explicit `flush().await` is the CLI shutdown mechanism. Periodic flushing remains available for long-running applications. A runtime-independent blocking client is explicitly outside this proposal and will not be implemented. + +Using only a detached background task was considered, but short-lived CLI commands can terminate before queued events are transmitted. A separate `reqwest::blocking` implementation was also considered, but it would duplicate transport code and expand the initial scope. + +### Preserve tolerant initialization and event behavior + +Invalid or incomplete app-key configuration will continue to disable tracking and log a diagnostic instead of preventing application startup. Event enqueueing will remain a no-op while disabled. Event properties must remain either absent or a JSON object. + +Introducing a fully fallible constructor and a new public error hierarchy was considered. That would be reasonable for a new SDK, but it conflicts with the goal of minimal adaptation. Public error refinement can follow as a separate change. + +### Replace WebView metadata with generic runtime metadata + +The crate will collect OS name, OS version, locale, and debug status without Tauri. It will provide generic Rust runtime or engine defaults and allow callers to override the engine name and version used in `systemProps` and the user agent. + +Removing the engine fields was considered, but preserving the payload shape is lower risk and keeps the retained event construction close to the existing implementation. + +### Keep the current in-memory batching and retry policy + +The dispatcher will continue to send at most 25 events per request, requeue events after network failures or server errors, and discard events rejected with non-server HTTP errors. The queue will remain process-local and in-memory. + +Changing retry policy, adding backoff, persistence, queue limits, or delivery acknowledgements would make this an SDK redesign rather than a low-change rewrite. + +### Do not install global panic hooks + +The generic client will provide tracking and flushing primitives but will not install or manage a global panic hook. Panic handling is application policy, and the existing implementation belongs to the Tauri shell that is being removed. + +## Risks / Trade-offs + +- [The rewrite breaks all existing Tauri plugin consumers] -> Mark the package conversion as breaking and rewrite documentation so the repository no longer claims Tauri compatibility. +- [In-place deletion removes a potentially useful plugin history from the working tree] -> Rely on Git history; do not retain dead compatibility code in the new crate. +- [Adaptation accidentally changes event payloads] -> Add serialization tests using representative events and verify required field names and metadata behavior. +- [CLI processes exit before delivery] -> Document and test explicit `flush().await` as part of normal command shutdown. +- [A periodic task can outlive useful client state or be difficult to stop] -> Return a task handle or cancellation guard so callers can explicitly manage it. +- [Concurrent flushes can reorder delivery] -> Preserve current locking behavior and document that callers should start at most one periodic worker per client. +- [Unbounded in-memory retry queue can grow during long outages] -> Document the inherited limitation and defer queue limits or persistence to a follow-up change. + +## Migration Plan + +1. Rewrite the root package metadata and public module surface for the generic Aptabase crate. +2. Adapt the existing configuration and system metadata modules to remove Tauri references. +3. Adapt the existing client and dispatcher modules and expose the generic tracking and flushing API. +4. Add unit and local HTTP integration tests for retained behavior. +5. Delete Tauri-only Rust code, dependencies, build scripts, permissions, webview bindings, JavaScript metadata, generated files, examples, and Tauri-focused documentation. +6. Run formatting, tests, checks, documentation tests, and dependency inspection on the rewritten root package. + +Rollback consists of reverting the change in Git. No persisted data or external migration is involved. + +## Open Questions + +None. The crates.io package name is `aptabase-rs`, and runtime-independent blocking client support is outside this proposal. diff --git a/openspec/changes/archive/2026-06-12-create-generic-aptabase-rust-client/proposal.md b/openspec/changes/archive/2026-06-12-create-generic-aptabase-rust-client/proposal.md new file mode 100644 index 0000000..8d5556e --- /dev/null +++ b/openspec/changes/archive/2026-06-12-create-generic-aptabase-rust-client/proposal.md @@ -0,0 +1,32 @@ +## Why + +The existing Tauri plugin already contains most of a generic Aptabase Rust client, but its package and public API are coupled to Tauri. Rewriting this source tree in place as a generic crate will let Rust CLI applications reuse the proven session, batching, retry, and ingestion behavior without maintaining any Tauri integration here. + +## What Changes + +- **BREAKING**: Replace the root `tauri-plugin-aptabase` package in this repository with the generic `aptabase-rs` Rust client crate. +- Reuse the existing configuration, client, dispatcher, session, system metadata, batching, and retry implementations with only the changes required to remove Tauri coupling. +- Expose a public client API for configuration, event tracking, asynchronous flushing, and optional periodic flushing. +- Keep runtime-independent blocking client support out of scope; callers use the asynchronous API from a Tokio runtime. +- Replace WebView-specific metadata collection with generic Rust application metadata while preserving the Aptabase event payload contract. +- Delete all Tauri-specific Rust integration, dependencies, build machinery, permissions, JavaScript bindings, generated plugin files, and Tauri examples from this repository. +- Rewrite the package metadata and documentation for the generic crate and CLI-oriented usage. + +## Capabilities + +### New Capabilities + +- `generic-rust-client`: Configure Aptabase, enqueue typed events, manage sessions, batch and retry ingestion requests, and flush analytics from non-Tauri Rust applications. + +### Modified Capabilities + +None. + +## Impact + +- The repository remains a single root Rust package, renamed to `aptabase-rs`, rather than a Tauri plugin. +- The implementation in `src/config.rs`, `src/client.rs`, `src/dispatcher.rs`, and most of `src/sys.rs` will be retained and adapted in place. +- The Tauri-specific portions of `src/lib.rs`, all of `src/commands.rs`, `build.rs`, `permissions/`, `webview-src/`, `webview-dist/`, `examples/helloworld/`, and JavaScript package metadata will be removed. +- Tauri and `tauri-plugin` dependencies will be removed; Tokio, Reqwest, Serde, `time`, `rand`, `os_info`, `sys-locale`, and `log` will remain where required. +- Existing users of `tauri-plugin-aptabase` cannot upgrade to this rewritten package as a compatible Tauri plugin; retaining or relocating that plugin is outside this change. +- Public lifecycle behavior must support short-lived CLI processes without requiring a Tauri application runtime. diff --git a/openspec/changes/archive/2026-06-12-create-generic-aptabase-rust-client/specs/generic-rust-client/spec.md b/openspec/changes/archive/2026-06-12-create-generic-aptabase-rust-client/specs/generic-rust-client/spec.md new file mode 100644 index 0000000..bafa18b --- /dev/null +++ b/openspec/changes/archive/2026-06-12-create-generic-aptabase-rust-client/specs/generic-rust-client/spec.md @@ -0,0 +1,119 @@ +## ADDED Requirements + +### Requirement: In-place generic crate replacement +The repository's root Rust package SHALL be named `aptabase-rs` and SHALL provide the generic Aptabase client. The source tree MUST NOT retain a Tauri plugin package, Tauri adapter, compatibility shim, workspace member for the old plugin, or Tauri-specific runtime and build dependencies. + +#### Scenario: Build the rewritten root package +- **WHEN** a developer builds the package at the repository root +- **THEN** Cargo builds the `aptabase-rs` package directly without building or linking Tauri + +#### Scenario: Inspect removed integration assets +- **WHEN** a developer inspects the rewritten source tree and package manifests +- **THEN** Tauri commands, builders, permissions, webview bindings, plugin build scripts, JavaScript package metadata, and Tauri examples are absent + +### Requirement: Framework-independent client construction +The system SHALL provide a Rust Aptabase client that can be compiled and constructed without depending on Tauri or `tauri-plugin`. Construction SHALL accept an Aptabase app key, an application version, and optional client configuration. + +#### Scenario: Construct client in a CLI application +- **WHEN** a Rust application constructs the client with an app key and application version +- **THEN** the client is available for event tracking and flushing without requiring an application framework or WebView + +### Requirement: App-key endpoint configuration +The client SHALL select the Aptabase ingestion endpoint from the app-key region, supporting US, EU, development, and self-hosted keys. A self-hosted key MUST have a configured host. Invalid or incomplete configuration SHALL disable tracking without preventing client construction. + +#### Scenario: Configure a hosted region +- **WHEN** the client is constructed with an `A-US-*` or `A-EU-*` app key +- **THEN** requests target the corresponding hosted region's `/api/v0/events` endpoint + +#### Scenario: Configure a self-hosted region +- **WHEN** the client is constructed with an `A-SH-*` app key and a custom host +- **THEN** requests target the custom host's `/api/v0/events` endpoint + +#### Scenario: Disable invalid configuration +- **WHEN** the client is constructed with an invalid app key or a self-hosted key without a host +- **THEN** construction succeeds and subsequent event tracking performs no network delivery + +### Requirement: Event enqueueing and payload compatibility +The client SHALL synchronously enqueue named events without performing network I/O at the call site. Each event SHALL contain an RFC 3339 UTC timestamp, session ID, event name, system properties, application version, SDK version, and optional properties. Supplied properties MUST be a JSON object. + +#### Scenario: Enqueue an event without properties +- **WHEN** a caller tracks an event name without properties on an enabled client +- **THEN** one event with the required Aptabase payload fields is added to the in-memory queue + +#### Scenario: Enqueue an event with object properties +- **WHEN** a caller tracks an event with a JSON object +- **THEN** the object is included in the event's `props` field + +#### Scenario: Reject non-object properties +- **WHEN** a caller tracks an event with a JSON value that is not an object +- **THEN** tracking returns a validation error and no event is enqueued + +### Requirement: Session lifecycle +The client SHALL assign a session ID to tracked events, reuse that ID while activity remains within the four-hour session timeout, and generate a new ID after the timeout is exceeded. + +#### Scenario: Reuse an active session +- **WHEN** multiple events are tracked less than four hours apart +- **THEN** the events contain the same session ID and the session activity time is refreshed + +#### Scenario: Rotate an expired session +- **WHEN** an event is tracked more than four hours after the previous session activity +- **THEN** the event contains a newly generated session ID + +### Requirement: Batched ingestion +The client SHALL flush queued events to Aptabase as JSON arrays containing no more than 25 events per request. Each request SHALL include the `App-Key` header, JSON content type, and a user agent derived from client system metadata. + +#### Scenario: Flush fewer than one batch +- **WHEN** an enabled client flushes between one and 25 queued events +- **THEN** it sends one request containing all queued events to the configured ingestion endpoint + +#### Scenario: Flush multiple batches +- **WHEN** an enabled client flushes more than 25 queued events +- **THEN** it sends multiple requests and no request contains more than 25 events + +#### Scenario: Flush an empty queue +- **WHEN** a client flushes with no queued events +- **THEN** no network request is made + +### Requirement: Delivery failure handling +The client SHALL requeue a batch after a transport failure or server-error response. The client SHALL discard a batch after a non-server HTTP error response, preserving the existing SDK delivery policy. + +#### Scenario: Retry a transport failure later +- **WHEN** a flush cannot send a batch because of a network or transport error +- **THEN** the batch remains queued for a later flush + +#### Scenario: Retry a server failure later +- **WHEN** Aptabase responds to a batch with a 5xx status +- **THEN** the batch remains queued for a later flush + +#### Scenario: Discard a rejected request +- **WHEN** Aptabase responds to a batch with a non-5xx unsuccessful status +- **THEN** the batch is removed from the queue and is not retried automatically + +### Requirement: Explicit asynchronous flushing +The client SHALL provide an asynchronous flush operation that can be awaited before a short-lived process exits. The operation SHALL attempt delivery of queued batches using Tokio. The crate SHALL NOT provide a runtime-independent blocking client as part of this change. + +#### Scenario: Flush before CLI exit +- **WHEN** a Tokio-based CLI tracks an event and awaits the client's flush operation before returning +- **THEN** the queued event is attempted before the command exits + +#### Scenario: Use the supported transport model +- **WHEN** a caller needs to deliver queued events +- **THEN** the caller uses the Tokio-based asynchronous flush API rather than a runtime-independent blocking client + +### Requirement: Optional periodic flushing +The client SHALL allow a caller with an active Tokio runtime to start periodic flushing using the configured interval. The caller SHALL receive a means to manage or stop the periodic worker. + +#### Scenario: Start periodic delivery +- **WHEN** a long-running application starts periodic flushing +- **THEN** the client attempts to flush queued events after each configured interval until the worker is stopped + +### Requirement: Generic and overridable system metadata +The client SHALL collect debug status, operating-system name, operating-system version, and locale without Tauri. It SHALL provide generic runtime metadata defaults and allow applications to override the runtime or engine name and version while retaining the existing `systemProps` field names. + +#### Scenario: Use generic metadata +- **WHEN** a Rust application tracks an event without metadata overrides +- **THEN** the event contains generic Rust-compatible system properties and does not query a WebView + +#### Scenario: Override runtime metadata +- **WHEN** an application supplies a runtime or engine name and version +- **THEN** tracked events contain those supplied values in `engineName` and `engineVersion` diff --git a/openspec/changes/archive/2026-06-12-create-generic-aptabase-rust-client/tasks.md b/openspec/changes/archive/2026-06-12-create-generic-aptabase-rust-client/tasks.md new file mode 100644 index 0000000..84112bf --- /dev/null +++ b/openspec/changes/archive/2026-06-12-create-generic-aptabase-rust-client/tasks.md @@ -0,0 +1,45 @@ +## 1. Rewrite The Root Package + +- [x] 1.1 Rename the root Cargo package to `aptabase-rs` and rewrite its metadata for the generic client without introducing a workspace or secondary crate. +- [x] 1.2 Remove the `tauri` dependency, `tauri-plugin` build dependency, plugin `links` metadata, and any features or metadata that only apply to Tauri. +- [x] 1.3 Define the root crate's public module surface and exports for client construction, options, metadata overrides, event tracking, and flushing. + +## 2. Adapt Configuration And Metadata + +- [x] 2.1 Move `InitOptions` into the generic root API and adapt app-key configuration in place, preserving US, EU, development, self-hosted, invalid-key, and flush-interval behavior. +- [x] 2.2 Remove `tauri::webview_version` from system metadata collection while retaining debug status, OS information, locale, and Flatpak detection. +- [x] 2.3 Add generic runtime or engine defaults and public configuration overrides for engine name and version. +- [x] 2.4 Add unit tests for hosted, development, self-hosted, and invalid app-key configuration and metadata overrides. + +## 3. Adapt The Client And Dispatcher + +- [x] 3.1 Retain the session ID and four-hour session lifecycle implementation in the rewritten root crate. +- [x] 3.2 Adapt event construction and synchronous enqueueing, preserving payload field names, application and SDK versions, disabled-client behavior, and JSON-object property validation. +- [x] 3.3 Retain the Reqwest dispatcher behavior for the ingestion path, request headers, user agent, ten-second timeout, in-memory queue, and 25-event batching. +- [x] 3.4 Preserve requeue behavior for transport and 5xx failures and discard behavior for non-5xx unsuccessful responses. +- [x] 3.5 Expose asynchronous explicit flushing for CLI shutdown and optional periodic flushing with a caller-manageable task handle or cancellation mechanism. +- [x] 3.6 Verify the public API and dependency features do not introduce a runtime-independent blocking client or `reqwest::blocking` transport. +- [x] 3.7 Remove the old Tauri-oriented `Builder`, `PanicHook`, and `EventTracker` public APIs and replace `src/lib.rs` with the generic crate API. + +## 4. Remove Tauri From The Source Tree + +- [x] 4.1 Delete `src/commands.rs` and all remaining Tauri imports, command handlers, managed-state integration, plugin builders, runtime event hooks, and WebView metadata code. +- [x] 4.2 Delete the root plugin `build.rs` and the `permissions/` directory, including generated schemas and command permissions. +- [x] 4.3 Delete `webview-src/`, `webview-dist/`, `package.json`, and other JavaScript or TypeScript build metadata used only by the Tauri plugin. +- [x] 4.4 Delete `examples/helloworld/` and all generated Tauri example artifacts. +- [x] 4.5 Search the retained source, manifests, and documentation and remove all claims or configuration that this repository still provides a Tauri plugin. + +## 5. Verify Core Behavior + +- [x] 5.1 Add tests that verify event payload serialization, required system properties, object-property acceptance, and non-object rejection. +- [x] 5.2 Add deterministic session tests for reuse within four hours and rotation after expiration. +- [x] 5.3 Add local HTTP test coverage for empty flushes, `App-Key` and content headers, endpoint selection, and batches of at most 25 events. +- [x] 5.4 Add delivery tests showing that transport and 5xx failures remain queued while non-5xx unsuccessful responses are discarded. +- [x] 5.5 Add a Tokio-based CLI example or integration test that tracks an event and awaits `flush` using the rewritten root crate. + +## 6. Documentation And Final Validation + +- [x] 6.1 Replace the Tauri README with generic crate documentation covering CLI construction, event tracking, explicit shutdown flushing, periodic flushing, self-hosted configuration, and privacy guidance. +- [x] 6.2 Update changelog and package metadata to state that this is a breaking in-place replacement named `aptabase-rs`. +- [x] 6.3 Run formatting, unit and integration tests, Cargo checks, and documentation tests for the rewritten root package. +- [x] 6.4 Inspect the dependency tree and repository files to verify that no Tauri dependency, Tauri source, plugin asset, or compatibility layer remains. diff --git a/openspec/specs/generic-rust-client/spec.md b/openspec/specs/generic-rust-client/spec.md new file mode 100644 index 0000000..ce4893e --- /dev/null +++ b/openspec/specs/generic-rust-client/spec.md @@ -0,0 +1,123 @@ +# generic-rust-client Specification + +## Purpose +Define the framework-independent Rust SDK for constructing an Aptabase client, enqueueing analytics events, managing sessions and metadata, selecting hosted or self-hosted endpoints, and delivering queued events through asynchronous Tokio-based batching and flushing. + +## Requirements +### Requirement: In-place generic crate replacement +The repository's root Rust package SHALL be named `aptabase-rs` and SHALL provide the generic Aptabase client. The source tree MUST NOT retain a Tauri plugin package, Tauri adapter, compatibility shim, workspace member for the old plugin, or Tauri-specific runtime and build dependencies. + +#### Scenario: Build the rewritten root package +- **WHEN** a developer builds the package at the repository root +- **THEN** Cargo builds the `aptabase-rs` package directly without building or linking Tauri + +#### Scenario: Inspect removed integration assets +- **WHEN** a developer inspects the rewritten source tree and package manifests +- **THEN** Tauri commands, builders, permissions, webview bindings, plugin build scripts, JavaScript package metadata, and Tauri examples are absent + +### Requirement: Framework-independent client construction +The system SHALL provide a Rust Aptabase client that can be compiled and constructed without depending on Tauri or `tauri-plugin`. Construction SHALL accept an Aptabase app key, an application version, and optional client configuration. + +#### Scenario: Construct client in a CLI application +- **WHEN** a Rust application constructs the client with an app key and application version +- **THEN** the client is available for event tracking and flushing without requiring an application framework or WebView + +### Requirement: App-key endpoint configuration +The client SHALL select the Aptabase ingestion endpoint from the app-key region, supporting US, EU, development, and self-hosted keys. A self-hosted key MUST have a configured host. Invalid or incomplete configuration SHALL disable tracking without preventing client construction. + +#### Scenario: Configure a hosted region +- **WHEN** the client is constructed with an `A-US-*` or `A-EU-*` app key +- **THEN** requests target the corresponding hosted region's `/api/v0/events` endpoint + +#### Scenario: Configure a self-hosted region +- **WHEN** the client is constructed with an `A-SH-*` app key and a custom host +- **THEN** requests target the custom host's `/api/v0/events` endpoint + +#### Scenario: Disable invalid configuration +- **WHEN** the client is constructed with an invalid app key or a self-hosted key without a host +- **THEN** construction succeeds and subsequent event tracking performs no network delivery + +### Requirement: Event enqueueing and payload compatibility +The client SHALL synchronously enqueue named events without performing network I/O at the call site. Each event SHALL contain an RFC 3339 UTC timestamp, session ID, event name, system properties, application version, SDK version, and optional properties. Supplied properties MUST be a JSON object. + +#### Scenario: Enqueue an event without properties +- **WHEN** a caller tracks an event name without properties on an enabled client +- **THEN** one event with the required Aptabase payload fields is added to the in-memory queue + +#### Scenario: Enqueue an event with object properties +- **WHEN** a caller tracks an event with a JSON object +- **THEN** the object is included in the event's `props` field + +#### Scenario: Reject non-object properties +- **WHEN** a caller tracks an event with a JSON value that is not an object +- **THEN** tracking returns a validation error and no event is enqueued + +### Requirement: Session lifecycle +The client SHALL assign a session ID to tracked events, reuse that ID while activity remains within the four-hour session timeout, and generate a new ID after the timeout is exceeded. + +#### Scenario: Reuse an active session +- **WHEN** multiple events are tracked less than four hours apart +- **THEN** the events contain the same session ID and the session activity time is refreshed + +#### Scenario: Rotate an expired session +- **WHEN** an event is tracked more than four hours after the previous session activity +- **THEN** the event contains a newly generated session ID + +### Requirement: Batched ingestion +The client SHALL flush queued events to Aptabase as JSON arrays containing no more than 25 events per request. Each request SHALL include the `App-Key` header, JSON content type, and a user agent derived from client system metadata. + +#### Scenario: Flush fewer than one batch +- **WHEN** an enabled client flushes between one and 25 queued events +- **THEN** it sends one request containing all queued events to the configured ingestion endpoint + +#### Scenario: Flush multiple batches +- **WHEN** an enabled client flushes more than 25 queued events +- **THEN** it sends multiple requests and no request contains more than 25 events + +#### Scenario: Flush an empty queue +- **WHEN** a client flushes with no queued events +- **THEN** no network request is made + +### Requirement: Delivery failure handling +The client SHALL requeue a batch after a transport failure or server-error response. The client SHALL discard a batch after a non-server HTTP error response, preserving the existing SDK delivery policy. + +#### Scenario: Retry a transport failure later +- **WHEN** a flush cannot send a batch because of a network or transport error +- **THEN** the batch remains queued for a later flush + +#### Scenario: Retry a server failure later +- **WHEN** Aptabase responds to a batch with a 5xx status +- **THEN** the batch remains queued for a later flush + +#### Scenario: Discard a rejected request +- **WHEN** Aptabase responds to a batch with a non-5xx unsuccessful status +- **THEN** the batch is removed from the queue and is not retried automatically + +### Requirement: Explicit asynchronous flushing +The client SHALL provide an asynchronous flush operation that can be awaited before a short-lived process exits. The operation SHALL attempt delivery of queued batches using Tokio. The crate SHALL NOT provide a runtime-independent blocking client as part of this change. + +#### Scenario: Flush before CLI exit +- **WHEN** a Tokio-based CLI tracks an event and awaits the client's flush operation before returning +- **THEN** the queued event is attempted before the command exits + +#### Scenario: Use the supported transport model +- **WHEN** a caller needs to deliver queued events +- **THEN** the caller uses the Tokio-based asynchronous flush API rather than a runtime-independent blocking client + +### Requirement: Optional periodic flushing +The client SHALL allow a caller with an active Tokio runtime to start periodic flushing using the configured interval. The caller SHALL receive a means to manage or stop the periodic worker. + +#### Scenario: Start periodic delivery +- **WHEN** a long-running application starts periodic flushing +- **THEN** the client attempts to flush queued events after each configured interval until the worker is stopped + +### Requirement: Generic and overridable system metadata +The client SHALL collect debug status, operating-system name, operating-system version, and locale without Tauri. It SHALL provide generic runtime metadata defaults and allow applications to override the runtime or engine name and version while retaining the existing `systemProps` field names. + +#### Scenario: Use generic metadata +- **WHEN** a Rust application tracks an event without metadata overrides +- **THEN** the event contains generic Rust-compatible system properties and does not query a WebView + +#### Scenario: Override runtime metadata +- **WHEN** an application supplies a runtime or engine name and version +- **THEN** tracked events contain those supplied values in `engineName` and `engineVersion` diff --git a/package.json b/package.json deleted file mode 100644 index 2c16439..0000000 --- a/package.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "name": "@aptabase/tauri", - "version": "0.4.2", - "private": false, - "type": "module", - "description": "Tauri Plugin for Aptabase: Open Source, Privacy-First and Simple Analytics for Mobile, Desktop and Web Apps", - "author": "Guilherme Oenning", - "browser": "webview-dist/index.js", - "main": "webview-dist/index.js", - "types": "webview-dist/index.d.ts", - "repository": { - "type": "git", - "url": "git+https://github.com/aptabase/tauri-plugin-aptabase.git" - }, - "bugs": { - "url": "https://github.com/aptabase/tauri-plugin-aptabase/issues" - }, - "homepage": "https://github.com/aptabase/tauri-plugin-aptabase", - "scripts": { - "build": "rollup -c ./webview-src/rollup.config.js", - "watch": "rollup -c ./webview-src/rollup.config.js -w", - "prepublishOnly": "npm run build", - "pretest": "npm run build" - }, - "files": [ - "README.md", - "LICENSE", - "webview-dist", - "package.json" - ], - "devDependencies": { - "@rollup/plugin-node-resolve": "^15.3.0", - "@rollup/plugin-typescript": "^12.1.1", - "@rollup/plugin-terser": "^0.4.4", - "rollup": "^4.27.4", - "typescript": "^5.7.2" - }, - "dependencies": { - "@tauri-apps/api": "^2.1.1", - "tslib": "^2.8.1" - } -} diff --git a/permissions/autogenerated/commands/track_event.toml b/permissions/autogenerated/commands/track_event.toml deleted file mode 100644 index 02d667d..0000000 --- a/permissions/autogenerated/commands/track_event.toml +++ /dev/null @@ -1,13 +0,0 @@ -# Automatically generated - DO NOT EDIT! - -"$schema" = "../../schemas/schema.json" - -[[permission]] -identifier = "allow-track-event" -description = "Enables the track_event command without any pre-configured scope." -commands.allow = ["track_event"] - -[[permission]] -identifier = "deny-track-event" -description = "Denies the track_event command without any pre-configured scope." -commands.deny = ["track_event"] diff --git a/permissions/autogenerated/reference.md b/permissions/autogenerated/reference.md deleted file mode 100644 index 652ec58..0000000 --- a/permissions/autogenerated/reference.md +++ /dev/null @@ -1,35 +0,0 @@ -## Permission Table - -
-

Welcome to Tauri!

- -
- -

- Click on the Tauri, Vite, and Svelte logos to learn more. -

- -
- -
- -
- -
- -

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 z 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

- - - - - - - - - - - - - - - -
IdentifierDescription
- -`aptabase:allow-track-event` - - - -Enables the track_event command without any pre-configured scope. - -
- -`aptabase:deny-track-event` - - - -Denies the track_event command without any pre-configured scope. - -
diff --git a/permissions/schemas/schema.json b/permissions/schemas/schema.json deleted file mode 100644 index 4c0ac2b..0000000 --- a/permissions/schemas/schema.json +++ /dev/null @@ -1,312 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "PermissionFile", - "description": "Permission file that can define a default permission, a set of permissions or a list of inlined permissions.", - "type": "object", - "properties": { - "default": { - "description": "The default permission set for the plugin", - "anyOf": [ - { - "$ref": "#/definitions/DefaultPermission" - }, - { - "type": "null" - } - ] - }, - "set": { - "description": "A list of permissions sets defined", - "type": "array", - "items": { - "$ref": "#/definitions/PermissionSet" - } - }, - "permission": { - "description": "A list of inlined permissions", - "default": [], - "type": "array", - "items": { - "$ref": "#/definitions/Permission" - } - } - }, - "definitions": { - "DefaultPermission": { - "description": "The default permission set of the plugin.\n\nWorks similarly to a permission with the \"default\" identifier.", - "type": "object", - "required": [ - "permissions" - ], - "properties": { - "version": { - "description": "The version of the permission.", - "type": [ - "integer", - "null" - ], - "format": "uint64", - "minimum": 1.0 - }, - "description": { - "description": "Human-readable description of what the permission does. Tauri convention is to use `

` headings in markdown content for Tauri documentation generation purposes.", - "type": [ - "string", - "null" - ] - }, - "permissions": { - "description": "All permissions this set contains.", - "type": "array", - "items": { - "type": "string" - } - } - } - }, - "PermissionSet": { - "description": "A set of direct permissions grouped together under a new name.", - "type": "object", - "required": [ - "description", - "identifier", - "permissions" - ], - "properties": { - "identifier": { - "description": "A unique identifier for the permission.", - "type": "string" - }, - "description": { - "description": "Human-readable description of what the permission does.", - "type": "string" - }, - "permissions": { - "description": "All permissions this set contains.", - "type": "array", - "items": { - "$ref": "#/definitions/PermissionKind" - } - } - } - }, - "Permission": { - "description": "Descriptions of explicit privileges of commands.\n\nIt can enable commands to be accessible in the frontend of the application.\n\nIf the scope is defined it can be used to fine grain control the access of individual or multiple commands.", - "type": "object", - "required": [ - "identifier" - ], - "properties": { - "version": { - "description": "The version of the permission.", - "type": [ - "integer", - "null" - ], - "format": "uint64", - "minimum": 1.0 - }, - "identifier": { - "description": "A unique identifier for the permission.", - "type": "string" - }, - "description": { - "description": "Human-readable description of what the permission does. Tauri internal convention is to use `

` headings in markdown content for Tauri documentation generation purposes.", - "type": [ - "string", - "null" - ] - }, - "commands": { - "description": "Allowed or denied commands when using this permission.", - "default": { - "allow": [], - "deny": [] - }, - "allOf": [ - { - "$ref": "#/definitions/Commands" - } - ] - }, - "scope": { - "description": "Allowed or denied scoped when using this permission.", - "allOf": [ - { - "$ref": "#/definitions/Scopes" - } - ] - }, - "platforms": { - "description": "Target platforms this permission applies. By default all platforms are affected by this permission.", - "type": [ - "array", - "null" - ], - "items": { - "$ref": "#/definitions/Target" - } - } - } - }, - "Commands": { - "description": "Allowed and denied commands inside a permission.\n\nIf two commands clash inside of `allow` and `deny`, it should be denied by default.", - "type": "object", - "properties": { - "allow": { - "description": "Allowed command.", - "default": [], - "type": "array", - "items": { - "type": "string" - } - }, - "deny": { - "description": "Denied command, which takes priority.", - "default": [], - "type": "array", - "items": { - "type": "string" - } - } - } - }, - "Scopes": { - "description": "An argument for fine grained behavior control of Tauri commands.\n\nIt can be of any serde serializable type and is used to allow or prevent certain actions inside a Tauri command. The configured scope is passed to the command and will be enforced by the command implementation.\n\n## Example\n\n```json { \"allow\": [{ \"path\": \"$HOME/**\" }], \"deny\": [{ \"path\": \"$HOME/secret.txt\" }] } ```", - "type": "object", - "properties": { - "allow": { - "description": "Data that defines what is allowed by the scope.", - "type": [ - "array", - "null" - ], - "items": { - "$ref": "#/definitions/Value" - } - }, - "deny": { - "description": "Data that defines what is denied by the scope. This should be prioritized by validation logic.", - "type": [ - "array", - "null" - ], - "items": { - "$ref": "#/definitions/Value" - } - } - } - }, - "Value": { - "description": "All supported ACL values.", - "anyOf": [ - { - "description": "Represents a null JSON value.", - "type": "null" - }, - { - "description": "Represents a [`bool`].", - "type": "boolean" - }, - { - "description": "Represents a valid ACL [`Number`].", - "allOf": [ - { - "$ref": "#/definitions/Number" - } - ] - }, - { - "description": "Represents a [`String`].", - "type": "string" - }, - { - "description": "Represents a list of other [`Value`]s.", - "type": "array", - "items": { - "$ref": "#/definitions/Value" - } - }, - { - "description": "Represents a map of [`String`] keys to [`Value`]s.", - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/Value" - } - } - ] - }, - "Number": { - "description": "A valid ACL number.", - "anyOf": [ - { - "description": "Represents an [`i64`].", - "type": "integer", - "format": "int64" - }, - { - "description": "Represents a [`f64`].", - "type": "number", - "format": "double" - } - ] - }, - "Target": { - "description": "Platform target.", - "oneOf": [ - { - "description": "MacOS.", - "type": "string", - "enum": [ - "macOS" - ] - }, - { - "description": "Windows.", - "type": "string", - "enum": [ - "windows" - ] - }, - { - "description": "Linux.", - "type": "string", - "enum": [ - "linux" - ] - }, - { - "description": "Android.", - "type": "string", - "enum": [ - "android" - ] - }, - { - "description": "iOS.", - "type": "string", - "enum": [ - "iOS" - ] - } - ] - }, - "PermissionKind": { - "type": "string", - "oneOf": [ - { - "description": "Enables the track_event command without any pre-configured scope.", - "type": "string", - "const": "allow-track-event", - "markdownDescription": "Enables the track_event command without any pre-configured scope." - }, - { - "description": "Denies the track_event command without any pre-configured scope.", - "type": "string", - "const": "deny-track-event", - "markdownDescription": "Denies the track_event command without any pre-configured scope." - } - ] - } - } -} \ No newline at end of file diff --git a/src/client.rs b/src/client.rs index da9d905..c933f94 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,14 +1,14 @@ use rand::Rng; use serde_json::{json, Value}; -use std::time::{SystemTime, UNIX_EPOCH}; use std::{ sync::{Arc, Mutex as SyncMutex}, - time::Duration, + time::{Duration, SystemTime, UNIX_EPOCH}, }; use time::{format_description::well_known::Rfc3339, OffsetDateTime}; +use tokio::task::JoinHandle; use crate::{ - config::Config, + config::{Config, InitOptions}, dispatcher::EventDispatcher, sys::{self, SystemProperties}, }; @@ -37,10 +37,10 @@ pub struct TrackingSession { } impl TrackingSession { - fn new() -> Self { + fn new_at(now: OffsetDateTime) -> Self { Self { id: new_session_id(), - last_touch_ts: OffsetDateTime::now_utc(), + last_touch_ts: now, } } } @@ -52,44 +52,67 @@ pub struct AptabaseClient { dispatcher: Arc, app_version: String, sys_info: SystemProperties, + flush_interval: Duration, } impl AptabaseClient { - /// Creates a new Aptabase client. - pub fn new(config: &Config, app_version: String) -> Self { - let sys_info = sys::get_info(); + /// Creates a client using the default options. + pub fn new(app_key: impl Into, app_version: impl Into) -> Self { + Self::with_options(app_key, app_version, InitOptions::default()) + } + + /// Creates a client using custom endpoint, interval, or engine metadata options. + pub fn with_options( + app_key: impl Into, + app_version: impl Into, + options: InitOptions, + ) -> Self { + let config = Config::new(app_key.into(), &options); + let sys_info = sys::get_info(&options); let is_enabled = !config.app_key.is_empty(); - let dispatcher = Arc::new(EventDispatcher::new(config, &sys_info)); + let dispatcher = Arc::new(EventDispatcher::new(&config, &sys_info)); Self { is_enabled, dispatcher, - session: SyncMutex::new(TrackingSession::new()), - app_version, + session: SyncMutex::new(TrackingSession::new_at(OffsetDateTime::now_utc())), + app_version: app_version.into(), sys_info, + flush_interval: config.flush_interval, } } - /// Starts the event dispatcher loop. - pub(crate) fn start_polling(&self, interval: Duration) { + /// Returns whether this client has a valid delivery configuration. + pub fn is_enabled(&self) -> bool { + self.is_enabled + } + + /// Starts periodic flushing on the current Tokio runtime. + /// + /// Abort the returned handle to stop the worker. + pub fn start_periodic_flush(&self) -> JoinHandle<()> { let dispatcher = self.dispatcher.clone(); + let interval = self.flush_interval; tokio::spawn(async move { loop { tokio::time::sleep(interval).await; dispatcher.flush().await; } - }); + }) } /// Returns the current session ID, creating a new one if necessary. - pub(crate) fn eval_session_id(&self) -> String { + fn eval_session_id(&self) -> String { + self.eval_session_id_at(OffsetDateTime::now_utc()) + } + + fn eval_session_id_at(&self, now: OffsetDateTime) -> String { let mut session = self.session.lock().expect("could not lock events"); - let now = OffsetDateTime::now_utc(); if (now - session.last_touch_ts) > SESSION_TIMEOUT { - *session = TrackingSession::new(); + *session = TrackingSession::new_at(now); } else { session.last_touch_ts = now; } @@ -138,11 +161,114 @@ impl AptabaseClient { pub async fn flush(&self) { self.dispatcher.flush().await; } +} - /// Flushes the event queue, blocking the current thread. - pub fn flush_blocking(&self) { - futures::executor::block_on(async { - self.flush().await; - }); +#[cfg(test)] +mod tests { + use super::*; + + fn client() -> AptabaseClient { + AptabaseClient::new("A-US-test", "1.2.3") + } + + #[test] + fn serializes_required_event_fields_and_object_properties() { + let client = client(); + client + .track_event("settings_saved", Some(json!({ "section": "privacy" }))) + .unwrap(); + + let events = client.dispatcher.queued_events(); + let event = &events[0]; + assert!(event["timestamp"].as_str().is_some()); + assert!(event["sessionId"].as_str().is_some()); + assert_eq!(event["eventName"], "settings_saved"); + assert_eq!(event["props"], json!({ "section": "privacy" })); + assert_eq!(event["systemProps"]["appVersion"], "1.2.3"); + assert_eq!(event["systemProps"]["engineName"], "Rust"); + assert_eq!(event["systemProps"]["engineVersion"], "unknown"); + assert!(event["systemProps"]["isDebug"].is_boolean()); + assert!(event["systemProps"]["osName"].is_string()); + assert!(event["systemProps"]["osVersion"].is_string()); + assert!(event["systemProps"]["locale"].is_string()); + assert_eq!( + event["systemProps"]["sdkVersion"], + concat!(env!("CARGO_PKG_NAME"), "@", env!("CARGO_PKG_VERSION")) + ); + } + + #[test] + fn rejects_non_object_properties_without_enqueueing() { + let client = client(); + + assert!(client + .track_event("invalid", Some(json!([1, 2, 3]))) + .is_err()); + assert!(client.dispatcher.is_empty()); + } + + #[test] + fn disabled_client_ignores_events() { + let client = AptabaseClient::new("invalid", "1.0.0"); + + assert!(!client.is_enabled()); + assert!(client + .track_event("ignored", Some(json!([1, 2, 3]))) + .is_ok()); + assert!(client.dispatcher.is_empty()); + } + + #[test] + fn reuses_session_within_four_hours() { + let client = client(); + let now = OffsetDateTime::UNIX_EPOCH; + *client.session.lock().unwrap() = TrackingSession { + id: "existing".into(), + last_touch_ts: now, + }; + + assert_eq!( + client.eval_session_id_at(now + Duration::from_secs(60)), + "existing" + ); + assert_eq!( + client.session.lock().unwrap().last_touch_ts, + now + Duration::from_secs(60) + ); + } + + #[test] + fn rotates_session_after_four_hours() { + let client = client(); + let now = OffsetDateTime::UNIX_EPOCH; + *client.session.lock().unwrap() = TrackingSession { + id: "expired".into(), + last_touch_ts: now, + }; + + let session_id = client.eval_session_id_at(now + SESSION_TIMEOUT + Duration::from_secs(1)); + assert_ne!(session_id, "expired"); + assert_eq!( + client.session.lock().unwrap().last_touch_ts, + now + SESSION_TIMEOUT + Duration::from_secs(1) + ); + } + + #[test] + fn applies_engine_metadata_overrides_to_events() { + let client = AptabaseClient::with_options( + "A-US-test", + "1.0.0", + InitOptions { + engine_name: Some("Custom Runtime".into()), + engine_version: Some("9.1".into()), + ..InitOptions::default() + }, + ); + client.track_event("started", None).unwrap(); + + let events = client.dispatcher.queued_events(); + assert_eq!(events[0]["systemProps"]["engineName"], "Custom Runtime"); + assert_eq!(events[0]["systemProps"]["engineVersion"], "9.1"); } } diff --git a/src/commands.rs b/src/commands.rs deleted file mode 100644 index 21839a9..0000000 --- a/src/commands.rs +++ /dev/null @@ -1,14 +0,0 @@ -use serde_json::Value; -use std::sync::Arc; -use tauri::{command, State}; - -use crate::client::AptabaseClient; - -#[command] -pub fn track_event( - client: State<'_, Arc>, - name: &str, - props: Option, -) -> Result<(), String> { - client.track_event(name, props) -} diff --git a/src/config.rs b/src/config.rs index 1985f11..99e7aff 100644 --- a/src/config.rs +++ b/src/config.rs @@ -3,13 +3,24 @@ use std::time::Duration; use log::debug; use reqwest::Url; -use crate::InitOptions; +/// Optional Aptabase client configuration. +#[derive(Default, Debug, Clone)] +pub struct InitOptions { + /// Base URL for an `A-SH-*` self-hosted app key. + pub host: Option, + /// Interval used by [`crate::AptabaseClient::start_periodic_flush`]. + pub flush_interval: Option, + /// Value reported as `engineName` in event system properties. + pub engine_name: Option, + /// Value reported as `engineVersion` in event system properties. + pub engine_version: Option, +} #[derive(Debug, Clone)] -pub struct Config { - pub app_key: String, - pub ingest_api_url: Url, - pub flush_interval: Duration, +pub(crate) struct Config { + pub(crate) app_key: String, + pub(crate) ingest_api_url: Url, + pub(crate) flush_interval: Duration, } static LOCAL: &str = "http://localhost:3000"; @@ -25,9 +36,13 @@ static DEFAULT_FLUSH_INTERVAL: Duration = Duration::from_secs(2); const VALID_REGIONS: &[&str] = &["US", "EU", "DEV", "SH"]; impl Config { - pub fn new(app_key: String, opts: InitOptions) -> Self { - let parts = app_key.split("-").collect::>(); - if parts.len() != 3 || !VALID_REGIONS.contains(&parts[1]) { + pub(crate) fn new(app_key: String, opts: &InitOptions) -> Self { + let parts = app_key.split('-').collect::>(); + if parts.len() != 3 + || parts[0] != "A" + || parts[2].is_empty() + || !VALID_REGIONS.contains(&parts[1]) + { debug!( "The Aptabase App Key '{}' is invalid. Tracking will be disabled.", app_key @@ -40,8 +55,8 @@ impl Config { "US" => US_REGION.into(), "DEV" => LOCAL.into(), "SH" => { - if let Some(host) = opts.host { - host + if let Some(host) = &opts.host { + host.clone() } else { debug!("Host parameter must be defined when using Self-Hosted App Key. Tracking will be disabled."); return Config::default(); @@ -50,9 +65,19 @@ impl Config { _ => return Config::default(), }; + let Ok(ingest_api_url) = + format!("{}/api/v0/events", base_url.trim_end_matches('/')).parse() + else { + debug!( + "The Aptabase host '{}' is invalid. Tracking will be disabled.", + base_url + ); + return Config::default(); + }; + Self { app_key, - ingest_api_url: format!("{}/api/v0/events", base_url).parse().unwrap(), + ingest_api_url, flush_interval: opts.flush_interval.unwrap_or(DEFAULT_FLUSH_INTERVAL), } } @@ -67,3 +92,74 @@ impl Default for Config { } } } + +#[cfg(test)] +mod tests { + use super::*; + + fn options() -> InitOptions { + InitOptions { + flush_interval: Some(Duration::from_secs(30)), + ..InitOptions::default() + } + } + + #[test] + fn configures_hosted_and_development_regions() { + let us = Config::new("A-US-123".into(), &options()); + let eu = Config::new("A-EU-123".into(), &options()); + let dev = Config::new("A-DEV-123".into(), &options()); + + assert_eq!( + us.ingest_api_url.as_str(), + "https://us.aptabase.com/api/v0/events" + ); + assert_eq!( + eu.ingest_api_url.as_str(), + "https://eu.aptabase.com/api/v0/events" + ); + assert_eq!( + dev.ingest_api_url.as_str(), + "http://localhost:3000/api/v0/events" + ); + assert_eq!(us.flush_interval, Duration::from_secs(30)); + } + + #[test] + fn configures_self_hosted_region() { + let config = Config::new( + "A-SH-123".into(), + &InitOptions { + host: Some("https://analytics.example.com/".into()), + ..InitOptions::default() + }, + ); + + assert_eq!( + config.ingest_api_url.as_str(), + "https://analytics.example.com/api/v0/events" + ); + } + + #[test] + fn invalid_configuration_disables_tracking() { + for app_key in ["", "invalid", "B-US-123", "A-US-", "A-XX-123"] { + assert!(Config::new(app_key.into(), &InitOptions::default()) + .app_key + .is_empty()); + } + + assert!(Config::new("A-SH-123".into(), &InitOptions::default()) + .app_key + .is_empty()); + assert!(Config::new( + "A-SH-123".into(), + &InitOptions { + host: Some("not a url".into()), + ..InitOptions::default() + } + ) + .app_key + .is_empty()); + } +} diff --git a/src/dispatcher.rs b/src/dispatcher.rs index b4885a4..4297923 100644 --- a/src/dispatcher.rs +++ b/src/dispatcher.rs @@ -65,6 +65,16 @@ impl EventDispatcher { queue.extend(events); } + #[cfg(test)] + pub(crate) fn queued_events(&self) -> Vec { + self.queue + .read() + .expect("could not lock queue for reading") + .iter() + .cloned() + .collect() + } + fn dequeue_many(&self, max: usize) -> Vec { let mut queue = self.queue.write().expect("could not lock queue"); if queue.is_empty() { @@ -115,7 +125,7 @@ impl EventDispatcher { }, Err(err) => { failed_items.extend(events_to_send); - debug!("failed to track_event: {}", err.to_string()); + debug!("failed to track_event: {}", err); } } } @@ -123,3 +133,181 @@ impl EventDispatcher { self.enqueue_many(failed_items); } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::{config::InitOptions, sys}; + use serde_json::json; + use std::{ + io::{Read, Write}, + net::{TcpListener, TcpStream}, + sync::mpsc, + thread, + }; + + #[derive(Debug)] + struct RecordedRequest { + request_line: String, + headers: Vec<(String, String)>, + body: Value, + } + + fn dispatcher(url: Url) -> EventDispatcher { + let config = Config { + app_key: "A-DEV-test".into(), + ingest_api_url: url, + flush_interval: Duration::from_secs(1), + }; + EventDispatcher::new(&config, &sys::get_info(&InitOptions::default())) + } + + fn read_request(stream: &mut TcpStream) -> RecordedRequest { + let mut bytes = Vec::new(); + let mut buffer = [0; 4096]; + let header_end = loop { + let count = stream.read(&mut buffer).unwrap(); + assert!( + count > 0, + "connection closed before request headers arrived" + ); + bytes.extend_from_slice(&buffer[..count]); + if let Some(index) = bytes.windows(4).position(|window| window == b"\r\n\r\n") { + break index + 4; + } + }; + + let headers_text = String::from_utf8(bytes[..header_end].to_vec()).unwrap(); + let mut lines = headers_text.split("\r\n"); + let request_line = lines.next().unwrap().to_string(); + let headers = lines + .filter_map(|line| line.split_once(':')) + .map(|(name, value)| (name.to_ascii_lowercase(), value.trim().to_string())) + .collect::>(); + let content_length = headers + .iter() + .find(|(name, _)| name == "content-length") + .and_then(|(_, value)| value.parse::().ok()) + .unwrap_or(0); + + while bytes.len() - header_end < content_length { + let count = stream.read(&mut buffer).unwrap(); + assert!(count > 0, "connection closed before request body arrived"); + bytes.extend_from_slice(&buffer[..count]); + } + + RecordedRequest { + request_line, + headers, + body: serde_json::from_slice(&bytes[header_end..header_end + content_length]).unwrap(), + } + } + + fn spawn_server(statuses: Vec) -> (Url, mpsc::Receiver) { + let listener = TcpListener::bind("127.0.0.1:0").unwrap(); + let address = listener.local_addr().unwrap(); + let (sender, receiver) = mpsc::channel(); + + thread::spawn(move || { + for status in statuses { + let (mut stream, _) = listener.accept().unwrap(); + let request = read_request(&mut stream); + sender.send(request).unwrap(); + let reason = if status == 200 { + "OK" + } else if status == 400 { + "Bad Request" + } else { + "Internal Server Error" + }; + write!( + stream, + "HTTP/1.1 {status} {reason}\r\nContent-Length: 0\r\nConnection: close\r\n\r\n" + ) + .unwrap(); + } + }); + + ( + format!("http://{address}/api/v0/events").parse().unwrap(), + receiver, + ) + } + + #[tokio::test] + async fn empty_flush_makes_no_request() { + let listener = TcpListener::bind("127.0.0.1:0").unwrap(); + let url = format!("http://{}/api/v0/events", listener.local_addr().unwrap()) + .parse() + .unwrap(); + let dispatcher = dispatcher(url); + + dispatcher.flush().await; + assert!(dispatcher.is_empty()); + } + + #[tokio::test] + async fn sends_headers_endpoint_and_batches_of_at_most_25() { + let (url, requests) = spawn_server(vec![200, 200]); + let dispatcher = dispatcher(url); + for index in 0..26 { + dispatcher.enqueue(json!({ "index": index })); + } + + dispatcher.flush().await; + + let first = requests.recv().unwrap(); + let second = requests.recv().unwrap(); + assert_eq!(first.request_line, "POST /api/v0/events HTTP/1.1"); + assert_eq!(first.body.as_array().unwrap().len(), 25); + assert_eq!(second.body.as_array().unwrap().len(), 1); + assert!(first + .headers + .contains(&("app-key".into(), "A-DEV-test".into()))); + assert!(first + .headers + .contains(&("content-type".into(), "application/json".into()))); + assert!(first + .headers + .iter() + .any(|(name, value)| { name == "user-agent" && value.contains("Rust/unknown") })); + assert!(dispatcher.is_empty()); + } + + #[tokio::test] + async fn requeues_transport_failures() { + let listener = TcpListener::bind("127.0.0.1:0").unwrap(); + let address = listener.local_addr().unwrap(); + drop(listener); + let dispatcher = dispatcher(format!("http://{address}/api/v0/events").parse().unwrap()); + dispatcher.enqueue(json!({ "event": 1 })); + + dispatcher.flush().await; + + assert_eq!(dispatcher.queued_events().len(), 1); + } + + #[tokio::test] + async fn requeues_server_errors() { + let (url, requests) = spawn_server(vec![500]); + let dispatcher = dispatcher(url); + dispatcher.enqueue(json!({ "event": 1 })); + + dispatcher.flush().await; + + requests.recv().unwrap(); + assert_eq!(dispatcher.queued_events().len(), 1); + } + + #[tokio::test] + async fn discards_non_server_http_errors() { + let (url, requests) = spawn_server(vec![400]); + let dispatcher = dispatcher(url); + dispatcher.enqueue(json!({ "event": 1 })); + + dispatcher.flush().await; + + requests.recv().unwrap(); + assert!(dispatcher.is_empty()); + } +} diff --git a/src/lib.rs b/src/lib.rs index a2b747c..356b374 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,144 +1,9 @@ +//! A framework-independent Aptabase client for Rust applications. + mod client; -mod commands; mod config; mod dispatcher; mod sys; -use std::{panic::PanicInfo, sync::Arc, time::Duration}; - -use client::AptabaseClient; -use config::Config; -use serde_json::Value; -use tauri::{ - plugin::{self, TauriPlugin}, - App, AppHandle, Manager, RunEvent, Runtime, Window, -}; - -#[derive(Default, Debug, Clone)] -pub struct InitOptions { - pub host: Option, - pub flush_interval: Option, -} - -/// The Aptabase Plugin builder -pub struct Builder { - app_key: String, - panic_hook: Option, - options: InitOptions, -} - -pub type PanicHook = - Box, String) + 'static + Sync + Send>; - -fn get_panic_message(info: &PanicInfo) -> String { - let payload = info.payload(); - if let Some(s) = payload.downcast_ref::<&str>() { - return s.to_string(); - } else if let Some(s) = payload.downcast_ref::() { - return s.to_string(); - } - - format!("{:?}", payload) -} - -impl Builder { - /// Creates a new builder. - pub fn new(app_key: &str) -> Self { - Self { - app_key: app_key.into(), - panic_hook: None, - options: Default::default(), - } - } - - /// Sets custom options to use for the Aptabase client. - pub fn with_options(mut self, opts: InitOptions) -> Self { - self.options = opts; - self - } - - /// Sets a custom panic hook. - pub fn with_panic_hook(mut self, hook: PanicHook) -> Self { - self.panic_hook = Some(hook); - self - } - - /// Builds and initializes the plugin - pub fn build(self) -> TauriPlugin { - plugin::Builder::new("aptabase") - .invoke_handler(tauri::generate_handler![commands::track_event]) - .setup(|app, _api| { - let cfg = Config::new(self.app_key, self.options); - let app_version = app.package_info().version.to_string(); - let client = Arc::new(AptabaseClient::new(&cfg, app_version)); - - client.start_polling(cfg.flush_interval); - - if let Some(hook) = self.panic_hook { - let default_panic = std::panic::take_hook(); - let hook_client = client.clone(); - std::panic::set_hook(Box::new(move |info| { - let msg = get_panic_message(info); - hook(&hook_client, info, msg); - hook_client.flush_blocking(); - default_panic(info); - })); - } - - app.manage(client); - Ok(()) - }) - .on_event(move |app, event| { - if let RunEvent::Exit = event { - let client = app.state::>(); - client.flush_blocking(); - } - }) - .build() - } -} - -/// Trait implemented by Tauri handlers -pub trait EventTracker { - /// Enqueues an event to be sent to the server. - fn track_event(&self, name: &str, props: Option) -> Result<(), String>; - - /// Flushes the event queue, blocking the current thread. - fn flush_events_blocking(&self); -} - -impl EventTracker for App { - fn track_event(&self, name: &str, props: Option) -> Result<(), String> { - let client = self.state::>(); - client.track_event(name, props) - } - - fn flush_events_blocking(&self) { - let client = self.state::>(); - client.flush_blocking(); - } -} - -impl EventTracker for AppHandle { - fn track_event(&self, name: &str, props: Option) -> Result<(), String> { - let client = self.state::>(); - client.track_event(name, props) - } - - fn flush_events_blocking(&self) { - let client = self.state::>(); - client.flush_blocking(); - } -} - -impl EventTracker for Window { - fn track_event(&self, name: &str, props: Option) -> Result<(), String> { - let client = self.state::>(); - client.track_event(name, props) - } - - fn flush_events_blocking(&self) { - let client = self.state::>(); - client.flush_blocking(); - } -} +pub use client::AptabaseClient; +pub use config::InitOptions; diff --git a/src/sys.rs b/src/sys.rs index 369d4d3..416b4b1 100644 --- a/src/sys.rs +++ b/src/sys.rs @@ -1,16 +1,7 @@ -use tauri::webview_version; +use crate::config::InitOptions; -#[cfg(target_os = "linux")] -static ENGINE_NAME: &str = "WebKitGTK"; - -#[cfg(target_os = "android")] -static ENGINE_NAME: &str = "Android System WebView"; - -#[cfg(any(target_os = "macos", target_os = "ios"))] -static ENGINE_NAME: &str = "WebKit"; - -#[cfg(target_os = "windows")] -static ENGINE_NAME: &str = "WebView2"; +static DEFAULT_ENGINE_NAME: &str = "Rust"; +static DEFAULT_ENGINE_VERSION: &str = "unknown"; #[cfg(debug_assertions)] static IS_DEBUG: bool = true; @@ -18,7 +9,8 @@ static IS_DEBUG: bool = true; #[cfg(not(debug_assertions))] static IS_DEBUG: bool = false; -pub struct SystemProperties { +#[derive(Debug, Clone, PartialEq, Eq)] +pub(crate) struct SystemProperties { pub is_debug: bool, pub os_name: String, pub os_version: String, @@ -36,10 +28,9 @@ fn is_flatpak() -> bool { .unwrap_or(false) } -pub fn get_info() -> SystemProperties { +pub(crate) fn get_info(options: &InitOptions) -> SystemProperties { let info = os_info::get(); let locale = sys_locale::get_locale().unwrap_or_default(); - let engine_version = webview_version().unwrap_or_default(); let os_name = match info.os_type() { os_info::Type::Macos => "macOS".to_string(), @@ -54,7 +45,38 @@ pub fn get_info() -> SystemProperties { os_name, os_version: info.version().to_string(), locale, - engine_name: ENGINE_NAME.to_string(), - engine_version, + engine_name: options + .engine_name + .clone() + .unwrap_or_else(|| DEFAULT_ENGINE_NAME.to_string()), + engine_version: options + .engine_version + .clone() + .unwrap_or_else(|| DEFAULT_ENGINE_VERSION.to_string()), + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn uses_generic_engine_defaults() { + let info = get_info(&InitOptions::default()); + + assert_eq!(info.engine_name, "Rust"); + assert_eq!(info.engine_version, "unknown"); + } + + #[test] + fn applies_engine_overrides() { + let info = get_info(&InitOptions { + engine_name: Some("My Runtime".into()), + engine_version: Some("2.4.0".into()), + ..InitOptions::default() + }); + + assert_eq!(info.engine_name, "My Runtime"); + assert_eq!(info.engine_version, "2.4.0"); } } diff --git a/webview-dist/index.d.ts b/webview-dist/index.d.ts deleted file mode 100644 index dab0a4e..0000000 --- a/webview-dist/index.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -type Props = { - [key: string]: string | number; -}; -export declare function trackEvent(name: string, props?: Props): Promise; -export {}; diff --git a/webview-dist/index.js b/webview-dist/index.js deleted file mode 100644 index 5f0290a..0000000 --- a/webview-dist/index.js +++ /dev/null @@ -1 +0,0 @@ -function n(n,e,r,t){return new(r||(r=Promise))((function(o,u){function i(n){try{p(t.next(n))}catch(n){u(n)}}function c(n){try{p(t.throw(n))}catch(n){u(n)}}function p(n){var e;n.done?o(n.value):(e=n.value,e instanceof r?e:new r((function(n){n(e)}))).then(i,c)}p((t=t.apply(n,e||[])).next())}))}function e(e,r){return n(this,void 0,void 0,(function*(){yield async function(n,e={},r){return window.__TAURI_INTERNALS__.invoke(n,e,r)}("plugin:aptabase|track_event",{name:e,props:r})}))}"function"==typeof SuppressedError&&SuppressedError,"function"==typeof SuppressedError&&SuppressedError;export{e as trackEvent}; diff --git a/webview-src/index.ts b/webview-src/index.ts deleted file mode 100644 index f98960f..0000000 --- a/webview-src/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { invoke } from '@tauri-apps/api/core' - -type Props = { - [key: string]: string | number; -}; - -export async function trackEvent(name: string, props?: Props): Promise { - await invoke('plugin:aptabase|track_event', { name, props }); -} diff --git a/webview-src/rollup.config.js b/webview-src/rollup.config.js deleted file mode 100644 index bf8450c..0000000 --- a/webview-src/rollup.config.js +++ /dev/null @@ -1,21 +0,0 @@ -import { nodeResolve } from '@rollup/plugin-node-resolve' -import typescript from '@rollup/plugin-typescript' -import terser from '@rollup/plugin-terser' - -export default { - input: './webview-src/index.ts', - output: { - dir: './webview-dist', - entryFileNames: '[name].js', - format: 'es', - exports: 'auto' - }, - plugins: [ - nodeResolve(), - terser(), - typescript({ - tsconfig: './webview-src/tsconfig.json', - moduleResolution: 'node' - }) - ] -} diff --git a/webview-src/tsconfig.json b/webview-src/tsconfig.json deleted file mode 100644 index 816d015..0000000 --- a/webview-src/tsconfig.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2015", - "strict": true, - "allowJs": true, - "esModuleInterop": true, - "baseUrl": ".", - "paths": { - "types": ["@types"] - }, - "declaration": true, - "declarationDir": "../webview-dist", - "rootDir": "./" - }, - "include": ["./"] -} From 3013e1354b269ee01d05832078fa1a31c87b9263 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Fri, 12 Jun 2026 14:34:48 +0100 Subject: [PATCH 05/83] Tidy up changes --- .gitignore | 7 + CHANGELOG.md | 54 - Cargo.lock | 67 +- Cargo.toml | 22 +- LICENSE | 3 +- README.md | 106 +- examples/cli.rs | 12 - examples/helloworld/.env.example | 1 + examples/helloworld/Cargo.lock | 2095 +++++++++++++++++ examples/helloworld/Cargo.toml | 10 + examples/helloworld/src/main.rs | 24 + llms.txt | 18 - mise.toml | 2 +- .../design.md | 40 +- .../proposal.md | 10 +- .../specs/generic-rust-client/spec.md | 35 +- .../tasks.md | 28 +- openspec/specs/generic-rust-client/spec.md | 37 +- src/client.rs | 176 +- src/config.rs | 122 +- src/dispatcher.rs | 199 +- src/lib.rs | 106 +- src/sys.rs | 45 +- 23 files changed, 2512 insertions(+), 707 deletions(-) delete mode 100644 CHANGELOG.md delete mode 100644 examples/cli.rs create mode 100644 examples/helloworld/.env.example create mode 100644 examples/helloworld/Cargo.lock create mode 100644 examples/helloworld/Cargo.toml create mode 100644 examples/helloworld/src/main.rs delete mode 100644 llms.txt diff --git a/.gitignore b/.gitignore index d677453..6e40d6c 100644 --- a/.gitignore +++ b/.gitignore @@ -20,6 +20,13 @@ target # rustc will dump stack traces when hitting an internal compiler error to PWD rustc-ice-*.txt +# +# https://www.toptal.com/developers/gitignore/api/dotenv +# + +### dotenv ### +.env + # # https://github.com/github/gitignore/blob/main/Global/mise.gitignore # diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index ebda304..0000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,54 +0,0 @@ -## Unreleased - -* **Breaking:** replace the Tauri plugin in place with the framework-independent `aptabase-rs` crate. -* Add synchronous event enqueueing, asynchronous explicit flushing, and caller-managed periodic flushing for Tokio applications. -* Remove all Tauri dependencies, APIs, build scripts, permissions, WebView bindings, JavaScript metadata, and Tauri examples. - -## 1.0.0 - -* Update Tauri v2 - -## 0.5.1 - -* Fixed an issue where `with_panic_hook` would swallow the panic after logging it - -## 0.5.0 - -* Fixed an issue where `with_panic_hook` would panic -* Added `msg: String` to `with_panic_hook` - -## 0.4.2 - -* Use new session id format - -## 0.4.1 - -* Automatic flush of events on app exit -* Fix User-Agent header - -## 0.4.0 - -* Events are now sent in batches to reduce network overhead -* While offline, events will be enqueue and sent when the app is back online -* Tauri 1.4 required - -## 0.3.2 - -* (macOS) Fixed an issue where sessions could span multiple days if the app was left open overnight - -## 0.3.1 - -* Wait for event to be flushed on panic - -## 0.3.0 - -* Add ability for panic hook to log panics to aptabase - -## 0.2.1 - -* Added support for automatic segregation of Debug/Release data source - -## 0.2.0 - -* BREAKING CHANGE: replaced the `init` function with a `Builder` struct, see README for example usage -* Ability to set custom hosts for self hosted servers diff --git a/Cargo.lock b/Cargo.lock index c42ff27..3c2c8c3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "android_system_properties" @@ -19,12 +19,14 @@ checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" [[package]] name = "aptabase-rs" -version = "1.0.0" +version = "0.1.0" dependencies = [ + "futures", "log", "os_info", "rand", "reqwest", + "serde", "serde_json", "sys-locale", "time", @@ -221,6 +223,21 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "futures" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + [[package]] name = "futures-channel" version = "0.3.32" @@ -228,6 +245,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" dependencies = [ "futures-core", + "futures-sink", ] [[package]] @@ -236,6 +254,34 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" +[[package]] +name = "futures-executor" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" + +[[package]] +name = "futures-macro" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "futures-sink" version = "0.3.32" @@ -254,8 +300,13 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" dependencies = [ + "futures-channel", "futures-core", + "futures-io", + "futures-macro", + "futures-sink", "futures-task", + "memchr", "pin-project-lite", "slab", ] @@ -1391,21 +1442,9 @@ dependencies = [ "mio", "pin-project-lite", "socket2", - "tokio-macros", "windows-sys 0.61.2", ] -[[package]] -name = "tokio-macros" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "tokio-native-tls" version = "0.3.1" diff --git a/Cargo.toml b/Cargo.toml index 723d4e1..4d2e2c2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,20 +1,19 @@ [package] name = "aptabase-rs" -version = "1.0.0" +version = "0.1.0" +authors = ["Eugรฉn Cowie"] +edition = "2024" +description = "Rust SDK for Aptabase: Open Source, Privacy-First and Simple Analytics for Mobile, Desktop and Web Apps" +homepage = "https://github.com/eugencowie/aptabase-rs" +repository = "https://github.com/eugencowie/aptabase-rs" license = "MIT" -description = "Aptabase analytics client for Rust applications" -authors = ["Guilherme Oenning"] -edition = "2021" -rust-version = "1.70" -readme = "README.md" -repository = "https://github.com/aptabase/aptabase-rs" -homepage = "https://aptabase.com" keywords = ["analytics", "aptabase", "telemetry"] categories = ["api-bindings"] -exclude = ["/openspec", "/AGENTS.md", "/mise.toml", "/mise.lock", "/llms.txt"] [dependencies] -tokio = { version = "1.43.0", features = ["rt", "time"] } +tokio = "1.43.0" +futures = "0.3.31" +serde = "1.0.217" serde_json = "1.0.138" reqwest = { version = "0.12.12", features = ["json"] } time = { version = "0.3.37", features = ["formatting"] } @@ -22,6 +21,3 @@ os_info = "3.9.2" rand = "0.9.0" log = "0.4.25" sys-locale = "0.3.2" - -[dev-dependencies] -tokio = { version = "1.43.0", features = ["macros", "rt-multi-thread", "time"] } diff --git a/LICENSE b/LICENSE index ee62ce0..9fde467 100644 --- a/LICENSE +++ b/LICENSE @@ -1,5 +1,6 @@ MIT License +Copyright (c) 2026 Eugรฉn Cowie Copyright (c) 2023 Sumbit Labs Ltd. Permission is hereby granted, free of charge, to any person obtaining a copy @@ -18,4 +19,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 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. \ No newline at end of file +SOFTWARE. diff --git a/README.md b/README.md index 8ab4609..23fcd7b 100644 --- a/README.md +++ b/README.md @@ -1,27 +1,39 @@ -# aptabase-rs +> [!NOTE] +> This project is not affiliated with Aptabase or Sumbit Labs Ltd. -`aptabase-rs` is a framework-independent Aptabase client for Rust applications. It synchronously queues events in memory and delivers them with an asynchronous Tokio/Reqwest transport. +# Rust SDK for Aptabase -## Installation +`aptabase-rs` is a framework-independent Rust SDK for Aptabase, an Open Source, Privacy-First, and Simple Analytics for Mobile, Desktop, and Web Apps. + +## Install + +Install the SDK by adding the following to your `Cargo.toml` file: ```toml [dependencies] -aptabase-rs = "1.0.0" +aptabase-rs = "0.1.0" serde_json = "1" tokio = { version = "1", features = ["macros", "rt-multi-thread"] } ``` -## CLI usage +## Usage + +First, you need to get your `App Key` from Aptabase, you can find it in the `Instructions` menu on the left side menu. -Create the client with an Aptabase app key and your application version. Event tracking does not perform network I/O; explicitly await `flush` before a short-lived process exits. +Then create the client with your app key and application version: ```rust -use aptabase_rs::AptabaseClient; +use aptabase_rs::Builder; use serde_json::json; +use std::error::Error; #[tokio::main] -async fn main() -> Result<(), Box> { - let client = AptabaseClient::new("A-US-your-app-key", env!("CARGO_PKG_VERSION")); +async fn main() -> Result<(), Box> { + let client = Builder::new( + "", + env!("CARGO_PKG_VERSION"), + ) + .build(); client.track_event("command_started", Some(json!({ "command": "sync" })))?; client.flush().await; @@ -30,59 +42,95 @@ async fn main() -> Result<(), Box> { } ``` +`Builder::build` constructs the client without performing network I/O or starting a background worker. Event tracking only enqueues in memory at the call site; explicitly await `flush` before a short-lived process exits. + Properties must be a `serde_json::Value::Object`. Invalid app keys disable tracking without making construction fail. ## Periodic flushing -Long-running applications can start a periodic worker on an active Tokio runtime. The interval defaults to 60 seconds in release builds and 2 seconds in debug builds. +Long-running applications can opt in to periodic flushing with `with_polling(true)`. When polling is enabled, construct the client inside an active Tokio runtime. The interval defaults to 60 seconds in release builds and 2 seconds in debug builds. ```rust -use aptabase_rs::AptabaseClient; +use aptabase_rs::{Builder, InitOptions}; +use std::time::Duration; -let client = AptabaseClient::new("A-EU-your-app-key", "2.0.0"); -let worker = client.start_periodic_flush(); +let client = Builder::new("A-EU-your-app-key", "2.0.0") + .with_options(InitOptions { + host: None, + flush_interval: Some(Duration::from_secs(30)), + }) + .with_polling(true) + .build(); client.track_event("service_started", None)?; - -// Stop the worker during shutdown, then deliver anything still queued. -worker.abort(); client.flush().await; # Ok::<(), Box>(()) ``` -Start at most one periodic worker per client. Events are kept only in memory, so applications should explicitly flush during orderly shutdown. +Events are kept only in memory, so applications should explicitly flush during orderly shutdown. ## Configuration -Use `InitOptions` for a self-hosted endpoint, a custom periodic interval, or runtime metadata overrides. +Use `InitOptions` for a self-hosted endpoint or for the interval used when periodic polling is enabled. ```rust -use aptabase_rs::{AptabaseClient, InitOptions}; +use aptabase_rs::{Builder, InitOptions}; use std::time::Duration; -let client = AptabaseClient::with_options( - "A-SH-your-app-key", - "1.4.0", - InitOptions { +let client = Builder::new("A-SH-your-app-key", "1.4.0") + .with_options(InitOptions { host: Some("https://analytics.example.com".into()), flush_interval: Some(Duration::from_secs(30)), - engine_name: Some("my-cli-runtime".into()), - engine_version: Some("1.4.0".into()), - }, -); + }) + .build(); ``` Hosted keys beginning with `A-US-` and `A-EU-` use Aptabase's regional endpoints. `A-DEV-` targets `http://localhost:3000`, and `A-SH-` requires `host`. +## Panic hook + +You can use the default panic hook to enqueue a `panic` event before the process continues to the default panic handler. If the panicking thread has an active Tokio runtime, the hook makes a best-effort delivery attempt with the same Tokio-backed transport. Without an active Tokio runtime on that thread, the panic event is only enqueued in memory before the previous panic hook runs. Applications that need predictable shutdown delivery should still call `flush().await` from normal control flow. + +```rust +use aptabase_rs::Builder; + +let client = Builder::new("A-EU-your-app-key", env!("CARGO_PKG_VERSION")) + .with_default_panic_hook() + .build(); +# let _ = client; +``` + +For custom panic event payloads, provide your own hook: + +```rust +use aptabase_rs::Builder; +use serde_json::json; + +let client = Builder::new("A-EU-your-app-key", env!("CARGO_PKG_VERSION")) + .with_panic_hook(Box::new(|client, info, message| { + let location = info + .location() + .map(|loc| format!("{}:{}:{}", loc.file(), loc.line(), loc.column())) + .unwrap_or_default(); + + let _ = client.track_event( + "panic", + Some(json!({ "info": format!("{} ({})", message, location) })), + ); + })) + .build(); +# let _ = client; +``` + ## Delivery behavior - Requests contain at most 25 events and use Aptabase's `/api/v0/events` endpoint. - Transport failures and HTTP 5xx responses are requeued in memory for a later flush. - Other unsuccessful HTTP responses are discarded. -- This crate provides only the asynchronous Tokio transport; it does not provide a blocking client. +- This crate provides only the asynchronous Tokio transport; it does not expose a blocking flush API. ## Privacy -The client never tracks events automatically. Applications decide which event names and properties to send. Each event includes the app version, SDK version, debug status, OS name and version, locale, and configured engine name and version. Avoid sending personal or sensitive data in event names or properties. +The client never tracks events automatically. Applications decide which event names and properties to send. Each event includes the app version, SDK version, debug status, OS name and version, and locale. Avoid sending personal or sensitive data in event names or properties. See [Aptabase](https://aptabase.com) for service documentation and privacy guidance. diff --git a/examples/cli.rs b/examples/cli.rs deleted file mode 100644 index 36f8711..0000000 --- a/examples/cli.rs +++ /dev/null @@ -1,12 +0,0 @@ -use aptabase_rs::AptabaseClient; - -#[tokio::main] -async fn main() -> Result<(), Box> { - let app_key = std::env::var("APTABASE_APP_KEY").unwrap_or_default(); - let client = AptabaseClient::new(app_key, env!("CARGO_PKG_VERSION")); - - client.track_event("cli_started", None)?; - client.flush().await; - - Ok(()) -} diff --git a/examples/helloworld/.env.example b/examples/helloworld/.env.example new file mode 100644 index 0000000..f50fe9d --- /dev/null +++ b/examples/helloworld/.env.example @@ -0,0 +1 @@ +APTABASE_KEY= diff --git a/examples/helloworld/Cargo.lock b/examples/helloworld/Cargo.lock new file mode 100644 index 0000000..786c082 --- /dev/null +++ b/examples/helloworld/Cargo.lock @@ -0,0 +1,2095 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anyhow" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + +[[package]] +name = "aptabase-rs" +version = "0.1.0" +dependencies = [ + "futures", + "log", + "os_info", + "rand", + "reqwest", + "serde", + "serde_json", + "sys-locale", + "time", + "tokio", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bitflags" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" + +[[package]] +name = "block2" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5" +dependencies = [ + "objc2", +] + +[[package]] +name = "bumpalo" +version = "3.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" + +[[package]] +name = "bytes" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" + +[[package]] +name = "cc" +version = "1.2.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dad887fd958be91b5098c0248def011f4523ab786cd411be668777e55063501f" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "deranged" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" + +[[package]] +name = "dispatch2" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" +dependencies = [ + "bitflags", + "objc2", +] + +[[package]] +name = "displaydoc" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "dotenvy" +version = "0.15.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" + +[[package]] +name = "dotenvy_macro" +version = "0.15.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb0235d912a8c749f4e0c9f18ca253b4c28cfefc1d2518096016d6e3230b6424" +dependencies = [ + "dotenvy", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "fastrand" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-executor" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" + +[[package]] +name = "futures-macro" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "slab", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi 5.3.0", + "wasip2", +] + +[[package]] +name = "getrandom" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +dependencies = [ + "cfg-if", + "libc", + "r-efi 6.0.0", + "wasip2", + "wasip3", +] + +[[package]] +name = "h2" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cb093c84e8bd9b188d4c4a8cb6579fc016968d14c99882163cd3ff402a4f155" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "helloworld" +version = "0.1.0" +dependencies = [ + "aptabase-rs", + "dotenvy_macro", + "serde_json", + "tokio", +] + +[[package]] +name = "http" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6970f50e31d6fc17d3fa27329444bfa74e196cf62e95052a3f6fee181dba6425" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "hyper" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55281c53a1894c864990125767da440a4e630446785086f52523b20033b74498" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "h2", + "http", + "http-body", + "httparse", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f" +dependencies = [ + "http", + "hyper", + "hyper-util", + "rustls", + "tokio", + "tokio-rustls", + "tower-service", +] + +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" +dependencies = [ + "base64", + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "ipnet", + "libc", + "percent-encoding", + "pin-project-lite", + "socket2", + "system-configuration", + "tokio", + "tower-service", + "tracing", + "windows-registry", +] + +[[package]] +name = "icu_collections" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" +dependencies = [ + "displaydoc", + "potential_utf", + "utf8_iter", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" + +[[package]] +name = "icu_properties" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" + +[[package]] +name = "icu_provider" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown 0.17.1", + "serde", + "serde_core", +] + +[[package]] +name = "ipnet" +version = "2.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "js-sys" +version = "0.3.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03d04c30968dffe80775bd4d7fb676131cd04a1fb46d2686dbffbaec2d9dfd31" +dependencies = [ + "cfg-if", + "futures-util", + "wasm-bindgen", +] + +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + +[[package]] +name = "litemap" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" + +[[package]] +name = "log" +version = "0.4.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "953f07c43838f8e6f9758cab68bf5bed85465e7587ebe0b823f1bcd81978ad3a" + +[[package]] +name = "memchr" +version = "2.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "mio" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "native-tls" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "465500e14ea162429d264d44189adc38b199b62b1c21eea9f69e4b73cb03bbf2" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "nix" +version = "0.31.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d" +dependencies = [ + "bitflags", + "cfg-if", + "cfg_aliases", + "libc", +] + +[[package]] +name = "num-conv" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" + +[[package]] +name = "objc2" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f" +dependencies = [ + "objc2-encode", +] + +[[package]] +name = "objc2-cloud-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ad74d880bb43877038da939b7427bba67e9dd42004a18b809ba7d87cee241c" +dependencies = [ + "bitflags", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-data" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b402a653efbb5e82ce4df10683b6b28027616a2715e90009947d50b8dd298fa" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" +dependencies = [ + "bitflags", + "dispatch2", + "objc2", +] + +[[package]] +name = "objc2-core-graphics" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807" +dependencies = [ + "bitflags", + "dispatch2", + "objc2", + "objc2-core-foundation", + "objc2-io-surface", +] + +[[package]] +name = "objc2-core-image" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d563b38d2b97209f8e861173de434bd0214cf020e3423a52624cd1d989f006" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-location" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca347214e24bc973fc025fd0d36ebb179ff30536ed1f80252706db19ee452009" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-text" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cde0dfb48d25d2b4862161a4d5fcc0e3c24367869ad306b0c9ec0073bfed92d" +dependencies = [ + "bitflags", + "objc2", + "objc2-core-foundation", + "objc2-core-graphics", +] + +[[package]] +name = "objc2-encode" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" + +[[package]] +name = "objc2-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" +dependencies = [ + "bitflags", + "block2", + "libc", + "objc2", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-io-surface" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d" +dependencies = [ + "bitflags", + "objc2", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-quartz-core" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c1358452b371bf9f104e21ec536d37a650eb10f7ee379fff67d2e08d537f1f" +dependencies = [ + "bitflags", + "objc2", + "objc2-core-foundation", + "objc2-foundation", +] + +[[package]] +name = "objc2-ui-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d87d638e33c06f577498cbcc50491496a3ed4246998a7fbba7ccb98b1e7eab22" +dependencies = [ + "bitflags", + "block2", + "objc2", + "objc2-cloud-kit", + "objc2-core-data", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-core-image", + "objc2-core-location", + "objc2-core-text", + "objc2-foundation", + "objc2-quartz-core", + "objc2-user-notifications", +] + +[[package]] +name = "objc2-user-notifications" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9df9128cbbfef73cda168416ccf7f837b62737d748333bfe9ab71c245d76613e" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "openssl" +version = "0.10.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77823a27f0babb03091cb9ed9ef80af3b39dbc82f97e8fa530374b7dafd87a45" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "openssl-probe" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" + +[[package]] +name = "openssl-sys" +version = "0.9.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b47e7e6bb2c38cd930d25a23b40fa52e068c10e85f3e03a7f5ba5aaca5713695" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "os_info" +version = "3.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf20a545b305cf1da722b236b5155c9bb35f1d5ceb28c048bd96ca842f41b5b" +dependencies = [ + "android_system_properties", + "log", + "nix", + "objc2", + "objc2-foundation", + "objc2-ui-kit", + "serde", + "windows-sys 0.61.2", +] + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "pkg-config" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" + +[[package]] +name = "potential_utf" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" +dependencies = [ + "zerovec", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn 2.0.117", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "rand" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" +dependencies = [ + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom 0.3.4", +] + +[[package]] +name = "reqwest" +version = "0.12.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-tls", + "hyper-util", + "js-sys", + "log", + "mime", + "native-tls", + "percent-encoding", + "pin-project-lite", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tokio-native-tls", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.17", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls" +version = "0.23.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b" +dependencies = [ + "once_cell", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pki-types" +version = "1.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9" +dependencies = [ + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "ryu" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" + +[[package]] +name = "schannel" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "security-framework" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" +dependencies = [ + "bitflags", + "core-foundation 0.10.1", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "serde_json" +version = "1.0.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" + +[[package]] +name = "socket2" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "sys-locale" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eab9a99a024a169fe8a903cf9d4a3b3601109bcc13bd9e3c6fff259138626c4" +dependencies = [ + "libc", +] + +[[package]] +name = "system-configuration" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" +dependencies = [ + "bitflags", + "core-foundation 0.9.4", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "tempfile" +version = "3.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" +dependencies = [ + "fastrand", + "getrandom 0.4.2", + "once_cell", + "rustix", + "windows-sys 0.61.2", +] + +[[package]] +name = "time" +version = "0.3.49" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711a53c2d47bbd818258c498c8dbfe186a2526c631495cfe7e078567f86b8469" +dependencies = [ + "deranged", + "num-conv", + "powerfmt", + "serde_core", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" + +[[package]] +name = "time-macros" +version = "0.2.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71c652a3727a9cbb9a02f707f530b618ce00d0ccd762009c8c23bd191df3c17d" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinystr" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tokio" +version = "1.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" +dependencies = [ + "bytes", + "libc", + "mio", + "pin-project-lite", + "socket2", + "tokio-macros", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-macros" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tower" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-http" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" +dependencies = [ + "bitflags", + "bytes", + "futures-util", + "http", + "http-body", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", + "url", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.4+wasi-0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487" +dependencies = [ + "wit-bindgen 0.57.1", +] + +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +dependencies = [ + "wit-bindgen 0.51.0", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.125" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ddb3f79143bced6de84270411622a2699cee572fc0875aeaf1e7867cf9fca1a" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "503b14d284f2c8dac03b819967e155ea753f573586193b2b2c95990cb5d69280" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.125" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e21a184b13fb19e157296e2c46056aec9092264fab83e4ba59e68c61b323c3d" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.125" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fecefd9c35bd935a20fc3fc344b5f29138961e4f47fb03297d88f2587afb5ebd" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn 2.0.117", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.125" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23939e44bb9a5d7576fa2b563dc2e136628f1224e88a8deed09e04858b77871f" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags", + "hashbrown 0.15.5", + "indexmap", + "semver", +] + +[[package]] +name = "web-sys" +version = "0.3.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6430a72df5eb332242960fe84b3002a241163998241eb596d4f739b9757061d" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-registry" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" +dependencies = [ + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "prettyplease", + "syn 2.0.117", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn 2.0.117", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] + +[[package]] +name = "writeable" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" + +[[package]] +name = "yoke" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "709fe23a0424b6a435d82152b1bd3fdfb0833487d5fa90d05d42762a9891fef5" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.8.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce1022995ff5ff5d841ad7d994facc23098cd40152f2c1d11cd607c6f530653f" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ae7f38b72ec2a254e2b87ef277cf2cd4fb97cbebf944faa6f33354da0867930" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "zerofrom" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" + +[[package]] +name = "zerotrie" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/examples/helloworld/Cargo.toml b/examples/helloworld/Cargo.toml new file mode 100644 index 0000000..7170031 --- /dev/null +++ b/examples/helloworld/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "helloworld" +version = "0.1.0" +edition = "2024" + +[dependencies] +aptabase-rs = { version = "0.1.0", path = "../.." } +dotenvy_macro = "0.15.7" +serde_json = "1.0.138" +tokio = { version = "1.43.0", features = ["macros", "rt-multi-thread"] } diff --git a/examples/helloworld/src/main.rs b/examples/helloworld/src/main.rs new file mode 100644 index 0000000..b6bbabf --- /dev/null +++ b/examples/helloworld/src/main.rs @@ -0,0 +1,24 @@ +// Copy .env.example to .env and set APTABASE_KEY. + +use aptabase_rs::Builder; +use dotenvy_macro::dotenv; +use serde_json::json; +use std::error::Error; + +#[tokio::main] +async fn main() -> Result<(), Box> { + let client = Builder::new(dotenv!("APTABASE_KEY"), env!("CARGO_PKG_VERSION")) + .with_default_panic_hook() + .with_polling(true) // long-running processes can use polling to send events at regular intervals + .build(); + + client.track_event( + "cli_started", + Some(json!({ + "msg": "Hello, world!" + })), + )?; + + client.flush().await; + Ok(()) +} diff --git a/llms.txt b/llms.txt deleted file mode 100644 index 584c07d..0000000 --- a/llms.txt +++ /dev/null @@ -1,18 +0,0 @@ -# Aptabase Rust Client - -Rust crate: `aptabase-rs` -Repository: https://github.com/aptabase/aptabase-rs - -`aptabase-rs` is a framework-independent Aptabase analytics client for Tokio-based Rust applications. - -## API - -- Construct with `AptabaseClient::new(app_key, app_version)`. -- Use `AptabaseClient::with_options(...)` for a self-hosted host, flush interval, or engine metadata overrides. -- Call `track_event(name, props)` to synchronously enqueue an event. Properties must be absent or a JSON object. -- Await `flush()` before a short-lived process exits. -- Call `start_periodic_flush()` in long-running applications and abort the returned Tokio task during shutdown. - -Hosted app keys use `A-US-*` or `A-EU-*`. Development keys use `A-DEV-*`. Self-hosted keys use `A-SH-*` and require `InitOptions::host`. - -The queue is in memory. Transport and HTTP 5xx failures are requeued; other unsuccessful HTTP responses are discarded. No events are tracked automatically. diff --git a/mise.toml b/mise.toml index 6f46233..b46fe2c 100644 --- a/mise.toml +++ b/mise.toml @@ -1,4 +1,4 @@ [tools] node = "latest" "npm:@fission-ai/openspec" = "latest" -rust = "nightly" +rust = "latest" diff --git a/openspec/changes/archive/2026-06-12-create-generic-aptabase-rust-client/design.md b/openspec/changes/archive/2026-06-12-create-generic-aptabase-rust-client/design.md index e634b3e..b58b078 100644 --- a/openspec/changes/archive/2026-06-12-create-generic-aptabase-rust-client/design.md +++ b/openspec/changes/archive/2026-06-12-create-generic-aptabase-rust-client/design.md @@ -10,21 +10,20 @@ This change is an in-place rewrite of the existing source tree. The repository w - Convert the repository's root package into the `aptabase-rs` crate with no Tauri dependency. - Retain and adapt the existing configuration, session, event construction, queue, batching, retry, and HTTP dispatch code with minimal behavioral changes. -- Provide explicit asynchronous flushing suitable for short-lived CLI applications and optional periodic flushing for long-running applications. -- Preserve the Aptabase ingestion endpoint, headers, batch size, event field names, session behavior, and retry policy. +- Provide explicit asynchronous flushing suitable for short-lived CLI applications and optional detached periodic flushing for long-running applications. +- Preserve the Aptabase ingestion endpoint, headers, batch size, retained event field names, session behavior, and retry policy. - Remove every Tauri-specific source file, dependency, build artifact definition, permission, frontend binding, example, and documentation path from this repository. -- Add focused tests around the retained behavior. **Non-Goals:** - Preserving the Tauri plugin API or maintaining source compatibility for existing plugin consumers. - Keeping a Tauri adapter, compatibility crate, workspace member, or deprecated shim in this repository. -- Adding automatic command, argument, crash, or usage tracking to CLI applications. +- Adding automatic command, argument, crash, or usage tracking to CLI applications without explicit opt-in. - Persisting unsent events to disk or bounding the in-memory queue in this change. - Changing Aptabase's event schema, ingestion protocol, or retry semantics. - Providing a synchronous HTTP implementation independent of Tokio. - Publishing `aptabase-rs` to crates.io as part of the code change. -- Providing a runtime-independent blocking client or a `reqwest::blocking` transport. +- Providing a separate blocking HTTP client or a `reqwest::blocking` transport. ## Decisions @@ -53,11 +52,12 @@ The root crate will expose a client that supports: - construction from an app key, app version, and options; - synchronous event enqueueing; - asynchronous `flush`; -- optional periodic flushing started from an active Tokio runtime. +- optional panic hooks; +- optional detached periodic flushing started from an active Tokio runtime. -Explicit `flush().await` is the CLI shutdown mechanism. Periodic flushing remains available for long-running applications. A runtime-independent blocking client is explicitly outside this proposal and will not be implemented. +Explicit `flush().await` is the supported CLI shutdown mechanism. The crate does not expose a blocking flush API because delivery uses the Tokio-backed async transport rather than a separate blocking HTTP client. Panic hooks may make a best-effort delivery attempt internally when the panicking thread has an active Tokio runtime; otherwise panic events remain queued in memory. Periodic flushing remains available for long-running applications as a detached task that runs until the Tokio runtime shuts down. -Using only a detached background task was considered, but short-lived CLI commands can terminate before queued events are transmitted. A separate `reqwest::blocking` implementation was also considered, but it would duplicate transport code and expand the initial scope. +Using only a background task would let short-lived CLI commands terminate before queued events are transmitted, so explicit flushing is part of the public API. A caller-managed polling handle and a separate `reqwest::blocking` implementation were considered, but both were left out to keep the adaptation narrow. ### Preserve tolerant initialization and event behavior @@ -65,11 +65,11 @@ Invalid or incomplete app-key configuration will continue to disable tracking an Introducing a fully fallible constructor and a new public error hierarchy was considered. That would be reasonable for a new SDK, but it conflicts with the goal of minimal adaptation. Public error refinement can follow as a separate change. -### Replace WebView metadata with generic runtime metadata +### Replace WebView metadata with generic system metadata -The crate will collect OS name, OS version, locale, and debug status without Tauri. It will provide generic Rust runtime or engine defaults and allow callers to override the engine name and version used in `systemProps` and the user agent. +The crate will collect OS name, OS version, locale, and debug status without Tauri. It will not provide runtime or engine metadata defaults, and it will omit the old WebView-specific `engineName` and `engineVersion` fields from `systemProps` and the user agent. -Removing the engine fields was considered, but preserving the payload shape is lower risk and keeps the retained event construction close to the existing implementation. +Preserving engine fields was considered, but retaining them without Tauri would require invented defaults or new public overrides that the implementation does not provide. ### Keep the current in-memory batching and retry policy @@ -77,17 +77,17 @@ The dispatcher will continue to send at most 25 events per request, requeue even Changing retry policy, adding backoff, persistence, queue limits, or delivery acknowledgements would make this an SDK redesign rather than a low-change rewrite. -### Do not install global panic hooks +### Retain opt-in panic hooks -The generic client will provide tracking and flushing primitives but will not install or manage a global panic hook. Panic handling is application policy, and the existing implementation belongs to the Tauri shell that is being removed. +The generic builder will retain opt-in custom and default panic hooks. Calling these APIs installs a global Rust panic hook, enqueues panic information, makes a best-effort delivery attempt through the Tokio-backed async transport when the panicking thread has an active Tokio runtime, and delegates to the previous panic hook. Without an active Tokio runtime on the panicking thread, panic events remain queued in memory. No panic hook is installed unless the application explicitly enables one. ## Risks / Trade-offs - [The rewrite breaks all existing Tauri plugin consumers] -> Mark the package conversion as breaking and rewrite documentation so the repository no longer claims Tauri compatibility. - [In-place deletion removes a potentially useful plugin history from the working tree] -> Rely on Git history; do not retain dead compatibility code in the new crate. -- [Adaptation accidentally changes event payloads] -> Add serialization tests using representative events and verify required field names and metadata behavior. -- [CLI processes exit before delivery] -> Document and test explicit `flush().await` as part of normal command shutdown. -- [A periodic task can outlive useful client state or be difficult to stop] -> Return a task handle or cancellation guard so callers can explicitly manage it. +- [Adaptation changes event payload metadata] -> Document that WebView engine fields are omitted by the generic client. +- [CLI processes exit before delivery] -> Document explicit `flush().await` as part of normal command shutdown. +- [A periodic task can outlive useful client state or be difficult to stop] -> Document that polling is detached and runs until Tokio runtime shutdown; a caller-managed handle can be added later. - [Concurrent flushes can reorder delivery] -> Preserve current locking behavior and document that callers should start at most one periodic worker per client. - [Unbounded in-memory retry queue can grow during long outages] -> Document the inherited limitation and defer queue limits or persistence to a follow-up change. @@ -96,12 +96,12 @@ The generic client will provide tracking and flushing primitives but will not in 1. Rewrite the root package metadata and public module surface for the generic Aptabase crate. 2. Adapt the existing configuration and system metadata modules to remove Tauri references. 3. Adapt the existing client and dispatcher modules and expose the generic tracking and flushing API. -4. Add unit and local HTTP integration tests for retained behavior. -5. Delete Tauri-only Rust code, dependencies, build scripts, permissions, webview bindings, JavaScript metadata, generated files, examples, and Tauri-focused documentation. -6. Run formatting, tests, checks, documentation tests, and dependency inspection on the rewritten root package. +4. Delete Tauri-only Rust code, dependencies, build scripts, permissions, webview bindings, JavaScript metadata, generated files, the old Tauri example, and Tauri-focused documentation. +5. Add a minimal Tokio CLI example for the rewritten root package. +6. Run Cargo checks, documentation tests, and dependency inspection on the rewritten root package. Rollback consists of reverting the change in Git. No persisted data or external migration is involved. ## Open Questions -None. The crates.io package name is `aptabase-rs`, and runtime-independent blocking client support is outside this proposal. +None. The crates.io package name is `aptabase-rs`, and a separate blocking HTTP transport is outside this proposal. diff --git a/openspec/changes/archive/2026-06-12-create-generic-aptabase-rust-client/proposal.md b/openspec/changes/archive/2026-06-12-create-generic-aptabase-rust-client/proposal.md index 8d5556e..8b42e94 100644 --- a/openspec/changes/archive/2026-06-12-create-generic-aptabase-rust-client/proposal.md +++ b/openspec/changes/archive/2026-06-12-create-generic-aptabase-rust-client/proposal.md @@ -6,9 +6,9 @@ The existing Tauri plugin already contains most of a generic Aptabase Rust clien - **BREAKING**: Replace the root `tauri-plugin-aptabase` package in this repository with the generic `aptabase-rs` Rust client crate. - Reuse the existing configuration, client, dispatcher, session, system metadata, batching, and retry implementations with only the changes required to remove Tauri coupling. -- Expose a public client API for configuration, event tracking, asynchronous flushing, and optional periodic flushing. -- Keep runtime-independent blocking client support out of scope; callers use the asynchronous API from a Tokio runtime. -- Replace WebView-specific metadata collection with generic Rust application metadata while preserving the Aptabase event payload contract. +- Expose a public client API for configuration, event tracking, asynchronous flushing, optional panic hooks, and detached periodic flushing. +- Keep a separate `reqwest::blocking` transport out of scope; callers use the Tokio-backed flush path. +- Replace WebView-specific metadata collection with generic Rust application metadata and omit WebView engine fields from event payloads. - Delete all Tauri-specific Rust integration, dependencies, build machinery, permissions, JavaScript bindings, generated plugin files, and Tauri examples from this repository. - Rewrite the package metadata and documentation for the generic crate and CLI-oriented usage. @@ -16,7 +16,7 @@ The existing Tauri plugin already contains most of a generic Aptabase Rust clien ### New Capabilities -- `generic-rust-client`: Configure Aptabase, enqueue typed events, manage sessions, batch and retry ingestion requests, and flush analytics from non-Tauri Rust applications. +- `generic-rust-client`: Configure Aptabase, enqueue typed events, manage sessions, batch and retry ingestion requests, optionally install panic hooks, and flush analytics from non-Tauri Rust applications. ### Modified Capabilities @@ -26,7 +26,7 @@ None. - The repository remains a single root Rust package, renamed to `aptabase-rs`, rather than a Tauri plugin. - The implementation in `src/config.rs`, `src/client.rs`, `src/dispatcher.rs`, and most of `src/sys.rs` will be retained and adapted in place. -- The Tauri-specific portions of `src/lib.rs`, all of `src/commands.rs`, `build.rs`, `permissions/`, `webview-src/`, `webview-dist/`, `examples/helloworld/`, and JavaScript package metadata will be removed. +- The Tauri-specific portions of `src/lib.rs`, all of `src/commands.rs`, `build.rs`, `permissions/`, `webview-src/`, `webview-dist/`, the old Tauri example, and JavaScript package metadata will be removed. A minimal Tokio CLI example remains under `examples/helloworld/`. - Tauri and `tauri-plugin` dependencies will be removed; Tokio, Reqwest, Serde, `time`, `rand`, `os_info`, `sys-locale`, and `log` will remain where required. - Existing users of `tauri-plugin-aptabase` cannot upgrade to this rewritten package as a compatible Tauri plugin; retaining or relocating that plugin is outside this change. - Public lifecycle behavior must support short-lived CLI processes without requiring a Tauri application runtime. diff --git a/openspec/changes/archive/2026-06-12-create-generic-aptabase-rust-client/specs/generic-rust-client/spec.md b/openspec/changes/archive/2026-06-12-create-generic-aptabase-rust-client/specs/generic-rust-client/spec.md index bafa18b..2a83692 100644 --- a/openspec/changes/archive/2026-06-12-create-generic-aptabase-rust-client/specs/generic-rust-client/spec.md +++ b/openspec/changes/archive/2026-06-12-create-generic-aptabase-rust-client/specs/generic-rust-client/spec.md @@ -34,7 +34,7 @@ The client SHALL select the Aptabase ingestion endpoint from the app-key region, - **THEN** construction succeeds and subsequent event tracking performs no network delivery ### Requirement: Event enqueueing and payload compatibility -The client SHALL synchronously enqueue named events without performing network I/O at the call site. Each event SHALL contain an RFC 3339 UTC timestamp, session ID, event name, system properties, application version, SDK version, and optional properties. Supplied properties MUST be a JSON object. +The client SHALL synchronously enqueue named events without performing network I/O at the call site. Each event SHALL contain an RFC 3339 UTC timestamp, session ID, event name, `systemProps` with debug status, operating-system name, operating-system version, locale, application version, SDK version, and optional properties. Supplied properties MUST be a JSON object. #### Scenario: Enqueue an event without properties - **WHEN** a caller tracks an event name without properties on an enabled client @@ -89,8 +89,8 @@ The client SHALL requeue a batch after a transport failure or server-error respo - **WHEN** Aptabase responds to a batch with a non-5xx unsuccessful status - **THEN** the batch is removed from the queue and is not retried automatically -### Requirement: Explicit asynchronous flushing -The client SHALL provide an asynchronous flush operation that can be awaited before a short-lived process exits. The operation SHALL attempt delivery of queued batches using Tokio. The crate SHALL NOT provide a runtime-independent blocking client as part of this change. +### Requirement: Explicit flushing +The client SHALL provide an asynchronous flush operation that can be awaited before a short-lived process exits. The operation SHALL attempt delivery of queued batches using Tokio. The crate SHALL NOT expose a blocking flush API or provide a separate `reqwest::blocking` transport as part of this change. #### Scenario: Flush before CLI exit - **WHEN** a Tokio-based CLI tracks an event and awaits the client's flush operation before returning @@ -98,22 +98,33 @@ The client SHALL provide an asynchronous flush operation that can be awaited bef #### Scenario: Use the supported transport model - **WHEN** a caller needs to deliver queued events -- **THEN** the caller uses the Tokio-based asynchronous flush API rather than a runtime-independent blocking client +- **THEN** the caller uses the Tokio-based asynchronous flush API ### Requirement: Optional periodic flushing -The client SHALL allow a caller with an active Tokio runtime to start periodic flushing using the configured interval. The caller SHALL receive a means to manage or stop the periodic worker. +The client SHALL allow a caller with an active Tokio runtime to start detached periodic flushing using the configured interval. The periodic worker SHALL run until the Tokio runtime shuts down. #### Scenario: Start periodic delivery - **WHEN** a long-running application starts periodic flushing -- **THEN** the client attempts to flush queued events after each configured interval until the worker is stopped +- **THEN** the client attempts to flush queued events after each configured interval until the Tokio runtime shuts down -### Requirement: Generic and overridable system metadata -The client SHALL collect debug status, operating-system name, operating-system version, and locale without Tauri. It SHALL provide generic runtime metadata defaults and allow applications to override the runtime or engine name and version while retaining the existing `systemProps` field names. +### Requirement: Generic system metadata +The client SHALL collect debug status, operating-system name, operating-system version, and locale without Tauri. It SHALL NOT query WebView runtime or engine metadata, and tracked events SHALL omit WebView-specific `engineName` and `engineVersion` fields. #### Scenario: Use generic metadata -- **WHEN** a Rust application tracks an event without metadata overrides +- **WHEN** a Rust application tracks an event - **THEN** the event contains generic Rust-compatible system properties and does not query a WebView -#### Scenario: Override runtime metadata -- **WHEN** an application supplies a runtime or engine name and version -- **THEN** tracked events contain those supplied values in `engineName` and `engineVersion` +#### Scenario: Omit WebView metadata +- **WHEN** a Rust application tracks an event +- **THEN** the event `systemProps` do not include `engineName` or `engineVersion` + +### Requirement: Optional panic hooks +The builder SHALL allow callers to install either a custom panic hook or a default panic hook. When a panic hook is installed, it SHALL enqueue panic information and then delegate to Rust's previous panic hook. If the panicking thread has an active Tokio runtime, the hook SHALL also make a best-effort delivery attempt using the Tokio-backed transport before delegation. Without an active Tokio runtime on the panicking thread, the hook SHALL leave the panic event queued in memory. The panic hook MUST NOT expose a public blocking flush API. + +#### Scenario: Install the default panic hook +- **WHEN** a caller enables the default panic hook +- **THEN** a panic event is enqueued with panic message and source location information before the previous panic hook runs + +#### Scenario: Install a custom panic hook +- **WHEN** a caller provides a custom panic hook +- **THEN** the hook receives the client, panic information, and extracted panic message so it can enqueue an application-specific panic event diff --git a/openspec/changes/archive/2026-06-12-create-generic-aptabase-rust-client/tasks.md b/openspec/changes/archive/2026-06-12-create-generic-aptabase-rust-client/tasks.md index 84112bf..501d11a 100644 --- a/openspec/changes/archive/2026-06-12-create-generic-aptabase-rust-client/tasks.md +++ b/openspec/changes/archive/2026-06-12-create-generic-aptabase-rust-client/tasks.md @@ -2,44 +2,44 @@ - [x] 1.1 Rename the root Cargo package to `aptabase-rs` and rewrite its metadata for the generic client without introducing a workspace or secondary crate. - [x] 1.2 Remove the `tauri` dependency, `tauri-plugin` build dependency, plugin `links` metadata, and any features or metadata that only apply to Tauri. -- [x] 1.3 Define the root crate's public module surface and exports for client construction, options, metadata overrides, event tracking, and flushing. +- [x] 1.3 Define the root crate's public module surface and exports for client construction, options, panic hooks, event tracking, and flushing. ## 2. Adapt Configuration And Metadata - [x] 2.1 Move `InitOptions` into the generic root API and adapt app-key configuration in place, preserving US, EU, development, self-hosted, invalid-key, and flush-interval behavior. - [x] 2.2 Remove `tauri::webview_version` from system metadata collection while retaining debug status, OS information, locale, and Flatpak detection. -- [x] 2.3 Add generic runtime or engine defaults and public configuration overrides for engine name and version. -- [x] 2.4 Add unit tests for hosted, development, self-hosted, and invalid app-key configuration and metadata overrides. +- [x] 2.3 Remove WebView engine metadata from the generic client while retaining debug status, OS information, locale, and Flatpak detection. +- [ ] 2.4 Add unit tests for hosted, development, self-hosted, and invalid app-key configuration. ## 3. Adapt The Client And Dispatcher - [x] 3.1 Retain the session ID and four-hour session lifecycle implementation in the rewritten root crate. -- [x] 3.2 Adapt event construction and synchronous enqueueing, preserving payload field names, application and SDK versions, disabled-client behavior, and JSON-object property validation. +- [x] 3.2 Adapt event construction and synchronous enqueueing, preserving retained payload field names, application and SDK versions, disabled-client behavior, and JSON-object property validation. - [x] 3.3 Retain the Reqwest dispatcher behavior for the ingestion path, request headers, user agent, ten-second timeout, in-memory queue, and 25-event batching. - [x] 3.4 Preserve requeue behavior for transport and 5xx failures and discard behavior for non-5xx unsuccessful responses. -- [x] 3.5 Expose asynchronous explicit flushing for CLI shutdown and optional periodic flushing with a caller-manageable task handle or cancellation mechanism. -- [x] 3.6 Verify the public API and dependency features do not introduce a runtime-independent blocking client or `reqwest::blocking` transport. -- [x] 3.7 Remove the old Tauri-oriented `Builder`, `PanicHook`, and `EventTracker` public APIs and replace `src/lib.rs` with the generic crate API. +- [x] 3.5 Expose asynchronous explicit flushing for CLI shutdown and optional detached periodic flushing. +- [x] 3.6 Verify the dependency features do not introduce a separate `reqwest::blocking` transport. +- [x] 3.7 Remove the old Tauri-oriented plugin builder and `EventTracker` public APIs and replace `src/lib.rs` with the generic builder, client, panic-hook, and flushing APIs. ## 4. Remove Tauri From The Source Tree - [x] 4.1 Delete `src/commands.rs` and all remaining Tauri imports, command handlers, managed-state integration, plugin builders, runtime event hooks, and WebView metadata code. - [x] 4.2 Delete the root plugin `build.rs` and the `permissions/` directory, including generated schemas and command permissions. - [x] 4.3 Delete `webview-src/`, `webview-dist/`, `package.json`, and other JavaScript or TypeScript build metadata used only by the Tauri plugin. -- [x] 4.4 Delete `examples/helloworld/` and all generated Tauri example artifacts. +- [x] 4.4 Replace the generated Tauri example with a minimal Tokio CLI example under `examples/helloworld/`. - [x] 4.5 Search the retained source, manifests, and documentation and remove all claims or configuration that this repository still provides a Tauri plugin. ## 5. Verify Core Behavior -- [x] 5.1 Add tests that verify event payload serialization, required system properties, object-property acceptance, and non-object rejection. -- [x] 5.2 Add deterministic session tests for reuse within four hours and rotation after expiration. -- [x] 5.3 Add local HTTP test coverage for empty flushes, `App-Key` and content headers, endpoint selection, and batches of at most 25 events. -- [x] 5.4 Add delivery tests showing that transport and 5xx failures remain queued while non-5xx unsuccessful responses are discarded. -- [x] 5.5 Add a Tokio-based CLI example or integration test that tracks an event and awaits `flush` using the rewritten root crate. +- [ ] 5.1 Add tests that verify event payload serialization, required system properties, object-property acceptance, and non-object rejection. +- [ ] 5.2 Add deterministic session tests for reuse within four hours and rotation after expiration. +- [ ] 5.3 Add local HTTP test coverage for empty flushes, `App-Key` and content headers, endpoint selection, and batches of at most 25 events. +- [ ] 5.4 Add delivery tests showing that transport and 5xx failures remain queued while non-5xx unsuccessful responses are discarded. +- [x] 5.5 Add a Tokio-based CLI example that tracks an event and awaits `flush` using the rewritten root crate. ## 6. Documentation And Final Validation - [x] 6.1 Replace the Tauri README with generic crate documentation covering CLI construction, event tracking, explicit shutdown flushing, periodic flushing, self-hosted configuration, and privacy guidance. - [x] 6.2 Update changelog and package metadata to state that this is a breaking in-place replacement named `aptabase-rs`. -- [x] 6.3 Run formatting, unit and integration tests, Cargo checks, and documentation tests for the rewritten root package. +- [ ] 6.3 Run formatting, unit and integration tests, Cargo checks, and documentation tests for the rewritten root package. - [x] 6.4 Inspect the dependency tree and repository files to verify that no Tauri dependency, Tauri source, plugin asset, or compatibility layer remains. diff --git a/openspec/specs/generic-rust-client/spec.md b/openspec/specs/generic-rust-client/spec.md index ce4893e..d28bb59 100644 --- a/openspec/specs/generic-rust-client/spec.md +++ b/openspec/specs/generic-rust-client/spec.md @@ -1,7 +1,7 @@ # generic-rust-client Specification ## Purpose -Define the framework-independent Rust SDK for constructing an Aptabase client, enqueueing analytics events, managing sessions and metadata, selecting hosted or self-hosted endpoints, and delivering queued events through asynchronous Tokio-based batching and flushing. +Define the framework-independent Rust SDK for constructing an Aptabase client, enqueueing analytics events, managing sessions and metadata, selecting hosted or self-hosted endpoints, and delivering queued events through Tokio-based batching and flushing. ## Requirements ### Requirement: In-place generic crate replacement @@ -38,7 +38,7 @@ The client SHALL select the Aptabase ingestion endpoint from the app-key region, - **THEN** construction succeeds and subsequent event tracking performs no network delivery ### Requirement: Event enqueueing and payload compatibility -The client SHALL synchronously enqueue named events without performing network I/O at the call site. Each event SHALL contain an RFC 3339 UTC timestamp, session ID, event name, system properties, application version, SDK version, and optional properties. Supplied properties MUST be a JSON object. +The client SHALL synchronously enqueue named events without performing network I/O at the call site. Each event SHALL contain an RFC 3339 UTC timestamp, session ID, event name, `systemProps` with debug status, operating-system name, operating-system version, locale, application version, SDK version, and optional properties. Supplied properties MUST be a JSON object. #### Scenario: Enqueue an event without properties - **WHEN** a caller tracks an event name without properties on an enabled client @@ -93,8 +93,8 @@ The client SHALL requeue a batch after a transport failure or server-error respo - **WHEN** Aptabase responds to a batch with a non-5xx unsuccessful status - **THEN** the batch is removed from the queue and is not retried automatically -### Requirement: Explicit asynchronous flushing -The client SHALL provide an asynchronous flush operation that can be awaited before a short-lived process exits. The operation SHALL attempt delivery of queued batches using Tokio. The crate SHALL NOT provide a runtime-independent blocking client as part of this change. +### Requirement: Explicit flushing +The client SHALL provide an asynchronous flush operation that can be awaited before a short-lived process exits. The operation SHALL attempt delivery of queued batches using Tokio. The crate SHALL NOT expose a blocking flush API or provide a separate `reqwest::blocking` transport as part of this change. #### Scenario: Flush before CLI exit - **WHEN** a Tokio-based CLI tracks an event and awaits the client's flush operation before returning @@ -102,22 +102,33 @@ The client SHALL provide an asynchronous flush operation that can be awaited bef #### Scenario: Use the supported transport model - **WHEN** a caller needs to deliver queued events -- **THEN** the caller uses the Tokio-based asynchronous flush API rather than a runtime-independent blocking client +- **THEN** the caller uses the Tokio-based asynchronous flush API ### Requirement: Optional periodic flushing -The client SHALL allow a caller with an active Tokio runtime to start periodic flushing using the configured interval. The caller SHALL receive a means to manage or stop the periodic worker. +The client SHALL allow a caller with an active Tokio runtime to start detached periodic flushing using the configured interval. The periodic worker SHALL run until the Tokio runtime shuts down. #### Scenario: Start periodic delivery - **WHEN** a long-running application starts periodic flushing -- **THEN** the client attempts to flush queued events after each configured interval until the worker is stopped +- **THEN** the client attempts to flush queued events after each configured interval until the Tokio runtime shuts down -### Requirement: Generic and overridable system metadata -The client SHALL collect debug status, operating-system name, operating-system version, and locale without Tauri. It SHALL provide generic runtime metadata defaults and allow applications to override the runtime or engine name and version while retaining the existing `systemProps` field names. +### Requirement: Generic system metadata +The client SHALL collect debug status, operating-system name, operating-system version, and locale without Tauri. It SHALL NOT query WebView runtime or engine metadata, and tracked events SHALL omit WebView-specific `engineName` and `engineVersion` fields. #### Scenario: Use generic metadata -- **WHEN** a Rust application tracks an event without metadata overrides +- **WHEN** a Rust application tracks an event - **THEN** the event contains generic Rust-compatible system properties and does not query a WebView -#### Scenario: Override runtime metadata -- **WHEN** an application supplies a runtime or engine name and version -- **THEN** tracked events contain those supplied values in `engineName` and `engineVersion` +#### Scenario: Omit WebView metadata +- **WHEN** a Rust application tracks an event +- **THEN** the event `systemProps` do not include `engineName` or `engineVersion` + +### Requirement: Optional panic hooks +The builder SHALL allow callers to install either a custom panic hook or a default panic hook. When a panic hook is installed, it SHALL enqueue panic information and then delegate to Rust's previous panic hook. If the panicking thread has an active Tokio runtime, the hook SHALL also make a best-effort delivery attempt using the Tokio-backed transport before delegation. Without an active Tokio runtime on the panicking thread, the hook SHALL leave the panic event queued in memory. The panic hook MUST NOT expose a public blocking flush API. + +#### Scenario: Install the default panic hook +- **WHEN** a caller enables the default panic hook +- **THEN** a panic event is enqueued with panic message and source location information before the previous panic hook runs + +#### Scenario: Install a custom panic hook +- **WHEN** a caller provides a custom panic hook +- **THEN** the hook receives the client, panic information, and extracted panic message so it can enqueue an application-specific panic event diff --git a/src/client.rs b/src/client.rs index c933f94..fe6d683 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,14 +1,14 @@ use rand::Rng; -use serde_json::{json, Value}; +use serde_json::{Value, json}; +use std::time::{SystemTime, UNIX_EPOCH}; use std::{ sync::{Arc, Mutex as SyncMutex}, - time::{Duration, SystemTime, UNIX_EPOCH}, + time::Duration, }; -use time::{format_description::well_known::Rfc3339, OffsetDateTime}; -use tokio::task::JoinHandle; +use time::{OffsetDateTime, format_description::well_known::Rfc3339}; use crate::{ - config::{Config, InitOptions}, + config::Config, dispatcher::EventDispatcher, sys::{self, SystemProperties}, }; @@ -37,10 +37,10 @@ pub struct TrackingSession { } impl TrackingSession { - fn new_at(now: OffsetDateTime) -> Self { + fn new() -> Self { Self { id: new_session_id(), - last_touch_ts: now, + last_touch_ts: OffsetDateTime::now_utc(), } } } @@ -52,67 +52,44 @@ pub struct AptabaseClient { dispatcher: Arc, app_version: String, sys_info: SystemProperties, - flush_interval: Duration, } impl AptabaseClient { - /// Creates a client using the default options. - pub fn new(app_key: impl Into, app_version: impl Into) -> Self { - Self::with_options(app_key, app_version, InitOptions::default()) - } - - /// Creates a client using custom endpoint, interval, or engine metadata options. - pub fn with_options( - app_key: impl Into, - app_version: impl Into, - options: InitOptions, - ) -> Self { - let config = Config::new(app_key.into(), &options); - let sys_info = sys::get_info(&options); + /// Creates a new Aptabase client. + pub(crate) fn new(config: &Config, app_version: String) -> Self { + let sys_info = sys::get_info(); let is_enabled = !config.app_key.is_empty(); - let dispatcher = Arc::new(EventDispatcher::new(&config, &sys_info)); + let dispatcher = Arc::new(EventDispatcher::new(config, &sys_info)); Self { is_enabled, dispatcher, - session: SyncMutex::new(TrackingSession::new_at(OffsetDateTime::now_utc())), - app_version: app_version.into(), + session: SyncMutex::new(TrackingSession::new()), + app_version, sys_info, - flush_interval: config.flush_interval, } } - /// Returns whether this client has a valid delivery configuration. - pub fn is_enabled(&self) -> bool { - self.is_enabled - } - - /// Starts periodic flushing on the current Tokio runtime. - /// - /// Abort the returned handle to stop the worker. - pub fn start_periodic_flush(&self) -> JoinHandle<()> { + /// Starts the event dispatcher loop. + pub(crate) fn start_polling(&self, interval: Duration) { let dispatcher = self.dispatcher.clone(); - let interval = self.flush_interval; tokio::spawn(async move { loop { tokio::time::sleep(interval).await; dispatcher.flush().await; } - }) + }); } /// Returns the current session ID, creating a new one if necessary. - fn eval_session_id(&self) -> String { - self.eval_session_id_at(OffsetDateTime::now_utc()) - } - - fn eval_session_id_at(&self, now: OffsetDateTime) -> String { + pub(crate) fn eval_session_id(&self) -> String { let mut session = self.session.lock().expect("could not lock events"); + let now = OffsetDateTime::now_utc(); if (now - session.last_touch_ts) > SESSION_TIMEOUT { - *session = TrackingSession::new_at(now); + *session = TrackingSession::new(); } else { session.last_touch_ts = now; } @@ -144,8 +121,6 @@ impl AptabaseClient { "osName": self.sys_info.os_name, "osVersion": self.sys_info.os_version, "locale": self.sys_info.locale, - "engineName": self.sys_info.engine_name, - "engineVersion": self.sys_info.engine_version, "appVersion": self.app_version, "sdkVersion": concat!(env!("CARGO_PKG_NAME"), "@", env!("CARGO_PKG_VERSION")) }, @@ -161,114 +136,11 @@ impl AptabaseClient { pub async fn flush(&self) { self.dispatcher.flush().await; } -} - -#[cfg(test)] -mod tests { - use super::*; - - fn client() -> AptabaseClient { - AptabaseClient::new("A-US-test", "1.2.3") - } - - #[test] - fn serializes_required_event_fields_and_object_properties() { - let client = client(); - client - .track_event("settings_saved", Some(json!({ "section": "privacy" }))) - .unwrap(); - - let events = client.dispatcher.queued_events(); - let event = &events[0]; - assert!(event["timestamp"].as_str().is_some()); - assert!(event["sessionId"].as_str().is_some()); - assert_eq!(event["eventName"], "settings_saved"); - assert_eq!(event["props"], json!({ "section": "privacy" })); - assert_eq!(event["systemProps"]["appVersion"], "1.2.3"); - assert_eq!(event["systemProps"]["engineName"], "Rust"); - assert_eq!(event["systemProps"]["engineVersion"], "unknown"); - assert!(event["systemProps"]["isDebug"].is_boolean()); - assert!(event["systemProps"]["osName"].is_string()); - assert!(event["systemProps"]["osVersion"].is_string()); - assert!(event["systemProps"]["locale"].is_string()); - assert_eq!( - event["systemProps"]["sdkVersion"], - concat!(env!("CARGO_PKG_NAME"), "@", env!("CARGO_PKG_VERSION")) - ); - } - - #[test] - fn rejects_non_object_properties_without_enqueueing() { - let client = client(); - assert!(client - .track_event("invalid", Some(json!([1, 2, 3]))) - .is_err()); - assert!(client.dispatcher.is_empty()); - } - - #[test] - fn disabled_client_ignores_events() { - let client = AptabaseClient::new("invalid", "1.0.0"); - - assert!(!client.is_enabled()); - assert!(client - .track_event("ignored", Some(json!([1, 2, 3]))) - .is_ok()); - assert!(client.dispatcher.is_empty()); - } - - #[test] - fn reuses_session_within_four_hours() { - let client = client(); - let now = OffsetDateTime::UNIX_EPOCH; - *client.session.lock().unwrap() = TrackingSession { - id: "existing".into(), - last_touch_ts: now, - }; - - assert_eq!( - client.eval_session_id_at(now + Duration::from_secs(60)), - "existing" - ); - assert_eq!( - client.session.lock().unwrap().last_touch_ts, - now + Duration::from_secs(60) - ); - } - - #[test] - fn rotates_session_after_four_hours() { - let client = client(); - let now = OffsetDateTime::UNIX_EPOCH; - *client.session.lock().unwrap() = TrackingSession { - id: "expired".into(), - last_touch_ts: now, - }; - - let session_id = client.eval_session_id_at(now + SESSION_TIMEOUT + Duration::from_secs(1)); - assert_ne!(session_id, "expired"); - assert_eq!( - client.session.lock().unwrap().last_touch_ts, - now + SESSION_TIMEOUT + Duration::from_secs(1) - ); - } - - #[test] - fn applies_engine_metadata_overrides_to_events() { - let client = AptabaseClient::with_options( - "A-US-test", - "1.0.0", - InitOptions { - engine_name: Some("Custom Runtime".into()), - engine_version: Some("9.1".into()), - ..InitOptions::default() - }, - ); - client.track_event("started", None).unwrap(); - - let events = client.dispatcher.queued_events(); - assert_eq!(events[0]["systemProps"]["engineName"], "Custom Runtime"); - assert_eq!(events[0]["systemProps"]["engineVersion"], "9.1"); + /// Flushes the event queue, blocking the current thread. + pub(crate) fn flush_blocking(&self) { + futures::executor::block_on(async { + self.flush().await; + }); } } diff --git a/src/config.rs b/src/config.rs index 99e7aff..2b3cc8a 100644 --- a/src/config.rs +++ b/src/config.rs @@ -3,24 +3,13 @@ use std::time::Duration; use log::debug; use reqwest::Url; -/// Optional Aptabase client configuration. -#[derive(Default, Debug, Clone)] -pub struct InitOptions { - /// Base URL for an `A-SH-*` self-hosted app key. - pub host: Option, - /// Interval used by [`crate::AptabaseClient::start_periodic_flush`]. - pub flush_interval: Option, - /// Value reported as `engineName` in event system properties. - pub engine_name: Option, - /// Value reported as `engineVersion` in event system properties. - pub engine_version: Option, -} +use crate::InitOptions; #[derive(Debug, Clone)] -pub(crate) struct Config { - pub(crate) app_key: String, - pub(crate) ingest_api_url: Url, - pub(crate) flush_interval: Duration, +pub struct Config { + pub app_key: String, + pub ingest_api_url: Url, + pub flush_interval: Duration, } static LOCAL: &str = "http://localhost:3000"; @@ -36,13 +25,9 @@ static DEFAULT_FLUSH_INTERVAL: Duration = Duration::from_secs(2); const VALID_REGIONS: &[&str] = &["US", "EU", "DEV", "SH"]; impl Config { - pub(crate) fn new(app_key: String, opts: &InitOptions) -> Self { - let parts = app_key.split('-').collect::>(); - if parts.len() != 3 - || parts[0] != "A" - || parts[2].is_empty() - || !VALID_REGIONS.contains(&parts[1]) - { + pub fn new(app_key: String, opts: InitOptions) -> Self { + let parts = app_key.split("-").collect::>(); + if parts.len() != 3 || !VALID_REGIONS.contains(&parts[1]) { debug!( "The Aptabase App Key '{}' is invalid. Tracking will be disabled.", app_key @@ -55,29 +40,21 @@ impl Config { "US" => US_REGION.into(), "DEV" => LOCAL.into(), "SH" => { - if let Some(host) = &opts.host { - host.clone() + if let Some(host) = opts.host { + host } else { - debug!("Host parameter must be defined when using Self-Hosted App Key. Tracking will be disabled."); + debug!( + "Host parameter must be defined when using Self-Hosted App Key. Tracking will be disabled." + ); return Config::default(); } } _ => return Config::default(), }; - let Ok(ingest_api_url) = - format!("{}/api/v0/events", base_url.trim_end_matches('/')).parse() - else { - debug!( - "The Aptabase host '{}' is invalid. Tracking will be disabled.", - base_url - ); - return Config::default(); - }; - Self { app_key, - ingest_api_url, + ingest_api_url: format!("{}/api/v0/events", base_url).parse().unwrap(), flush_interval: opts.flush_interval.unwrap_or(DEFAULT_FLUSH_INTERVAL), } } @@ -92,74 +69,3 @@ impl Default for Config { } } } - -#[cfg(test)] -mod tests { - use super::*; - - fn options() -> InitOptions { - InitOptions { - flush_interval: Some(Duration::from_secs(30)), - ..InitOptions::default() - } - } - - #[test] - fn configures_hosted_and_development_regions() { - let us = Config::new("A-US-123".into(), &options()); - let eu = Config::new("A-EU-123".into(), &options()); - let dev = Config::new("A-DEV-123".into(), &options()); - - assert_eq!( - us.ingest_api_url.as_str(), - "https://us.aptabase.com/api/v0/events" - ); - assert_eq!( - eu.ingest_api_url.as_str(), - "https://eu.aptabase.com/api/v0/events" - ); - assert_eq!( - dev.ingest_api_url.as_str(), - "http://localhost:3000/api/v0/events" - ); - assert_eq!(us.flush_interval, Duration::from_secs(30)); - } - - #[test] - fn configures_self_hosted_region() { - let config = Config::new( - "A-SH-123".into(), - &InitOptions { - host: Some("https://analytics.example.com/".into()), - ..InitOptions::default() - }, - ); - - assert_eq!( - config.ingest_api_url.as_str(), - "https://analytics.example.com/api/v0/events" - ); - } - - #[test] - fn invalid_configuration_disables_tracking() { - for app_key in ["", "invalid", "B-US-123", "A-US-", "A-XX-123"] { - assert!(Config::new(app_key.into(), &InitOptions::default()) - .app_key - .is_empty()); - } - - assert!(Config::new("A-SH-123".into(), &InitOptions::default()) - .app_key - .is_empty()); - assert!(Config::new( - "A-SH-123".into(), - &InitOptions { - host: Some("not a url".into()), - ..InitOptions::default() - } - ) - .app_key - .is_empty()); - } -} diff --git a/src/dispatcher.rs b/src/dispatcher.rs index 4297923..bcf943b 100644 --- a/src/dispatcher.rs +++ b/src/dispatcher.rs @@ -7,10 +7,10 @@ use std::{ use log::{debug, trace}; use reqwest::{ - header::{HeaderMap, HeaderValue}, Url, + header::{HeaderMap, HeaderValue}, }; -use serde_json::{json, Value}; +use serde_json::{Value, json}; use crate::{config::Config, sys::SystemProperties}; @@ -30,10 +30,7 @@ impl EventDispatcher { headers.insert("App-Key", app_key_header); headers.insert("Content-Type", HeaderValue::from_static("application/json")); - let user_agent = format!( - "{}/{} {}/{} {}", - sys.os_name, sys.os_version, sys.engine_name, sys.engine_version, sys.locale - ); + let user_agent = format!("{}/{} {}", sys.os_name, sys.os_version, sys.locale); let http_client = reqwest::Client::builder() .timeout(HTTP_REQUEST_TIMEOUT) .default_headers(headers) @@ -65,16 +62,6 @@ impl EventDispatcher { queue.extend(events); } - #[cfg(test)] - pub(crate) fn queued_events(&self) -> Vec { - self.queue - .read() - .expect("could not lock queue for reading") - .iter() - .cloned() - .collect() - } - fn dequeue_many(&self, max: usize) -> Vec { let mut queue = self.queue.write().expect("could not lock queue"); if queue.is_empty() { @@ -125,7 +112,7 @@ impl EventDispatcher { }, Err(err) => { failed_items.extend(events_to_send); - debug!("failed to track_event: {}", err); + debug!("failed to track_event: {}", err.to_string()); } } } @@ -133,181 +120,3 @@ impl EventDispatcher { self.enqueue_many(failed_items); } } - -#[cfg(test)] -mod tests { - use super::*; - use crate::{config::InitOptions, sys}; - use serde_json::json; - use std::{ - io::{Read, Write}, - net::{TcpListener, TcpStream}, - sync::mpsc, - thread, - }; - - #[derive(Debug)] - struct RecordedRequest { - request_line: String, - headers: Vec<(String, String)>, - body: Value, - } - - fn dispatcher(url: Url) -> EventDispatcher { - let config = Config { - app_key: "A-DEV-test".into(), - ingest_api_url: url, - flush_interval: Duration::from_secs(1), - }; - EventDispatcher::new(&config, &sys::get_info(&InitOptions::default())) - } - - fn read_request(stream: &mut TcpStream) -> RecordedRequest { - let mut bytes = Vec::new(); - let mut buffer = [0; 4096]; - let header_end = loop { - let count = stream.read(&mut buffer).unwrap(); - assert!( - count > 0, - "connection closed before request headers arrived" - ); - bytes.extend_from_slice(&buffer[..count]); - if let Some(index) = bytes.windows(4).position(|window| window == b"\r\n\r\n") { - break index + 4; - } - }; - - let headers_text = String::from_utf8(bytes[..header_end].to_vec()).unwrap(); - let mut lines = headers_text.split("\r\n"); - let request_line = lines.next().unwrap().to_string(); - let headers = lines - .filter_map(|line| line.split_once(':')) - .map(|(name, value)| (name.to_ascii_lowercase(), value.trim().to_string())) - .collect::>(); - let content_length = headers - .iter() - .find(|(name, _)| name == "content-length") - .and_then(|(_, value)| value.parse::().ok()) - .unwrap_or(0); - - while bytes.len() - header_end < content_length { - let count = stream.read(&mut buffer).unwrap(); - assert!(count > 0, "connection closed before request body arrived"); - bytes.extend_from_slice(&buffer[..count]); - } - - RecordedRequest { - request_line, - headers, - body: serde_json::from_slice(&bytes[header_end..header_end + content_length]).unwrap(), - } - } - - fn spawn_server(statuses: Vec) -> (Url, mpsc::Receiver) { - let listener = TcpListener::bind("127.0.0.1:0").unwrap(); - let address = listener.local_addr().unwrap(); - let (sender, receiver) = mpsc::channel(); - - thread::spawn(move || { - for status in statuses { - let (mut stream, _) = listener.accept().unwrap(); - let request = read_request(&mut stream); - sender.send(request).unwrap(); - let reason = if status == 200 { - "OK" - } else if status == 400 { - "Bad Request" - } else { - "Internal Server Error" - }; - write!( - stream, - "HTTP/1.1 {status} {reason}\r\nContent-Length: 0\r\nConnection: close\r\n\r\n" - ) - .unwrap(); - } - }); - - ( - format!("http://{address}/api/v0/events").parse().unwrap(), - receiver, - ) - } - - #[tokio::test] - async fn empty_flush_makes_no_request() { - let listener = TcpListener::bind("127.0.0.1:0").unwrap(); - let url = format!("http://{}/api/v0/events", listener.local_addr().unwrap()) - .parse() - .unwrap(); - let dispatcher = dispatcher(url); - - dispatcher.flush().await; - assert!(dispatcher.is_empty()); - } - - #[tokio::test] - async fn sends_headers_endpoint_and_batches_of_at_most_25() { - let (url, requests) = spawn_server(vec![200, 200]); - let dispatcher = dispatcher(url); - for index in 0..26 { - dispatcher.enqueue(json!({ "index": index })); - } - - dispatcher.flush().await; - - let first = requests.recv().unwrap(); - let second = requests.recv().unwrap(); - assert_eq!(first.request_line, "POST /api/v0/events HTTP/1.1"); - assert_eq!(first.body.as_array().unwrap().len(), 25); - assert_eq!(second.body.as_array().unwrap().len(), 1); - assert!(first - .headers - .contains(&("app-key".into(), "A-DEV-test".into()))); - assert!(first - .headers - .contains(&("content-type".into(), "application/json".into()))); - assert!(first - .headers - .iter() - .any(|(name, value)| { name == "user-agent" && value.contains("Rust/unknown") })); - assert!(dispatcher.is_empty()); - } - - #[tokio::test] - async fn requeues_transport_failures() { - let listener = TcpListener::bind("127.0.0.1:0").unwrap(); - let address = listener.local_addr().unwrap(); - drop(listener); - let dispatcher = dispatcher(format!("http://{address}/api/v0/events").parse().unwrap()); - dispatcher.enqueue(json!({ "event": 1 })); - - dispatcher.flush().await; - - assert_eq!(dispatcher.queued_events().len(), 1); - } - - #[tokio::test] - async fn requeues_server_errors() { - let (url, requests) = spawn_server(vec![500]); - let dispatcher = dispatcher(url); - dispatcher.enqueue(json!({ "event": 1 })); - - dispatcher.flush().await; - - requests.recv().unwrap(); - assert_eq!(dispatcher.queued_events().len(), 1); - } - - #[tokio::test] - async fn discards_non_server_http_errors() { - let (url, requests) = spawn_server(vec![400]); - let dispatcher = dispatcher(url); - dispatcher.enqueue(json!({ "event": 1 })); - - dispatcher.flush().await; - - requests.recv().unwrap(); - assert!(dispatcher.is_empty()); - } -} diff --git a/src/lib.rs b/src/lib.rs index 356b374..2e50c7c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,9 +1,109 @@ -//! A framework-independent Aptabase client for Rust applications. - mod client; mod config; mod dispatcher; mod sys; +use std::{panic::PanicHookInfo, sync::Arc, time::Duration}; + pub use client::AptabaseClient; -pub use config::InitOptions; +use config::Config; +use serde_json::json; + +#[derive(Default, Debug, Clone)] +pub struct InitOptions { + pub host: Option, + pub flush_interval: Option, +} + +/// The Aptabase client builder +pub struct Builder { + app_key: String, + app_version: String, + enable_polling: bool, + panic_hook: Option, + options: InitOptions, +} + +pub type PanicHook = + Box, String) + 'static + Sync + Send>; + +fn get_panic_message(info: &PanicHookInfo) -> String { + let payload = info.payload(); + if let Some(s) = payload.downcast_ref::<&str>() { + return s.to_string(); + } else if let Some(s) = payload.downcast_ref::() { + return s.to_string(); + } + + format!("{:?}", payload) +} + +impl Builder { + /// Creates a new builder. + pub fn new(app_key: &str, app_version: &str) -> Self { + Self { + app_key: app_key.into(), + app_version: app_version.into(), + enable_polling: false, + panic_hook: None, + options: Default::default(), + } + } + + /// Sets custom options to use for the Aptabase client. + pub fn with_options(mut self, opts: InitOptions) -> Self { + self.options = opts; + self + } + + /// Sets whether polling should be enabled. + pub fn with_polling(mut self, enable: bool) -> Self { + self.enable_polling = enable; + self + } + + /// Sets a custom panic hook. + pub fn with_panic_hook(mut self, hook: PanicHook) -> Self { + self.panic_hook = Some(hook); + self + } + + /// Enables the default panic hook. + pub fn with_default_panic_hook(self) -> Self { + self.with_panic_hook(Box::new(|client, info, msg| { + let location = info + .location() + .map(|loc| format!("{}:{}:{}", loc.file(), loc.line(), loc.column())) + .unwrap_or_else(|| "".to_string()); + let _ = client.track_event( + "panic", + Some(json!({ + "info": format!("{} ({})", msg, location), + })), + ); + })) + } + + /// Builds and initializes the client + pub fn build(self) -> Arc { + let cfg = Config::new(self.app_key, self.options); + let client = Arc::new(AptabaseClient::new(&cfg, self.app_version)); + + if self.enable_polling { + client.start_polling(cfg.flush_interval); + } + + if let Some(hook) = self.panic_hook { + let default_panic = std::panic::take_hook(); + let hook_client = client.clone(); + std::panic::set_hook(Box::new(move |info| { + let msg = get_panic_message(info); + hook(&hook_client, info, msg); + hook_client.flush_blocking(); + default_panic(info); + })); + } + + client + } +} diff --git a/src/sys.rs b/src/sys.rs index 416b4b1..bd1a662 100644 --- a/src/sys.rs +++ b/src/sys.rs @@ -1,22 +1,14 @@ -use crate::config::InitOptions; - -static DEFAULT_ENGINE_NAME: &str = "Rust"; -static DEFAULT_ENGINE_VERSION: &str = "unknown"; - #[cfg(debug_assertions)] static IS_DEBUG: bool = true; #[cfg(not(debug_assertions))] static IS_DEBUG: bool = false; -#[derive(Debug, Clone, PartialEq, Eq)] -pub(crate) struct SystemProperties { +pub struct SystemProperties { pub is_debug: bool, pub os_name: String, pub os_version: String, pub locale: String, - pub engine_name: String, - pub engine_version: String, } #[cfg(target_os = "linux")] @@ -28,7 +20,7 @@ fn is_flatpak() -> bool { .unwrap_or(false) } -pub(crate) fn get_info(options: &InitOptions) -> SystemProperties { +pub fn get_info() -> SystemProperties { let info = os_info::get(); let locale = sys_locale::get_locale().unwrap_or_default(); @@ -45,38 +37,5 @@ pub(crate) fn get_info(options: &InitOptions) -> SystemProperties { os_name, os_version: info.version().to_string(), locale, - engine_name: options - .engine_name - .clone() - .unwrap_or_else(|| DEFAULT_ENGINE_NAME.to_string()), - engine_version: options - .engine_version - .clone() - .unwrap_or_else(|| DEFAULT_ENGINE_VERSION.to_string()), - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn uses_generic_engine_defaults() { - let info = get_info(&InitOptions::default()); - - assert_eq!(info.engine_name, "Rust"); - assert_eq!(info.engine_version, "unknown"); - } - - #[test] - fn applies_engine_overrides() { - let info = get_info(&InitOptions { - engine_name: Some("My Runtime".into()), - engine_version: Some("2.4.0".into()), - ..InitOptions::default() - }); - - assert_eq!(info.engine_name, "My Runtime"); - assert_eq!(info.engine_version, "2.4.0"); } } From 1053255e1060214fe49b455f307ee57152f04444 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Mon, 22 Jun 2026 08:49:46 +0100 Subject: [PATCH 06/83] Add AI disclosure --- AI_DISCLOSURE.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 AI_DISCLOSURE.md diff --git a/AI_DISCLOSURE.md b/AI_DISCLOSURE.md new file mode 100644 index 0000000..6ca1727 --- /dev/null +++ b/AI_DISCLOSURE.md @@ -0,0 +1,18 @@ +--- +disclosure-default: ai-assisted +models-used: [gpt-5.5] +providers: [OpenAI] +scope: | + Specifications are typically AI-generated with manual review. + Code is typically AI-assisted. + Tests are typically AI-generated. + Documentation is typically human-written. + Upstream project has no AI disclosure. +last-updated: 2026-06-01 +--- + +# AI Disclosure + +This repository follows the [ai-disclosure convention](https://github.com/ggfevans/ai-disclosure). + +See per-file `SPDX-AI-Disclosure:` headers for overrides on individual files. From f72dd6b46ef5e3417852809e9f0433149ef6beb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Mon, 22 Jun 2026 08:50:05 +0100 Subject: [PATCH 07/83] Configure Dependabot --- .github/dependabot.yml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..1a3d457 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,20 @@ +version: 2 + +updates: + - package-ecosystem: cargo + directory: / + schedule: + interval: weekly + commit-message: + prefix: chore + include: scope + labels: [chore] + + - package-ecosystem: github-actions + directory: / + schedule: + interval: weekly + commit-message: + prefix: chore + include: scope + labels: [chore] From 3fe41a243c95529e915855fed230a98c74d7d597 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Mon, 22 Jun 2026 08:50:52 +0100 Subject: [PATCH 08/83] Configure CI workflow --- .github/workflows/ci.yml | 18 ++++++++++++++++++ Cargo.toml | 6 ++++++ mise.lock | 6 +++++- mise.toml | 11 ++++++++++- src/client.rs | 13 ++++++------- src/dispatcher.rs | 2 +- 6 files changed, 46 insertions(+), 10 deletions(-) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..8d5291a --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,18 @@ +permissions: + contents: read + +on: + pull_request: + push: + branches: [develop] + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: dtolnay/rust-toolchain@stable + - uses: Swatinem/rust-cache@v2 + - run: cargo fmt --check + - run: cargo clippy --all-targets + - run: cargo test diff --git a/Cargo.toml b/Cargo.toml index 4d2e2c2..2cfe85e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,3 +21,9 @@ os_info = "3.9.2" rand = "0.9.0" log = "0.4.25" sys-locale = "0.3.2" + +[lints.rust] +warnings = "deny" + +[lints.clippy] +all = "deny" diff --git a/mise.lock b/mise.lock index 166d2ae..2c0f6cb 100644 --- a/mise.lock +++ b/mise.lock @@ -1,5 +1,9 @@ # @generated - this file is auto-generated by `mise lock` https://mise.jdx.dev/dev-tools/mise-lock.html +[[tools.actionlint]] +version = "1.7.12" +backend = "aqua:rhysd/actionlint" + [[tools.node]] version = "26.2.0" backend = "core:node" @@ -37,5 +41,5 @@ version = "1.3.1" backend = "npm:@fission-ai/openspec" [[tools.rust]] -version = "nightly" +version = "stable" backend = "core:rust" diff --git a/mise.toml b/mise.toml index b46fe2c..e1db8f9 100644 --- a/mise.toml +++ b/mise.toml @@ -1,4 +1,13 @@ [tools] +actionlint = "latest" node = "latest" "npm:@fission-ai/openspec" = "latest" -rust = "latest" +rust = "stable" + +[tasks] +format = "cargo fmt" +lint = "cargo clippy --all-targets" +test = "cargo test" + +[tasks.ci] +depends = ["format --check", "lint", "test"] diff --git a/src/client.rs b/src/client.rs index fe6d683..977772d 100644 --- a/src/client.rs +++ b/src/client.rs @@ -103,13 +103,12 @@ impl AptabaseClient { return Ok(()); } - if let Some(props) = &props { - if !matches!(props, Value::Object(_)) { - return Err( - "props must be `None` or the `Object` variation of `serde_json::Value`" - .to_owned(), - ); - } + if let Some(props) = &props + && !matches!(props, Value::Object(_)) + { + return Err( + "props must be `None` or the `Object` variation of `serde_json::Value`".to_owned(), + ); } let ev = json!({ diff --git a/src/dispatcher.rs b/src/dispatcher.rs index bcf943b..ff96417 100644 --- a/src/dispatcher.rs +++ b/src/dispatcher.rs @@ -112,7 +112,7 @@ impl EventDispatcher { }, Err(err) => { failed_items.extend(events_to_send); - debug!("failed to track_event: {}", err.to_string()); + debug!("failed to track_event: {}", err); } } } From 9fe211f1d752f576a7410fc84f5a84d8b016a7b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Mon, 22 Jun 2026 08:52:09 +0100 Subject: [PATCH 09/83] Configure release workflow --- .github/workflows/release-plz.yml | 69 +++++++++++++++++++++++++++++++ Cargo.toml | 42 +++++++++++++++++++ mise.lock | 12 ++++++ mise.toml | 5 +++ release-plz.toml | 9 ++++ 5 files changed, 137 insertions(+) create mode 100644 .github/workflows/release-plz.yml create mode 100644 release-plz.toml diff --git a/.github/workflows/release-plz.yml b/.github/workflows/release-plz.yml new file mode 100644 index 0000000..03b9356 --- /dev/null +++ b/.github/workflows/release-plz.yml @@ -0,0 +1,69 @@ +name: Release-plz + +on: + push: + branches: + - develop + +jobs: + release-plz-release: + name: Release-plz release + runs-on: ubuntu-latest + if: ${{ github.repository_owner == 'eugencowie' }} + permissions: + contents: write + steps: + - &checkout + name: Checkout repository + uses: actions/checkout@v6 + with: + fetch-depth: 0 + persist-credentials: true + token: ${{ secrets.RELEASE_PLZ_TOKEN }} + - &install-rust + name: Install Rust toolchain + uses: dtolnay/rust-toolchain@stable + - name: Run release-plz + uses: release-plz/action@v0.5 + with: + command: release + env: + GITHUB_TOKEN: ${{ secrets.RELEASE_PLZ_TOKEN }} + CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} + + release-plz-pr: + name: Release-plz PR + runs-on: ubuntu-latest + if: ${{ github.repository_owner == 'eugencowie' }} + permissions: + pull-requests: write + contents: write + concurrency: + group: release-plz-${{ github.ref }} + cancel-in-progress: false + steps: + - *checkout + - *install-rust + - name: Generate changelog + id: git-cliff + uses: orhun/git-cliff-action@v4 + with: + args: --bump + env: + OUTPUT: CHANGELOG.md + GITHUB_REPO: ${{ github.repository }} + - name: Install release-plz + uses: taiki-e/install-action@v2 + with: + tool: release-plz + - name: Set release version + env: + RELEASE_VERSION: ${{ steps.git-cliff.outputs.version }} + run: release-plz set-version "${RELEASE_VERSION#v}" + - name: Run release-plz + uses: release-plz/action@v0.5 + with: + command: release-pr + env: + GITHUB_TOKEN: ${{ secrets.RELEASE_PLZ_TOKEN }} + CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} diff --git a/Cargo.toml b/Cargo.toml index 2cfe85e..fd0b862 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,3 +27,45 @@ warnings = "deny" [lints.clippy] all = "deny" + +[workspace.metadata.git-cliff.changelog] +output = "CHANGELOG.md" +header = "# Changelog" +body = """ + +{% if version %}\ + ## {{ version | trim_start_matches(pat="v") }} +{% else %}\ + ## Unreleased +{% endif %}\ +{% for group, commits in commits | group_by(attribute="group") %} + ### {{ group | striptags | trim | upper_first }} + {% for commit in commits %} + - {% if commit.scope %}*({{ commit.scope }})* {% endif %}\ + {% if commit.breaking %}**BREAKING CHANGE**: {% endif %}\ + {{ commit.message | upper_first }}\ + {% endfor %} +{% endfor %} +""" + +[workspace.metadata.git-cliff.git] +commit_parsers = [ + { message = "^feat", group = "๐Ÿš€ Features" }, + { message = "^fix", group = "๐Ÿ› Bug Fixes" }, + { message = "^doc", group = "๐Ÿ“š Documentation" }, + { message = "^perf", group = "โšก Performance" }, + { message = "^refactor", group = "๐Ÿšœ Refactor" }, + { message = "^style", group = "๐ŸŽจ Styling" }, + { message = "^test", group = "๐Ÿงช Testing" }, + { message = "^chore\\(release\\)", skip = true }, + { message = "^chore\\(deps.*\\)", skip = true }, + { message = "^chore\\(pr\\)", skip = true }, + { message = "^chore\\(pull\\)", skip = true }, + { message = "^chore|^ci", group = "โš™๏ธ Miscellaneous Tasks" }, + { body = ".*security", group = "๐Ÿ›ก๏ธ Security" }, + { message = "^revert", group = "โ—€๏ธ Revert" }, + { message = ".*", group = "๐Ÿ’ผ Other" }, +] +commit_preprocessors = [ + { pattern = "\\(#([0-9]+)\\)", replace = "([#${1}](https://github.com/eugencowie/aptabase-rs/pull/${1}))" }, +] diff --git a/mise.lock b/mise.lock index 2c0f6cb..ee516a6 100644 --- a/mise.lock +++ b/mise.lock @@ -4,6 +4,14 @@ version = "1.7.12" backend = "aqua:rhysd/actionlint" +[[tools.gh]] +version = "2.93.0" +backend = "aqua:cli/cli" + +[[tools.git-cliff]] +version = "2.13.1" +backend = "aqua:orhun/git-cliff" + [[tools.node]] version = "26.2.0" backend = "core:node" @@ -40,6 +48,10 @@ url = "https://nodejs.org/dist/v26.2.0/node-v26.2.0-win-x64.zip" version = "1.3.1" backend = "npm:@fission-ai/openspec" +[[tools.release-plz]] +version = "0.3.158" +backend = "aqua:release-plz/release-plz" + [[tools.rust]] version = "stable" backend = "core:rust" diff --git a/mise.toml b/mise.toml index e1db8f9..b74e28c 100644 --- a/mise.toml +++ b/mise.toml @@ -1,13 +1,18 @@ [tools] actionlint = "latest" +gh = "latest" +git-cliff = "latest" node = "latest" "npm:@fission-ai/openspec" = "latest" +release-plz = "latest" rust = "stable" [tasks] format = "cargo fmt" lint = "cargo clippy --all-targets" test = "cargo test" +changelog = "git cliff --bump" +release = "release-plz update" [tasks.ci] depends = ["format --check", "lint", "test"] diff --git a/release-plz.toml b/release-plz.toml new file mode 100644 index 0000000..97cb404 --- /dev/null +++ b/release-plz.toml @@ -0,0 +1,9 @@ +[workspace] +allow_dirty = true +changelog_update = false +git_release_enable = false +pr_name = "chore(release): Release {{ version }}" +pr_branch_prefix = "release/" +pr_labels = ["chore"] +release_always = false +semver_check = false From 8a3e01cb8a8b76569676d7eff005c36e19140035 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Mon, 22 Jun 2026 12:56:33 +0100 Subject: [PATCH 10/83] Update readme --- README.md | 99 +++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 64 insertions(+), 35 deletions(-) diff --git a/README.md b/README.md index 23fcd7b..cc51e65 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,8 @@ Install the SDK by adding the following to your `Cargo.toml` file: +`Cargo.toml` + ```toml [dependencies] aptabase-rs = "0.1.0" @@ -22,70 +24,97 @@ First, you need to get your `App Key` from Aptabase, you can find it in the `Ins Then create the client with your app key and application version: +`src/main.rs` + +```rust +let client = aptabase_rs::Builder::new( + "", // ๐Ÿ‘ˆ this is where you enter your App Key + env!("CARGO_PKG_VERSION"), +) +.build(); +``` + +You can then start sending events from Rust by calling the `track_event` method on `client`. + +As an example, you can add `app_started` and `app_exited` events like this: + ```rust use aptabase_rs::Builder; -use serde_json::json; use std::error::Error; #[tokio::main] async fn main() -> Result<(), Box> { - let client = Builder::new( - "", - env!("CARGO_PKG_VERSION"), - ) - .build(); + let client = Builder::new("", env!("CARGO_PKG_VERSION")).build(); + client.track_event("app_started", None); - client.track_event("command_started", Some(json!({ "command": "sync" })))?; + // do some work here... + + client.track_event("app_exited", None); client.flush().await; - Ok(()) } ``` -`Builder::build` constructs the client without performing network I/O or starting a background worker. Event tracking only enqueues in memory at the call site; explicitly await `flush` before a short-lived process exits. +A few important notes: -Properties must be a `serde_json::Value::Object`. Invalid app keys disable tracking without making construction fail. +1. The SDK will automatically enhance the event with some useful information, like the OS, the app version, and other things. +2. You're in control of what gets sent to Aptabase. This SDK does not automatically track any events, you need to call `track_event` manually. + - Because of this, it's generally recommended to at least track an event at startup. +3. You do not need to await for the `track_event` function, it'll run in the background. +4. Only strings and numbers values are allowed on custom properties. -## Periodic flushing +## Providing the APTABASE_KEY via .env -Long-running applications can opt in to periodic flushing with `with_polling(true)`. When polling is enabled, construct the client inside an active Tokio runtime. The interval defaults to 60 seconds in release builds and 2 seconds in debug builds. +It's possible to load the APTABASE_KEY from a .env file at compile time using the `dotenvy_macro` crate. The `.env` file needs to be +in the project directory for the `dotevny_macro` crate to find it properly. + +Add the `use` declaration to where you are building the SDK (likely `main.rs`), and then call it where you would put the key. ```rust -use aptabase_rs::{Builder, InitOptions}; -use std::time::Duration; +use aptabase_rs::Builder; +use dotenvy_macro::dotenv; +use std::error::Error; -let client = Builder::new("A-EU-your-app-key", "2.0.0") - .with_options(InitOptions { - host: None, - flush_interval: Some(Duration::from_secs(30)), - }) - .with_polling(true) - .build(); +#[tokio::main] +async fn main() -> Result<(), Box> { + let client = Builder::new(dotenv!("APTABASE_KEY"), env!("CARGO_PKG_VERSION")).build(); + client.track_event("app_started", None); -client.track_event("service_started", None)?; -client.flush().await; -# Ok::<(), Box>(()) + // do some work here... + + client.track_event("app_exited", None); + client.flush().await; + Ok(()) +} ``` -Events are kept only in memory, so applications should explicitly flush during orderly shutdown. +## Periodic flushing -## Configuration +Calling `track_event` only enqueues events to be sent to the server, you need to explicitly await `flush` to actually send the queued events. For short-lived applications, this would typically be done at the end of the application's lifecycle. -Use `InitOptions` for a self-hosted endpoint or for the interval used when periodic polling is enabled. +Long-running applications can opt in to periodic flushing with `with_polling(true)`. The interval defaults to 60 seconds in release builds and 2 seconds in debug builds. This can be customized with `with_options(InitOptions)`. It is important that you still flush manually before the applcation exits, so any unsent events are sent to the server. ```rust use aptabase_rs::{Builder, InitOptions}; +use std::error::Error; use std::time::Duration; -let client = Builder::new("A-SH-your-app-key", "1.4.0") - .with_options(InitOptions { - host: Some("https://analytics.example.com".into()), - flush_interval: Some(Duration::from_secs(30)), - }) - .build(); -``` +#[tokio::main] +async fn main() -> Result<(), Box> { + let client = Builder::new("", env!("CARGO_PKG_VERSION")) + .with_polling(true) + .with_options(InitOptions { + flush_interval: Some(Duration::from_secs(30)), + host: None, + }) + .build(); + + // do some long-running work here... -Hosted keys beginning with `A-US-` and `A-EU-` use Aptabase's regional endpoints. `A-DEV-` targets `http://localhost:3000`, and `A-SH-` requires `host`. + client.flush().await; + Ok(()) +} +``` ## Panic hook From aa227236db0055437c108ebed4db663a830fd064 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Mon, 22 Jun 2026 12:57:20 +0100 Subject: [PATCH 11/83] Fix changelog --- Cargo.toml | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index fd0b862..3fec3d6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,25 +47,25 @@ body = """ {% endfor %} {% endfor %} """ +postprocessors = [ + { pattern = "\\(#([0-9]+)\\)", replace = "([#${1}](https://github.com/eugencowie/aptabase-rs/pull/${1}))" }, +] [workspace.metadata.git-cliff.git] +filter_commits = true commit_parsers = [ - { message = "^feat", group = "๐Ÿš€ Features" }, - { message = "^fix", group = "๐Ÿ› Bug Fixes" }, - { message = "^doc", group = "๐Ÿ“š Documentation" }, - { message = "^perf", group = "โšก Performance" }, - { message = "^refactor", group = "๐Ÿšœ Refactor" }, - { message = "^style", group = "๐ŸŽจ Styling" }, - { message = "^test", group = "๐Ÿงช Testing" }, + { message = "^feat: .+ \\(#[0-9]+\\)", group = "๐Ÿš€ Features" }, + { message = "^fix: .+ \\(#[0-9]+\\)", group = "๐Ÿ› Bug Fixes" }, + { message = "^docs?: .+ \\(#[0-9]+\\)", group = "๐Ÿ“š Documentation" }, + { message = "^perf: .+ \\(#[0-9]+\\)", group = "โšก Performance" }, + { message = "^refactor: .+ \\(#[0-9]+\\)", group = "๐Ÿšœ Refactor" }, + { message = "^style: .+ \\(#[0-9]+\\)", group = "๐ŸŽจ Styling" }, + { message = "^test: .+ \\(#[0-9]+\\)", group = "๐Ÿงช Testing" }, { message = "^chore\\(release\\)", skip = true }, { message = "^chore\\(deps.*\\)", skip = true }, { message = "^chore\\(pr\\)", skip = true }, { message = "^chore\\(pull\\)", skip = true }, - { message = "^chore|^ci", group = "โš™๏ธ Miscellaneous Tasks" }, - { body = ".*security", group = "๐Ÿ›ก๏ธ Security" }, - { message = "^revert", group = "โ—€๏ธ Revert" }, - { message = ".*", group = "๐Ÿ’ผ Other" }, -] -commit_preprocessors = [ - { pattern = "\\(#([0-9]+)\\)", replace = "([#${1}](https://github.com/eugencowie/aptabase-rs/pull/${1}))" }, + { message = "^(chore|ci): .+ \\(#[0-9]+\\)", group = "โš™๏ธ Miscellaneous Tasks" }, + { message = "^[a-z]+: .+ \\(#[0-9]+\\)", body = ".*security", group = "๐Ÿ›ก๏ธ Security" }, + { message = "^revert: .+ \\(#[0-9]+\\)", group = "โ—€๏ธ Revert" }, ] From 1db10f5ff43df1d30db9f78c688c4d14a838ae17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Mon, 22 Jun 2026 20:57:04 +0100 Subject: [PATCH 12/83] chore(release): Release 0.1.0 --- CHANGELOG.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..c57abbc --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,15 @@ +# Changelog + +## 0.1.0 + +### ๐Ÿš€ Features + +- Generic Rust SDK for Aptabase ([#1](https://github.com/eugencowie/aptabase-rs/pull/1)) + +### ๐Ÿ“š Documentation + +- Update readme ([#4](https://github.com/eugencowie/aptabase-rs/pull/4)) + +### โš™๏ธ Miscellaneous Tasks + +- Configure release workflow ([#2](https://github.com/eugencowie/aptabase-rs/pull/2)) From bba2e9a9940016ba3cff0054893ffaf33b9a144c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Mon, 22 Jun 2026 21:27:16 +0100 Subject: [PATCH 13/83] Fix release workflow --- release-plz.toml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/release-plz.toml b/release-plz.toml index 97cb404..e7cb105 100644 --- a/release-plz.toml +++ b/release-plz.toml @@ -1,9 +1,8 @@ [workspace] allow_dirty = true changelog_update = false -git_release_enable = false +git_release_name = "{{ version }}" pr_name = "chore(release): Release {{ version }}" pr_branch_prefix = "release/" pr_labels = ["chore"] release_always = false -semver_check = false From 83eab903a1726ebf1832e04a1fd5ca7abf0555f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Mon, 22 Jun 2026 21:27:24 +0100 Subject: [PATCH 14/83] Update readme --- README.md | 13 +++++++------ src/lib.rs | 6 +++--- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index cc51e65..5224478 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,9 @@ Then create the client with your app key and application version: `src/main.rs` ```rust -let client = aptabase_rs::Builder::new( +use aptabase_rs::Builder; + +let client = Builder::new( "", // ๐Ÿ‘ˆ this is where you enter your App Key env!("CARGO_PKG_VERSION"), ) @@ -92,7 +94,7 @@ async fn main() -> Result<(), Box> { Calling `track_event` only enqueues events to be sent to the server, you need to explicitly await `flush` to actually send the queued events. For short-lived applications, this would typically be done at the end of the application's lifecycle. -Long-running applications can opt in to periodic flushing with `with_polling(true)`. The interval defaults to 60 seconds in release builds and 2 seconds in debug builds. This can be customized with `with_options(InitOptions)`. It is important that you still flush manually before the applcation exits, so any unsent events are sent to the server. +Long-running applications can opt in to periodic flushing with `with_polling(true)`. The interval defaults to 60 seconds in release builds and 2 seconds in debug builds. This can be customized with `with_options(InitOptions)`. It is important that you still flush manually before the applcation exits, so that any remaining events in the queue are sent to the server. ```rust use aptabase_rs::{Builder, InitOptions}; @@ -118,15 +120,14 @@ async fn main() -> Result<(), Box> { ## Panic hook -You can use the default panic hook to enqueue a `panic` event before the process continues to the default panic handler. If the panicking thread has an active Tokio runtime, the hook makes a best-effort delivery attempt with the same Tokio-backed transport. Without an active Tokio runtime on that thread, the panic event is only enqueued in memory before the previous panic hook runs. Applications that need predictable shutdown delivery should still call `flush().await` from normal control flow. +You can use the default panic hook to enqueue a `panic` event before the process continues to the default panic handler. If the panicking thread has an active Tokio runtime, the hook makes a best-effort delivery attempt with the same Tokio-backed transport. Without an active Tokio runtime on that thread, the panic event is only enqueued in memory before the previous panic hook runs. ```rust use aptabase_rs::Builder; -let client = Builder::new("A-EU-your-app-key", env!("CARGO_PKG_VERSION")) +let client = Builder::new("", env!("CARGO_PKG_VERSION")) .with_default_panic_hook() .build(); -# let _ = client; ``` For custom panic event payloads, provide your own hook: @@ -135,7 +136,7 @@ For custom panic event payloads, provide your own hook: use aptabase_rs::Builder; use serde_json::json; -let client = Builder::new("A-EU-your-app-key", env!("CARGO_PKG_VERSION")) +let client = Builder::new("", env!("CARGO_PKG_VERSION")) .with_panic_hook(Box::new(|client, info, message| { let location = info .location() diff --git a/src/lib.rs b/src/lib.rs index 2e50c7c..3ef2047 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -70,15 +70,15 @@ impl Builder { /// Enables the default panic hook. pub fn with_default_panic_hook(self) -> Self { - self.with_panic_hook(Box::new(|client, info, msg| { + self.with_panic_hook(Box::new(|client, info, message| { let location = info .location() .map(|loc| format!("{}:{}:{}", loc.file(), loc.line(), loc.column())) - .unwrap_or_else(|| "".to_string()); + .unwrap_or_default(); let _ = client.track_event( "panic", Some(json!({ - "info": format!("{} ({})", msg, location), + "info": format!("{} ({})", message, location), })), ); })) From 8ec197202e4a6dbbc2f7fb1a5cd3cf0fc24b8ea1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Mon, 22 Jun 2026 21:30:24 +0100 Subject: [PATCH 15/83] chore(release): Release 0.1.1 --- CHANGELOG.md | 6 ++++++ Cargo.lock | 2 +- Cargo.toml | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c57abbc..2b39229 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 0.1.1 + +### โš™๏ธ Miscellaneous Tasks + +- Fix release workflow ([#5](https://github.com/eugencowie/aptabase-rs/pull/5)) + ## 0.1.0 ### ๐Ÿš€ Features diff --git a/Cargo.lock b/Cargo.lock index 3c2c8c3..811df8c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19,7 +19,7 @@ checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" [[package]] name = "aptabase-rs" -version = "0.1.0" +version = "0.1.1" dependencies = [ "futures", "log", diff --git a/Cargo.toml b/Cargo.toml index 3fec3d6..d99f27a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "aptabase-rs" -version = "0.1.0" +version = "0.1.1" authors = ["Eugรฉn Cowie"] edition = "2024" description = "Rust SDK for Aptabase: Open Source, Privacy-First and Simple Analytics for Mobile, Desktop and Web Apps" From 043a7886d70bd8bf272977ee7499920bd86ffd75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Mon, 22 Jun 2026 23:42:30 +0100 Subject: [PATCH 16/83] Fix build error in example --- Cargo.toml | 42 ------------------------------------------ cliff.toml | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 42 deletions(-) create mode 100644 cliff.toml diff --git a/Cargo.toml b/Cargo.toml index d99f27a..f5923d2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,45 +27,3 @@ warnings = "deny" [lints.clippy] all = "deny" - -[workspace.metadata.git-cliff.changelog] -output = "CHANGELOG.md" -header = "# Changelog" -body = """ - -{% if version %}\ - ## {{ version | trim_start_matches(pat="v") }} -{% else %}\ - ## Unreleased -{% endif %}\ -{% for group, commits in commits | group_by(attribute="group") %} - ### {{ group | striptags | trim | upper_first }} - {% for commit in commits %} - - {% if commit.scope %}*({{ commit.scope }})* {% endif %}\ - {% if commit.breaking %}**BREAKING CHANGE**: {% endif %}\ - {{ commit.message | upper_first }}\ - {% endfor %} -{% endfor %} -""" -postprocessors = [ - { pattern = "\\(#([0-9]+)\\)", replace = "([#${1}](https://github.com/eugencowie/aptabase-rs/pull/${1}))" }, -] - -[workspace.metadata.git-cliff.git] -filter_commits = true -commit_parsers = [ - { message = "^feat: .+ \\(#[0-9]+\\)", group = "๐Ÿš€ Features" }, - { message = "^fix: .+ \\(#[0-9]+\\)", group = "๐Ÿ› Bug Fixes" }, - { message = "^docs?: .+ \\(#[0-9]+\\)", group = "๐Ÿ“š Documentation" }, - { message = "^perf: .+ \\(#[0-9]+\\)", group = "โšก Performance" }, - { message = "^refactor: .+ \\(#[0-9]+\\)", group = "๐Ÿšœ Refactor" }, - { message = "^style: .+ \\(#[0-9]+\\)", group = "๐ŸŽจ Styling" }, - { message = "^test: .+ \\(#[0-9]+\\)", group = "๐Ÿงช Testing" }, - { message = "^chore\\(release\\)", skip = true }, - { message = "^chore\\(deps.*\\)", skip = true }, - { message = "^chore\\(pr\\)", skip = true }, - { message = "^chore\\(pull\\)", skip = true }, - { message = "^(chore|ci): .+ \\(#[0-9]+\\)", group = "โš™๏ธ Miscellaneous Tasks" }, - { message = "^[a-z]+: .+ \\(#[0-9]+\\)", body = ".*security", group = "๐Ÿ›ก๏ธ Security" }, - { message = "^revert: .+ \\(#[0-9]+\\)", group = "โ—€๏ธ Revert" }, -] diff --git a/cliff.toml b/cliff.toml new file mode 100644 index 0000000..e7ce53c --- /dev/null +++ b/cliff.toml @@ -0,0 +1,41 @@ +[changelog] +output = "CHANGELOG.md" +header = "# Changelog" +body = """ + +{% if version %}\ + ## {{ version | trim_start_matches(pat="v") }} +{% else %}\ + ## Unreleased +{% endif %}\ +{% for group, commits in commits | group_by(attribute="group") %} + ### {{ group | striptags | trim | upper_first }} + {% for commit in commits %} + - {% if commit.scope %}*({{ commit.scope }})* {% endif %}\ + {% if commit.breaking %}**BREAKING CHANGE**: {% endif %}\ + {{ commit.message | upper_first }}\ + {% endfor %} +{% endfor %} +""" +postprocessors = [ + { pattern = "\\(#([0-9]+)\\)", replace = "([#${1}](https://github.com/eugencowie/aptabase-rs/pull/${1}))" }, +] + +[git] +filter_commits = true +commit_parsers = [ + { message = "^feat: .+ \\(#[0-9]+\\)", group = "๐Ÿš€ Features" }, + { message = "^fix: .+ \\(#[0-9]+\\)", group = "๐Ÿ› Bug Fixes" }, + { message = "^docs?: .+ \\(#[0-9]+\\)", group = "๐Ÿ“š Documentation" }, + { message = "^perf: .+ \\(#[0-9]+\\)", group = "โšก Performance" }, + { message = "^refactor: .+ \\(#[0-9]+\\)", group = "๐Ÿšœ Refactor" }, + { message = "^style: .+ \\(#[0-9]+\\)", group = "๐ŸŽจ Styling" }, + { message = "^test: .+ \\(#[0-9]+\\)", group = "๐Ÿงช Testing" }, + { message = "^chore\\(release\\)", skip = true }, + { message = "^chore\\(deps.*\\)", skip = true }, + { message = "^chore\\(pr\\)", skip = true }, + { message = "^chore\\(pull\\)", skip = true }, + { message = "^(chore|ci): .+ \\(#[0-9]+\\)", group = "โš™๏ธ Miscellaneous Tasks" }, + { message = "^[a-z]+: .+ \\(#[0-9]+\\)", body = ".*security", group = "๐Ÿ›ก๏ธ Security" }, + { message = "^revert: .+ \\(#[0-9]+\\)", group = "โ—€๏ธ Revert" }, +] From eb3d50ec8bca6a0095bba7fe6fe0c302cad42e28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Mon, 22 Jun 2026 23:50:08 +0100 Subject: [PATCH 17/83] Tidy up readme --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 5224478..0a482e8 100644 --- a/README.md +++ b/README.md @@ -149,7 +149,6 @@ let client = Builder::new("", env!("CARGO_PKG_VERSION")) ); })) .build(); -# let _ = client; ``` ## Delivery behavior From acad9fd7f588979af3c235a5f2fd36c839208978 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Tue, 23 Jun 2026 00:00:56 +0100 Subject: [PATCH 18/83] chore(release): Release 0.1.2 --- CHANGELOG.md | 10 ++++++++++ Cargo.lock | 2 +- Cargo.toml | 2 +- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b39229..c963221 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # Changelog +## 0.1.2 + +### ๐Ÿ› Bug Fixes + +- Fix build error in example ([#7](https://github.com/eugencowie/aptabase-rs/pull/7)) + +### ๐Ÿ“š Documentation + +- Tidy up readme ([#9](https://github.com/eugencowie/aptabase-rs/pull/9)) + ## 0.1.1 ### โš™๏ธ Miscellaneous Tasks diff --git a/Cargo.lock b/Cargo.lock index 811df8c..2d304c3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19,7 +19,7 @@ checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" [[package]] name = "aptabase-rs" -version = "0.1.1" +version = "0.1.2" dependencies = [ "futures", "log", diff --git a/Cargo.toml b/Cargo.toml index f5923d2..9da70cb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "aptabase-rs" -version = "0.1.1" +version = "0.1.2" authors = ["Eugรฉn Cowie"] edition = "2024" description = "Rust SDK for Aptabase: Open Source, Privacy-First and Simple Analytics for Mobile, Desktop and Web Apps" From 0dd0427cd6f751565457d531162a1adc3960cd27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Thu, 25 Jun 2026 12:55:46 +0100 Subject: [PATCH 19/83] Add custom session ID support --- README.md | 22 +++++ mise.lock | 95 +++++++++++++++---- .../.openspec.yaml | 2 + .../design.md | 32 +++++++ .../proposal.md | 25 +++++ .../specs/custom-session-id/spec.md | 20 ++++ .../tasks.md | 12 +++ openspec/specs/custom-session-id/spec.md | 24 +++++ openspec/specs/generic-rust-client/spec.md | 2 +- src/client.rs | 26 ++++- src/config.rs | 5 +- src/lib.rs | 39 +++++++- 12 files changed, 277 insertions(+), 27 deletions(-) create mode 100644 openspec/changes/archive/2026-06-25-support-custom-session-id/.openspec.yaml create mode 100644 openspec/changes/archive/2026-06-25-support-custom-session-id/design.md create mode 100644 openspec/changes/archive/2026-06-25-support-custom-session-id/proposal.md create mode 100644 openspec/changes/archive/2026-06-25-support-custom-session-id/specs/custom-session-id/spec.md create mode 100644 openspec/changes/archive/2026-06-25-support-custom-session-id/tasks.md create mode 100644 openspec/specs/custom-session-id/spec.md diff --git a/README.md b/README.md index 0a482e8..6bfe4fc 100644 --- a/README.md +++ b/README.md @@ -90,6 +90,28 @@ async fn main() -> Result<(), Box> { } ``` +## Persisting sessions + +By default, the SDK generates an in-memory session ID which rotates every 4 hours or whenever the app restarts. Applications which start and exit frequently may want to persist the session ID to disk so it can be reused across restarts. + +To implement persistent sessions, generate a new session ID with `aptabase_rs::new_session_id()` and persist it to your application's storage (file, database, etc.). This can be loaded at startup and passed to the builder with `Builder::with_session_id()`. When you do this, your application is responsible for rotating the persisted session ID; if it keeps reusing the same stored ID, those runs will be grouped into the same session. + +```rust +use aptabase_rs::Builder; +use std::fs; + +let session_path = "aptabase-session-id"; +let session_id = fs::read_to_string(session_path).unwrap_or_else(|_| { + let id = aptabase_rs::new_session_id(); + fs::write(session_path, &id).expect("failed to persist Aptabase session ID"); + id +}); + +let client = Builder::new("", env!("CARGO_PKG_VERSION")) + .with_session_id(session_id) + .build(); +``` + ## Periodic flushing Calling `track_event` only enqueues events to be sent to the server, you need to explicitly await `flush` to actually send the queued events. For short-lived applications, this would typically be done at the end of the application's lifecycle. diff --git a/mise.lock b/mise.lock index ee516a6..9ee178b 100644 --- a/mise.lock +++ b/mise.lock @@ -5,53 +5,112 @@ version = "1.7.12" backend = "aqua:rhysd/actionlint" [[tools.gh]] -version = "2.93.0" +version = "2.95.0" backend = "aqua:cli/cli" +[tools.gh."platforms.linux-arm64"] +checksum = "sha256:d41e0b3b6218e5741c8bb4db39b16e53a59e0e06299a8489bd38f623ef7ebaae" +url = "https://github.com/cli/cli/releases/download/v2.95.0/gh_2.95.0_linux_arm64.tar.gz" +provenance = "github-attestations" + +[tools.gh."platforms.linux-arm64-musl"] +checksum = "sha256:d41e0b3b6218e5741c8bb4db39b16e53a59e0e06299a8489bd38f623ef7ebaae" +url = "https://github.com/cli/cli/releases/download/v2.95.0/gh_2.95.0_linux_arm64.tar.gz" +provenance = "github-attestations" + +[tools.gh."platforms.linux-x64"] +checksum = "sha256:25d1e4729e8808c9ed3d613e96ebd3f3e44446f2d368c89d878a71a36ddb3d8c" +url = "https://github.com/cli/cli/releases/download/v2.95.0/gh_2.95.0_linux_amd64.tar.gz" +provenance = "github-attestations" + +[tools.gh."platforms.linux-x64-musl"] +checksum = "sha256:25d1e4729e8808c9ed3d613e96ebd3f3e44446f2d368c89d878a71a36ddb3d8c" +url = "https://github.com/cli/cli/releases/download/v2.95.0/gh_2.95.0_linux_amd64.tar.gz" +provenance = "github-attestations" + +[tools.gh."platforms.macos-arm64"] +checksum = "sha256:3677f9c27965825f9c7d50395473c134edaea4b484373ef6b25de653570a0489" +url = "https://github.com/cli/cli/releases/download/v2.95.0/gh_2.95.0_macOS_arm64.zip" +provenance = "github-attestations" + +[tools.gh."platforms.macos-x64"] +checksum = "sha256:985707e9ac60c95ed51cddd808c338b481abe69fffa77e9d6547c3750045f77e" +url = "https://github.com/cli/cli/releases/download/v2.95.0/gh_2.95.0_macOS_amd64.zip" +provenance = "github-attestations" + +[tools.gh."platforms.windows-x64"] +checksum = "sha256:19a7154161ada9cfaa9e57edb752ecc679b75c391a62e4f7b586eea1df30b5bb" +url = "https://github.com/cli/cli/releases/download/v2.95.0/gh_2.95.0_windows_amd64.zip" +provenance = "github-attestations" + [[tools.git-cliff]] version = "2.13.1" backend = "aqua:orhun/git-cliff" [[tools.node]] -version = "26.2.0" +version = "26.3.1" backend = "core:node" [tools.node."platforms.linux-arm64"] -checksum = "sha256:bf52461d25017479cdc549642c4e7a2303ac5c5395d7c3d026d5d8b3fa283c05" -url = "https://nodejs.org/dist/v26.2.0/node-v26.2.0-linux-arm64.tar.gz" +checksum = "sha256:2f0829b201e9db20996ae15bce62138df1e3d317775b005778b05cf7b19714f1" +url = "https://nodejs.org/dist/v26.3.1/node-v26.3.1-linux-arm64.tar.gz" [tools.node."platforms.linux-arm64-musl"] -checksum = "sha256:bf52461d25017479cdc549642c4e7a2303ac5c5395d7c3d026d5d8b3fa283c05" -url = "https://nodejs.org/dist/v26.2.0/node-v26.2.0-linux-arm64.tar.gz" +checksum = "sha256:2f0829b201e9db20996ae15bce62138df1e3d317775b005778b05cf7b19714f1" +url = "https://nodejs.org/dist/v26.3.1/node-v26.3.1-linux-arm64.tar.gz" [tools.node."platforms.linux-x64"] -checksum = "sha256:8e5b4b969bb2414aca2ff84d2b99608511aedca32bf8a8427e6187dbdd703bbe" -url = "https://nodejs.org/dist/v26.2.0/node-v26.2.0-linux-x64.tar.gz" +checksum = "sha256:e892cd615e637edebcf22f9653d80fba63167ad6754d20881fd52cc37be81441" +url = "https://nodejs.org/dist/v26.3.1/node-v26.3.1-linux-x64.tar.gz" [tools.node."platforms.linux-x64-musl"] -checksum = "sha256:8e5b4b969bb2414aca2ff84d2b99608511aedca32bf8a8427e6187dbdd703bbe" -url = "https://nodejs.org/dist/v26.2.0/node-v26.2.0-linux-x64.tar.gz" +checksum = "sha256:e892cd615e637edebcf22f9653d80fba63167ad6754d20881fd52cc37be81441" +url = "https://nodejs.org/dist/v26.3.1/node-v26.3.1-linux-x64.tar.gz" [tools.node."platforms.macos-arm64"] -checksum = "sha256:2d8575b6a749c0bc66cc6ecd22a952f7d9bbf3b0ea76fcb0ed3ce858018afff7" -url = "https://nodejs.org/dist/v26.2.0/node-v26.2.0-darwin-arm64.tar.gz" +checksum = "sha256:3f624ab0d774553c0d28b968e141d8c676a35a2811fb0b7b356ba9cbdce15f74" +url = "https://nodejs.org/dist/v26.3.1/node-v26.3.1-darwin-arm64.tar.gz" [tools.node."platforms.macos-x64"] -checksum = "sha256:26a5480d0dd952be86beae83e6636dacd7a3ffd17ef51d0b26bc9b260f2b42e9" -url = "https://nodejs.org/dist/v26.2.0/node-v26.2.0-darwin-x64.tar.gz" +checksum = "sha256:3ec9e5a28c641c088f3d04ad38721bfdedb2f8aa8c031979fa93df08b5a92e58" +url = "https://nodejs.org/dist/v26.3.1/node-v26.3.1-darwin-x64.tar.gz" [tools.node."platforms.windows-x64"] -checksum = "sha256:371071a4f7e2c8a5dd280730049c685911feecc59f50ebc488d675dc1087c69c" -url = "https://nodejs.org/dist/v26.2.0/node-v26.2.0-win-x64.zip" +checksum = "sha256:45001b289ebffe7b22260898f3750059183d8246042b88e8ffa4337e65e6763e" +url = "https://nodejs.org/dist/v26.3.1/node-v26.3.1-win-x64.zip" [[tools."npm:@fission-ai/openspec"]] -version = "1.3.1" +version = "1.4.1" backend = "npm:@fission-ai/openspec" [[tools.release-plz]] -version = "0.3.158" +version = "0.3.159" backend = "aqua:release-plz/release-plz" +[tools.release-plz."platforms.linux-arm64"] +checksum = "sha256:58b80ecce86ac24cbecf3f4cf37eeb0412583a0adf3a8ae608b6a4eb71737f35" +url = "https://github.com/release-plz/release-plz/releases/download/release-plz-v0.3.159/release-plz-aarch64-unknown-linux-musl.tar.gz" + +[tools.release-plz."platforms.linux-arm64-musl"] +checksum = "sha256:58b80ecce86ac24cbecf3f4cf37eeb0412583a0adf3a8ae608b6a4eb71737f35" +url = "https://github.com/release-plz/release-plz/releases/download/release-plz-v0.3.159/release-plz-aarch64-unknown-linux-musl.tar.gz" + +[tools.release-plz."platforms.linux-x64"] +checksum = "sha256:b634dcb4a1b0724e00e924ab50e685704ef2bf8c6ebb809f4ac2ab16ac8458d9" +url = "https://github.com/release-plz/release-plz/releases/download/release-plz-v0.3.159/release-plz-x86_64-unknown-linux-musl.tar.gz" + +[tools.release-plz."platforms.linux-x64-musl"] +checksum = "sha256:b634dcb4a1b0724e00e924ab50e685704ef2bf8c6ebb809f4ac2ab16ac8458d9" +url = "https://github.com/release-plz/release-plz/releases/download/release-plz-v0.3.159/release-plz-x86_64-unknown-linux-musl.tar.gz" + +[tools.release-plz."platforms.macos-arm64"] +checksum = "sha256:c9ff7e895a018c6d6c1cd62c85d0ff296a8e6dd1647bc816b9251d8056b72e2f" +url = "https://github.com/release-plz/release-plz/releases/download/release-plz-v0.3.159/release-plz-aarch64-apple-darwin.tar.gz" + +[tools.release-plz."platforms.windows-x64"] +checksum = "sha256:cd6d49c63a8800a40599e2f85a488e9c5efbacba6e53e3e45eb71aa02b1b2876" +url = "https://github.com/release-plz/release-plz/releases/download/release-plz-v0.3.159/release-plz-x86_64-pc-windows-msvc.tar.gz" + [[tools.rust]] version = "stable" backend = "core:rust" diff --git a/openspec/changes/archive/2026-06-25-support-custom-session-id/.openspec.yaml b/openspec/changes/archive/2026-06-25-support-custom-session-id/.openspec.yaml new file mode 100644 index 0000000..de73b34 --- /dev/null +++ b/openspec/changes/archive/2026-06-25-support-custom-session-id/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-06-25 diff --git a/openspec/changes/archive/2026-06-25-support-custom-session-id/design.md b/openspec/changes/archive/2026-06-25-support-custom-session-id/design.md new file mode 100644 index 0000000..0f6d647 --- /dev/null +++ b/openspec/changes/archive/2026-06-25-support-custom-session-id/design.md @@ -0,0 +1,32 @@ +## Context + +`aptabase-rs` currently creates a `TrackingSession` when `Builder::build()` constructs an `AptabaseClient`. That works for long-running processes, but short-lived applications get a new session ID on every run. `aptabase-cpp` handles this by letting the caller generate or provide a session ID and persist it outside the SDK. + +## Goals / Non-Goals + +**Goals:** +- Let applications reuse a persisted session ID across process runs. +- Keep the existing automatic session generation and four-hour timeout behavior. +- Match the C++ SDK's caller-owned session model with minimal Rust API surface. + +**Non-Goals:** +- Do not add SDK-owned disk persistence. +- Do not add `StartSession` / `EndSession` lifecycle methods. +- Do not add a storage abstraction or new dependency. + +## Decisions + +- Expose the existing session ID generator as public API, likely as `aptabase_rs::new_session_id()`. + - Alternative: force callers to invent IDs. Rejected because the SDK already has the Aptabase-compatible generator. +- Add `Builder::with_session_id(...)` to seed the initial session. + - Alternative: add `AptabaseClient::set_session_id(...)`. Rejected because the immediate need is process startup reuse, not mutable session management. +- Treat an empty supplied session ID as absent and generate one. + - Alternative: return a validation error. Rejected because builder methods currently do not return `Result`, and this mirrors the C++ SDK's empty-string behavior. +- Keep timeout rotation unchanged after initialization. + - Alternative: make caller-supplied IDs permanent. Rejected because it would bypass the existing session lifecycle. + +## Risks / Trade-offs + +- Caller forgets to persist the generated ID -> The SDK cannot group sessions across runs; README should show caller-owned persistence explicitly. +- Caller supplies unstable or shared IDs -> Events may be grouped incorrectly; document that the application owns ID scope. +- No getter for current session ID -> Callers that want persistence should generate or load the ID before building the client. diff --git a/openspec/changes/archive/2026-06-25-support-custom-session-id/proposal.md b/openspec/changes/archive/2026-06-25-support-custom-session-id/proposal.md new file mode 100644 index 0000000..85465d0 --- /dev/null +++ b/openspec/changes/archive/2026-06-25-support-custom-session-id/proposal.md @@ -0,0 +1,25 @@ +## Why + +Short-lived programs create a new in-memory Aptabase session on every run, so events from repeated invocations cannot be grouped unless the application can persist and reuse a session ID. The C++ SDK handles this by letting callers provide a custom session ID; `aptabase-rs` should expose the same minimal control without owning disk persistence. + +## What Changes + +- Add a public session ID generator so applications can create SDK-compatible session IDs when they need one. +- Add a builder option for supplying an initial session ID. +- Continue generating a session ID automatically when the caller does not provide one. +- Keep session storage and persistence outside the SDK. + +## Capabilities + +### New Capabilities +- `custom-session-id`: Caller-owned session ID generation and initialization for applications that persist session state across process runs. + +### Modified Capabilities +- None. + +## Impact + +- Public Rust API: `Builder` gains a custom-session option and the crate exposes session ID generation. +- Session initialization changes in `src/client.rs`; automatic timeout rotation remains unchanged. +- README usage docs should show caller-owned persistence for short-lived applications. +- Tests should cover generated IDs and caller-supplied session IDs. diff --git a/openspec/changes/archive/2026-06-25-support-custom-session-id/specs/custom-session-id/spec.md b/openspec/changes/archive/2026-06-25-support-custom-session-id/specs/custom-session-id/spec.md new file mode 100644 index 0000000..954c699 --- /dev/null +++ b/openspec/changes/archive/2026-06-25-support-custom-session-id/specs/custom-session-id/spec.md @@ -0,0 +1,20 @@ +## ADDED Requirements + +### Requirement: Caller-owned session IDs +The client SHALL provide API to generate a session ID and to initialize a client with a caller-supplied session ID. When no session ID is supplied, the client SHALL continue generating one automatically. The SDK MUST NOT persist session IDs to disk. + +#### Scenario: Generate a reusable session ID +- **WHEN** a caller requests a new session ID from the SDK +- **THEN** the SDK returns a non-empty session ID suitable for use as an event `sessionId` + +#### Scenario: Reuse a caller-supplied session ID +- **WHEN** a client is constructed with a caller-supplied session ID and tracks an event before the session timeout +- **THEN** the event contains the supplied session ID + +#### Scenario: Preserve automatic session generation +- **WHEN** a client is constructed without a caller-supplied session ID +- **THEN** tracked events contain an SDK-generated session ID + +#### Scenario: Treat empty session ID as absent +- **WHEN** a client is constructed with an empty caller-supplied session ID +- **THEN** tracked events contain an SDK-generated session ID diff --git a/openspec/changes/archive/2026-06-25-support-custom-session-id/tasks.md b/openspec/changes/archive/2026-06-25-support-custom-session-id/tasks.md new file mode 100644 index 0000000..f742e7e --- /dev/null +++ b/openspec/changes/archive/2026-06-25-support-custom-session-id/tasks.md @@ -0,0 +1,12 @@ +## 1. API and Session Initialization + +- [x] 1.1 Expose the existing session ID generator as public `new_session_id()` API. +- [x] 1.2 Add `Builder::with_session_id(...)` and store the optional session ID on the builder. +- [x] 1.3 Pass the optional session ID into `AptabaseClient::new(...)`. +- [x] 1.4 Initialize `TrackingSession` with the non-empty supplied ID or generate one when absent/empty. + +## 2. Verification and Docs + +- [x] 2.1 Add focused tests for generated IDs, supplied IDs, and empty supplied IDs. +- [x] 2.2 Update README usage docs with caller-owned session ID persistence. +- [x] 2.3 Run formatting and test checks for the crate. diff --git a/openspec/specs/custom-session-id/spec.md b/openspec/specs/custom-session-id/spec.md new file mode 100644 index 0000000..c7e1347 --- /dev/null +++ b/openspec/specs/custom-session-id/spec.md @@ -0,0 +1,24 @@ +# custom-session-id Specification + +## Purpose +Define caller-owned session ID support for applications that need to persist or reuse Aptabase sessions across client construction. + +## Requirements +### Requirement: Caller-owned session IDs +The client SHALL provide API to generate a session ID and to initialize a client with a caller-supplied session ID. When no session ID is supplied, the client SHALL continue generating one automatically. The SDK MUST NOT persist session IDs to disk. + +#### Scenario: Generate a reusable session ID +- **WHEN** a caller requests a new session ID from the SDK +- **THEN** the SDK returns a non-empty session ID suitable for use as an event `sessionId` + +#### Scenario: Reuse a caller-supplied session ID +- **WHEN** a client is constructed with a caller-supplied session ID and tracks an event before the session timeout +- **THEN** the event contains the supplied session ID + +#### Scenario: Preserve automatic session generation +- **WHEN** a client is constructed without a caller-supplied session ID +- **THEN** tracked events contain an SDK-generated session ID + +#### Scenario: Treat empty session ID as absent +- **WHEN** a client is constructed with an empty caller-supplied session ID +- **THEN** tracked events contain an SDK-generated session ID diff --git a/openspec/specs/generic-rust-client/spec.md b/openspec/specs/generic-rust-client/spec.md index d28bb59..cf39469 100644 --- a/openspec/specs/generic-rust-client/spec.md +++ b/openspec/specs/generic-rust-client/spec.md @@ -1,7 +1,7 @@ # generic-rust-client Specification ## Purpose -Define the framework-independent Rust SDK for constructing an Aptabase client, enqueueing analytics events, managing sessions and metadata, selecting hosted or self-hosted endpoints, and delivering queued events through Tokio-based batching and flushing. +Define the framework-independent Rust SDK surface created by extracting the existing `tauri-plugin-aptabase` client behavior for use by generic Rust applications. This covers construction, event enqueueing, baseline session lifecycle, metadata, hosted or self-hosted endpoint selection, and Tokio-based batching and flushing. ## Requirements ### Requirement: In-place generic crate replacement diff --git a/src/client.rs b/src/client.rs index 977772d..134c8b0 100644 --- a/src/client.rs +++ b/src/client.rs @@ -15,7 +15,7 @@ use crate::{ static SESSION_TIMEOUT: Duration = Duration::from_secs(4 * 60 * 60); -fn new_session_id() -> String { +pub fn new_session_id() -> String { let epoch_in_seconds = SystemTime::now() .duration_since(UNIX_EPOCH) .expect("time went backwards") @@ -37,9 +37,11 @@ pub struct TrackingSession { } impl TrackingSession { - fn new() -> Self { + fn new(id: Option) -> Self { Self { - id: new_session_id(), + id: id + .filter(|id| !id.is_empty()) + .unwrap_or_else(new_session_id), last_touch_ts: OffsetDateTime::now_utc(), } } @@ -65,7 +67,7 @@ impl AptabaseClient { Self { is_enabled, dispatcher, - session: SyncMutex::new(TrackingSession::new()), + session: SyncMutex::new(TrackingSession::new(config.session_id.clone())), app_version, sys_info, } @@ -89,7 +91,7 @@ impl AptabaseClient { let now = OffsetDateTime::now_utc(); if (now - session.last_touch_ts) > SESSION_TIMEOUT { - *session = TrackingSession::new(); + *session = TrackingSession::new(None); } else { session.last_touch_ts = now; } @@ -143,3 +145,17 @@ impl AptabaseClient { }); } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn new_session_id_is_not_empty() { + // Act + let session_id = new_session_id(); + + // Assert + assert!(!session_id.is_empty()); + } +} diff --git a/src/config.rs b/src/config.rs index 2b3cc8a..a0b6c77 100644 --- a/src/config.rs +++ b/src/config.rs @@ -8,6 +8,7 @@ use crate::InitOptions; #[derive(Debug, Clone)] pub struct Config { pub app_key: String, + pub session_id: Option, pub ingest_api_url: Url, pub flush_interval: Duration, } @@ -25,7 +26,7 @@ static DEFAULT_FLUSH_INTERVAL: Duration = Duration::from_secs(2); const VALID_REGIONS: &[&str] = &["US", "EU", "DEV", "SH"]; impl Config { - pub fn new(app_key: String, opts: InitOptions) -> Self { + pub fn new(app_key: String, session_id: Option, opts: InitOptions) -> Self { let parts = app_key.split("-").collect::>(); if parts.len() != 3 || !VALID_REGIONS.contains(&parts[1]) { debug!( @@ -54,6 +55,7 @@ impl Config { Self { app_key, + session_id, ingest_api_url: format!("{}/api/v0/events", base_url).parse().unwrap(), flush_interval: opts.flush_interval.unwrap_or(DEFAULT_FLUSH_INTERVAL), } @@ -64,6 +66,7 @@ impl Default for Config { fn default() -> Self { Self { app_key: String::new(), + session_id: None, ingest_api_url: Url::parse(LOCAL).unwrap(), flush_interval: DEFAULT_FLUSH_INTERVAL, } diff --git a/src/lib.rs b/src/lib.rs index 3ef2047..3c3d4f7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,7 +5,7 @@ mod sys; use std::{panic::PanicHookInfo, sync::Arc, time::Duration}; -pub use client::AptabaseClient; +pub use client::{AptabaseClient, new_session_id}; use config::Config; use serde_json::json; @@ -19,6 +19,7 @@ pub struct InitOptions { pub struct Builder { app_key: String, app_version: String, + session_id: Option, enable_polling: bool, panic_hook: Option, options: InitOptions, @@ -44,6 +45,7 @@ impl Builder { Self { app_key: app_key.into(), app_version: app_version.into(), + session_id: None, enable_polling: false, panic_hook: None, options: Default::default(), @@ -56,6 +58,12 @@ impl Builder { self } + /// Sets the initial session ID. + pub fn with_session_id(mut self, session_id: impl Into) -> Self { + self.session_id = Some(session_id.into()); + self + } + /// Sets whether polling should be enabled. pub fn with_polling(mut self, enable: bool) -> Self { self.enable_polling = enable; @@ -86,7 +94,7 @@ impl Builder { /// Builds and initializes the client pub fn build(self) -> Arc { - let cfg = Config::new(self.app_key, self.options); + let cfg = Config::new(self.app_key, self.session_id, self.options); let client = Arc::new(AptabaseClient::new(&cfg, self.app_version)); if self.enable_polling { @@ -107,3 +115,30 @@ impl Builder { client } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn builder_uses_supplied_session_id() { + // Act + let client = Builder::new("A-DEV-123", "test") + .with_session_id("persisted-session") + .build(); + + // Assert + assert_eq!(client.eval_session_id(), "persisted-session"); + } + + #[test] + fn builder_treats_empty_session_id_as_absent() { + // Act + let client = Builder::new("A-DEV-123", "test") + .with_session_id("") + .build(); + + // Assert + assert!(client.eval_session_id().is_empty()); + } +} From 5ee890442ac74212fcc80d8f9ac5a6de41a3cf2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Thu, 25 Jun 2026 19:00:52 +0100 Subject: [PATCH 20/83] Fix failing test --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 3c3d4f7..2a7c137 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -139,6 +139,6 @@ mod tests { .build(); // Assert - assert!(client.eval_session_id().is_empty()); + assert!(!client.eval_session_id().is_empty()); } } From 2bc435c6c2794039231b4329e8cdc90b3b47b9ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Thu, 25 Jun 2026 19:04:05 +0100 Subject: [PATCH 21/83] chore(release): Release 0.2.0 --- CHANGELOG.md | 6 ++++++ Cargo.lock | 2 +- Cargo.toml | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c963221..6fe4cb0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 0.2.0 + +### ๐Ÿš€ Features + +- Add custom session ID support ([#10](https://github.com/eugencowie/aptabase-rs/pull/10)) + ## 0.1.2 ### ๐Ÿ› Bug Fixes diff --git a/Cargo.lock b/Cargo.lock index 2d304c3..7ef3bc8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19,7 +19,7 @@ checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" [[package]] name = "aptabase-rs" -version = "0.1.2" +version = "0.2.0" dependencies = [ "futures", "log", diff --git a/Cargo.toml b/Cargo.toml index 9da70cb..7afda46 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "aptabase-rs" -version = "0.1.2" +version = "0.2.0" authors = ["Eugรฉn Cowie"] edition = "2024" description = "Rust SDK for Aptabase: Open Source, Privacy-First and Simple Analytics for Mobile, Desktop and Web Apps" From 4791370bcfbe7eac02fc98672452926b3d3d8b94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Wed, 29 Jul 2026 11:21:02 +0100 Subject: [PATCH 22/83] Run CI and releases from main --- .github/workflows/ci.yml | 2 +- .github/workflows/release-plz.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8d5291a..91a9cdb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,7 +4,7 @@ permissions: on: pull_request: push: - branches: [develop] + branches: [main] jobs: test: diff --git a/.github/workflows/release-plz.yml b/.github/workflows/release-plz.yml index 03b9356..521f965 100644 --- a/.github/workflows/release-plz.yml +++ b/.github/workflows/release-plz.yml @@ -3,7 +3,7 @@ name: Release-plz on: push: branches: - - develop + - main jobs: release-plz-release: From 1e7cd30551c8087bb037e55123141d0fd791b4a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Wed, 29 Jul 2026 11:31:07 +0100 Subject: [PATCH 23/83] Update development environment --- AGENTS.md | 11 +- Cargo.lock | 467 ++++++++++++++++------------------------------------- mise.lock | 178 +++++++++++++++----- mise.toml | 34 +++- 4 files changed, 308 insertions(+), 382 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 0c6811a..6a89382 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,3 +1,10 @@ -# Repository Guidelines +# Agent Instructions -If a command is unavailable, retry it as `mise exec -- `. +## Development environment + +This project uses mise to manage the development environment. + +- Run `mise tasks` to see the available tasks. +- Use `mise run ` for standard operations. +- Run `mise ls -l` to see the managed tools. +- When invoking a managed tool directly, use `mise exec -- [args]` rather than invoking the tool by its bare name. diff --git a/Cargo.lock b/Cargo.lock index 7ef3bc8..7a84e68 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,12 +11,6 @@ dependencies = [ "libc", ] -[[package]] -name = "anyhow" -version = "1.0.102" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" - [[package]] name = "aptabase-rs" version = "0.2.0" @@ -47,9 +41,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "bitflags" -version = "2.13.0" +version = "2.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" +checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da" [[package]] name = "block2" @@ -68,15 +62,15 @@ checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" [[package]] name = "bytes" -version = "1.11.1" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" +checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04" [[package]] name = "cc" -version = "1.2.63" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "556e016178bb5662a08681bbe0f00f8e17631781a4dfc8c45e466e4b185ec27f" +checksum = "5add81bb678e6cb321aff7fa0dc7689ad82b112dbc032cea19f91d6b8e3582b9" dependencies = [ "find-msvc-tools", "shlex", @@ -90,9 +84,9 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "cfg_aliases" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +checksum = "f079e83a288787bcd14a6aea84cee5c87a67c5a3e660c30f557a3d24761b3527" [[package]] name = "core-foundation" @@ -125,9 +119,6 @@ name = "deranged" version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" -dependencies = [ - "powerfmt", -] [[package]] name = "dispatch2" @@ -141,13 +132,13 @@ dependencies = [ [[package]] name = "displaydoc" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f" +checksum = "c6232dd377dcc64799954cbd3a9bb882e9cdc1308ccd87b1c098f1fb2eaf82a8" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 3.0.3", ] [[package]] @@ -177,9 +168,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" +checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223" [[package]] name = "find-msvc-tools" @@ -193,12 +184,6 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" -[[package]] -name = "foldhash" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" - [[package]] name = "foreign-types" version = "0.3.2" @@ -225,9 +210,9 @@ dependencies = [ [[package]] name = "futures" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +checksum = "a88cf1f829d945f548cf8fec32c61b1f202b6d93b45848602fc02af4b12ad218" dependencies = [ "futures-channel", "futures-core", @@ -240,9 +225,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +checksum = "262590f4fe6afeb0bc83be1daa64e52657fe185690a958af7f3ad0e92085c5ae" dependencies = [ "futures-core", "futures-sink", @@ -250,15 +235,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" +checksum = "2cd50c473c80f6d7c3670a752354b8e569b1a7cbfdc0419ec88e5edad85e0dc7" [[package]] name = "futures-executor" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +checksum = "6754879cc9f2c66f88c6e5c35344bb0bdb0708b0352b1201815667c7eabc7458" dependencies = [ "futures-core", "futures-task", @@ -267,38 +252,38 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" +checksum = "4577ecaa3c4f96589d473f679a71b596316f6641bc350038b962a5daf0085d7a" [[package]] name = "futures-macro" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +checksum = "2d6d3cde68c518367be28956066ddfef33813991b77a55005a69dae04bf3b10b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] name = "futures-sink" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" +checksum = "e34418ac499d6305c2fb5ad0ed2f6ac998c5f8ca209b4510f7f94242c647e307" [[package]] name = "futures-task" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" +checksum = "b231ed28831efb4a61a08580c4bc233ec56bc009f4cd8f52da2c3cb97df0c109" [[package]] name = "futures-util" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +checksum = "a77a90a256fce34da66415271e30f94ee91c57b04b8a2c042d9cf3220179deaa" dependencies = [ "futures-channel", "futures-core", @@ -336,22 +321,20 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" dependencies = [ "cfg-if", "libc", "r-efi 6.0.0", - "wasip2", - "wasip3", ] [[package]] name = "h2" -version = "0.4.14" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "171fefbc92fe4a4de27e0698d6a5b392d6a0e333506bc49133760b3bcf948733" +checksum = "6cb093c84e8bd9b188d4c4a8cb6579fc016968d14c99882163cd3ff402a4f155" dependencies = [ "atomic-waker", "bytes", @@ -366,32 +349,17 @@ dependencies = [ "tracing", ] -[[package]] -name = "hashbrown" -version = "0.15.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" -dependencies = [ - "foldhash", -] - [[package]] name = "hashbrown" version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" -[[package]] -name = "heck" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" - [[package]] name = "http" -version = "1.4.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8be7462df143984c4598a256ef469b251d7d7f9e271135073e78fc535414f3d0" +checksum = "6970f50e31d6fc17d3fa27329444bfa74e196cf62e95052a3f6fee181dba6425" dependencies = [ "bytes", "itoa", @@ -399,9 +367,9 @@ dependencies = [ [[package]] name = "http-body" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +checksum = "ca2a8f2913ee65f60facd6a5905613afaa448497a0230cc41ce022d93290bc2c" dependencies = [ "bytes", "http", @@ -409,9 +377,9 @@ dependencies = [ [[package]] name = "http-body-util" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +checksum = "e9f41fd6a08e4d4ec69df65976da761afd5ad5e58a9d4acb46bd1c953a9e3ff2" dependencies = [ "bytes", "futures-core", @@ -428,9 +396,9 @@ checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] name = "hyper" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55281c53a1894c864990125767da440a4e630446785086f52523b20033b74498" +checksum = "d22053281f852e11534f5198498373cbb59295120a20771d90f7ed1897490a72" dependencies = [ "atomic-waker", "bytes", @@ -585,12 +553,6 @@ dependencies = [ "zerovec", ] -[[package]] -name = "id-arena" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" - [[package]] name = "idna" version = "1.1.0" @@ -619,9 +581,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" dependencies = [ "equivalent", - "hashbrown 0.17.1", - "serde", - "serde_core", + "hashbrown", ] [[package]] @@ -638,27 +598,20 @@ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "js-sys" -version = "0.3.99" +version = "0.3.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "142bc4740e452c1e57ade0cbc129f139c9093e354346f0872ef985f4f5cf5f11" +checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102" dependencies = [ "cfg-if", "futures-util", - "once_cell", "wasm-bindgen", ] -[[package]] -name = "leb128fmt" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" - [[package]] name = "libc" -version = "0.2.186" +version = "0.2.189" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" +checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" [[package]] name = "linux-raw-sys" @@ -674,15 +627,15 @@ checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" [[package]] name = "log" -version = "0.4.32" +version = "0.4.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "953f07c43838f8e6f9758cab68bf5bed85465e7587ebe0b823f1bcd81978ad3a" +checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" [[package]] name = "memchr" -version = "2.8.1" +version = "2.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8" +checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" [[package]] name = "mime" @@ -692,9 +645,9 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "mio" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda" +checksum = "30d65c71f1ce40ab09135ce117d742b9f8a19ff91a41a8b57ed50bc2de59c427" dependencies = [ "libc", "wasi", @@ -903,9 +856,9 @@ checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" [[package]] name = "openssl" -version = "0.10.80" +version = "0.10.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a45fa2aa886c42762255da344f0a0d313e254066c46aad76f300c3d3da62d967" +checksum = "77823a27f0babb03091cb9ed9ef80af3b39dbc82f97e8fa530374b7dafd87a45" dependencies = [ "bitflags", "cfg-if", @@ -923,7 +876,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -934,9 +887,9 @@ checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" [[package]] name = "openssl-sys" -version = "0.9.116" +version = "0.9.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f28a22dc7140cda5f096e5e7724a6962ca81a7f8bfd2979f9b18c11af56318c4" +checksum = "b47e7e6bb2c38cd930d25a23b40fa52e068c10e85f3e03a7f5ba5aaca5713695" dependencies = [ "cc", "libc", @@ -1002,30 +955,20 @@ dependencies = [ "zerocopy", ] -[[package]] -name = "prettyplease" -version = "0.2.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" -dependencies = [ - "proc-macro2", - "syn", -] - [[package]] name = "proc-macro2" -version = "1.0.106" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.45" +version = "1.0.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" dependencies = [ "proc-macro2", ] @@ -1044,9 +987,9 @@ checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" [[package]] name = "rand" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" +checksum = "b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41" dependencies = [ "rand_chacha", "rand_core", @@ -1140,9 +1083,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.40" +version = "0.23.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b" +checksum = "3c54fcab019b409d04215d3a17cb438fd7fbf192ee61461f20f4fe18704bc138" dependencies = [ "once_cell", "rustls-pki-types", @@ -1153,9 +1096,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.14.1" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9" +checksum = "2f4925028c7eb5d1fcdaf196971378ed9d2c1c4efc7dc5d011256f76c99c0a96" dependencies = [ "zeroize", ] @@ -1173,9 +1116,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" +checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" [[package]] name = "ryu" @@ -1215,17 +1158,11 @@ dependencies = [ "libc", ] -[[package]] -name = "semver" -version = "1.0.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" - [[package]] name = "serde" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba" dependencies = [ "serde_core", "serde_derive", @@ -1233,29 +1170,29 @@ dependencies = [ [[package]] name = "serde_core" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 3.0.3", ] [[package]] name = "serde_json" -version = "1.0.150" +version = "1.0.151" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" +checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14" dependencies = [ "itoa", "memchr", @@ -1290,15 +1227,15 @@ checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" [[package]] name = "smallvec" -version = "1.15.1" +version = "1.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" [[package]] name = "socket2" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51" +checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4" dependencies = [ "libc", "windows-sys 0.61.2", @@ -1318,9 +1255,20 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.117" +version = "2.0.119" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "3.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3" dependencies = [ "proc-macro2", "quote", @@ -1344,7 +1292,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -1384,7 +1332,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", - "getrandom 0.4.2", + "getrandom 0.4.3", "once_cell", "rustix", "windows-sys 0.61.2", @@ -1392,12 +1340,11 @@ dependencies = [ [[package]] name = "time" -version = "0.3.47" +version = "0.3.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" +checksum = "3e1d5e639ff6bab73cb6885cc7e7b1de96c3f32c68ec55f3952614bec1092244" dependencies = [ "deranged", - "itoa", "num-conv", "powerfmt", "serde_core", @@ -1407,15 +1354,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" +checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" [[package]] name = "time-macros" -version = "0.2.27" +version = "0.2.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" +checksum = "7e689342a48d2ea927c87ea50cabf8594854bf940e9310208848d680d668ed85" dependencies = [ "num-conv", "time-core", @@ -1433,9 +1380,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.52.3" +version = "1.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" +checksum = "202caea871b69668250d242070849eb495be178ed697a3e98aebce5bc81a0bed" dependencies = [ "bytes", "libc", @@ -1467,13 +1414,14 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.18" +version = "0.7.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" +checksum = "494815d09bf52b5548659851081238f0ca39ff638363907596da739561c62c52" dependencies = [ "bytes", "futures-core", "futures-sink", + "libc", "pin-project-lite", "tokio", ] @@ -1554,12 +1502,6 @@ version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" -[[package]] -name = "unicode-xid" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" - [[package]] name = "untrusted" version = "0.9.0" @@ -1607,27 +1549,18 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasip2" -version = "1.0.3+wasi-0.2.9" +version = "1.0.4+wasi-0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" +checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487" dependencies = [ - "wit-bindgen 0.57.1", -] - -[[package]] -name = "wasip3" -version = "0.4.0+wasi-0.3.0-rc-2026-01-06" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" -dependencies = [ - "wit-bindgen 0.51.0", + "wit-bindgen", ] [[package]] name = "wasm-bindgen" -version = "0.2.122" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ed04576f974d2b2fba0f38c51dbc5518011e38c36bf1143164be765528fd409" +checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4" dependencies = [ "cfg-if", "once_cell", @@ -1638,9 +1571,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.72" +version = "0.4.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9473dbd2991ae90b6291c3c32c30c6187ac49aa32f9905d1cce280ec1e110b0f" +checksum = "c62df1340f32221cb9c54d6a27b030e3dba64361d4a95bed55f9aacb44da291d" dependencies = [ "js-sys", "wasm-bindgen", @@ -1648,9 +1581,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.122" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "916151b09da36bd82f6615cbf3a419e2f0ba23a03c6160e8e92eb6bd4aa1dec6" +checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1658,65 +1591,31 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.122" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "299047362ccbfce148b67ab7e73349f77748e00c8296f9542adfad2ad82c5c5e" +checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e" dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn", + "syn 2.0.119", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.122" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a929b2c61f11ba3e9bc35b50c1f25cb38e0e892c0c231ae2b8cf78d5dad4437" +checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24" dependencies = [ "unicode-ident", ] -[[package]] -name = "wasm-encoder" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" -dependencies = [ - "leb128fmt", - "wasmparser", -] - -[[package]] -name = "wasm-metadata" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" -dependencies = [ - "anyhow", - "indexmap", - "wasm-encoder", - "wasmparser", -] - -[[package]] -name = "wasmparser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" -dependencies = [ - "bitflags", - "hashbrown 0.15.5", - "indexmap", - "semver", -] - [[package]] name = "web-sys" -version = "0.3.99" +version = "0.3.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d621441cfc37b84979402712047321980c178f299193a3589d05b99e8763436" +checksum = "8622dcb61c0bcc9fffa6938bed81210af2da9a7e4a1a834b2e37a59b6dfb6141" dependencies = [ "js-sys", "wasm-bindgen", @@ -1839,100 +1738,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" -[[package]] -name = "wit-bindgen" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" -dependencies = [ - "wit-bindgen-rust-macro", -] - [[package]] name = "wit-bindgen" version = "0.57.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" -[[package]] -name = "wit-bindgen-core" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" -dependencies = [ - "anyhow", - "heck", - "wit-parser", -] - -[[package]] -name = "wit-bindgen-rust" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" -dependencies = [ - "anyhow", - "heck", - "indexmap", - "prettyplease", - "syn", - "wasm-metadata", - "wit-bindgen-core", - "wit-component", -] - -[[package]] -name = "wit-bindgen-rust-macro" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" -dependencies = [ - "anyhow", - "prettyplease", - "proc-macro2", - "quote", - "syn", - "wit-bindgen-core", - "wit-bindgen-rust", -] - -[[package]] -name = "wit-component" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" -dependencies = [ - "anyhow", - "bitflags", - "indexmap", - "log", - "serde", - "serde_derive", - "serde_json", - "wasm-encoder", - "wasm-metadata", - "wasmparser", - "wit-parser", -] - -[[package]] -name = "wit-parser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" -dependencies = [ - "anyhow", - "id-arena", - "indexmap", - "log", - "semver", - "serde", - "serde_derive", - "serde_json", - "unicode-xid", - "wasmparser", -] - [[package]] name = "writeable" version = "0.6.3" @@ -1958,28 +1769,28 @@ checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", "synstructure", ] [[package]] name = "zerocopy" -version = "0.8.50" +version = "0.8.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b065d4f0e55f82fae73202e189638116a87c55ab6b8e6c2721e13dd9d854ad1" +checksum = "b5a105cd7b140f6eeec8acff2ea38135d3cab283ada58540f629fe51e46696eb" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.50" +version = "0.8.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b631b19d36a892ab55420c92dbc83ccd79274f25be714855d3074aa71cab639" +checksum = "0fe976fb70c78cd64cccfe3a6fc142244e8a77b70959b30faf9d0ac37ee228eb" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -1999,15 +1810,15 @@ checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", "synstructure", ] [[package]] name = "zeroize" -version = "1.8.2" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" +checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" [[package]] name = "zerotrie" @@ -2039,11 +1850,11 @@ checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] name = "zmij" -version = "1.0.21" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" +checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" diff --git a/mise.lock b/mise.lock index 9ee178b..92b414d 100644 --- a/mise.lock +++ b/mise.lock @@ -4,112 +4,202 @@ version = "1.7.12" backend = "aqua:rhysd/actionlint" +[tools.actionlint."platforms.linux-arm64"] +checksum = "sha256:325e971b6ba9bfa504672e29be93c24981eeb1c07576d730e9f7c8805afff0c6" +url = "https://github.com/rhysd/actionlint/releases/download/v1.7.12/actionlint_1.7.12_linux_arm64.tar.gz" +url_api = "https://api.github.com/repos/rhysd/actionlint/releases/assets/384924897" +provenance = "github-attestations" + +[tools.actionlint."platforms.linux-arm64-musl"] +checksum = "sha256:325e971b6ba9bfa504672e29be93c24981eeb1c07576d730e9f7c8805afff0c6" +url = "https://github.com/rhysd/actionlint/releases/download/v1.7.12/actionlint_1.7.12_linux_arm64.tar.gz" +url_api = "https://api.github.com/repos/rhysd/actionlint/releases/assets/384924897" +provenance = "github-attestations" + +[tools.actionlint."platforms.linux-x64"] +checksum = "sha256:8aca8db96f1b94770f1b0d72b6dddcb1ebb8123cb3712530b08cc387b349a3d8" +url = "https://github.com/rhysd/actionlint/releases/download/v1.7.12/actionlint_1.7.12_linux_amd64.tar.gz" +url_api = "https://api.github.com/repos/rhysd/actionlint/releases/assets/384924896" +provenance = "github-attestations" + +[tools.actionlint."platforms.linux-x64-musl"] +checksum = "sha256:8aca8db96f1b94770f1b0d72b6dddcb1ebb8123cb3712530b08cc387b349a3d8" +url = "https://github.com/rhysd/actionlint/releases/download/v1.7.12/actionlint_1.7.12_linux_amd64.tar.gz" +url_api = "https://api.github.com/repos/rhysd/actionlint/releases/assets/384924896" +provenance = "github-attestations" + +[tools.actionlint."platforms.macos-arm64"] +checksum = "sha256:aba9ced2dee8d27fecca3dc7feb1a7f9a52caefa1eb46f3271ea66b6e0e6953f" +url = "https://github.com/rhysd/actionlint/releases/download/v1.7.12/actionlint_1.7.12_darwin_arm64.tar.gz" +url_api = "https://api.github.com/repos/rhysd/actionlint/releases/assets/384924893" +provenance = "github-attestations" + +[tools.actionlint."platforms.macos-x64"] +checksum = "sha256:5b44c3bc2255115c9b69e30efc0fecdf498fdb63c5d58e17084fd5f16324c644" +url = "https://github.com/rhysd/actionlint/releases/download/v1.7.12/actionlint_1.7.12_darwin_amd64.tar.gz" +url_api = "https://api.github.com/repos/rhysd/actionlint/releases/assets/384924880" +provenance = "github-attestations" + +[tools.actionlint."platforms.windows-x64"] +checksum = "sha256:6e7241b51e6817ea6a047693d8e6fed13b31819c9a0dd6c5a726e1592d22f6e9" +url = "https://github.com/rhysd/actionlint/releases/download/v1.7.12/actionlint_1.7.12_windows_amd64.zip" +url_api = "https://api.github.com/repos/rhysd/actionlint/releases/assets/384924919" +provenance = "github-attestations" + [[tools.gh]] -version = "2.95.0" +version = "2.96.0" backend = "aqua:cli/cli" [tools.gh."platforms.linux-arm64"] -checksum = "sha256:d41e0b3b6218e5741c8bb4db39b16e53a59e0e06299a8489bd38f623ef7ebaae" -url = "https://github.com/cli/cli/releases/download/v2.95.0/gh_2.95.0_linux_arm64.tar.gz" +checksum = "sha256:06f86ec7103d41993b76cd78072f43595c34aaa56506d971d9860e67140bf909" +url = "https://github.com/cli/cli/releases/download/v2.96.0/gh_2.96.0_linux_arm64.tar.gz" +url_api = "https://api.github.com/repos/cli/cli/releases/assets/464728549" provenance = "github-attestations" [tools.gh."platforms.linux-arm64-musl"] -checksum = "sha256:d41e0b3b6218e5741c8bb4db39b16e53a59e0e06299a8489bd38f623ef7ebaae" -url = "https://github.com/cli/cli/releases/download/v2.95.0/gh_2.95.0_linux_arm64.tar.gz" +checksum = "sha256:06f86ec7103d41993b76cd78072f43595c34aaa56506d971d9860e67140bf909" +url = "https://github.com/cli/cli/releases/download/v2.96.0/gh_2.96.0_linux_arm64.tar.gz" +url_api = "https://api.github.com/repos/cli/cli/releases/assets/464728549" provenance = "github-attestations" [tools.gh."platforms.linux-x64"] -checksum = "sha256:25d1e4729e8808c9ed3d613e96ebd3f3e44446f2d368c89d878a71a36ddb3d8c" -url = "https://github.com/cli/cli/releases/download/v2.95.0/gh_2.95.0_linux_amd64.tar.gz" +checksum = "sha256:83d5c2ccad5498f58bf6368acb1ab32588cf43ab3a4b1c301bf36328b1c8bd60" +url = "https://github.com/cli/cli/releases/download/v2.96.0/gh_2.96.0_linux_amd64.tar.gz" +url_api = "https://api.github.com/repos/cli/cli/releases/assets/464728543" provenance = "github-attestations" [tools.gh."platforms.linux-x64-musl"] -checksum = "sha256:25d1e4729e8808c9ed3d613e96ebd3f3e44446f2d368c89d878a71a36ddb3d8c" -url = "https://github.com/cli/cli/releases/download/v2.95.0/gh_2.95.0_linux_amd64.tar.gz" +checksum = "sha256:83d5c2ccad5498f58bf6368acb1ab32588cf43ab3a4b1c301bf36328b1c8bd60" +url = "https://github.com/cli/cli/releases/download/v2.96.0/gh_2.96.0_linux_amd64.tar.gz" +url_api = "https://api.github.com/repos/cli/cli/releases/assets/464728543" provenance = "github-attestations" [tools.gh."platforms.macos-arm64"] -checksum = "sha256:3677f9c27965825f9c7d50395473c134edaea4b484373ef6b25de653570a0489" -url = "https://github.com/cli/cli/releases/download/v2.95.0/gh_2.95.0_macOS_arm64.zip" +checksum = "sha256:f23a0c37d963aacc3bed703ccbd59b41c5ca22101fab7f00eb2b7cad23aba463" +url = "https://github.com/cli/cli/releases/download/v2.96.0/gh_2.96.0_macOS_arm64.zip" +url_api = "https://api.github.com/repos/cli/cli/releases/assets/464728562" provenance = "github-attestations" [tools.gh."platforms.macos-x64"] -checksum = "sha256:985707e9ac60c95ed51cddd808c338b481abe69fffa77e9d6547c3750045f77e" -url = "https://github.com/cli/cli/releases/download/v2.95.0/gh_2.95.0_macOS_amd64.zip" +checksum = "sha256:4bd449df9ad639391bc62b8032546f0fe9edcd8526e06682a4f88abd8c5d163c" +url = "https://github.com/cli/cli/releases/download/v2.96.0/gh_2.96.0_macOS_amd64.zip" +url_api = "https://api.github.com/repos/cli/cli/releases/assets/464728560" provenance = "github-attestations" [tools.gh."platforms.windows-x64"] -checksum = "sha256:19a7154161ada9cfaa9e57edb752ecc679b75c391a62e4f7b586eea1df30b5bb" -url = "https://github.com/cli/cli/releases/download/v2.95.0/gh_2.95.0_windows_amd64.zip" +checksum = "sha256:c2d6acc935cd2f00e2144d7e036d5cd82e6b6bd5594e8c75aa75ef2a4ed6aac3" +url = "https://github.com/cli/cli/releases/download/v2.96.0/gh_2.96.0_windows_amd64.zip" +url_api = "https://api.github.com/repos/cli/cli/releases/assets/464728577" provenance = "github-attestations" [[tools.git-cliff]] version = "2.13.1" backend = "aqua:orhun/git-cliff" +[tools.git-cliff."platforms.linux-arm64"] +checksum = "sha256:4054c124b926c117f3fa048939bc8be0a954f29f3b6f367627e8cb22c1971882" +url = "https://github.com/orhun/git-cliff/releases/download/v2.13.1/git-cliff-2.13.1-aarch64-unknown-linux-musl.tar.gz" +url_api = "https://api.github.com/repos/orhun/git-cliff/releases/assets/405850720" + +[tools.git-cliff."platforms.linux-arm64-musl"] +checksum = "sha256:4054c124b926c117f3fa048939bc8be0a954f29f3b6f367627e8cb22c1971882" +url = "https://github.com/orhun/git-cliff/releases/download/v2.13.1/git-cliff-2.13.1-aarch64-unknown-linux-musl.tar.gz" +url_api = "https://api.github.com/repos/orhun/git-cliff/releases/assets/405850720" + +[tools.git-cliff."platforms.linux-x64"] +checksum = "sha256:200d2535da6d9703f3bcc8a4d159c3b55eacdb01cf2148c55b3eee9dd04d5249" +url = "https://github.com/orhun/git-cliff/releases/download/v2.13.1/git-cliff-2.13.1-x86_64-unknown-linux-musl.tar.gz" +url_api = "https://api.github.com/repos/orhun/git-cliff/releases/assets/405850671" + +[tools.git-cliff."platforms.linux-x64-musl"] +checksum = "sha256:200d2535da6d9703f3bcc8a4d159c3b55eacdb01cf2148c55b3eee9dd04d5249" +url = "https://github.com/orhun/git-cliff/releases/download/v2.13.1/git-cliff-2.13.1-x86_64-unknown-linux-musl.tar.gz" +url_api = "https://api.github.com/repos/orhun/git-cliff/releases/assets/405850671" + +[tools.git-cliff."platforms.macos-arm64"] +checksum = "sha256:21547ae4a0421164070ab75c2522864ea5565858a011fabc5f583061b20f1226" +url = "https://github.com/orhun/git-cliff/releases/download/v2.13.1/git-cliff-2.13.1-aarch64-apple-darwin.tar.gz" +url_api = "https://api.github.com/repos/orhun/git-cliff/releases/assets/405849885" + +[tools.git-cliff."platforms.macos-x64"] +checksum = "sha256:6e60ae390d375cecb9d8008c49f0e724a8dfe40390b532ef5501e421d2cc8acb" +url = "https://github.com/orhun/git-cliff/releases/download/v2.13.1/git-cliff-2.13.1-x86_64-apple-darwin.tar.gz" +url_api = "https://api.github.com/repos/orhun/git-cliff/releases/assets/405850193" + +[tools.git-cliff."platforms.windows-x64"] +checksum = "sha256:3ae3a5549e85c7ad5b20192ebcfee4371269deca51255f6f2f2e051c6541f5ca" +url = "https://github.com/orhun/git-cliff/releases/download/v2.13.1/git-cliff-2.13.1-x86_64-pc-windows-msvc.zip" +url_api = "https://api.github.com/repos/orhun/git-cliff/releases/assets/405851866" + [[tools.node]] -version = "26.3.1" +version = "26.5.0" backend = "core:node" [tools.node."platforms.linux-arm64"] -checksum = "sha256:2f0829b201e9db20996ae15bce62138df1e3d317775b005778b05cf7b19714f1" -url = "https://nodejs.org/dist/v26.3.1/node-v26.3.1-linux-arm64.tar.gz" +checksum = "sha256:308e5fe89a82461ba5a6cf15ff5221b2cdbd7ae87600aa72bb3c3fbdc66412d1" +url = "https://nodejs.org/dist/v26.5.0/node-v26.5.0-linux-arm64.tar.gz" [tools.node."platforms.linux-arm64-musl"] -checksum = "sha256:2f0829b201e9db20996ae15bce62138df1e3d317775b005778b05cf7b19714f1" -url = "https://nodejs.org/dist/v26.3.1/node-v26.3.1-linux-arm64.tar.gz" +checksum = "sha256:1ab31aef6561191693f366cacd75bbcefa0301e49a18bc05554c21893bbdb490" +url = "https://unofficial-builds.nodejs.org/download/release/v26.5.0/node-v26.5.0-linux-arm64-musl.tar.gz" [tools.node."platforms.linux-x64"] -checksum = "sha256:e892cd615e637edebcf22f9653d80fba63167ad6754d20881fd52cc37be81441" -url = "https://nodejs.org/dist/v26.3.1/node-v26.3.1-linux-x64.tar.gz" +checksum = "sha256:22b5f47ad6ae78837e4c2b846019965ce1a06ba143de176102294a1bf44fc677" +url = "https://nodejs.org/dist/v26.5.0/node-v26.5.0-linux-x64.tar.gz" [tools.node."platforms.linux-x64-musl"] -checksum = "sha256:e892cd615e637edebcf22f9653d80fba63167ad6754d20881fd52cc37be81441" -url = "https://nodejs.org/dist/v26.3.1/node-v26.3.1-linux-x64.tar.gz" +checksum = "sha256:00f1398411a4216c5a6ecaad3b825a0da5ec00e79ee8c173ab65a094d97b9ad8" +url = "https://unofficial-builds.nodejs.org/download/release/v26.5.0/node-v26.5.0-linux-x64-musl.tar.gz" [tools.node."platforms.macos-arm64"] -checksum = "sha256:3f624ab0d774553c0d28b968e141d8c676a35a2811fb0b7b356ba9cbdce15f74" -url = "https://nodejs.org/dist/v26.3.1/node-v26.3.1-darwin-arm64.tar.gz" +checksum = "sha256:ee920559aaa2391569cff4d737e3b83963430e3a14dedd91bfe0ff53171b5af9" +url = "https://nodejs.org/dist/v26.5.0/node-v26.5.0-darwin-arm64.tar.gz" [tools.node."platforms.macos-x64"] -checksum = "sha256:3ec9e5a28c641c088f3d04ad38721bfdedb2f8aa8c031979fa93df08b5a92e58" -url = "https://nodejs.org/dist/v26.3.1/node-v26.3.1-darwin-x64.tar.gz" +checksum = "sha256:98293394c945a24e64e00b4177bf075ec963ea70b34d1d2e24bd4a71716d334f" +url = "https://nodejs.org/dist/v26.5.0/node-v26.5.0-darwin-x64.tar.gz" [tools.node."platforms.windows-x64"] -checksum = "sha256:45001b289ebffe7b22260898f3750059183d8246042b88e8ffa4337e65e6763e" -url = "https://nodejs.org/dist/v26.3.1/node-v26.3.1-win-x64.zip" +checksum = "sha256:d3b2277dbcccfdf24ef6302928f64f484cff1d77a6d3caa3a28f4d20ce9158f6" +url = "https://nodejs.org/dist/v26.5.0/node-v26.5.0-win-x64.zip" [[tools."npm:@fission-ai/openspec"]] -version = "1.4.1" +version = "1.7.0" backend = "npm:@fission-ai/openspec" [[tools.release-plz]] -version = "0.3.159" +version = "0.3.160" backend = "aqua:release-plz/release-plz" [tools.release-plz."platforms.linux-arm64"] -checksum = "sha256:58b80ecce86ac24cbecf3f4cf37eeb0412583a0adf3a8ae608b6a4eb71737f35" -url = "https://github.com/release-plz/release-plz/releases/download/release-plz-v0.3.159/release-plz-aarch64-unknown-linux-musl.tar.gz" +checksum = "sha256:43022dc9192f6da8d7f1e9157fc492a8339502b29623432b172f5353b0a4283a" +url = "https://github.com/release-plz/release-plz/releases/download/release-plz-v0.3.160/release-plz-aarch64-unknown-linux-musl.tar.gz" +url_api = "https://api.github.com/repos/release-plz/release-plz/releases/assets/476456802" [tools.release-plz."platforms.linux-arm64-musl"] -checksum = "sha256:58b80ecce86ac24cbecf3f4cf37eeb0412583a0adf3a8ae608b6a4eb71737f35" -url = "https://github.com/release-plz/release-plz/releases/download/release-plz-v0.3.159/release-plz-aarch64-unknown-linux-musl.tar.gz" +checksum = "sha256:43022dc9192f6da8d7f1e9157fc492a8339502b29623432b172f5353b0a4283a" +url = "https://github.com/release-plz/release-plz/releases/download/release-plz-v0.3.160/release-plz-aarch64-unknown-linux-musl.tar.gz" +url_api = "https://api.github.com/repos/release-plz/release-plz/releases/assets/476456802" [tools.release-plz."platforms.linux-x64"] -checksum = "sha256:b634dcb4a1b0724e00e924ab50e685704ef2bf8c6ebb809f4ac2ab16ac8458d9" -url = "https://github.com/release-plz/release-plz/releases/download/release-plz-v0.3.159/release-plz-x86_64-unknown-linux-musl.tar.gz" +checksum = "sha256:47a7ced8ce2870b767f47232adbfd5c561784af7cd0dbe1a83456977fe786d4a" +url = "https://github.com/release-plz/release-plz/releases/download/release-plz-v0.3.160/release-plz-x86_64-unknown-linux-musl.tar.gz" +url_api = "https://api.github.com/repos/release-plz/release-plz/releases/assets/476456450" [tools.release-plz."platforms.linux-x64-musl"] -checksum = "sha256:b634dcb4a1b0724e00e924ab50e685704ef2bf8c6ebb809f4ac2ab16ac8458d9" -url = "https://github.com/release-plz/release-plz/releases/download/release-plz-v0.3.159/release-plz-x86_64-unknown-linux-musl.tar.gz" +checksum = "sha256:47a7ced8ce2870b767f47232adbfd5c561784af7cd0dbe1a83456977fe786d4a" +url = "https://github.com/release-plz/release-plz/releases/download/release-plz-v0.3.160/release-plz-x86_64-unknown-linux-musl.tar.gz" +url_api = "https://api.github.com/repos/release-plz/release-plz/releases/assets/476456450" [tools.release-plz."platforms.macos-arm64"] -checksum = "sha256:c9ff7e895a018c6d6c1cd62c85d0ff296a8e6dd1647bc816b9251d8056b72e2f" -url = "https://github.com/release-plz/release-plz/releases/download/release-plz-v0.3.159/release-plz-aarch64-apple-darwin.tar.gz" +checksum = "sha256:4ed9c58e2deac929154236580b7228c9ce00c7af4c6a9010f81cec3a3d012f4d" +url = "https://github.com/release-plz/release-plz/releases/download/release-plz-v0.3.160/release-plz-aarch64-apple-darwin.tar.gz" +url_api = "https://api.github.com/repos/release-plz/release-plz/releases/assets/476455697" [tools.release-plz."platforms.windows-x64"] -checksum = "sha256:cd6d49c63a8800a40599e2f85a488e9c5efbacba6e53e3e45eb71aa02b1b2876" -url = "https://github.com/release-plz/release-plz/releases/download/release-plz-v0.3.159/release-plz-x86_64-pc-windows-msvc.tar.gz" +checksum = "sha256:febcc0361cdfa557b30def8edf5d49aecc628b101727bbae29140950c4adb92b" +url = "https://github.com/release-plz/release-plz/releases/download/release-plz-v0.3.160/release-plz-x86_64-pc-windows-msvc.tar.gz" +url_api = "https://api.github.com/repos/release-plz/release-plz/releases/assets/476459873" [[tools.rust]] version = "stable" diff --git a/mise.toml b/mise.toml index b74e28c..cfbad3c 100644 --- a/mise.toml +++ b/mise.toml @@ -7,12 +7,30 @@ node = "latest" release-plz = "latest" rust = "stable" -[tasks] -format = "cargo fmt" -lint = "cargo clippy --all-targets" -test = "cargo test" -changelog = "git cliff --bump" -release = "release-plz update" +[tasks.test] +description = "Run tests" +run = "cargo test" -[tasks.ci] -depends = ["format --check", "lint", "test"] +[tasks.format] +description = "Fix formatting issues" +run = "cargo fmt" + +[tasks.lint] +description = "Fix linting issues" +run = "cargo clippy --all-targets --fix --allow-dirty" + +[tasks.check] +description = "Check formatting/linting/tests" +run = [ + "cargo fmt --check", + "cargo clippy --all-targets", + "cargo test" +] + +[tasks.changelog] +description = "Update changelog" +run = "git cliff --bump" + +[tasks.release] +description = "Perform release process locally" +run = "release-plz update" From 5e2f22f6188619f87c200bc4872ac6219db1fbff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Wed, 29 Jul 2026 11:36:41 +0100 Subject: [PATCH 24/83] Set up project skills integration - Add skills tooling and lockfile - Configure environment initialization and ignore generated agent files - Update AI disclosure metadata --- .gitignore | 2 + AI_DISCLOSURE.md | 9 ++- mise.lock | 4 ++ mise.toml | 9 +++ skills-lock.json | 167 +++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 188 insertions(+), 3 deletions(-) create mode 100644 skills-lock.json diff --git a/.gitignore b/.gitignore index 6e40d6c..bfa0928 100644 --- a/.gitignore +++ b/.gitignore @@ -44,6 +44,8 @@ mise/*.local.toml # https://github.com/github/gitignore/blob/main/Global/Agents.gitignore # +.agents/ +CLAUDE.md GEMINI.md WARP.md CRUSH.md diff --git a/AI_DISCLOSURE.md b/AI_DISCLOSURE.md index 6ca1727..c10a9e8 100644 --- a/AI_DISCLOSURE.md +++ b/AI_DISCLOSURE.md @@ -1,14 +1,17 @@ --- disclosure-default: ai-assisted -models-used: [gpt-5.5] -providers: [OpenAI] +models-used: + - gpt-5.5 + - gpt-5.6-sol +providers: + - OpenAI scope: | Specifications are typically AI-generated with manual review. Code is typically AI-assisted. Tests are typically AI-generated. Documentation is typically human-written. Upstream project has no AI disclosure. -last-updated: 2026-06-01 +last-updated: 2026-07-01 --- # AI Disclosure diff --git a/mise.lock b/mise.lock index 92b414d..8e1b9fa 100644 --- a/mise.lock +++ b/mise.lock @@ -167,6 +167,10 @@ url = "https://nodejs.org/dist/v26.5.0/node-v26.5.0-win-x64.zip" version = "1.7.0" backend = "npm:@fission-ai/openspec" +[[tools."npm:skills"]] +version = "1.5.20" +backend = "npm:skills" + [[tools.release-plz]] version = "0.3.160" backend = "aqua:release-plz/release-plz" diff --git a/mise.toml b/mise.toml index cfbad3c..dee9373 100644 --- a/mise.toml +++ b/mise.toml @@ -4,9 +4,18 @@ gh = "latest" git-cliff = "latest" node = "latest" "npm:@fission-ai/openspec" = "latest" +"npm:skills" = "latest" release-plz = "latest" rust = "stable" +[tasks.init] +description = "Initialise development environment" +run = [ + "skills experimental_install", + "cp -r .agents/. .claude", + "echo @AGENTS.md > CLAUDE.md" +] + [tasks.test] description = "Run tests" run = "cargo test" diff --git a/skills-lock.json b/skills-lock.json new file mode 100644 index 0000000..483b028 --- /dev/null +++ b/skills-lock.json @@ -0,0 +1,167 @@ +{ + "version": 1, + "skills": { + "ask-matt": { + "source": "mattpocock/skills", + "sourceType": "github", + "skillPath": "skills/engineering/ask-matt/SKILL.md", + "computedHash": "0f843160e34a24f5bd12cdc7de7d40951e77fbdc05ce8f891b37ca32eac2c44d" + }, + "code-review": { + "source": "mattpocock/skills", + "sourceType": "github", + "skillPath": "skills/engineering/code-review/SKILL.md", + "computedHash": "31d149a480eaa68c11e32f5ee77f0fd0b98a906834d531d881d502352edd0b8e" + }, + "codebase-design": { + "source": "mattpocock/skills", + "sourceType": "github", + "skillPath": "skills/engineering/codebase-design/SKILL.md", + "computedHash": "6d9d51d8caa01633fd00cf87089c5618c982321f942ac69f62565dc001c6b22f" + }, + "diagnosing-bugs": { + "source": "mattpocock/skills", + "sourceType": "github", + "skillPath": "skills/engineering/diagnosing-bugs/SKILL.md", + "computedHash": "fd6c99466b7ba43be624e6e66ed6d7af2796ded7218f24c83820823977819e22" + }, + "domain-modeling": { + "source": "mattpocock/skills", + "sourceType": "github", + "skillPath": "skills/engineering/domain-modeling/SKILL.md", + "computedHash": "363cb0f53b0b431e7c00086ad1f823500b7e1b70b5616ee969c979f0934e9e6e" + }, + "grill-me": { + "source": "mattpocock/skills", + "sourceType": "github", + "skillPath": "skills/productivity/grill-me/SKILL.md", + "computedHash": "f361db4e15e6bfd562a9282b1dccda513910a50061f9e838ce017be9c69dde3f" + }, + "grill-with-docs": { + "source": "mattpocock/skills", + "sourceType": "github", + "skillPath": "skills/engineering/grill-with-docs/SKILL.md", + "computedHash": "9c460cbd94fd3c63cdef967dbdb6e66ca687103cdc380cd37834e4d10b738f78" + }, + "grilling": { + "source": "mattpocock/skills", + "sourceType": "github", + "skillPath": "skills/productivity/grilling/SKILL.md", + "computedHash": "368d3dd7251247c69f3656d93dc83c8f1577792eacac2111f1e7981db2ece49b" + }, + "handoff": { + "source": "mattpocock/skills", + "sourceType": "github", + "skillPath": "skills/productivity/handoff/SKILL.md", + "computedHash": "ad03e8d4ea3cbbff66420eb7ba3cc375b5cbe1821a2449b53e863256cf5b5cde" + }, + "implement": { + "source": "mattpocock/skills", + "sourceType": "github", + "skillPath": "skills/engineering/implement/SKILL.md", + "computedHash": "2139cfedf24791adbc839aaab6019cff158af1e28bfead020ec6e0ce01b3e74d" + }, + "improve-codebase-architecture": { + "source": "mattpocock/skills", + "sourceType": "github", + "skillPath": "skills/engineering/improve-codebase-architecture/SKILL.md", + "computedHash": "66e8a50c83c3c724fcfe0769701b665c56cec220cc4f49cc1aee8bdfc07de94a" + }, + "openspec-apply-change": { + "source": "fission-ai/openspec", + "sourceType": "github", + "skillPath": "skills/openspec-apply-change/SKILL.md", + "computedHash": "a26831558e94403617e9e13ff34d252f5c954bec5e1473f3ef096132ee09746f" + }, + "openspec-archive-change": { + "source": "fission-ai/openspec", + "sourceType": "github", + "skillPath": "skills/openspec-archive-change/SKILL.md", + "computedHash": "da824754dd7cfdf390363bf92b60ad14d511eb9adf266acae1a63eaafd754acf" + }, + "openspec-explore": { + "source": "fission-ai/openspec", + "sourceType": "github", + "skillPath": "skills/openspec-explore/SKILL.md", + "computedHash": "c8f081a70b69c1858745f1476562e824434ae5fb73ea457ee9869231d60f8b99" + }, + "openspec-propose": { + "source": "fission-ai/openspec", + "sourceType": "github", + "skillPath": "skills/openspec-propose/SKILL.md", + "computedHash": "b825dc0946fb1c36368ea6c7c7340c274819a541a7899b6906451dd4297ff45d" + }, + "openspec-sync-specs": { + "source": "fission-ai/openspec", + "sourceType": "github", + "skillPath": "skills/openspec-sync-specs/SKILL.md", + "computedHash": "deac2437dac4964354da14c38b8ccebaafc5d478c7db3ee65f979f923f677ce7" + }, + "prototype": { + "source": "mattpocock/skills", + "sourceType": "github", + "skillPath": "skills/engineering/prototype/SKILL.md", + "computedHash": "faba901c53a6ca245174c4ba5db3929d14253232b3050fdbd46539720adf1ab8" + }, + "research": { + "source": "mattpocock/skills", + "sourceType": "github", + "skillPath": "skills/engineering/research/SKILL.md", + "computedHash": "bd3e2c6826671d82c86ed0da3dac3370ebcf63b0fe847f91bb444e1fb7dac21b" + }, + "resolving-merge-conflicts": { + "source": "mattpocock/skills", + "sourceType": "github", + "skillPath": "skills/engineering/resolving-merge-conflicts/SKILL.md", + "computedHash": "28aad6f8b1b7025abc8892fa8890f68fe1533499b28a565f416b159131d22fad" + }, + "setup-matt-pocock-skills": { + "source": "mattpocock/skills", + "sourceType": "github", + "skillPath": "skills/engineering/setup-matt-pocock-skills/SKILL.md", + "computedHash": "74e894a3509e2676d4cdb771c8eace087092430635e845e02a9cc2f757c552a4" + }, + "tdd": { + "source": "mattpocock/skills", + "sourceType": "github", + "skillPath": "skills/engineering/tdd/SKILL.md", + "computedHash": "81eca2a5b53a63f481c0849be7a663a8cd43d5cf53f32b644ec0a2f50cf91aa2" + }, + "teach": { + "source": "mattpocock/skills", + "sourceType": "github", + "skillPath": "skills/productivity/teach/SKILL.md", + "computedHash": "68999bb1a241384b2f921f3321d779b7d05eb6089077b70d88cee2aee3d75ccc" + }, + "to-spec": { + "source": "mattpocock/skills", + "sourceType": "github", + "skillPath": "skills/engineering/to-spec/SKILL.md", + "computedHash": "0f544cd0c099c06f0dd0b7b9ee98b4237218e7e95fd3d3c02e791efbaf74bacb" + }, + "to-tickets": { + "source": "mattpocock/skills", + "sourceType": "github", + "skillPath": "skills/engineering/to-tickets/SKILL.md", + "computedHash": "0349eee265e7005e667b4543de849672106c46d545e998f37d3319300bb07751" + }, + "triage": { + "source": "mattpocock/skills", + "sourceType": "github", + "skillPath": "skills/engineering/triage/SKILL.md", + "computedHash": "7c923b6a677cfe689500721f08307e2a4c46797ff169dc55ef6c34a431a0d533" + }, + "wayfinder": { + "source": "mattpocock/skills", + "sourceType": "github", + "skillPath": "skills/engineering/wayfinder/SKILL.md", + "computedHash": "c9e18cefd77b6b5b0ee35f59ce2fd96359aa2d2f02e3479e12c873c5402d9d43" + }, + "writing-great-skills": { + "source": "mattpocock/skills", + "sourceType": "github", + "skillPath": "skills/productivity/writing-great-skills/SKILL.md", + "computedHash": "4deb21855fb4deeeb1b8217b041faff003fd0c915d24a22f636851c520bc9c5c" + } + } +} From 0669c26008bff8074533ca7f9694723ba2ee941c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Wed, 29 Jul 2026 12:58:04 +0100 Subject: [PATCH 25/83] Update release task wording Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- mise.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mise.toml b/mise.toml index dee9373..4167e14 100644 --- a/mise.toml +++ b/mise.toml @@ -41,5 +41,5 @@ description = "Update changelog" run = "git cliff --bump" [tasks.release] -description = "Perform release process locally" +description = "Update release versions and changelogs locally" run = "release-plz update" From 3697ff97485b0b71b3a4a6c6a3e509c83076ca1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Sun, 4 Oct 2026 19:19:13 +0100 Subject: [PATCH 26/83] Remove OpenSpec files and skills integration - Delete openspec specs, archived changes and config - Remove skills lock file and init task from mise - Drop npm:skills tool from mise.toml and mise.lock - Delete AI_DISCLOSURE.md and .gitattributes --- .gitattributes | 2 - AI_DISCLOSURE.md | 21 --- mise.lock | 40 ----- mise.toml | 11 -- .../.openspec.yaml | 2 - .../design.md | 107 ----------- .../proposal.md | 32 ---- .../specs/generic-rust-client/spec.md | 130 -------------- .../tasks.md | 45 ----- .../.openspec.yaml | 2 - .../design.md | 32 ---- .../proposal.md | 25 --- .../specs/custom-session-id/spec.md | 20 --- .../tasks.md | 12 -- openspec/config.yaml | 1 - openspec/specs/custom-session-id/spec.md | 24 --- openspec/specs/generic-rust-client/spec.md | 134 -------------- skills-lock.json | 167 ------------------ 18 files changed, 807 deletions(-) delete mode 100644 .gitattributes delete mode 100644 AI_DISCLOSURE.md delete mode 100644 openspec/changes/archive/2026-06-12-create-generic-aptabase-rust-client/.openspec.yaml delete mode 100644 openspec/changes/archive/2026-06-12-create-generic-aptabase-rust-client/design.md delete mode 100644 openspec/changes/archive/2026-06-12-create-generic-aptabase-rust-client/proposal.md delete mode 100644 openspec/changes/archive/2026-06-12-create-generic-aptabase-rust-client/specs/generic-rust-client/spec.md delete mode 100644 openspec/changes/archive/2026-06-12-create-generic-aptabase-rust-client/tasks.md delete mode 100644 openspec/changes/archive/2026-06-25-support-custom-session-id/.openspec.yaml delete mode 100644 openspec/changes/archive/2026-06-25-support-custom-session-id/design.md delete mode 100644 openspec/changes/archive/2026-06-25-support-custom-session-id/proposal.md delete mode 100644 openspec/changes/archive/2026-06-25-support-custom-session-id/specs/custom-session-id/spec.md delete mode 100644 openspec/changes/archive/2026-06-25-support-custom-session-id/tasks.md delete mode 100644 openspec/config.yaml delete mode 100644 openspec/specs/custom-session-id/spec.md delete mode 100644 openspec/specs/generic-rust-client/spec.md delete mode 100644 skills-lock.json diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index 423ae39..0000000 --- a/.gitattributes +++ /dev/null @@ -1,2 +0,0 @@ -openspec/** linguist-generated=true -*.lock linguist-generated=true diff --git a/AI_DISCLOSURE.md b/AI_DISCLOSURE.md deleted file mode 100644 index c10a9e8..0000000 --- a/AI_DISCLOSURE.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -disclosure-default: ai-assisted -models-used: - - gpt-5.5 - - gpt-5.6-sol -providers: - - OpenAI -scope: | - Specifications are typically AI-generated with manual review. - Code is typically AI-assisted. - Tests are typically AI-generated. - Documentation is typically human-written. - Upstream project has no AI disclosure. -last-updated: 2026-07-01 ---- - -# AI Disclosure - -This repository follows the [ai-disclosure convention](https://github.com/ggfevans/ai-disclosure). - -See per-file `SPDX-AI-Disclosure:` headers for overrides on individual files. diff --git a/mise.lock b/mise.lock index 8e1b9fa..96b5f3b 100644 --- a/mise.lock +++ b/mise.lock @@ -131,46 +131,6 @@ checksum = "sha256:3ae3a5549e85c7ad5b20192ebcfee4371269deca51255f6f2f2e051c6541f url = "https://github.com/orhun/git-cliff/releases/download/v2.13.1/git-cliff-2.13.1-x86_64-pc-windows-msvc.zip" url_api = "https://api.github.com/repos/orhun/git-cliff/releases/assets/405851866" -[[tools.node]] -version = "26.5.0" -backend = "core:node" - -[tools.node."platforms.linux-arm64"] -checksum = "sha256:308e5fe89a82461ba5a6cf15ff5221b2cdbd7ae87600aa72bb3c3fbdc66412d1" -url = "https://nodejs.org/dist/v26.5.0/node-v26.5.0-linux-arm64.tar.gz" - -[tools.node."platforms.linux-arm64-musl"] -checksum = "sha256:1ab31aef6561191693f366cacd75bbcefa0301e49a18bc05554c21893bbdb490" -url = "https://unofficial-builds.nodejs.org/download/release/v26.5.0/node-v26.5.0-linux-arm64-musl.tar.gz" - -[tools.node."platforms.linux-x64"] -checksum = "sha256:22b5f47ad6ae78837e4c2b846019965ce1a06ba143de176102294a1bf44fc677" -url = "https://nodejs.org/dist/v26.5.0/node-v26.5.0-linux-x64.tar.gz" - -[tools.node."platforms.linux-x64-musl"] -checksum = "sha256:00f1398411a4216c5a6ecaad3b825a0da5ec00e79ee8c173ab65a094d97b9ad8" -url = "https://unofficial-builds.nodejs.org/download/release/v26.5.0/node-v26.5.0-linux-x64-musl.tar.gz" - -[tools.node."platforms.macos-arm64"] -checksum = "sha256:ee920559aaa2391569cff4d737e3b83963430e3a14dedd91bfe0ff53171b5af9" -url = "https://nodejs.org/dist/v26.5.0/node-v26.5.0-darwin-arm64.tar.gz" - -[tools.node."platforms.macos-x64"] -checksum = "sha256:98293394c945a24e64e00b4177bf075ec963ea70b34d1d2e24bd4a71716d334f" -url = "https://nodejs.org/dist/v26.5.0/node-v26.5.0-darwin-x64.tar.gz" - -[tools.node."platforms.windows-x64"] -checksum = "sha256:d3b2277dbcccfdf24ef6302928f64f484cff1d77a6d3caa3a28f4d20ce9158f6" -url = "https://nodejs.org/dist/v26.5.0/node-v26.5.0-win-x64.zip" - -[[tools."npm:@fission-ai/openspec"]] -version = "1.7.0" -backend = "npm:@fission-ai/openspec" - -[[tools."npm:skills"]] -version = "1.5.20" -backend = "npm:skills" - [[tools.release-plz]] version = "0.3.160" backend = "aqua:release-plz/release-plz" diff --git a/mise.toml b/mise.toml index 4167e14..269e4f9 100644 --- a/mise.toml +++ b/mise.toml @@ -2,20 +2,9 @@ actionlint = "latest" gh = "latest" git-cliff = "latest" -node = "latest" -"npm:@fission-ai/openspec" = "latest" -"npm:skills" = "latest" release-plz = "latest" rust = "stable" -[tasks.init] -description = "Initialise development environment" -run = [ - "skills experimental_install", - "cp -r .agents/. .claude", - "echo @AGENTS.md > CLAUDE.md" -] - [tasks.test] description = "Run tests" run = "cargo test" diff --git a/openspec/changes/archive/2026-06-12-create-generic-aptabase-rust-client/.openspec.yaml b/openspec/changes/archive/2026-06-12-create-generic-aptabase-rust-client/.openspec.yaml deleted file mode 100644 index e0c0898..0000000 --- a/openspec/changes/archive/2026-06-12-create-generic-aptabase-rust-client/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-06-11 diff --git a/openspec/changes/archive/2026-06-12-create-generic-aptabase-rust-client/design.md b/openspec/changes/archive/2026-06-12-create-generic-aptabase-rust-client/design.md deleted file mode 100644 index b58b078..0000000 --- a/openspec/changes/archive/2026-06-12-create-generic-aptabase-rust-client/design.md +++ /dev/null @@ -1,107 +0,0 @@ -## Context - -The current root package combines a mostly framework-independent Aptabase client with a Tauri-specific shell. `config.rs`, `client.rs`, and `dispatcher.rs` contain the reusable behavior, while `lib.rs`, `commands.rs`, `build.rs`, permissions, webview bindings, package metadata, and examples make this repository a Tauri plugin. The only direct Tauri dependency inside the otherwise reusable modules is WebView version collection in `sys.rs`. - -This change is an in-place rewrite of the existing source tree. The repository will not become a workspace and will not retain a Tauri adapter. The root package itself becomes the generic `aptabase-rs` crate, and all Tauri-specific code and assets are deleted. - -## Goals / Non-Goals - -**Goals:** - -- Convert the repository's root package into the `aptabase-rs` crate with no Tauri dependency. -- Retain and adapt the existing configuration, session, event construction, queue, batching, retry, and HTTP dispatch code with minimal behavioral changes. -- Provide explicit asynchronous flushing suitable for short-lived CLI applications and optional detached periodic flushing for long-running applications. -- Preserve the Aptabase ingestion endpoint, headers, batch size, retained event field names, session behavior, and retry policy. -- Remove every Tauri-specific source file, dependency, build artifact definition, permission, frontend binding, example, and documentation path from this repository. - -**Non-Goals:** - -- Preserving the Tauri plugin API or maintaining source compatibility for existing plugin consumers. -- Keeping a Tauri adapter, compatibility crate, workspace member, or deprecated shim in this repository. -- Adding automatic command, argument, crash, or usage tracking to CLI applications without explicit opt-in. -- Persisting unsent events to disk or bounding the in-memory queue in this change. -- Changing Aptabase's event schema, ingestion protocol, or retry semantics. -- Providing a synchronous HTTP implementation independent of Tokio. -- Publishing `aptabase-rs` to crates.io as part of the code change. -- Providing a separate blocking HTTP client or a `reqwest::blocking` transport. - -## Decisions - -### Rewrite the root package in place - -The existing root `Cargo.toml`, `src/`, README, and repository metadata will be rewritten for the `aptabase-rs` package. No Cargo workspace or secondary crate directory will be introduced. - -This directly matches the intended repository ownership and minimizes structural churn. A workspace containing both a generic core and the Tauri plugin was considered and rejected because the Tauri code is no longer wanted in this repository. - -### Delete Tauri integration rather than preserve an adapter - -The Tauri builder, managed state, command, `EventTracker` implementations, panic-hook integration, exit lifecycle integration, plugin build script, permissions, webview bindings, JavaScript package, and Tauri example will be removed. - -Retaining a compatibility adapter was considered, but it would preserve dependencies and maintenance responsibilities that this rewrite is explicitly intended to eliminate. This is therefore a breaking package replacement rather than a backward-compatible extraction. - -### Adapt existing modules instead of reimplementing the protocol - -The generic crate will be based directly on the existing `config`, `client`, `dispatcher`, and `sys` modules. Type and method names may be simplified at the public boundary, but event construction, session generation, batching, and retry code should remain structurally recognizable. - -A ground-up implementation was considered, but it would increase the risk of changing established payload details and offline behavior for little benefit. - -### Make the client own framework-independent lifecycle operations - -The root crate will expose a client that supports: - -- construction from an app key, app version, and options; -- synchronous event enqueueing; -- asynchronous `flush`; -- optional panic hooks; -- optional detached periodic flushing started from an active Tokio runtime. - -Explicit `flush().await` is the supported CLI shutdown mechanism. The crate does not expose a blocking flush API because delivery uses the Tokio-backed async transport rather than a separate blocking HTTP client. Panic hooks may make a best-effort delivery attempt internally when the panicking thread has an active Tokio runtime; otherwise panic events remain queued in memory. Periodic flushing remains available for long-running applications as a detached task that runs until the Tokio runtime shuts down. - -Using only a background task would let short-lived CLI commands terminate before queued events are transmitted, so explicit flushing is part of the public API. A caller-managed polling handle and a separate `reqwest::blocking` implementation were considered, but both were left out to keep the adaptation narrow. - -### Preserve tolerant initialization and event behavior - -Invalid or incomplete app-key configuration will continue to disable tracking and log a diagnostic instead of preventing application startup. Event enqueueing will remain a no-op while disabled. Event properties must remain either absent or a JSON object. - -Introducing a fully fallible constructor and a new public error hierarchy was considered. That would be reasonable for a new SDK, but it conflicts with the goal of minimal adaptation. Public error refinement can follow as a separate change. - -### Replace WebView metadata with generic system metadata - -The crate will collect OS name, OS version, locale, and debug status without Tauri. It will not provide runtime or engine metadata defaults, and it will omit the old WebView-specific `engineName` and `engineVersion` fields from `systemProps` and the user agent. - -Preserving engine fields was considered, but retaining them without Tauri would require invented defaults or new public overrides that the implementation does not provide. - -### Keep the current in-memory batching and retry policy - -The dispatcher will continue to send at most 25 events per request, requeue events after network failures or server errors, and discard events rejected with non-server HTTP errors. The queue will remain process-local and in-memory. - -Changing retry policy, adding backoff, persistence, queue limits, or delivery acknowledgements would make this an SDK redesign rather than a low-change rewrite. - -### Retain opt-in panic hooks - -The generic builder will retain opt-in custom and default panic hooks. Calling these APIs installs a global Rust panic hook, enqueues panic information, makes a best-effort delivery attempt through the Tokio-backed async transport when the panicking thread has an active Tokio runtime, and delegates to the previous panic hook. Without an active Tokio runtime on the panicking thread, panic events remain queued in memory. No panic hook is installed unless the application explicitly enables one. - -## Risks / Trade-offs - -- [The rewrite breaks all existing Tauri plugin consumers] -> Mark the package conversion as breaking and rewrite documentation so the repository no longer claims Tauri compatibility. -- [In-place deletion removes a potentially useful plugin history from the working tree] -> Rely on Git history; do not retain dead compatibility code in the new crate. -- [Adaptation changes event payload metadata] -> Document that WebView engine fields are omitted by the generic client. -- [CLI processes exit before delivery] -> Document explicit `flush().await` as part of normal command shutdown. -- [A periodic task can outlive useful client state or be difficult to stop] -> Document that polling is detached and runs until Tokio runtime shutdown; a caller-managed handle can be added later. -- [Concurrent flushes can reorder delivery] -> Preserve current locking behavior and document that callers should start at most one periodic worker per client. -- [Unbounded in-memory retry queue can grow during long outages] -> Document the inherited limitation and defer queue limits or persistence to a follow-up change. - -## Migration Plan - -1. Rewrite the root package metadata and public module surface for the generic Aptabase crate. -2. Adapt the existing configuration and system metadata modules to remove Tauri references. -3. Adapt the existing client and dispatcher modules and expose the generic tracking and flushing API. -4. Delete Tauri-only Rust code, dependencies, build scripts, permissions, webview bindings, JavaScript metadata, generated files, the old Tauri example, and Tauri-focused documentation. -5. Add a minimal Tokio CLI example for the rewritten root package. -6. Run Cargo checks, documentation tests, and dependency inspection on the rewritten root package. - -Rollback consists of reverting the change in Git. No persisted data or external migration is involved. - -## Open Questions - -None. The crates.io package name is `aptabase-rs`, and a separate blocking HTTP transport is outside this proposal. diff --git a/openspec/changes/archive/2026-06-12-create-generic-aptabase-rust-client/proposal.md b/openspec/changes/archive/2026-06-12-create-generic-aptabase-rust-client/proposal.md deleted file mode 100644 index 8b42e94..0000000 --- a/openspec/changes/archive/2026-06-12-create-generic-aptabase-rust-client/proposal.md +++ /dev/null @@ -1,32 +0,0 @@ -## Why - -The existing Tauri plugin already contains most of a generic Aptabase Rust client, but its package and public API are coupled to Tauri. Rewriting this source tree in place as a generic crate will let Rust CLI applications reuse the proven session, batching, retry, and ingestion behavior without maintaining any Tauri integration here. - -## What Changes - -- **BREAKING**: Replace the root `tauri-plugin-aptabase` package in this repository with the generic `aptabase-rs` Rust client crate. -- Reuse the existing configuration, client, dispatcher, session, system metadata, batching, and retry implementations with only the changes required to remove Tauri coupling. -- Expose a public client API for configuration, event tracking, asynchronous flushing, optional panic hooks, and detached periodic flushing. -- Keep a separate `reqwest::blocking` transport out of scope; callers use the Tokio-backed flush path. -- Replace WebView-specific metadata collection with generic Rust application metadata and omit WebView engine fields from event payloads. -- Delete all Tauri-specific Rust integration, dependencies, build machinery, permissions, JavaScript bindings, generated plugin files, and Tauri examples from this repository. -- Rewrite the package metadata and documentation for the generic crate and CLI-oriented usage. - -## Capabilities - -### New Capabilities - -- `generic-rust-client`: Configure Aptabase, enqueue typed events, manage sessions, batch and retry ingestion requests, optionally install panic hooks, and flush analytics from non-Tauri Rust applications. - -### Modified Capabilities - -None. - -## Impact - -- The repository remains a single root Rust package, renamed to `aptabase-rs`, rather than a Tauri plugin. -- The implementation in `src/config.rs`, `src/client.rs`, `src/dispatcher.rs`, and most of `src/sys.rs` will be retained and adapted in place. -- The Tauri-specific portions of `src/lib.rs`, all of `src/commands.rs`, `build.rs`, `permissions/`, `webview-src/`, `webview-dist/`, the old Tauri example, and JavaScript package metadata will be removed. A minimal Tokio CLI example remains under `examples/helloworld/`. -- Tauri and `tauri-plugin` dependencies will be removed; Tokio, Reqwest, Serde, `time`, `rand`, `os_info`, `sys-locale`, and `log` will remain where required. -- Existing users of `tauri-plugin-aptabase` cannot upgrade to this rewritten package as a compatible Tauri plugin; retaining or relocating that plugin is outside this change. -- Public lifecycle behavior must support short-lived CLI processes without requiring a Tauri application runtime. diff --git a/openspec/changes/archive/2026-06-12-create-generic-aptabase-rust-client/specs/generic-rust-client/spec.md b/openspec/changes/archive/2026-06-12-create-generic-aptabase-rust-client/specs/generic-rust-client/spec.md deleted file mode 100644 index 2a83692..0000000 --- a/openspec/changes/archive/2026-06-12-create-generic-aptabase-rust-client/specs/generic-rust-client/spec.md +++ /dev/null @@ -1,130 +0,0 @@ -## ADDED Requirements - -### Requirement: In-place generic crate replacement -The repository's root Rust package SHALL be named `aptabase-rs` and SHALL provide the generic Aptabase client. The source tree MUST NOT retain a Tauri plugin package, Tauri adapter, compatibility shim, workspace member for the old plugin, or Tauri-specific runtime and build dependencies. - -#### Scenario: Build the rewritten root package -- **WHEN** a developer builds the package at the repository root -- **THEN** Cargo builds the `aptabase-rs` package directly without building or linking Tauri - -#### Scenario: Inspect removed integration assets -- **WHEN** a developer inspects the rewritten source tree and package manifests -- **THEN** Tauri commands, builders, permissions, webview bindings, plugin build scripts, JavaScript package metadata, and Tauri examples are absent - -### Requirement: Framework-independent client construction -The system SHALL provide a Rust Aptabase client that can be compiled and constructed without depending on Tauri or `tauri-plugin`. Construction SHALL accept an Aptabase app key, an application version, and optional client configuration. - -#### Scenario: Construct client in a CLI application -- **WHEN** a Rust application constructs the client with an app key and application version -- **THEN** the client is available for event tracking and flushing without requiring an application framework or WebView - -### Requirement: App-key endpoint configuration -The client SHALL select the Aptabase ingestion endpoint from the app-key region, supporting US, EU, development, and self-hosted keys. A self-hosted key MUST have a configured host. Invalid or incomplete configuration SHALL disable tracking without preventing client construction. - -#### Scenario: Configure a hosted region -- **WHEN** the client is constructed with an `A-US-*` or `A-EU-*` app key -- **THEN** requests target the corresponding hosted region's `/api/v0/events` endpoint - -#### Scenario: Configure a self-hosted region -- **WHEN** the client is constructed with an `A-SH-*` app key and a custom host -- **THEN** requests target the custom host's `/api/v0/events` endpoint - -#### Scenario: Disable invalid configuration -- **WHEN** the client is constructed with an invalid app key or a self-hosted key without a host -- **THEN** construction succeeds and subsequent event tracking performs no network delivery - -### Requirement: Event enqueueing and payload compatibility -The client SHALL synchronously enqueue named events without performing network I/O at the call site. Each event SHALL contain an RFC 3339 UTC timestamp, session ID, event name, `systemProps` with debug status, operating-system name, operating-system version, locale, application version, SDK version, and optional properties. Supplied properties MUST be a JSON object. - -#### Scenario: Enqueue an event without properties -- **WHEN** a caller tracks an event name without properties on an enabled client -- **THEN** one event with the required Aptabase payload fields is added to the in-memory queue - -#### Scenario: Enqueue an event with object properties -- **WHEN** a caller tracks an event with a JSON object -- **THEN** the object is included in the event's `props` field - -#### Scenario: Reject non-object properties -- **WHEN** a caller tracks an event with a JSON value that is not an object -- **THEN** tracking returns a validation error and no event is enqueued - -### Requirement: Session lifecycle -The client SHALL assign a session ID to tracked events, reuse that ID while activity remains within the four-hour session timeout, and generate a new ID after the timeout is exceeded. - -#### Scenario: Reuse an active session -- **WHEN** multiple events are tracked less than four hours apart -- **THEN** the events contain the same session ID and the session activity time is refreshed - -#### Scenario: Rotate an expired session -- **WHEN** an event is tracked more than four hours after the previous session activity -- **THEN** the event contains a newly generated session ID - -### Requirement: Batched ingestion -The client SHALL flush queued events to Aptabase as JSON arrays containing no more than 25 events per request. Each request SHALL include the `App-Key` header, JSON content type, and a user agent derived from client system metadata. - -#### Scenario: Flush fewer than one batch -- **WHEN** an enabled client flushes between one and 25 queued events -- **THEN** it sends one request containing all queued events to the configured ingestion endpoint - -#### Scenario: Flush multiple batches -- **WHEN** an enabled client flushes more than 25 queued events -- **THEN** it sends multiple requests and no request contains more than 25 events - -#### Scenario: Flush an empty queue -- **WHEN** a client flushes with no queued events -- **THEN** no network request is made - -### Requirement: Delivery failure handling -The client SHALL requeue a batch after a transport failure or server-error response. The client SHALL discard a batch after a non-server HTTP error response, preserving the existing SDK delivery policy. - -#### Scenario: Retry a transport failure later -- **WHEN** a flush cannot send a batch because of a network or transport error -- **THEN** the batch remains queued for a later flush - -#### Scenario: Retry a server failure later -- **WHEN** Aptabase responds to a batch with a 5xx status -- **THEN** the batch remains queued for a later flush - -#### Scenario: Discard a rejected request -- **WHEN** Aptabase responds to a batch with a non-5xx unsuccessful status -- **THEN** the batch is removed from the queue and is not retried automatically - -### Requirement: Explicit flushing -The client SHALL provide an asynchronous flush operation that can be awaited before a short-lived process exits. The operation SHALL attempt delivery of queued batches using Tokio. The crate SHALL NOT expose a blocking flush API or provide a separate `reqwest::blocking` transport as part of this change. - -#### Scenario: Flush before CLI exit -- **WHEN** a Tokio-based CLI tracks an event and awaits the client's flush operation before returning -- **THEN** the queued event is attempted before the command exits - -#### Scenario: Use the supported transport model -- **WHEN** a caller needs to deliver queued events -- **THEN** the caller uses the Tokio-based asynchronous flush API - -### Requirement: Optional periodic flushing -The client SHALL allow a caller with an active Tokio runtime to start detached periodic flushing using the configured interval. The periodic worker SHALL run until the Tokio runtime shuts down. - -#### Scenario: Start periodic delivery -- **WHEN** a long-running application starts periodic flushing -- **THEN** the client attempts to flush queued events after each configured interval until the Tokio runtime shuts down - -### Requirement: Generic system metadata -The client SHALL collect debug status, operating-system name, operating-system version, and locale without Tauri. It SHALL NOT query WebView runtime or engine metadata, and tracked events SHALL omit WebView-specific `engineName` and `engineVersion` fields. - -#### Scenario: Use generic metadata -- **WHEN** a Rust application tracks an event -- **THEN** the event contains generic Rust-compatible system properties and does not query a WebView - -#### Scenario: Omit WebView metadata -- **WHEN** a Rust application tracks an event -- **THEN** the event `systemProps` do not include `engineName` or `engineVersion` - -### Requirement: Optional panic hooks -The builder SHALL allow callers to install either a custom panic hook or a default panic hook. When a panic hook is installed, it SHALL enqueue panic information and then delegate to Rust's previous panic hook. If the panicking thread has an active Tokio runtime, the hook SHALL also make a best-effort delivery attempt using the Tokio-backed transport before delegation. Without an active Tokio runtime on the panicking thread, the hook SHALL leave the panic event queued in memory. The panic hook MUST NOT expose a public blocking flush API. - -#### Scenario: Install the default panic hook -- **WHEN** a caller enables the default panic hook -- **THEN** a panic event is enqueued with panic message and source location information before the previous panic hook runs - -#### Scenario: Install a custom panic hook -- **WHEN** a caller provides a custom panic hook -- **THEN** the hook receives the client, panic information, and extracted panic message so it can enqueue an application-specific panic event diff --git a/openspec/changes/archive/2026-06-12-create-generic-aptabase-rust-client/tasks.md b/openspec/changes/archive/2026-06-12-create-generic-aptabase-rust-client/tasks.md deleted file mode 100644 index 501d11a..0000000 --- a/openspec/changes/archive/2026-06-12-create-generic-aptabase-rust-client/tasks.md +++ /dev/null @@ -1,45 +0,0 @@ -## 1. Rewrite The Root Package - -- [x] 1.1 Rename the root Cargo package to `aptabase-rs` and rewrite its metadata for the generic client without introducing a workspace or secondary crate. -- [x] 1.2 Remove the `tauri` dependency, `tauri-plugin` build dependency, plugin `links` metadata, and any features or metadata that only apply to Tauri. -- [x] 1.3 Define the root crate's public module surface and exports for client construction, options, panic hooks, event tracking, and flushing. - -## 2. Adapt Configuration And Metadata - -- [x] 2.1 Move `InitOptions` into the generic root API and adapt app-key configuration in place, preserving US, EU, development, self-hosted, invalid-key, and flush-interval behavior. -- [x] 2.2 Remove `tauri::webview_version` from system metadata collection while retaining debug status, OS information, locale, and Flatpak detection. -- [x] 2.3 Remove WebView engine metadata from the generic client while retaining debug status, OS information, locale, and Flatpak detection. -- [ ] 2.4 Add unit tests for hosted, development, self-hosted, and invalid app-key configuration. - -## 3. Adapt The Client And Dispatcher - -- [x] 3.1 Retain the session ID and four-hour session lifecycle implementation in the rewritten root crate. -- [x] 3.2 Adapt event construction and synchronous enqueueing, preserving retained payload field names, application and SDK versions, disabled-client behavior, and JSON-object property validation. -- [x] 3.3 Retain the Reqwest dispatcher behavior for the ingestion path, request headers, user agent, ten-second timeout, in-memory queue, and 25-event batching. -- [x] 3.4 Preserve requeue behavior for transport and 5xx failures and discard behavior for non-5xx unsuccessful responses. -- [x] 3.5 Expose asynchronous explicit flushing for CLI shutdown and optional detached periodic flushing. -- [x] 3.6 Verify the dependency features do not introduce a separate `reqwest::blocking` transport. -- [x] 3.7 Remove the old Tauri-oriented plugin builder and `EventTracker` public APIs and replace `src/lib.rs` with the generic builder, client, panic-hook, and flushing APIs. - -## 4. Remove Tauri From The Source Tree - -- [x] 4.1 Delete `src/commands.rs` and all remaining Tauri imports, command handlers, managed-state integration, plugin builders, runtime event hooks, and WebView metadata code. -- [x] 4.2 Delete the root plugin `build.rs` and the `permissions/` directory, including generated schemas and command permissions. -- [x] 4.3 Delete `webview-src/`, `webview-dist/`, `package.json`, and other JavaScript or TypeScript build metadata used only by the Tauri plugin. -- [x] 4.4 Replace the generated Tauri example with a minimal Tokio CLI example under `examples/helloworld/`. -- [x] 4.5 Search the retained source, manifests, and documentation and remove all claims or configuration that this repository still provides a Tauri plugin. - -## 5. Verify Core Behavior - -- [ ] 5.1 Add tests that verify event payload serialization, required system properties, object-property acceptance, and non-object rejection. -- [ ] 5.2 Add deterministic session tests for reuse within four hours and rotation after expiration. -- [ ] 5.3 Add local HTTP test coverage for empty flushes, `App-Key` and content headers, endpoint selection, and batches of at most 25 events. -- [ ] 5.4 Add delivery tests showing that transport and 5xx failures remain queued while non-5xx unsuccessful responses are discarded. -- [x] 5.5 Add a Tokio-based CLI example that tracks an event and awaits `flush` using the rewritten root crate. - -## 6. Documentation And Final Validation - -- [x] 6.1 Replace the Tauri README with generic crate documentation covering CLI construction, event tracking, explicit shutdown flushing, periodic flushing, self-hosted configuration, and privacy guidance. -- [x] 6.2 Update changelog and package metadata to state that this is a breaking in-place replacement named `aptabase-rs`. -- [ ] 6.3 Run formatting, unit and integration tests, Cargo checks, and documentation tests for the rewritten root package. -- [x] 6.4 Inspect the dependency tree and repository files to verify that no Tauri dependency, Tauri source, plugin asset, or compatibility layer remains. diff --git a/openspec/changes/archive/2026-06-25-support-custom-session-id/.openspec.yaml b/openspec/changes/archive/2026-06-25-support-custom-session-id/.openspec.yaml deleted file mode 100644 index de73b34..0000000 --- a/openspec/changes/archive/2026-06-25-support-custom-session-id/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-06-25 diff --git a/openspec/changes/archive/2026-06-25-support-custom-session-id/design.md b/openspec/changes/archive/2026-06-25-support-custom-session-id/design.md deleted file mode 100644 index 0f6d647..0000000 --- a/openspec/changes/archive/2026-06-25-support-custom-session-id/design.md +++ /dev/null @@ -1,32 +0,0 @@ -## Context - -`aptabase-rs` currently creates a `TrackingSession` when `Builder::build()` constructs an `AptabaseClient`. That works for long-running processes, but short-lived applications get a new session ID on every run. `aptabase-cpp` handles this by letting the caller generate or provide a session ID and persist it outside the SDK. - -## Goals / Non-Goals - -**Goals:** -- Let applications reuse a persisted session ID across process runs. -- Keep the existing automatic session generation and four-hour timeout behavior. -- Match the C++ SDK's caller-owned session model with minimal Rust API surface. - -**Non-Goals:** -- Do not add SDK-owned disk persistence. -- Do not add `StartSession` / `EndSession` lifecycle methods. -- Do not add a storage abstraction or new dependency. - -## Decisions - -- Expose the existing session ID generator as public API, likely as `aptabase_rs::new_session_id()`. - - Alternative: force callers to invent IDs. Rejected because the SDK already has the Aptabase-compatible generator. -- Add `Builder::with_session_id(...)` to seed the initial session. - - Alternative: add `AptabaseClient::set_session_id(...)`. Rejected because the immediate need is process startup reuse, not mutable session management. -- Treat an empty supplied session ID as absent and generate one. - - Alternative: return a validation error. Rejected because builder methods currently do not return `Result`, and this mirrors the C++ SDK's empty-string behavior. -- Keep timeout rotation unchanged after initialization. - - Alternative: make caller-supplied IDs permanent. Rejected because it would bypass the existing session lifecycle. - -## Risks / Trade-offs - -- Caller forgets to persist the generated ID -> The SDK cannot group sessions across runs; README should show caller-owned persistence explicitly. -- Caller supplies unstable or shared IDs -> Events may be grouped incorrectly; document that the application owns ID scope. -- No getter for current session ID -> Callers that want persistence should generate or load the ID before building the client. diff --git a/openspec/changes/archive/2026-06-25-support-custom-session-id/proposal.md b/openspec/changes/archive/2026-06-25-support-custom-session-id/proposal.md deleted file mode 100644 index 85465d0..0000000 --- a/openspec/changes/archive/2026-06-25-support-custom-session-id/proposal.md +++ /dev/null @@ -1,25 +0,0 @@ -## Why - -Short-lived programs create a new in-memory Aptabase session on every run, so events from repeated invocations cannot be grouped unless the application can persist and reuse a session ID. The C++ SDK handles this by letting callers provide a custom session ID; `aptabase-rs` should expose the same minimal control without owning disk persistence. - -## What Changes - -- Add a public session ID generator so applications can create SDK-compatible session IDs when they need one. -- Add a builder option for supplying an initial session ID. -- Continue generating a session ID automatically when the caller does not provide one. -- Keep session storage and persistence outside the SDK. - -## Capabilities - -### New Capabilities -- `custom-session-id`: Caller-owned session ID generation and initialization for applications that persist session state across process runs. - -### Modified Capabilities -- None. - -## Impact - -- Public Rust API: `Builder` gains a custom-session option and the crate exposes session ID generation. -- Session initialization changes in `src/client.rs`; automatic timeout rotation remains unchanged. -- README usage docs should show caller-owned persistence for short-lived applications. -- Tests should cover generated IDs and caller-supplied session IDs. diff --git a/openspec/changes/archive/2026-06-25-support-custom-session-id/specs/custom-session-id/spec.md b/openspec/changes/archive/2026-06-25-support-custom-session-id/specs/custom-session-id/spec.md deleted file mode 100644 index 954c699..0000000 --- a/openspec/changes/archive/2026-06-25-support-custom-session-id/specs/custom-session-id/spec.md +++ /dev/null @@ -1,20 +0,0 @@ -## ADDED Requirements - -### Requirement: Caller-owned session IDs -The client SHALL provide API to generate a session ID and to initialize a client with a caller-supplied session ID. When no session ID is supplied, the client SHALL continue generating one automatically. The SDK MUST NOT persist session IDs to disk. - -#### Scenario: Generate a reusable session ID -- **WHEN** a caller requests a new session ID from the SDK -- **THEN** the SDK returns a non-empty session ID suitable for use as an event `sessionId` - -#### Scenario: Reuse a caller-supplied session ID -- **WHEN** a client is constructed with a caller-supplied session ID and tracks an event before the session timeout -- **THEN** the event contains the supplied session ID - -#### Scenario: Preserve automatic session generation -- **WHEN** a client is constructed without a caller-supplied session ID -- **THEN** tracked events contain an SDK-generated session ID - -#### Scenario: Treat empty session ID as absent -- **WHEN** a client is constructed with an empty caller-supplied session ID -- **THEN** tracked events contain an SDK-generated session ID diff --git a/openspec/changes/archive/2026-06-25-support-custom-session-id/tasks.md b/openspec/changes/archive/2026-06-25-support-custom-session-id/tasks.md deleted file mode 100644 index f742e7e..0000000 --- a/openspec/changes/archive/2026-06-25-support-custom-session-id/tasks.md +++ /dev/null @@ -1,12 +0,0 @@ -## 1. API and Session Initialization - -- [x] 1.1 Expose the existing session ID generator as public `new_session_id()` API. -- [x] 1.2 Add `Builder::with_session_id(...)` and store the optional session ID on the builder. -- [x] 1.3 Pass the optional session ID into `AptabaseClient::new(...)`. -- [x] 1.4 Initialize `TrackingSession` with the non-empty supplied ID or generate one when absent/empty. - -## 2. Verification and Docs - -- [x] 2.1 Add focused tests for generated IDs, supplied IDs, and empty supplied IDs. -- [x] 2.2 Update README usage docs with caller-owned session ID persistence. -- [x] 2.3 Run formatting and test checks for the crate. diff --git a/openspec/config.yaml b/openspec/config.yaml deleted file mode 100644 index b4bbeb9..0000000 --- a/openspec/config.yaml +++ /dev/null @@ -1 +0,0 @@ -schema: spec-driven diff --git a/openspec/specs/custom-session-id/spec.md b/openspec/specs/custom-session-id/spec.md deleted file mode 100644 index c7e1347..0000000 --- a/openspec/specs/custom-session-id/spec.md +++ /dev/null @@ -1,24 +0,0 @@ -# custom-session-id Specification - -## Purpose -Define caller-owned session ID support for applications that need to persist or reuse Aptabase sessions across client construction. - -## Requirements -### Requirement: Caller-owned session IDs -The client SHALL provide API to generate a session ID and to initialize a client with a caller-supplied session ID. When no session ID is supplied, the client SHALL continue generating one automatically. The SDK MUST NOT persist session IDs to disk. - -#### Scenario: Generate a reusable session ID -- **WHEN** a caller requests a new session ID from the SDK -- **THEN** the SDK returns a non-empty session ID suitable for use as an event `sessionId` - -#### Scenario: Reuse a caller-supplied session ID -- **WHEN** a client is constructed with a caller-supplied session ID and tracks an event before the session timeout -- **THEN** the event contains the supplied session ID - -#### Scenario: Preserve automatic session generation -- **WHEN** a client is constructed without a caller-supplied session ID -- **THEN** tracked events contain an SDK-generated session ID - -#### Scenario: Treat empty session ID as absent -- **WHEN** a client is constructed with an empty caller-supplied session ID -- **THEN** tracked events contain an SDK-generated session ID diff --git a/openspec/specs/generic-rust-client/spec.md b/openspec/specs/generic-rust-client/spec.md deleted file mode 100644 index cf39469..0000000 --- a/openspec/specs/generic-rust-client/spec.md +++ /dev/null @@ -1,134 +0,0 @@ -# generic-rust-client Specification - -## Purpose -Define the framework-independent Rust SDK surface created by extracting the existing `tauri-plugin-aptabase` client behavior for use by generic Rust applications. This covers construction, event enqueueing, baseline session lifecycle, metadata, hosted or self-hosted endpoint selection, and Tokio-based batching and flushing. - -## Requirements -### Requirement: In-place generic crate replacement -The repository's root Rust package SHALL be named `aptabase-rs` and SHALL provide the generic Aptabase client. The source tree MUST NOT retain a Tauri plugin package, Tauri adapter, compatibility shim, workspace member for the old plugin, or Tauri-specific runtime and build dependencies. - -#### Scenario: Build the rewritten root package -- **WHEN** a developer builds the package at the repository root -- **THEN** Cargo builds the `aptabase-rs` package directly without building or linking Tauri - -#### Scenario: Inspect removed integration assets -- **WHEN** a developer inspects the rewritten source tree and package manifests -- **THEN** Tauri commands, builders, permissions, webview bindings, plugin build scripts, JavaScript package metadata, and Tauri examples are absent - -### Requirement: Framework-independent client construction -The system SHALL provide a Rust Aptabase client that can be compiled and constructed without depending on Tauri or `tauri-plugin`. Construction SHALL accept an Aptabase app key, an application version, and optional client configuration. - -#### Scenario: Construct client in a CLI application -- **WHEN** a Rust application constructs the client with an app key and application version -- **THEN** the client is available for event tracking and flushing without requiring an application framework or WebView - -### Requirement: App-key endpoint configuration -The client SHALL select the Aptabase ingestion endpoint from the app-key region, supporting US, EU, development, and self-hosted keys. A self-hosted key MUST have a configured host. Invalid or incomplete configuration SHALL disable tracking without preventing client construction. - -#### Scenario: Configure a hosted region -- **WHEN** the client is constructed with an `A-US-*` or `A-EU-*` app key -- **THEN** requests target the corresponding hosted region's `/api/v0/events` endpoint - -#### Scenario: Configure a self-hosted region -- **WHEN** the client is constructed with an `A-SH-*` app key and a custom host -- **THEN** requests target the custom host's `/api/v0/events` endpoint - -#### Scenario: Disable invalid configuration -- **WHEN** the client is constructed with an invalid app key or a self-hosted key without a host -- **THEN** construction succeeds and subsequent event tracking performs no network delivery - -### Requirement: Event enqueueing and payload compatibility -The client SHALL synchronously enqueue named events without performing network I/O at the call site. Each event SHALL contain an RFC 3339 UTC timestamp, session ID, event name, `systemProps` with debug status, operating-system name, operating-system version, locale, application version, SDK version, and optional properties. Supplied properties MUST be a JSON object. - -#### Scenario: Enqueue an event without properties -- **WHEN** a caller tracks an event name without properties on an enabled client -- **THEN** one event with the required Aptabase payload fields is added to the in-memory queue - -#### Scenario: Enqueue an event with object properties -- **WHEN** a caller tracks an event with a JSON object -- **THEN** the object is included in the event's `props` field - -#### Scenario: Reject non-object properties -- **WHEN** a caller tracks an event with a JSON value that is not an object -- **THEN** tracking returns a validation error and no event is enqueued - -### Requirement: Session lifecycle -The client SHALL assign a session ID to tracked events, reuse that ID while activity remains within the four-hour session timeout, and generate a new ID after the timeout is exceeded. - -#### Scenario: Reuse an active session -- **WHEN** multiple events are tracked less than four hours apart -- **THEN** the events contain the same session ID and the session activity time is refreshed - -#### Scenario: Rotate an expired session -- **WHEN** an event is tracked more than four hours after the previous session activity -- **THEN** the event contains a newly generated session ID - -### Requirement: Batched ingestion -The client SHALL flush queued events to Aptabase as JSON arrays containing no more than 25 events per request. Each request SHALL include the `App-Key` header, JSON content type, and a user agent derived from client system metadata. - -#### Scenario: Flush fewer than one batch -- **WHEN** an enabled client flushes between one and 25 queued events -- **THEN** it sends one request containing all queued events to the configured ingestion endpoint - -#### Scenario: Flush multiple batches -- **WHEN** an enabled client flushes more than 25 queued events -- **THEN** it sends multiple requests and no request contains more than 25 events - -#### Scenario: Flush an empty queue -- **WHEN** a client flushes with no queued events -- **THEN** no network request is made - -### Requirement: Delivery failure handling -The client SHALL requeue a batch after a transport failure or server-error response. The client SHALL discard a batch after a non-server HTTP error response, preserving the existing SDK delivery policy. - -#### Scenario: Retry a transport failure later -- **WHEN** a flush cannot send a batch because of a network or transport error -- **THEN** the batch remains queued for a later flush - -#### Scenario: Retry a server failure later -- **WHEN** Aptabase responds to a batch with a 5xx status -- **THEN** the batch remains queued for a later flush - -#### Scenario: Discard a rejected request -- **WHEN** Aptabase responds to a batch with a non-5xx unsuccessful status -- **THEN** the batch is removed from the queue and is not retried automatically - -### Requirement: Explicit flushing -The client SHALL provide an asynchronous flush operation that can be awaited before a short-lived process exits. The operation SHALL attempt delivery of queued batches using Tokio. The crate SHALL NOT expose a blocking flush API or provide a separate `reqwest::blocking` transport as part of this change. - -#### Scenario: Flush before CLI exit -- **WHEN** a Tokio-based CLI tracks an event and awaits the client's flush operation before returning -- **THEN** the queued event is attempted before the command exits - -#### Scenario: Use the supported transport model -- **WHEN** a caller needs to deliver queued events -- **THEN** the caller uses the Tokio-based asynchronous flush API - -### Requirement: Optional periodic flushing -The client SHALL allow a caller with an active Tokio runtime to start detached periodic flushing using the configured interval. The periodic worker SHALL run until the Tokio runtime shuts down. - -#### Scenario: Start periodic delivery -- **WHEN** a long-running application starts periodic flushing -- **THEN** the client attempts to flush queued events after each configured interval until the Tokio runtime shuts down - -### Requirement: Generic system metadata -The client SHALL collect debug status, operating-system name, operating-system version, and locale without Tauri. It SHALL NOT query WebView runtime or engine metadata, and tracked events SHALL omit WebView-specific `engineName` and `engineVersion` fields. - -#### Scenario: Use generic metadata -- **WHEN** a Rust application tracks an event -- **THEN** the event contains generic Rust-compatible system properties and does not query a WebView - -#### Scenario: Omit WebView metadata -- **WHEN** a Rust application tracks an event -- **THEN** the event `systemProps` do not include `engineName` or `engineVersion` - -### Requirement: Optional panic hooks -The builder SHALL allow callers to install either a custom panic hook or a default panic hook. When a panic hook is installed, it SHALL enqueue panic information and then delegate to Rust's previous panic hook. If the panicking thread has an active Tokio runtime, the hook SHALL also make a best-effort delivery attempt using the Tokio-backed transport before delegation. Without an active Tokio runtime on the panicking thread, the hook SHALL leave the panic event queued in memory. The panic hook MUST NOT expose a public blocking flush API. - -#### Scenario: Install the default panic hook -- **WHEN** a caller enables the default panic hook -- **THEN** a panic event is enqueued with panic message and source location information before the previous panic hook runs - -#### Scenario: Install a custom panic hook -- **WHEN** a caller provides a custom panic hook -- **THEN** the hook receives the client, panic information, and extracted panic message so it can enqueue an application-specific panic event diff --git a/skills-lock.json b/skills-lock.json deleted file mode 100644 index 483b028..0000000 --- a/skills-lock.json +++ /dev/null @@ -1,167 +0,0 @@ -{ - "version": 1, - "skills": { - "ask-matt": { - "source": "mattpocock/skills", - "sourceType": "github", - "skillPath": "skills/engineering/ask-matt/SKILL.md", - "computedHash": "0f843160e34a24f5bd12cdc7de7d40951e77fbdc05ce8f891b37ca32eac2c44d" - }, - "code-review": { - "source": "mattpocock/skills", - "sourceType": "github", - "skillPath": "skills/engineering/code-review/SKILL.md", - "computedHash": "31d149a480eaa68c11e32f5ee77f0fd0b98a906834d531d881d502352edd0b8e" - }, - "codebase-design": { - "source": "mattpocock/skills", - "sourceType": "github", - "skillPath": "skills/engineering/codebase-design/SKILL.md", - "computedHash": "6d9d51d8caa01633fd00cf87089c5618c982321f942ac69f62565dc001c6b22f" - }, - "diagnosing-bugs": { - "source": "mattpocock/skills", - "sourceType": "github", - "skillPath": "skills/engineering/diagnosing-bugs/SKILL.md", - "computedHash": "fd6c99466b7ba43be624e6e66ed6d7af2796ded7218f24c83820823977819e22" - }, - "domain-modeling": { - "source": "mattpocock/skills", - "sourceType": "github", - "skillPath": "skills/engineering/domain-modeling/SKILL.md", - "computedHash": "363cb0f53b0b431e7c00086ad1f823500b7e1b70b5616ee969c979f0934e9e6e" - }, - "grill-me": { - "source": "mattpocock/skills", - "sourceType": "github", - "skillPath": "skills/productivity/grill-me/SKILL.md", - "computedHash": "f361db4e15e6bfd562a9282b1dccda513910a50061f9e838ce017be9c69dde3f" - }, - "grill-with-docs": { - "source": "mattpocock/skills", - "sourceType": "github", - "skillPath": "skills/engineering/grill-with-docs/SKILL.md", - "computedHash": "9c460cbd94fd3c63cdef967dbdb6e66ca687103cdc380cd37834e4d10b738f78" - }, - "grilling": { - "source": "mattpocock/skills", - "sourceType": "github", - "skillPath": "skills/productivity/grilling/SKILL.md", - "computedHash": "368d3dd7251247c69f3656d93dc83c8f1577792eacac2111f1e7981db2ece49b" - }, - "handoff": { - "source": "mattpocock/skills", - "sourceType": "github", - "skillPath": "skills/productivity/handoff/SKILL.md", - "computedHash": "ad03e8d4ea3cbbff66420eb7ba3cc375b5cbe1821a2449b53e863256cf5b5cde" - }, - "implement": { - "source": "mattpocock/skills", - "sourceType": "github", - "skillPath": "skills/engineering/implement/SKILL.md", - "computedHash": "2139cfedf24791adbc839aaab6019cff158af1e28bfead020ec6e0ce01b3e74d" - }, - "improve-codebase-architecture": { - "source": "mattpocock/skills", - "sourceType": "github", - "skillPath": "skills/engineering/improve-codebase-architecture/SKILL.md", - "computedHash": "66e8a50c83c3c724fcfe0769701b665c56cec220cc4f49cc1aee8bdfc07de94a" - }, - "openspec-apply-change": { - "source": "fission-ai/openspec", - "sourceType": "github", - "skillPath": "skills/openspec-apply-change/SKILL.md", - "computedHash": "a26831558e94403617e9e13ff34d252f5c954bec5e1473f3ef096132ee09746f" - }, - "openspec-archive-change": { - "source": "fission-ai/openspec", - "sourceType": "github", - "skillPath": "skills/openspec-archive-change/SKILL.md", - "computedHash": "da824754dd7cfdf390363bf92b60ad14d511eb9adf266acae1a63eaafd754acf" - }, - "openspec-explore": { - "source": "fission-ai/openspec", - "sourceType": "github", - "skillPath": "skills/openspec-explore/SKILL.md", - "computedHash": "c8f081a70b69c1858745f1476562e824434ae5fb73ea457ee9869231d60f8b99" - }, - "openspec-propose": { - "source": "fission-ai/openspec", - "sourceType": "github", - "skillPath": "skills/openspec-propose/SKILL.md", - "computedHash": "b825dc0946fb1c36368ea6c7c7340c274819a541a7899b6906451dd4297ff45d" - }, - "openspec-sync-specs": { - "source": "fission-ai/openspec", - "sourceType": "github", - "skillPath": "skills/openspec-sync-specs/SKILL.md", - "computedHash": "deac2437dac4964354da14c38b8ccebaafc5d478c7db3ee65f979f923f677ce7" - }, - "prototype": { - "source": "mattpocock/skills", - "sourceType": "github", - "skillPath": "skills/engineering/prototype/SKILL.md", - "computedHash": "faba901c53a6ca245174c4ba5db3929d14253232b3050fdbd46539720adf1ab8" - }, - "research": { - "source": "mattpocock/skills", - "sourceType": "github", - "skillPath": "skills/engineering/research/SKILL.md", - "computedHash": "bd3e2c6826671d82c86ed0da3dac3370ebcf63b0fe847f91bb444e1fb7dac21b" - }, - "resolving-merge-conflicts": { - "source": "mattpocock/skills", - "sourceType": "github", - "skillPath": "skills/engineering/resolving-merge-conflicts/SKILL.md", - "computedHash": "28aad6f8b1b7025abc8892fa8890f68fe1533499b28a565f416b159131d22fad" - }, - "setup-matt-pocock-skills": { - "source": "mattpocock/skills", - "sourceType": "github", - "skillPath": "skills/engineering/setup-matt-pocock-skills/SKILL.md", - "computedHash": "74e894a3509e2676d4cdb771c8eace087092430635e845e02a9cc2f757c552a4" - }, - "tdd": { - "source": "mattpocock/skills", - "sourceType": "github", - "skillPath": "skills/engineering/tdd/SKILL.md", - "computedHash": "81eca2a5b53a63f481c0849be7a663a8cd43d5cf53f32b644ec0a2f50cf91aa2" - }, - "teach": { - "source": "mattpocock/skills", - "sourceType": "github", - "skillPath": "skills/productivity/teach/SKILL.md", - "computedHash": "68999bb1a241384b2f921f3321d779b7d05eb6089077b70d88cee2aee3d75ccc" - }, - "to-spec": { - "source": "mattpocock/skills", - "sourceType": "github", - "skillPath": "skills/engineering/to-spec/SKILL.md", - "computedHash": "0f544cd0c099c06f0dd0b7b9ee98b4237218e7e95fd3d3c02e791efbaf74bacb" - }, - "to-tickets": { - "source": "mattpocock/skills", - "sourceType": "github", - "skillPath": "skills/engineering/to-tickets/SKILL.md", - "computedHash": "0349eee265e7005e667b4543de849672106c46d545e998f37d3319300bb07751" - }, - "triage": { - "source": "mattpocock/skills", - "sourceType": "github", - "skillPath": "skills/engineering/triage/SKILL.md", - "computedHash": "7c923b6a677cfe689500721f08307e2a4c46797ff169dc55ef6c34a431a0d533" - }, - "wayfinder": { - "source": "mattpocock/skills", - "sourceType": "github", - "skillPath": "skills/engineering/wayfinder/SKILL.md", - "computedHash": "c9e18cefd77b6b5b0ee35f59ce2fd96359aa2d2f02e3479e12c873c5402d9d43" - }, - "writing-great-skills": { - "source": "mattpocock/skills", - "sourceType": "github", - "skillPath": "skills/productivity/writing-great-skills/SKILL.md", - "computedHash": "4deb21855fb4deeeb1b8217b041faff003fd0c915d24a22f636851c520bc9c5c" - } - } -} From 532bdbba3dc46fa82b84e1190c310c64f2c5d8b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Sun, 4 Oct 2026 19:45:08 +0100 Subject: [PATCH 27/83] Clarify package description and README intro - Describe the crate as an unofficial, framework-independent SDK - Replace README note with intro stating it is a community project - Point Tauri users to the official tauri-plugin-aptabase --- README.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 6bfe4fc..0882410 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,8 @@ -> [!NOTE] -> This project is not affiliated with Aptabase or Sumbit Labs Ltd. - # Rust SDK for Aptabase -`aptabase-rs` is a framework-independent Rust SDK for Aptabase, an Open Source, Privacy-First, and Simple Analytics for Mobile, Desktop, and Web Apps. +An unofficial, framework-independent Rust SDK for [Aptabase](https://aptabase.com), the open source, privacy-first, and simple analytics platform for mobile, desktop and web apps. + +This is a community project and is not affiliated with or endorsed by Aptabase. It is derived from the official [`tauri-plugin-aptabase`](https://github.com/aptabase/tauri-plugin-aptabase). If you're building a Tauri app, use that instead. ## Install From 2ceb19558575b0fcb585a9c6865acb38ae968677 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Sun, 4 Oct 2026 20:34:07 +0100 Subject: [PATCH 28/83] Restructure CI workflow and bump checkout to v7 - Add workflow name and rename job to "Build & Check" - Drop top-level read-only permissions block - Update actions/checkout from v6 to v7 --- .github/workflows/ci.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 91a9cdb..3b9daba 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,16 +1,16 @@ -permissions: - contents: read +name: CI on: - pull_request: push: branches: [main] + pull_request: jobs: - test: + check: + name: Build & Check runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 - uses: dtolnay/rust-toolchain@stable - uses: Swatinem/rust-cache@v2 - run: cargo fmt --check From ec3707a48b0cf4f4fd5d3bad31801afa76a12f7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Sun, 4 Oct 2026 21:09:46 +0100 Subject: [PATCH 29/83] Keep reqwest's non-TLS default features Disabling reqwest's default features for the TLS flags also dropped charset, http2 and system-proxy. Re-enable them explicitly. --- Cargo.lock | 169 ++++++++++++++++++++++++++++++++++++++++++++++++++++- Cargo.toml | 2 +- 2 files changed, 168 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7093b6f..59c9d4e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -149,6 +149,16 @@ dependencies = [ "memchr", ] +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation" version = "0.10.1" @@ -165,6 +175,12 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +[[package]] +name = "core_detect" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f8f80099a98041a3d1622845c271458a2d73e688351bf3cb999266764b81d48" + [[package]] name = "cpufeatures" version = "0.3.1" @@ -207,6 +223,26 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" +[[package]] +name = "encoding_rs" +version = "0.8.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e985e0451871ad22fb8d2b6b076e2028a502a0d3950998c2c5c0a4f9b5d9679" +dependencies = [ + "cfg-if", + "core_detect", + "multiversion_no_op", + "rustversion", + "scopeguard", + "simdutf8", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + [[package]] name = "errno" version = "0.3.14" @@ -229,6 +265,12 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + [[package]] name = "foreign-types" version = "0.3.2" @@ -374,6 +416,31 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "h2" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef8e5e5a340588f4452631496976cf8636d4a7ecf600239fdc27615d2530bc16" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + [[package]] name = "http" version = "1.4.2" @@ -423,6 +490,7 @@ dependencies = [ "bytes", "futures-channel", "futures-core", + "h2", "http", "http-body", "httparse", @@ -482,9 +550,11 @@ dependencies = [ "percent-encoding", "pin-project-lite", "socket2", + "system-configuration", "tokio", "tower-service", "tracing", + "windows-registry", ] [[package]] @@ -590,6 +660,16 @@ dependencies = [ "icu_properties", ] +[[package]] +name = "indexmap" +version = "2.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc4e190f5d26ca7051642629da2c52fc03bde85a03197c99408dcd291734c855" +dependencies = [ + "equivalent", + "hashbrown", +] + [[package]] name = "ipnet" version = "2.12.0" @@ -708,6 +788,12 @@ version = "2.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + [[package]] name = "mio" version = "1.2.2" @@ -719,6 +805,12 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "multiversion_no_op" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "743fb55ba31b18fb1ecef6bdc9aa2743314978ac084044301a7eee33fb99a20d" + [[package]] name = "native-tls" version = "0.2.18" @@ -1126,7 +1218,9 @@ checksum = "16a1cfa75cc186dd73d5818e510e042e40927bccc9c236b061cea97e1eb08029" dependencies = [ "base64 0.23.1", "bytes", + "encoding_rs", "futures-core", + "h2", "http", "http-body", "http-body-util", @@ -1136,6 +1230,7 @@ dependencies = [ "hyper-util", "js-sys", "log", + "mime", "native-tls", "percent-encoding", "pin-project-lite", @@ -1242,7 +1337,7 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1167586491e2b18b8bfbb293e8180ec17c201c4f076d7cb3070ca964e7598f98" dependencies = [ - "core-foundation", + "core-foundation 0.10.1", "core-foundation-sys", "jni", "log", @@ -1299,6 +1394,12 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + [[package]] name = "security-framework" version = "3.7.0" @@ -1306,7 +1407,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" dependencies = [ "bitflags", - "core-foundation", + "core-foundation 0.10.1", "core-foundation-sys", "libc", "security-framework-sys", @@ -1478,6 +1579,27 @@ dependencies = [ "libc", ] +[[package]] +name = "system-configuration" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" +dependencies = [ + "bitflags", + "core-foundation 0.9.4", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "tempfile" version = "3.27.0" @@ -1591,6 +1713,20 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-util" +version = "0.7.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "494815d09bf52b5548659851081238f0ca39ff638363907596da739561c62c52" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "libc", + "pin-project-lite", + "tokio", +] + [[package]] name = "tower" version = "0.5.3" @@ -1821,6 +1957,35 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" +[[package]] +name = "windows-registry" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" +dependencies = [ + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + [[package]] name = "windows-sys" version = "0.52.0" diff --git a/Cargo.toml b/Cargo.toml index 64fde2c..b7af6e5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ tokio = "1.43.0" futures = "0.3.31" serde = "1.0.217" serde_json = "1.0.138" -reqwest = { version = "0.13.4", default-features = false, features = ["json"] } +reqwest = { version = "0.13.4", default-features = false, features = ["json", "charset", "http2", "system-proxy"] } time = { version = "0.3.37", features = ["formatting"] } os_info = "3.9.2" rand = "0.10.0" From 9f8ec5a55b71378fcbd30ca63a2d2afb7b8cb384 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Sun, 4 Oct 2026 21:29:36 +0100 Subject: [PATCH 30/83] Add guidance on citing upstream PRs and issues --- AGENTS.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index 6a89382..7a0c65d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -8,3 +8,7 @@ This project uses mise to manage the development environment. - Use `mise run ` for standard operations. - Run `mise ls -l` to see the managed tools. - When invoking a managed tool directly, use `mise exec -- [args]` rather than invoking the tool by its bare name. + +## Upstream references + +This repo is a fork of `aptabase/tauri-plugin-aptabase`. In commit messages, PR descriptions and comments, upstream PRs and issues should be cited as plain text by title or commit SHA. Avoid links and `#123` shorthand: GitHub cross-references those, which spams the upstream project with irrelevant notifications. From 7c632eb32bdbc9b77cae847d1710e8bf6cbe9e34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Sun, 4 Oct 2026 23:01:40 +0100 Subject: [PATCH 31/83] Keep upstream commits and tags out of the changelog Generate .cliffignore from commits off main's first-parent chain, only treat v-prefixed tags as releases, accept breaking-change markers in merge titles, and bump the minor version for breaking changes pre-1.0. --- .gitignore | 7 +++++++ cliff.toml | 25 +++++++++++++++---------- mise.toml | 7 ++++++- 3 files changed, 28 insertions(+), 11 deletions(-) diff --git a/.gitignore b/.gitignore index bfa0928..b0f117d 100644 --- a/.gitignore +++ b/.gitignore @@ -27,6 +27,13 @@ rustc-ice-*.txt ### dotenv ### .env +# +# git-cliff +# + +# Generated by `mise run changelog` +.cliffignore + # # https://github.com/github/gitignore/blob/main/Global/mise.gitignore # diff --git a/cliff.toml b/cliff.toml index e7ce53c..5d3320a 100644 --- a/cliff.toml +++ b/cliff.toml @@ -23,19 +23,24 @@ postprocessors = [ [git] filter_commits = true +tag_pattern = "^v[0-9]" commit_parsers = [ - { message = "^feat: .+ \\(#[0-9]+\\)", group = "๐Ÿš€ Features" }, - { message = "^fix: .+ \\(#[0-9]+\\)", group = "๐Ÿ› Bug Fixes" }, - { message = "^docs?: .+ \\(#[0-9]+\\)", group = "๐Ÿ“š Documentation" }, - { message = "^perf: .+ \\(#[0-9]+\\)", group = "โšก Performance" }, - { message = "^refactor: .+ \\(#[0-9]+\\)", group = "๐Ÿšœ Refactor" }, - { message = "^style: .+ \\(#[0-9]+\\)", group = "๐ŸŽจ Styling" }, - { message = "^test: .+ \\(#[0-9]+\\)", group = "๐Ÿงช Testing" }, + { message = "^feat!?: .+ \\(#[0-9]+\\)", group = "๐Ÿš€ Features" }, + { message = "^fix!?: .+ \\(#[0-9]+\\)", group = "๐Ÿ› Bug Fixes" }, + { message = "^docs?!?: .+ \\(#[0-9]+\\)", group = "๐Ÿ“š Documentation" }, + { message = "^perf!?: .+ \\(#[0-9]+\\)", group = "โšก Performance" }, + { message = "^refactor!?: .+ \\(#[0-9]+\\)", group = "๐Ÿšœ Refactor" }, + { message = "^style!?: .+ \\(#[0-9]+\\)", group = "๐ŸŽจ Styling" }, + { message = "^test!?: .+ \\(#[0-9]+\\)", group = "๐Ÿงช Testing" }, { message = "^chore\\(release\\)", skip = true }, { message = "^chore\\(deps.*\\)", skip = true }, { message = "^chore\\(pr\\)", skip = true }, { message = "^chore\\(pull\\)", skip = true }, - { message = "^(chore|ci): .+ \\(#[0-9]+\\)", group = "โš™๏ธ Miscellaneous Tasks" }, - { message = "^[a-z]+: .+ \\(#[0-9]+\\)", body = ".*security", group = "๐Ÿ›ก๏ธ Security" }, - { message = "^revert: .+ \\(#[0-9]+\\)", group = "โ—€๏ธ Revert" }, + { message = "^(chore|ci)!?: .+ \\(#[0-9]+\\)", group = "โš™๏ธ Miscellaneous Tasks" }, + { message = "^[a-z]+!?: .+ \\(#[0-9]+\\)", body = ".*security", group = "๐Ÿ›ก๏ธ Security" }, + { message = "^revert!?: .+ \\(#[0-9]+\\)", group = "โ—€๏ธ Revert" }, ] + +[bump] +# Breaking changes bump the minor version while pre-1.0 +breaking_always_bump_major = false diff --git a/mise.toml b/mise.toml index 269e4f9..48b84f9 100644 --- a/mise.toml +++ b/mise.toml @@ -27,7 +27,12 @@ run = [ [tasks.changelog] description = "Update changelog" -run = "git cliff --bump" +shell = "bash -c" +run = [ + # Skip commits off main's first-parent chain (branch and upstream commits) + "comm -23 <(git rev-list HEAD | sort) <(git rev-list --first-parent HEAD | sort) > .cliffignore", + "git cliff --bump" +] [tasks.release] description = "Update release versions and changelogs locally" From 2a86765ecee9c99dd65f390f4dd99f41ca5ddae7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Sun, 4 Oct 2026 23:02:13 +0100 Subject: [PATCH 32/83] Fix example's dependency on the SDK Drop the version requirement from the path dependency so release bumps can't break it, and refresh the example's lockfile. --- examples/helloworld/Cargo.lock | 589 +++++++++++++++++++++------------ examples/helloworld/Cargo.toml | 2 +- 2 files changed, 370 insertions(+), 221 deletions(-) diff --git a/examples/helloworld/Cargo.lock b/examples/helloworld/Cargo.lock index 786c082..d0fc3bc 100644 --- a/examples/helloworld/Cargo.lock +++ b/examples/helloworld/Cargo.lock @@ -19,7 +19,7 @@ checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" [[package]] name = "aptabase-rs" -version = "0.1.0" +version = "0.2.0" dependencies = [ "futures", "log", @@ -39,12 +39,41 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" +[[package]] +name = "aws-lc-rs" +version = "1.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b281d307588d634de920874890732659e2e7672f72b5e10e81badc1a8a83621e" +dependencies = [ + "aws-lc-sys", + "zeroize", +] + +[[package]] +name = "aws-lc-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bff6c3b54fad79a2e60b8102caf565819711497c1f5f092f49508e2f5c31b27" +dependencies = [ + "cc", + "cmake", + "dunce", + "fs_extra", + "pkg-config", +] + [[package]] name = "base64" version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "base64" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac07cdecf99051d9a5238b80f35af32cdeba5b336e55d957b318b50137e18da5" + [[package]] name = "bitflags" version = "2.13.0" @@ -79,6 +108,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dad887fd958be91b5098c0248def011f4523ab786cd411be668777e55063501f" dependencies = [ "find-msvc-tools", + "jobserver", + "libc", "shlex", ] @@ -94,6 +125,36 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +[[package]] +name = "chacha20" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65c35e4b699c7e15ccbe7ee35c005e4fc0a278d22238a2857e6ce2dadeda1b06" +dependencies = [ + "cfg-if", + "cpufeatures", + "rand_core", +] + +[[package]] +name = "cmake" +version = "0.1.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0f78a02292a74a88ac736019ab962ece0bc380e3f977bf72e376c5d78ff0678" +dependencies = [ + "cc", +] + +[[package]] +name = "combine" +version = "4.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfc320937d09e6de266b31b9afb480f197d7a861be86be7cb2ea7e5d1bfffc5e" +dependencies = [ + "bytes", + "memchr", +] + [[package]] name = "core-foundation" version = "0.9.4" @@ -120,6 +181,15 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +[[package]] +name = "cpufeatures" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca28b0ae3115b884660db4118d803791fd6756b6e88f39c0f3f7859060d7566" +dependencies = [ + "libc", +] + [[package]] name = "deranged" version = "0.5.8" @@ -165,6 +235,12 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + [[package]] name = "encoding_rs" version = "0.8.35" @@ -180,22 +256,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" -[[package]] -name = "errno" -version = "0.3.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" -dependencies = [ - "libc", - "windows-sys 0.61.2", -] - -[[package]] -name = "fastrand" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" - [[package]] name = "find-msvc-tools" version = "0.1.9" @@ -214,21 +274,6 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - [[package]] name = "form_urlencoded" version = "1.2.2" @@ -238,6 +283,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + [[package]] name = "futures" version = "0.3.32" @@ -333,20 +384,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi", -] - -[[package]] -name = "getrandom" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" -dependencies = [ - "cfg-if", - "libc", - "r-efi 5.3.0", - "wasip2", + "wasm-bindgen", ] [[package]] @@ -356,10 +397,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" dependencies = [ "cfg-if", + "js-sys", "libc", - "r-efi 6.0.0", + "r-efi", + "rand_core", "wasip2", "wasip3", + "wasm-bindgen", ] [[package]] @@ -487,29 +531,13 @@ dependencies = [ "tower-service", ] -[[package]] -name = "hyper-tls" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" -dependencies = [ - "bytes", - "http-body-util", - "hyper", - "hyper-util", - "native-tls", - "tokio", - "tokio-native-tls", - "tower-service", -] - [[package]] name = "hyper-util" version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" dependencies = [ - "base64", + "base64 0.22.1", "bytes", "futures-channel", "futures-util", @@ -661,6 +689,65 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" +[[package]] +name = "jni" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5efd9a482cf3a427f00d6b35f14332adc7902ce91efb778580e180ff90fa3498" +dependencies = [ + "cfg-if", + "combine", + "jni-macros", + "jni-sys", + "log", + "simd_cesu8", + "thiserror", + "walkdir", + "windows-link", +] + +[[package]] +name = "jni-macros" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a00109accc170f0bdb141fed3e393c565b6f5e072365c3bd58f5b062591560a3" +dependencies = [ + "proc-macro2", + "quote", + "rustc_version", + "simd_cesu8", + "syn 2.0.117", +] + +[[package]] +name = "jni-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2" +dependencies = [ + "jni-sys-macros", +] + +[[package]] +name = "jni-sys-macros" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" +dependencies = [ + "quote", + "syn 2.0.117", +] + +[[package]] +name = "jobserver" +version = "0.1.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c00acbd29eabad4a2392fa0e921c874934dbbf4194312ad20f04a0ed67a3cb3" +dependencies = [ + "getrandom 0.4.2", + "libc", +] + [[package]] name = "js-sys" version = "0.3.102" @@ -684,12 +771,6 @@ version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" -[[package]] -name = "linux-raw-sys" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" - [[package]] name = "litemap" version = "0.8.2" @@ -702,6 +783,12 @@ version = "0.4.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "953f07c43838f8e6f9758cab68bf5bed85465e7587ebe0b823f1bcd81978ad3a" +[[package]] +name = "lru-slab" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4050469837a6ff301cd14c1f8f24f88549e6d548f24f64e2148eb0f72cebc51f" + [[package]] name = "memchr" version = "2.8.2" @@ -725,23 +812,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "native-tls" -version = "0.2.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "465500e14ea162429d264d44189adc38b199b62b1c21eea9f69e4b73cb03bbf2" -dependencies = [ - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", -] - [[package]] name = "nix" version = "0.31.3" @@ -925,49 +995,12 @@ version = "1.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" -[[package]] -name = "openssl" -version = "0.10.81" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77823a27f0babb03091cb9ed9ef80af3b39dbc82f97e8fa530374b7dafd87a45" -dependencies = [ - "bitflags", - "cfg-if", - "foreign-types", - "libc", - "openssl-macros", - "openssl-sys", -] - -[[package]] -name = "openssl-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - [[package]] name = "openssl-probe" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" -[[package]] -name = "openssl-sys" -version = "0.9.117" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b47e7e6bb2c38cd930d25a23b40fa52e068c10e85f3e03a7f5ba5aaca5713695" -dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", -] - [[package]] name = "os_info" version = "3.15.0" @@ -1017,15 +1050,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" -[[package]] -name = "ppv-lite86" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" -dependencies = [ - "zerocopy", -] - [[package]] name = "prettyplease" version = "0.2.37" @@ -1045,6 +1069,63 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "quinn" +version = "0.11.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4051e23e9185c255a7e33ef59cdbca87a22d359052eecd22fc6b901fb37d9d11" +dependencies = [ + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls", + "socket2", + "thiserror", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e750cca55fe4f0439a15d0bb529da9651e79993e8e72c61a899a36d462befbe" +dependencies = [ + "aws-lc-rs", + "bytes", + "getrandom 0.4.2", + "lru-slab", + "rand", + "rand_pcg", + "ring", + "rustc-hash", + "rustls", + "rustls-pki-types", + "slab", + "thiserror", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af66907df18639dcf4db56ca65490cabc4b27a97dbadd96f2926cca73298f016" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.61.2", +] + [[package]] name = "quote" version = "1.0.45" @@ -1054,12 +1135,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "r-efi" -version = "5.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" - [[package]] name = "r-efi" version = "6.0.0" @@ -1068,40 +1143,37 @@ checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" [[package]] name = "rand" -version = "0.9.4" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" +checksum = "65c9fb96cbc91e3478eaae79a69fcd3f1ae4ad052e471fe6732fff548984b4af" dependencies = [ - "rand_chacha", + "chacha20", + "getrandom 0.4.2", "rand_core", ] [[package]] -name = "rand_chacha" -version = "0.9.0" +name = "rand_core" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" -dependencies = [ - "ppv-lite86", - "rand_core", -] +checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" [[package]] -name = "rand_core" -version = "0.9.5" +name = "rand_pcg" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +checksum = "caa0f4137e1c0a72f4c651489402276c8e8e1cf081f3b0ba156d2cbeef09e86a" dependencies = [ - "getrandom 0.3.4", + "rand_core", ] [[package]] name = "reqwest" -version = "0.12.28" +version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" +checksum = "16a1cfa75cc186dd73d5818e510e042e40927bccc9c236b061cea97e1eb08029" dependencies = [ - "base64", + "base64 0.23.1", "bytes", "encoding_rs", "futures-core", @@ -1111,21 +1183,21 @@ dependencies = [ "http-body-util", "hyper", "hyper-rustls", - "hyper-tls", "hyper-util", "js-sys", "log", "mime", - "native-tls", "percent-encoding", "pin-project-lite", + "quinn", + "rustls", "rustls-pki-types", + "rustls-platform-verifier", "serde", "serde_json", - "serde_urlencoded", "sync_wrapper", "tokio", - "tokio-native-tls", + "tokio-rustls", "tower", "tower-http", "tower-service", @@ -1150,16 +1222,18 @@ dependencies = [ ] [[package]] -name = "rustix" -version = "1.1.4" +name = "rustc-hash" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ - "bitflags", - "errno", - "libc", - "linux-raw-sys", - "windows-sys 0.61.2", + "semver", ] [[package]] @@ -1168,6 +1242,7 @@ version = "0.23.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b" dependencies = [ + "aws-lc-rs", "once_cell", "rustls-pki-types", "rustls-webpki", @@ -1175,21 +1250,62 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rustls-native-certs" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dab5152771c58876a2146916e53e35057e1a4dfa2b9df0f0305b07f611fdea4d" +dependencies = [ + "openssl-probe", + "rustls-pki-types", + "schannel", + "security-framework", +] + [[package]] name = "rustls-pki-types" version = "1.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9" dependencies = [ + "web-time", "zeroize", ] +[[package]] +name = "rustls-platform-verifier" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1167586491e2b18b8bfbb293e8180ec17c201c4f076d7cb3070ca964e7598f98" +dependencies = [ + "core-foundation 0.10.1", + "core-foundation-sys", + "jni", + "log", + "once_cell", + "rustls", + "rustls-native-certs", + "rustls-platform-verifier-android", + "rustls-webpki", + "security-framework", + "security-framework-sys", + "webpki-root-certs", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls-platform-verifier-android" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eec689c0bc40ff2458a5977b6619cb718087084a18e02a131c599b62d05e1a5f" + [[package]] name = "rustls-webpki" version = "0.103.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" dependencies = [ + "aws-lc-rs", "ring", "rustls-pki-types", "untrusted", @@ -1202,10 +1318,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] -name = "ryu" -version = "1.0.23" +name = "same-file" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] [[package]] name = "schannel" @@ -1289,22 +1408,26 @@ dependencies = [ ] [[package]] -name = "serde_urlencoded" -version = "0.7.1" +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + +[[package]] +name = "simd_cesu8" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +checksum = "11031e251abf8611c80f460e19dbdeb54a66db918e49c65a7065b46ac7aec520" dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde", + "rustc_version", + "simdutf8", ] [[package]] -name = "shlex" -version = "2.0.1" +name = "simdutf8" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" [[package]] name = "slab" @@ -1362,6 +1485,17 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8593e8e72159ed2257d083c7a454a85cbf854f37a0966d8d483aff8c8a3ebcee" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "sync_wrapper" version = "1.0.2" @@ -1413,16 +1547,23 @@ dependencies = [ ] [[package]] -name = "tempfile" -version = "3.27.0" +name = "thiserror" +version = "2.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" +checksum = "09e52cb86a36cede5cb101bf8908837b3e4c6e5e59fe7fd85c23fb56200d189e" dependencies = [ - "fastrand", - "getrandom 0.4.2", - "once_cell", - "rustix", - "windows-sys 0.61.2", + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe5197923287db20a58125f0bc85c062f7f2c892de97b18c356f9efb14b28524" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", ] [[package]] @@ -1465,6 +1606,12 @@ dependencies = [ "zerovec", ] +[[package]] +name = "tinyvec" +version = "1.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd3ca314f692efd6c868f8408f53fe444634a845f96c028b97d35f6a1f79f0ee" + [[package]] name = "tokio" version = "1.52.3" @@ -1491,16 +1638,6 @@ dependencies = [ "syn 2.0.117", ] -[[package]] -name = "tokio-native-tls" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" -dependencies = [ - "native-tls", - "tokio", -] - [[package]] name = "tokio-rustls" version = "0.26.4" @@ -1631,10 +1768,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] -name = "vcpkg" -version = "0.2.15" +name = "walkdir" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] [[package]] name = "want" @@ -1768,6 +1909,34 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki-root-certs" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b96554aa2acc8ccdb7e1c9a58a7a68dd5d13bccc69cd124cb09406db612a1c9b" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + [[package]] name = "windows-link" version = "0.2.1" @@ -2008,26 +2177,6 @@ dependencies = [ "synstructure", ] -[[package]] -name = "zerocopy" -version = "0.8.52" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce1022995ff5ff5d841ad7d994facc23098cd40152f2c1d11cd607c6f530653f" -dependencies = [ - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.8.52" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ae7f38b72ec2a254e2b87ef277cf2cd4fb97cbebf944faa6f33354da0867930" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - [[package]] name = "zerofrom" version = "0.1.8" diff --git a/examples/helloworld/Cargo.toml b/examples/helloworld/Cargo.toml index 7170031..5e1ed08 100644 --- a/examples/helloworld/Cargo.toml +++ b/examples/helloworld/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2024" [dependencies] -aptabase-rs = { version = "0.1.0", path = "../.." } +aptabase-rs = { path = "../.." } dotenvy_macro = "0.15.7" serde_json = "1.0.138" tokio = { version = "1.43.0", features = ["macros", "rt-multi-thread"] } From cd653387f176333b9bc2f55b657629c065ba7478 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Sun, 4 Oct 2026 23:02:33 +0100 Subject: [PATCH 33/83] Check TLS feature sets and the example in CI --- .github/workflows/ci.yml | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3b9daba..fd8e56a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,3 +16,36 @@ jobs: - run: cargo fmt --check - run: cargo clippy --all-targets - run: cargo test + + features: + name: Clippy (${{ matrix.name }}) + runs-on: ubuntu-latest + strategy: + matrix: + include: + - name: native-tls + flags: --no-default-features --features native-tls + - name: rustls-tls + flags: --no-default-features --features rustls-tls + - name: no TLS + flags: --no-default-features + steps: + - uses: actions/checkout@v7 + - uses: dtolnay/rust-toolchain@stable + - uses: Swatinem/rust-cache@v2 + - run: cargo clippy --all-targets ${{ matrix.flags }} + + example: + name: Example + runs-on: ubuntu-latest + defaults: + run: + working-directory: examples/helloworld + steps: + - uses: actions/checkout@v7 + - uses: dtolnay/rust-toolchain@stable + - uses: Swatinem/rust-cache@v2 + with: + workspaces: examples/helloworld + - run: cp .env.example .env + - run: cargo clippy --all-targets --locked From 841d40f7a052aaff2e80e40417d48429d4d0ea6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Sun, 4 Oct 2026 23:02:51 +0100 Subject: [PATCH 34/83] Document TLS backend features --- README.md | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 0882410..0c00a6e 100644 --- a/README.md +++ b/README.md @@ -12,11 +12,30 @@ Install the SDK by adding the following to your `Cargo.toml` file: ```toml [dependencies] -aptabase-rs = "0.1.0" +aptabase-rs = "0.3" serde_json = "1" tokio = { version = "1", features = ["macros", "rt-multi-thread"] } ``` +### TLS backends + +The SDK uses [`reqwest`](https://crates.io/crates/reqwest) to send events, and you can choose its TLS backend with Cargo features: + +| Feature | Backend | +| --- | --- | +| `default-tls` (default) | `reqwest`'s default backend, currently `rustls` | +| `rustls-tls` | `rustls` | +| `native-tls` | The platform's native TLS library (OpenSSL on Linux) | + +To switch backend, disable the default features: + +```toml +[dependencies] +aptabase-rs = { version = "0.3", default-features = false, features = ["native-tls"] } +``` + +Without any TLS feature, the SDK can only send events over plain HTTP. That works with `DEV` app keys, which send to `http://localhost:3000`, and with self-hosted servers on an `http://` host. Events sent to an `https://` host fail and are only logged at `debug` level. + ## Usage First, you need to get your `App Key` from Aptabase, you can find it in the `Instructions` menu on the left side menu. From 38cc868690a648d400e4f655ac3cd2d0173202e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Sun, 4 Oct 2026 23:58:41 +0100 Subject: [PATCH 35/83] Use the official plugin's title in README intro - Link to "Tauri Plugin for Aptabase" instead of the crate name in the README's derivation note --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0c00a6e..0443a16 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ An unofficial, framework-independent Rust SDK for [Aptabase](https://aptabase.com), the open source, privacy-first, and simple analytics platform for mobile, desktop and web apps. -This is a community project and is not affiliated with or endorsed by Aptabase. It is derived from the official [`tauri-plugin-aptabase`](https://github.com/aptabase/tauri-plugin-aptabase). If you're building a Tauri app, use that instead. +This is a community project and is not affiliated with or endorsed by Aptabase. It is derived from the official [Tauri Plugin for Aptabase](https://github.com/aptabase/tauri-plugin-aptabase). If you're building a Tauri app, use that instead. ## Install From 03f8849b6544789357d3cfbb2af0f394d5b69697 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Mon, 5 Oct 2026 00:01:50 +0100 Subject: [PATCH 36/83] Keep upstream commits out of release PR changelogs --- .github/workflows/release-plz.yml | 4 ++++ mise.toml | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release-plz.yml b/.github/workflows/release-plz.yml index 521f965..383fa1c 100644 --- a/.github/workflows/release-plz.yml +++ b/.github/workflows/release-plz.yml @@ -44,6 +44,10 @@ jobs: steps: - *checkout - *install-rust + # Keep in sync with the changelog task in mise.toml. + - name: Skip commits off the first-parent chain + shell: bash + run: comm -23 <(git rev-list HEAD | sort) <(git rev-list --first-parent HEAD | sort) > .cliffignore - name: Generate changelog id: git-cliff uses: orhun/git-cliff-action@v4 diff --git a/mise.toml b/mise.toml index 48b84f9..30c442d 100644 --- a/mise.toml +++ b/mise.toml @@ -29,7 +29,8 @@ run = [ description = "Update changelog" shell = "bash -c" run = [ - # Skip commits off main's first-parent chain (branch and upstream commits) + # Skip commits off HEAD's first-parent chain (branch and upstream commits), so + # run this on main. Keep in sync with the release-plz-pr job in release-plz.yml. "comm -23 <(git rev-list HEAD | sort) <(git rev-list --first-parent HEAD | sort) > .cliffignore", "git cliff --bump" ] From 6efb6d58946ef59b34ba8f873a58617f9385750d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Mon, 5 Oct 2026 00:02:53 +0100 Subject: [PATCH 37/83] Let release bumps through the example job Release-plz doesn't update the example's lockfile, so building it with --locked would fail every release PR. Also name the no-TLS matrix entry like the feature names. --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fd8e56a..c272d06 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,7 +27,7 @@ jobs: flags: --no-default-features --features native-tls - name: rustls-tls flags: --no-default-features --features rustls-tls - - name: no TLS + - name: no-tls flags: --no-default-features steps: - uses: actions/checkout@v7 @@ -48,4 +48,4 @@ jobs: with: workspaces: examples/helloworld - run: cp .env.example .env - - run: cargo clippy --all-targets --locked + - run: cargo clippy --all-targets From c3033da5f2d16ea68176d333e996a95d10aa34cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Mon, 5 Oct 2026 01:34:26 +0100 Subject: [PATCH 38/83] Move TLS backends section below delivery behavior --- README.md | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 0443a16..5f721d3 100644 --- a/README.md +++ b/README.md @@ -17,25 +17,6 @@ serde_json = "1" tokio = { version = "1", features = ["macros", "rt-multi-thread"] } ``` -### TLS backends - -The SDK uses [`reqwest`](https://crates.io/crates/reqwest) to send events, and you can choose its TLS backend with Cargo features: - -| Feature | Backend | -| --- | --- | -| `default-tls` (default) | `reqwest`'s default backend, currently `rustls` | -| `rustls-tls` | `rustls` | -| `native-tls` | The platform's native TLS library (OpenSSL on Linux) | - -To switch backend, disable the default features: - -```toml -[dependencies] -aptabase-rs = { version = "0.3", default-features = false, features = ["native-tls"] } -``` - -Without any TLS feature, the SDK can only send events over plain HTTP. That works with `DEV` app keys, which send to `http://localhost:3000`, and with self-hosted servers on an `http://` host. Events sent to an `https://` host fail and are only logged at `debug` level. - ## Usage First, you need to get your `App Key` from Aptabase, you can find it in the `Instructions` menu on the left side menu. @@ -198,6 +179,25 @@ let client = Builder::new("", env!("CARGO_PKG_VERSION")) - Other unsuccessful HTTP responses are discarded. - This crate provides only the asynchronous Tokio transport; it does not expose a blocking flush API. +## TLS backends + +The SDK uses [`reqwest`](https://crates.io/crates/reqwest) to send events, and you can choose its TLS backend with Cargo features: + +| Feature | Backend | +| --- | --- | +| `default-tls` (default) | `reqwest`'s default backend, currently `rustls` | +| `rustls-tls` | `rustls` | +| `native-tls` | The platform's native TLS library (OpenSSL on Linux) | + +To switch backend, disable the default features: + +```toml +[dependencies] +aptabase-rs = { version = "0.3", default-features = false, features = ["native-tls"] } +``` + +Without any TLS feature, the SDK can only send events over plain HTTP. That works with `DEV` app keys, which send to `http://localhost:3000`, and with self-hosted servers on an `http://` host. Events sent to an `https://` host fail and are only logged at `debug` level. + ## Privacy The client never tracks events automatically. Applications decide which event names and properties to send. Each event includes the app version, SDK version, debug status, OS name and version, and locale. Avoid sending personal or sensitive data in event names or properties. From abd6a26df723f31c34ab76ddea18c44d27248223 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Oct 2026 00:39:52 +0000 Subject: [PATCH 39/83] chore(deps): bump actions/checkout from 6 to 7 Bumps [actions/checkout](https://github.com/actions/checkout) from 6 to 7. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v6...v7) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '7' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/release-plz.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-plz.yml b/.github/workflows/release-plz.yml index 383fa1c..c5f4d5b 100644 --- a/.github/workflows/release-plz.yml +++ b/.github/workflows/release-plz.yml @@ -15,7 +15,7 @@ jobs: steps: - &checkout name: Checkout repository - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: fetch-depth: 0 persist-credentials: true From a56c8d1ce4090016b3f77b2fed21162e90eca51f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Oct 2026 00:40:07 +0000 Subject: [PATCH 40/83] chore(deps): bump log from 0.4.33 to 0.4.34 Bumps [log](https://github.com/rust-lang/log) from 0.4.33 to 0.4.34. - [Release notes](https://github.com/rust-lang/log/releases) - [Changelog](https://github.com/rust-lang/log/blob/master/CHANGELOG.md) - [Commits](https://github.com/rust-lang/log/compare/0.4.33...0.4.34) --- updated-dependencies: - dependency-name: log dependency-version: 0.4.34 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 59c9d4e..414de7a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -772,9 +772,9 @@ checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" [[package]] name = "log" -version = "0.4.33" +version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" +checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6" [[package]] name = "lru-slab" From a338a3b181d37c887a5746a5aea6746c9bf8055a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Oct 2026 00:40:12 +0000 Subject: [PATCH 41/83] chore(deps): bump time from 0.3.54 to 0.3.55 Bumps [time](https://github.com/time-rs/time) from 0.3.54 to 0.3.55. - [Release notes](https://github.com/time-rs/time/releases) - [Changelog](https://github.com/time-rs/time/blob/main/CHANGELOG.md) - [Commits](https://github.com/time-rs/time/compare/v0.3.54...v0.3.55) --- updated-dependencies: - dependency-name: time dependency-version: 0.3.55 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 59c9d4e..6dd7bad 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1635,9 +1635,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.54" +version = "0.3.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e1d5e639ff6bab73cb6885cc7e7b1de96c3f32c68ec55f3952614bec1092244" +checksum = "cdb87b95ec50ddfa440816d227a17b2ccbdda963a316a727fda0fc4334f7d134" dependencies = [ "deranged", "num-conv", From 8f739cb4f5661e093dc7053e86ede29c1f4078f8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Oct 2026 00:40:17 +0000 Subject: [PATCH 42/83] chore(deps): bump futures from 0.3.33 to 0.3.34 Bumps [futures](https://github.com/rust-lang/futures-rs) from 0.3.33 to 0.3.34. - [Release notes](https://github.com/rust-lang/futures-rs/releases) - [Changelog](https://github.com/rust-lang/futures-rs/blob/main/CHANGELOG.md) - [Commits](https://github.com/rust-lang/futures-rs/compare/0.3.33...0.3.34) --- updated-dependencies: - dependency-name: futures dependency-version: 0.3.34 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 59c9d4e..18b65c4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -303,9 +303,9 @@ checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" [[package]] name = "futures" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a88cf1f829d945f548cf8fec32c61b1f202b6d93b45848602fc02af4b12ad218" +checksum = "9a31d2a3fbaaeb2af2368bbdd904aa8e812d3c04a1ee10d3171f52d556e5d0a3" dependencies = [ "futures-channel", "futures-core", @@ -318,9 +318,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "262590f4fe6afeb0bc83be1daa64e52657fe185690a958af7f3ad0e92085c5ae" +checksum = "b1f9e3d69d39e4862ffed03ed071a76f9a13ba1d9109d355b0f0aa6b15e393c4" dependencies = [ "futures-core", "futures-sink", @@ -328,15 +328,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cd50c473c80f6d7c3670a752354b8e569b1a7cbfdc0419ec88e5edad85e0dc7" +checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e" [[package]] name = "futures-executor" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6754879cc9f2c66f88c6e5c35344bb0bdb0708b0352b1201815667c7eabc7458" +checksum = "031b47cf1a3c6cc8bc2fc76cd437f521619387907d469316e7c0bc278f1f5432" dependencies = [ "futures-core", "futures-task", @@ -345,38 +345,38 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4577ecaa3c4f96589d473f679a71b596316f6641bc350038b962a5daf0085d7a" +checksum = "53c0fa8157de1303bfffdaa1cc2a673bfffb60102f76b0ef4441659124373fed" [[package]] name = "futures-macro" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d6d3cde68c518367be28956066ddfef33813991b77a55005a69dae04bf3b10b" +checksum = "9fb9654ba8355388abeb8dcb4fc62f511300867002afc858860463bdd9fe0c44" dependencies = [ "proc-macro2", "quote", - "syn 2.0.119", + "syn 3.0.3", ] [[package]] name = "futures-sink" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e34418ac499d6305c2fb5ad0ed2f6ac998c5f8ca209b4510f7f94242c647e307" +checksum = "1944426bf7d03f1d14f708785e4b33efd750b36d48a157b836b3efc15ede8e1d" [[package]] name = "futures-task" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b231ed28831efb4a61a08580c4bc233ec56bc009f4cd8f52da2c3cb97df0c109" +checksum = "cd417de3d1d015fc3bfd2b1ea46dfc7bab72ef86f1cc7cc9c78e728b34a6d1fd" [[package]] name = "futures-util" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a77a90a256fce34da66415271e30f94ee91c57b04b8a2c042d9cf3220179deaa" +checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc" dependencies = [ "futures-channel", "futures-core", From e6b288977d83436a1f4d956878073cc02700f8cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Mon, 5 Oct 2026 01:45:55 +0100 Subject: [PATCH 43/83] chore(release): Release 0.3.0 --- CHANGELOG.md | 15 +++++++++++++++ Cargo.lock | 2 +- Cargo.toml | 2 +- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6fe4cb0..11e4d65 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,20 @@ # Changelog +## 0.3.0 + +### ๐Ÿš€ Features + +- **BREAKING CHANGE**: Switch default TLS backend to rustls ([#16](https://github.com/eugencowie/aptabase-rs/pull/16)) + +### ๐Ÿ“š Documentation + +- Remove specs and skills from repo ([#14](https://github.com/eugencowie/aptabase-rs/pull/14)) + +### โš™๏ธ Miscellaneous Tasks + +- Run CI and releases from main ([#12](https://github.com/eugencowie/aptabase-rs/pull/12)) +- Merge upstream changes ([#15](https://github.com/eugencowie/aptabase-rs/pull/15)) + ## 0.2.0 ### ๐Ÿš€ Features diff --git a/Cargo.lock b/Cargo.lock index 48806b9..b4ad5ea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13,7 +13,7 @@ dependencies = [ [[package]] name = "aptabase-rs" -version = "0.2.0" +version = "0.3.0" dependencies = [ "futures", "log", diff --git a/Cargo.toml b/Cargo.toml index b7af6e5..7f31b2c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "aptabase-rs" -version = "0.2.0" +version = "0.3.0" authors = ["Eugรฉn Cowie"] edition = "2024" description = "Rust SDK for Aptabase: Open Source, Privacy-First and Simple Analytics for Mobile, Desktop and Web Apps" From 23607f6054f2e81913583a969ab2083657b033fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Mon, 5 Oct 2026 10:50:57 +0100 Subject: [PATCH 44/83] Update rustls to 0.23.45 rustls 0.23.42 is affected by RUSTSEC-2026-0285, which is patched in 0.23.45. rustls-webpki moves to 0.103.15 alongside it. This only changes the lockfile, which downstream crates ignore: their own lockfiles decide which rustls they build, and reqwest's requirement already admits the patched release. --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b4ad5ea..9637fc0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1297,9 +1297,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.42" +version = "0.23.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c54fcab019b409d04215d3a17cb438fd7fbf192ee61461f20f4fe18704bc138" +checksum = "0d41d731c7d2f962d1ccc364cec258de3c0e93b38c2fb3ba97ac74513048d634" dependencies = [ "aws-lc-rs", "once_cell", @@ -1360,9 +1360,9 @@ checksum = "eec689c0bc40ff2458a5977b6619cb718087084a18e02a131c599b62d05e1a5f" [[package]] name = "rustls-webpki" -version = "0.103.13" +version = "0.103.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" +checksum = "f3c3cf1d8b1e7d4927e2d154c3fcb02979afb9939629c62cd9048d4f07b60ac2" dependencies = [ "aws-lc-rs", "ring", From 5802cec3b2404d38a726833eca04c873ad5ad079 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Mon, 5 Oct 2026 10:51:20 +0100 Subject: [PATCH 45/83] Stop tracking the example's lockfile examples/helloworld is not part of a workspace, so release-plz never updates its lockfile: it still recorded aptabase-rs 0.2.0 after the 0.3.0 release, and any cargo command in the example rewrote it. The example always builds against the local crate, so the lockfile pins nothing useful. Untrack it and ignore lockfiles under examples/. --- .gitignore | 8 + examples/helloworld/Cargo.lock | 2244 -------------------------------- 2 files changed, 8 insertions(+), 2244 deletions(-) delete mode 100644 examples/helloworld/Cargo.lock diff --git a/.gitignore b/.gitignore index b0f117d..606e3b4 100644 --- a/.gitignore +++ b/.gitignore @@ -27,6 +27,14 @@ rustc-ice-*.txt ### dotenv ### .env +# +# Examples +# + +# Each example builds against the local crate, so a committed lockfile +# only goes stale whenever the crate's version changes +examples/*/Cargo.lock + # # git-cliff # diff --git a/examples/helloworld/Cargo.lock b/examples/helloworld/Cargo.lock deleted file mode 100644 index d0fc3bc..0000000 --- a/examples/helloworld/Cargo.lock +++ /dev/null @@ -1,2244 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 4 - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - -[[package]] -name = "anyhow" -version = "1.0.102" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" - -[[package]] -name = "aptabase-rs" -version = "0.2.0" -dependencies = [ - "futures", - "log", - "os_info", - "rand", - "reqwest", - "serde", - "serde_json", - "sys-locale", - "time", - "tokio", -] - -[[package]] -name = "atomic-waker" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" - -[[package]] -name = "aws-lc-rs" -version = "1.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b281d307588d634de920874890732659e2e7672f72b5e10e81badc1a8a83621e" -dependencies = [ - "aws-lc-sys", - "zeroize", -] - -[[package]] -name = "aws-lc-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bff6c3b54fad79a2e60b8102caf565819711497c1f5f092f49508e2f5c31b27" -dependencies = [ - "cc", - "cmake", - "dunce", - "fs_extra", - "pkg-config", -] - -[[package]] -name = "base64" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" - -[[package]] -name = "base64" -version = "0.23.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac07cdecf99051d9a5238b80f35af32cdeba5b336e55d957b318b50137e18da5" - -[[package]] -name = "bitflags" -version = "2.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" - -[[package]] -name = "block2" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5" -dependencies = [ - "objc2", -] - -[[package]] -name = "bumpalo" -version = "3.20.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" - -[[package]] -name = "bytes" -version = "1.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" - -[[package]] -name = "cc" -version = "1.2.64" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dad887fd958be91b5098c0248def011f4523ab786cd411be668777e55063501f" -dependencies = [ - "find-msvc-tools", - "jobserver", - "libc", - "shlex", -] - -[[package]] -name = "cfg-if" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" - -[[package]] -name = "cfg_aliases" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" - -[[package]] -name = "chacha20" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65c35e4b699c7e15ccbe7ee35c005e4fc0a278d22238a2857e6ce2dadeda1b06" -dependencies = [ - "cfg-if", - "cpufeatures", - "rand_core", -] - -[[package]] -name = "cmake" -version = "0.1.58" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0f78a02292a74a88ac736019ab962ece0bc380e3f977bf72e376c5d78ff0678" -dependencies = [ - "cc", -] - -[[package]] -name = "combine" -version = "4.6.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfc320937d09e6de266b31b9afb480f197d7a861be86be7cb2ea7e5d1bfffc5e" -dependencies = [ - "bytes", - "memchr", -] - -[[package]] -name = "core-foundation" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" - -[[package]] -name = "cpufeatures" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca28b0ae3115b884660db4118d803791fd6756b6e88f39c0f3f7859060d7566" -dependencies = [ - "libc", -] - -[[package]] -name = "deranged" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" - -[[package]] -name = "dispatch2" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" -dependencies = [ - "bitflags", - "objc2", -] - -[[package]] -name = "displaydoc" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "dotenvy" -version = "0.15.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" - -[[package]] -name = "dotenvy_macro" -version = "0.15.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb0235d912a8c749f4e0c9f18ca253b4c28cfefc1d2518096016d6e3230b6424" -dependencies = [ - "dotenvy", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "dunce" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" - -[[package]] -name = "encoding_rs" -version = "0.8.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "equivalent" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" - -[[package]] -name = "find-msvc-tools" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "foldhash" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" - -[[package]] -name = "form_urlencoded" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" -dependencies = [ - "percent-encoding", -] - -[[package]] -name = "fs_extra" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" - -[[package]] -name = "futures" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" - -[[package]] -name = "futures-executor" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-io" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" - -[[package]] -name = "futures-macro" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "futures-sink" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" - -[[package]] -name = "futures-task" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" - -[[package]] -name = "futures-util" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "slab", -] - -[[package]] -name = "getrandom" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "wasi", - "wasm-bindgen", -] - -[[package]] -name = "getrandom" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "r-efi", - "rand_core", - "wasip2", - "wasip3", - "wasm-bindgen", -] - -[[package]] -name = "h2" -version = "0.4.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cb093c84e8bd9b188d4c4a8cb6579fc016968d14c99882163cd3ff402a4f155" -dependencies = [ - "atomic-waker", - "bytes", - "fnv", - "futures-core", - "futures-sink", - "http", - "indexmap", - "slab", - "tokio", - "tokio-util", - "tracing", -] - -[[package]] -name = "hashbrown" -version = "0.15.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" -dependencies = [ - "foldhash", -] - -[[package]] -name = "hashbrown" -version = "0.17.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" - -[[package]] -name = "heck" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" - -[[package]] -name = "helloworld" -version = "0.1.0" -dependencies = [ - "aptabase-rs", - "dotenvy_macro", - "serde_json", - "tokio", -] - -[[package]] -name = "http" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6970f50e31d6fc17d3fa27329444bfa74e196cf62e95052a3f6fee181dba6425" -dependencies = [ - "bytes", - "itoa", -] - -[[package]] -name = "http-body" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" -dependencies = [ - "bytes", - "http", -] - -[[package]] -name = "http-body-util" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" -dependencies = [ - "bytes", - "futures-core", - "http", - "http-body", - "pin-project-lite", -] - -[[package]] -name = "httparse" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" - -[[package]] -name = "hyper" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55281c53a1894c864990125767da440a4e630446785086f52523b20033b74498" -dependencies = [ - "atomic-waker", - "bytes", - "futures-channel", - "futures-core", - "h2", - "http", - "http-body", - "httparse", - "itoa", - "pin-project-lite", - "smallvec", - "tokio", - "want", -] - -[[package]] -name = "hyper-rustls" -version = "0.27.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f" -dependencies = [ - "http", - "hyper", - "hyper-util", - "rustls", - "tokio", - "tokio-rustls", - "tower-service", -] - -[[package]] -name = "hyper-util" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" -dependencies = [ - "base64 0.22.1", - "bytes", - "futures-channel", - "futures-util", - "http", - "http-body", - "hyper", - "ipnet", - "libc", - "percent-encoding", - "pin-project-lite", - "socket2", - "system-configuration", - "tokio", - "tower-service", - "tracing", - "windows-registry", -] - -[[package]] -name = "icu_collections" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" -dependencies = [ - "displaydoc", - "potential_utf", - "utf8_iter", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_locale_core" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" -dependencies = [ - "displaydoc", - "litemap", - "tinystr", - "writeable", - "zerovec", -] - -[[package]] -name = "icu_normalizer" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" -dependencies = [ - "icu_collections", - "icu_normalizer_data", - "icu_properties", - "icu_provider", - "smallvec", - "zerovec", -] - -[[package]] -name = "icu_normalizer_data" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" - -[[package]] -name = "icu_properties" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" -dependencies = [ - "icu_collections", - "icu_locale_core", - "icu_properties_data", - "icu_provider", - "zerotrie", - "zerovec", -] - -[[package]] -name = "icu_properties_data" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" - -[[package]] -name = "icu_provider" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" -dependencies = [ - "displaydoc", - "icu_locale_core", - "writeable", - "yoke", - "zerofrom", - "zerotrie", - "zerovec", -] - -[[package]] -name = "id-arena" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" - -[[package]] -name = "idna" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" -dependencies = [ - "idna_adapter", - "smallvec", - "utf8_iter", -] - -[[package]] -name = "idna_adapter" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" -dependencies = [ - "icu_normalizer", - "icu_properties", -] - -[[package]] -name = "indexmap" -version = "2.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" -dependencies = [ - "equivalent", - "hashbrown 0.17.1", - "serde", - "serde_core", -] - -[[package]] -name = "ipnet" -version = "2.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" - -[[package]] -name = "itoa" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" - -[[package]] -name = "jni" -version = "0.22.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5efd9a482cf3a427f00d6b35f14332adc7902ce91efb778580e180ff90fa3498" -dependencies = [ - "cfg-if", - "combine", - "jni-macros", - "jni-sys", - "log", - "simd_cesu8", - "thiserror", - "walkdir", - "windows-link", -] - -[[package]] -name = "jni-macros" -version = "0.22.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a00109accc170f0bdb141fed3e393c565b6f5e072365c3bd58f5b062591560a3" -dependencies = [ - "proc-macro2", - "quote", - "rustc_version", - "simd_cesu8", - "syn 2.0.117", -] - -[[package]] -name = "jni-sys" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2" -dependencies = [ - "jni-sys-macros", -] - -[[package]] -name = "jni-sys-macros" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" -dependencies = [ - "quote", - "syn 2.0.117", -] - -[[package]] -name = "jobserver" -version = "0.1.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c00acbd29eabad4a2392fa0e921c874934dbbf4194312ad20f04a0ed67a3cb3" -dependencies = [ - "getrandom 0.4.2", - "libc", -] - -[[package]] -name = "js-sys" -version = "0.3.102" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03d04c30968dffe80775bd4d7fb676131cd04a1fb46d2686dbffbaec2d9dfd31" -dependencies = [ - "cfg-if", - "futures-util", - "wasm-bindgen", -] - -[[package]] -name = "leb128fmt" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" - -[[package]] -name = "libc" -version = "0.2.186" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" - -[[package]] -name = "litemap" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" - -[[package]] -name = "log" -version = "0.4.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "953f07c43838f8e6f9758cab68bf5bed85465e7587ebe0b823f1bcd81978ad3a" - -[[package]] -name = "lru-slab" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4050469837a6ff301cd14c1f8f24f88549e6d548f24f64e2148eb0f72cebc51f" - -[[package]] -name = "memchr" -version = "2.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4" - -[[package]] -name = "mime" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" - -[[package]] -name = "mio" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda" -dependencies = [ - "libc", - "wasi", - "windows-sys 0.61.2", -] - -[[package]] -name = "nix" -version = "0.31.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d" -dependencies = [ - "bitflags", - "cfg-if", - "cfg_aliases", - "libc", -] - -[[package]] -name = "num-conv" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" - -[[package]] -name = "objc2" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f" -dependencies = [ - "objc2-encode", -] - -[[package]] -name = "objc2-cloud-kit" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73ad74d880bb43877038da939b7427bba67e9dd42004a18b809ba7d87cee241c" -dependencies = [ - "bitflags", - "objc2", - "objc2-foundation", -] - -[[package]] -name = "objc2-core-data" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b402a653efbb5e82ce4df10683b6b28027616a2715e90009947d50b8dd298fa" -dependencies = [ - "objc2", - "objc2-foundation", -] - -[[package]] -name = "objc2-core-foundation" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" -dependencies = [ - "bitflags", - "dispatch2", - "objc2", -] - -[[package]] -name = "objc2-core-graphics" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807" -dependencies = [ - "bitflags", - "dispatch2", - "objc2", - "objc2-core-foundation", - "objc2-io-surface", -] - -[[package]] -name = "objc2-core-image" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5d563b38d2b97209f8e861173de434bd0214cf020e3423a52624cd1d989f006" -dependencies = [ - "objc2", - "objc2-foundation", -] - -[[package]] -name = "objc2-core-location" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca347214e24bc973fc025fd0d36ebb179ff30536ed1f80252706db19ee452009" -dependencies = [ - "objc2", - "objc2-foundation", -] - -[[package]] -name = "objc2-core-text" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cde0dfb48d25d2b4862161a4d5fcc0e3c24367869ad306b0c9ec0073bfed92d" -dependencies = [ - "bitflags", - "objc2", - "objc2-core-foundation", - "objc2-core-graphics", -] - -[[package]] -name = "objc2-encode" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" - -[[package]] -name = "objc2-foundation" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" -dependencies = [ - "bitflags", - "block2", - "libc", - "objc2", - "objc2-core-foundation", -] - -[[package]] -name = "objc2-io-surface" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d" -dependencies = [ - "bitflags", - "objc2", - "objc2-core-foundation", -] - -[[package]] -name = "objc2-quartz-core" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96c1358452b371bf9f104e21ec536d37a650eb10f7ee379fff67d2e08d537f1f" -dependencies = [ - "bitflags", - "objc2", - "objc2-core-foundation", - "objc2-foundation", -] - -[[package]] -name = "objc2-ui-kit" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d87d638e33c06f577498cbcc50491496a3ed4246998a7fbba7ccb98b1e7eab22" -dependencies = [ - "bitflags", - "block2", - "objc2", - "objc2-cloud-kit", - "objc2-core-data", - "objc2-core-foundation", - "objc2-core-graphics", - "objc2-core-image", - "objc2-core-location", - "objc2-core-text", - "objc2-foundation", - "objc2-quartz-core", - "objc2-user-notifications", -] - -[[package]] -name = "objc2-user-notifications" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9df9128cbbfef73cda168416ccf7f837b62737d748333bfe9ab71c245d76613e" -dependencies = [ - "objc2", - "objc2-foundation", -] - -[[package]] -name = "once_cell" -version = "1.21.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" - -[[package]] -name = "openssl-probe" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" - -[[package]] -name = "os_info" -version = "3.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf20a545b305cf1da722b236b5155c9bb35f1d5ceb28c048bd96ca842f41b5b" -dependencies = [ - "android_system_properties", - "log", - "nix", - "objc2", - "objc2-foundation", - "objc2-ui-kit", - "serde", - "windows-sys 0.61.2", -] - -[[package]] -name = "percent-encoding" -version = "2.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" - -[[package]] -name = "pin-project-lite" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" - -[[package]] -name = "pkg-config" -version = "0.3.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" - -[[package]] -name = "potential_utf" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" -dependencies = [ - "zerovec", -] - -[[package]] -name = "powerfmt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" - -[[package]] -name = "prettyplease" -version = "0.2.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" -dependencies = [ - "proc-macro2", - "syn 2.0.117", -] - -[[package]] -name = "proc-macro2" -version = "1.0.106" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quinn" -version = "0.11.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4051e23e9185c255a7e33ef59cdbca87a22d359052eecd22fc6b901fb37d9d11" -dependencies = [ - "bytes", - "cfg_aliases", - "pin-project-lite", - "quinn-proto", - "quinn-udp", - "rustc-hash", - "rustls", - "socket2", - "thiserror", - "tokio", - "tracing", - "web-time", -] - -[[package]] -name = "quinn-proto" -version = "0.11.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e750cca55fe4f0439a15d0bb529da9651e79993e8e72c61a899a36d462befbe" -dependencies = [ - "aws-lc-rs", - "bytes", - "getrandom 0.4.2", - "lru-slab", - "rand", - "rand_pcg", - "ring", - "rustc-hash", - "rustls", - "rustls-pki-types", - "slab", - "thiserror", - "tinyvec", - "tracing", - "web-time", -] - -[[package]] -name = "quinn-udp" -version = "0.5.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af66907df18639dcf4db56ca65490cabc4b27a97dbadd96f2926cca73298f016" -dependencies = [ - "cfg_aliases", - "libc", - "once_cell", - "socket2", - "tracing", - "windows-sys 0.61.2", -] - -[[package]] -name = "quote" -version = "1.0.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "r-efi" -version = "6.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" - -[[package]] -name = "rand" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65c9fb96cbc91e3478eaae79a69fcd3f1ae4ad052e471fe6732fff548984b4af" -dependencies = [ - "chacha20", - "getrandom 0.4.2", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" - -[[package]] -name = "rand_pcg" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "caa0f4137e1c0a72f4c651489402276c8e8e1cf081f3b0ba156d2cbeef09e86a" -dependencies = [ - "rand_core", -] - -[[package]] -name = "reqwest" -version = "0.13.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16a1cfa75cc186dd73d5818e510e042e40927bccc9c236b061cea97e1eb08029" -dependencies = [ - "base64 0.23.1", - "bytes", - "encoding_rs", - "futures-core", - "h2", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-rustls", - "hyper-util", - "js-sys", - "log", - "mime", - "percent-encoding", - "pin-project-lite", - "quinn", - "rustls", - "rustls-pki-types", - "rustls-platform-verifier", - "serde", - "serde_json", - "sync_wrapper", - "tokio", - "tokio-rustls", - "tower", - "tower-http", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "ring" -version = "0.17.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" -dependencies = [ - "cc", - "cfg-if", - "getrandom 0.2.17", - "libc", - "untrusted", - "windows-sys 0.52.0", -] - -[[package]] -name = "rustc-hash" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d" - -[[package]] -name = "rustc_version" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" -dependencies = [ - "semver", -] - -[[package]] -name = "rustls" -version = "0.23.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b" -dependencies = [ - "aws-lc-rs", - "once_cell", - "rustls-pki-types", - "rustls-webpki", - "subtle", - "zeroize", -] - -[[package]] -name = "rustls-native-certs" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dab5152771c58876a2146916e53e35057e1a4dfa2b9df0f0305b07f611fdea4d" -dependencies = [ - "openssl-probe", - "rustls-pki-types", - "schannel", - "security-framework", -] - -[[package]] -name = "rustls-pki-types" -version = "1.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9" -dependencies = [ - "web-time", - "zeroize", -] - -[[package]] -name = "rustls-platform-verifier" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1167586491e2b18b8bfbb293e8180ec17c201c4f076d7cb3070ca964e7598f98" -dependencies = [ - "core-foundation 0.10.1", - "core-foundation-sys", - "jni", - "log", - "once_cell", - "rustls", - "rustls-native-certs", - "rustls-platform-verifier-android", - "rustls-webpki", - "security-framework", - "security-framework-sys", - "webpki-root-certs", - "windows-sys 0.61.2", -] - -[[package]] -name = "rustls-platform-verifier-android" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eec689c0bc40ff2458a5977b6619cb718087084a18e02a131c599b62d05e1a5f" - -[[package]] -name = "rustls-webpki" -version = "0.103.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" -dependencies = [ - "aws-lc-rs", - "ring", - "rustls-pki-types", - "untrusted", -] - -[[package]] -name = "rustversion" -version = "1.0.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" - -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "schannel" -version = "0.1.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" -dependencies = [ - "windows-sys 0.61.2", -] - -[[package]] -name = "security-framework" -version = "3.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" -dependencies = [ - "bitflags", - "core-foundation 0.10.1", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "semver" -version = "1.0.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" - -[[package]] -name = "serde" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" -dependencies = [ - "serde_core", - "serde_derive", -] - -[[package]] -name = "serde_core" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "serde_json" -version = "1.0.150" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" -dependencies = [ - "itoa", - "memchr", - "serde", - "serde_core", - "zmij", -] - -[[package]] -name = "shlex" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" - -[[package]] -name = "simd_cesu8" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11031e251abf8611c80f460e19dbdeb54a66db918e49c65a7065b46ac7aec520" -dependencies = [ - "rustc_version", - "simdutf8", -] - -[[package]] -name = "simdutf8" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" - -[[package]] -name = "slab" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" - -[[package]] -name = "smallvec" -version = "1.15.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" - -[[package]] -name = "socket2" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51" -dependencies = [ - "libc", - "windows-sys 0.61.2", -] - -[[package]] -name = "stable_deref_trait" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" - -[[package]] -name = "subtle" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" - -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.117" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "3.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8593e8e72159ed2257d083c7a454a85cbf854f37a0966d8d483aff8c8a3ebcee" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "sync_wrapper" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" -dependencies = [ - "futures-core", -] - -[[package]] -name = "synstructure" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "sys-locale" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eab9a99a024a169fe8a903cf9d4a3b3601109bcc13bd9e3c6fff259138626c4" -dependencies = [ - "libc", -] - -[[package]] -name = "system-configuration" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" -dependencies = [ - "bitflags", - "core-foundation 0.9.4", - "system-configuration-sys", -] - -[[package]] -name = "system-configuration-sys" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "thiserror" -version = "2.0.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09e52cb86a36cede5cb101bf8908837b3e4c6e5e59fe7fd85c23fb56200d189e" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "2.0.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe5197923287db20a58125f0bc85c062f7f2c892de97b18c356f9efb14b28524" -dependencies = [ - "proc-macro2", - "quote", - "syn 3.0.6", -] - -[[package]] -name = "time" -version = "0.3.49" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711a53c2d47bbd818258c498c8dbfe186a2526c631495cfe7e078567f86b8469" -dependencies = [ - "deranged", - "num-conv", - "powerfmt", - "serde_core", - "time-core", - "time-macros", -] - -[[package]] -name = "time-core" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" - -[[package]] -name = "time-macros" -version = "0.2.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71c652a3727a9cbb9a02f707f530b618ce00d0ccd762009c8c23bd191df3c17d" -dependencies = [ - "num-conv", - "time-core", -] - -[[package]] -name = "tinystr" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" -dependencies = [ - "displaydoc", - "zerovec", -] - -[[package]] -name = "tinyvec" -version = "1.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd3ca314f692efd6c868f8408f53fe444634a845f96c028b97d35f6a1f79f0ee" - -[[package]] -name = "tokio" -version = "1.52.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" -dependencies = [ - "bytes", - "libc", - "mio", - "pin-project-lite", - "socket2", - "tokio-macros", - "windows-sys 0.61.2", -] - -[[package]] -name = "tokio-macros" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "tokio-rustls" -version = "0.26.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" -dependencies = [ - "rustls", - "tokio", -] - -[[package]] -name = "tokio-util" -version = "0.7.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "tower" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" -dependencies = [ - "futures-core", - "futures-util", - "pin-project-lite", - "sync_wrapper", - "tokio", - "tower-layer", - "tower-service", -] - -[[package]] -name = "tower-http" -version = "0.6.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" -dependencies = [ - "bitflags", - "bytes", - "futures-util", - "http", - "http-body", - "pin-project-lite", - "tower", - "tower-layer", - "tower-service", - "url", -] - -[[package]] -name = "tower-layer" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" - -[[package]] -name = "tower-service" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" - -[[package]] -name = "tracing" -version = "0.1.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" -dependencies = [ - "pin-project-lite", - "tracing-core", -] - -[[package]] -name = "tracing-core" -version = "0.1.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" -dependencies = [ - "once_cell", -] - -[[package]] -name = "try-lock" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" - -[[package]] -name = "unicode-ident" -version = "1.0.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" - -[[package]] -name = "unicode-xid" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" - -[[package]] -name = "untrusted" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" - -[[package]] -name = "url" -version = "2.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", - "serde", -] - -[[package]] -name = "utf8_iter" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" - -[[package]] -name = "walkdir" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" -dependencies = [ - "same-file", - "winapi-util", -] - -[[package]] -name = "want" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" -dependencies = [ - "try-lock", -] - -[[package]] -name = "wasi" -version = "0.11.1+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" - -[[package]] -name = "wasip2" -version = "1.0.4+wasi-0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487" -dependencies = [ - "wit-bindgen 0.57.1", -] - -[[package]] -name = "wasip3" -version = "0.4.0+wasi-0.3.0-rc-2026-01-06" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" -dependencies = [ - "wit-bindgen 0.51.0", -] - -[[package]] -name = "wasm-bindgen" -version = "0.2.125" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ddb3f79143bced6de84270411622a2699cee572fc0875aeaf1e7867cf9fca1a" -dependencies = [ - "cfg-if", - "once_cell", - "rustversion", - "wasm-bindgen-macro", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.75" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "503b14d284f2c8dac03b819967e155ea753f573586193b2b2c95990cb5d69280" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.125" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e21a184b13fb19e157296e2c46056aec9092264fab83e4ba59e68c61b323c3d" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.125" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fecefd9c35bd935a20fc3fc344b5f29138961e4f47fb03297d88f2587afb5ebd" -dependencies = [ - "bumpalo", - "proc-macro2", - "quote", - "syn 2.0.117", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.125" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23939e44bb9a5d7576fa2b563dc2e136628f1224e88a8deed09e04858b77871f" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "wasm-encoder" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" -dependencies = [ - "leb128fmt", - "wasmparser", -] - -[[package]] -name = "wasm-metadata" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" -dependencies = [ - "anyhow", - "indexmap", - "wasm-encoder", - "wasmparser", -] - -[[package]] -name = "wasmparser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" -dependencies = [ - "bitflags", - "hashbrown 0.15.5", - "indexmap", - "semver", -] - -[[package]] -name = "web-sys" -version = "0.3.102" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6430a72df5eb332242960fe84b3002a241163998241eb596d4f739b9757061d" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "web-time" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "webpki-root-certs" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b96554aa2acc8ccdb7e1c9a58a7a68dd5d13bccc69cd124cb09406db612a1c9b" -dependencies = [ - "rustls-pki-types", -] - -[[package]] -name = "winapi-util" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" -dependencies = [ - "windows-sys 0.61.2", -] - -[[package]] -name = "windows-link" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" - -[[package]] -name = "windows-registry" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" -dependencies = [ - "windows-link", - "windows-result", - "windows-strings", -] - -[[package]] -name = "windows-result" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-strings" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-sys" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-sys" -version = "0.61.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-targets" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" - -[[package]] -name = "wit-bindgen" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" -dependencies = [ - "wit-bindgen-rust-macro", -] - -[[package]] -name = "wit-bindgen" -version = "0.57.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" - -[[package]] -name = "wit-bindgen-core" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" -dependencies = [ - "anyhow", - "heck", - "wit-parser", -] - -[[package]] -name = "wit-bindgen-rust" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" -dependencies = [ - "anyhow", - "heck", - "indexmap", - "prettyplease", - "syn 2.0.117", - "wasm-metadata", - "wit-bindgen-core", - "wit-component", -] - -[[package]] -name = "wit-bindgen-rust-macro" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" -dependencies = [ - "anyhow", - "prettyplease", - "proc-macro2", - "quote", - "syn 2.0.117", - "wit-bindgen-core", - "wit-bindgen-rust", -] - -[[package]] -name = "wit-component" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" -dependencies = [ - "anyhow", - "bitflags", - "indexmap", - "log", - "serde", - "serde_derive", - "serde_json", - "wasm-encoder", - "wasm-metadata", - "wasmparser", - "wit-parser", -] - -[[package]] -name = "wit-parser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" -dependencies = [ - "anyhow", - "id-arena", - "indexmap", - "log", - "semver", - "serde", - "serde_derive", - "serde_json", - "unicode-xid", - "wasmparser", -] - -[[package]] -name = "writeable" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" - -[[package]] -name = "yoke" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "709fe23a0424b6a435d82152b1bd3fdfb0833487d5fa90d05d42762a9891fef5" -dependencies = [ - "stable_deref_trait", - "yoke-derive", - "zerofrom", -] - -[[package]] -name = "yoke-derive" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", - "synstructure", -] - -[[package]] -name = "zerofrom" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" -dependencies = [ - "zerofrom-derive", -] - -[[package]] -name = "zerofrom-derive" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", - "synstructure", -] - -[[package]] -name = "zeroize" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" - -[[package]] -name = "zerotrie" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" -dependencies = [ - "displaydoc", - "yoke", - "zerofrom", -] - -[[package]] -name = "zerovec" -version = "0.11.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" -dependencies = [ - "yoke", - "zerofrom", - "zerovec-derive", -] - -[[package]] -name = "zerovec-derive" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "zmij" -version = "1.0.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" From 8986d730ae8d02266946d9f72857528f281274fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Mon, 5 Oct 2026 10:48:50 +0100 Subject: [PATCH 46/83] Match upstream's Rust edition Build on edition 2021, as upstream does, instead of 2024. This brings the shared files back to upstream's text, which are the hunks most likely to conflict when merging upstream: - rustfmt's style edition follows the language edition, so imports are ordered and lines wrapped the way upstream does. - clippy::collapsible_if no longer suggests let-chains, which are 2024-only, so upstream's nested if-let stays as written. Let-chains now fail to compile, so they cannot drift back in. Edition 2021 defaults to resolver 2, so set resolver 3 explicitly to keep the resolver edition 2024 gave us. The edition applies crate-wide, so fork-owned code follows the same dialect. This is deliberate: one dialect everywhere beats rules that depend on a file's provenance. --- Cargo.toml | 3 ++- src/client.rs | 17 +++++++++-------- src/config.rs | 4 +--- src/dispatcher.rs | 4 ++-- src/lib.rs | 2 +- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7f31b2c..4789205 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,8 @@ name = "aptabase-rs" version = "0.3.0" authors = ["Eugรฉn Cowie"] -edition = "2024" +edition = "2021" +resolver = "3" description = "Rust SDK for Aptabase: Open Source, Privacy-First and Simple Analytics for Mobile, Desktop and Web Apps" homepage = "https://github.com/eugencowie/aptabase-rs" repository = "https://github.com/eugencowie/aptabase-rs" diff --git a/src/client.rs b/src/client.rs index 8325782..1ec47cc 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,11 +1,11 @@ use rand::RngExt; -use serde_json::{Value, json}; +use serde_json::{json, Value}; use std::time::{SystemTime, UNIX_EPOCH}; use std::{ sync::{Arc, Mutex as SyncMutex}, time::Duration, }; -use time::{OffsetDateTime, format_description::well_known::Rfc3339}; +use time::{format_description::well_known::Rfc3339, OffsetDateTime}; use crate::{ config::Config, @@ -105,12 +105,13 @@ impl AptabaseClient { return Ok(()); } - if let Some(props) = &props - && !matches!(props, Value::Object(_)) - { - return Err( - "props must be `None` or the `Object` variation of `serde_json::Value`".to_owned(), - ); + if let Some(props) = &props { + if !matches!(props, Value::Object(_)) { + return Err( + "props must be `None` or the `Object` variation of `serde_json::Value`" + .to_owned(), + ); + } } let ev = json!({ diff --git a/src/config.rs b/src/config.rs index a0b6c77..1e5c1a1 100644 --- a/src/config.rs +++ b/src/config.rs @@ -44,9 +44,7 @@ impl Config { if let Some(host) = opts.host { host } else { - debug!( - "Host parameter must be defined when using Self-Hosted App Key. Tracking will be disabled." - ); + debug!("Host parameter must be defined when using Self-Hosted App Key. Tracking will be disabled."); return Config::default(); } } diff --git a/src/dispatcher.rs b/src/dispatcher.rs index ff96417..9012314 100644 --- a/src/dispatcher.rs +++ b/src/dispatcher.rs @@ -7,10 +7,10 @@ use std::{ use log::{debug, trace}; use reqwest::{ - Url, header::{HeaderMap, HeaderValue}, + Url, }; -use serde_json::{Value, json}; +use serde_json::{json, Value}; use crate::{config::Config, sys::SystemProperties}; diff --git a/src/lib.rs b/src/lib.rs index 2a7c137..bea3c1c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,7 +5,7 @@ mod sys; use std::{panic::PanicHookInfo, sync::Arc, time::Duration}; -pub use client::{AptabaseClient, new_session_id}; +pub use client::{new_session_id, AptabaseClient}; use config::Config; use serde_json::json; From 2591068e1b37cfd5b40b81fc827b679a281e98ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Mon, 5 Oct 2026 10:48:51 +0100 Subject: [PATCH 47/83] Allow a clippy lint that restyles shared files clippy::to_string_in_format_args forced a rewrite of upstream text that carried no behavioural change. Allow it and restore upstream's phrasing, so the remaining diff in this file is behaviour only. --- Cargo.toml | 3 ++- src/dispatcher.rs | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4789205..ac44b87 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,4 +33,5 @@ rustls-tls = ["reqwest/rustls"] warnings = "deny" [lints.clippy] -all = "deny" +all = { level = "deny", priority = -1 } +to_string_in_format_args = "allow" # match upstream diff --git a/src/dispatcher.rs b/src/dispatcher.rs index 9012314..bc49e79 100644 --- a/src/dispatcher.rs +++ b/src/dispatcher.rs @@ -112,7 +112,7 @@ impl EventDispatcher { }, Err(err) => { failed_items.extend(events_to_send); - debug!("failed to track_event: {}", err); + debug!("failed to track_event: {}", err.to_string()); } } } From 6c4815492e7b5aabcc1be009febf15837683ca40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Mon, 5 Oct 2026 10:48:56 +0100 Subject: [PATCH 48/83] Declare a minimum supported Rust version of 1.88 time 0.3.46 and later require Rust 1.88, and every release before 0.3.47 is affected by RUSTSEC-2026-0009. A lower floor would mean holding time back on a version with a known advisory, so 1.88 is the lowest version worth supporting. Upstream's 1.81 is out of reach. Edition 2024 already required 1.85, so users on 1.85 to 1.87 can build 0.3.0 but will need 1.88 for the next release. Declaring it gives older toolchains a clear error instead of a build failure deep in a dependency, and resolver 3 now prefers dependency versions that support 1.88 when the lockfile is updated. The current lockfile already does, so it is unchanged. --- Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.toml b/Cargo.toml index ac44b87..d9015f9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,7 @@ name = "aptabase-rs" version = "0.3.0" authors = ["Eugรฉn Cowie"] edition = "2021" +rust-version = "1.88" resolver = "3" description = "Rust SDK for Aptabase: Open Source, Privacy-First and Simple Analytics for Mobile, Desktop and Web Apps" homepage = "https://github.com/eugencowie/aptabase-rs" From 3091bca7d53aa8f1d712b8c0e1c819832c8414b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Mon, 5 Oct 2026 10:49:44 +0100 Subject: [PATCH 49/83] Run CI and local checks on the MSRV toolchain Pin Rust to 1.88 in mise.toml and install it through mise-action in both workflows, so CI, releases and local checks all use the toolchain the crate declares as its minimum. This replaces dtolnay/rust-toolchain on stable: one toolchain in one file, and new lints arrive only when the MSRV is raised deliberately rather than whenever stable moves. The check job now runs `mise run check`, so CI and local checks share one definition. That task first fails if rustc does not match rust-version, so mise.toml and Cargo.toml cannot drift apart. The trade-off is that CI no longer tests on stable, so breakage that only shows up on newer compilers would surface through users rather than CI. Clippy 1.88 flags uninlined_format_args in upstream's text, which later releases no longer do. Allow it, as with to_string_in_format_args, so the shared files keep upstream's phrasing. --- .github/workflows/ci.yml | 16 ++++++++++------ .github/workflows/release-plz.yml | 4 +++- Cargo.toml | 1 + mise.lock | 5 ++++- mise.toml | 4 +++- 5 files changed, 21 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c272d06..5199c14 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,11 +11,11 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v7 - - uses: dtolnay/rust-toolchain@stable + - uses: jdx/mise-action@v5 + with: + install_args: rust - uses: Swatinem/rust-cache@v2 - - run: cargo fmt --check - - run: cargo clippy --all-targets - - run: cargo test + - run: mise run check features: name: Clippy (${{ matrix.name }}) @@ -31,7 +31,9 @@ jobs: flags: --no-default-features steps: - uses: actions/checkout@v7 - - uses: dtolnay/rust-toolchain@stable + - uses: jdx/mise-action@v5 + with: + install_args: rust - uses: Swatinem/rust-cache@v2 - run: cargo clippy --all-targets ${{ matrix.flags }} @@ -43,7 +45,9 @@ jobs: working-directory: examples/helloworld steps: - uses: actions/checkout@v7 - - uses: dtolnay/rust-toolchain@stable + - uses: jdx/mise-action@v5 + with: + install_args: rust - uses: Swatinem/rust-cache@v2 with: workspaces: examples/helloworld diff --git a/.github/workflows/release-plz.yml b/.github/workflows/release-plz.yml index c5f4d5b..9061718 100644 --- a/.github/workflows/release-plz.yml +++ b/.github/workflows/release-plz.yml @@ -22,7 +22,9 @@ jobs: token: ${{ secrets.RELEASE_PLZ_TOKEN }} - &install-rust name: Install Rust toolchain - uses: dtolnay/rust-toolchain@stable + uses: jdx/mise-action@v5 + with: + install_args: rust - name: Run release-plz uses: release-plz/action@v0.5 with: diff --git a/Cargo.toml b/Cargo.toml index d9015f9..dd40dde 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,3 +36,4 @@ warnings = "deny" [lints.clippy] all = { level = "deny", priority = -1 } to_string_in_format_args = "allow" # match upstream +uninlined_format_args = "allow" # match upstream diff --git a/mise.lock b/mise.lock index 96b5f3b..f32ff8c 100644 --- a/mise.lock +++ b/mise.lock @@ -166,5 +166,8 @@ url = "https://github.com/release-plz/release-plz/releases/download/release-plz- url_api = "https://api.github.com/repos/release-plz/release-plz/releases/assets/476459873" [[tools.rust]] -version = "stable" +version = "1.88.0" backend = "core:rust" + +[tools.rust.options] +components = "clippy,rustfmt" diff --git a/mise.toml b/mise.toml index 30c442d..52f1549 100644 --- a/mise.toml +++ b/mise.toml @@ -3,7 +3,7 @@ actionlint = "latest" gh = "latest" git-cliff = "latest" release-plz = "latest" -rust = "stable" +rust = { version = "1.88", components = ["clippy", "rustfmt"] } # the MSRV [tasks.test] description = "Run tests" @@ -20,6 +20,8 @@ run = "cargo clippy --all-targets --fix --allow-dirty" [tasks.check] description = "Check formatting/linting/tests" run = [ + # Fail if the pinned toolchain has drifted from rust-version in Cargo.toml + 'grep -q "^rust-version = \"$(rustc -V | cut -d" " -f2 | cut -d. -f1,2)\"" Cargo.toml', "cargo fmt --check", "cargo clippy --all-targets", "cargo test" From 18382d26184cb2229cce9bb2e39e66efea93d670 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Mon, 5 Oct 2026 11:59:58 +0100 Subject: [PATCH 50/83] Use flow-style mappings for single-key action inputs in CI - Collapse one-line `with:` blocks on mise-action and rust-cache steps into inline `{ key: value }` form --- .github/workflows/ci.yml | 12 ++++-------- .github/workflows/release-plz.yml | 3 +-- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5199c14..32cb65c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,8 +12,7 @@ jobs: steps: - uses: actions/checkout@v7 - uses: jdx/mise-action@v5 - with: - install_args: rust + with: { install_args: rust } - uses: Swatinem/rust-cache@v2 - run: mise run check @@ -32,8 +31,7 @@ jobs: steps: - uses: actions/checkout@v7 - uses: jdx/mise-action@v5 - with: - install_args: rust + with: { install_args: rust } - uses: Swatinem/rust-cache@v2 - run: cargo clippy --all-targets ${{ matrix.flags }} @@ -46,10 +44,8 @@ jobs: steps: - uses: actions/checkout@v7 - uses: jdx/mise-action@v5 - with: - install_args: rust + with: { install_args: rust } - uses: Swatinem/rust-cache@v2 - with: - workspaces: examples/helloworld + with: { workspaces: examples/helloworld } - run: cp .env.example .env - run: cargo clippy --all-targets diff --git a/.github/workflows/release-plz.yml b/.github/workflows/release-plz.yml index 9061718..a82cfcb 100644 --- a/.github/workflows/release-plz.yml +++ b/.github/workflows/release-plz.yml @@ -23,8 +23,7 @@ jobs: - &install-rust name: Install Rust toolchain uses: jdx/mise-action@v5 - with: - install_args: rust + with: { install_args: rust } - name: Run release-plz uses: release-plz/action@v0.5 with: From accbf64e2d0bef1ac6f6aced2b8919de4702b6ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Mon, 5 Oct 2026 12:09:57 +0100 Subject: [PATCH 51/83] Reorder Cargo.toml package fields and drop resolver to match upstream - Move license and description right after version - Remove explicit resolver = "3" (edition 2021 defaults to resolver 2) --- Cargo.toml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index dd40dde..baffff6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,14 +1,13 @@ [package] name = "aptabase-rs" version = "0.3.0" +license = "MIT" +description = "Rust SDK for Aptabase: Open Source, Privacy-First and Simple Analytics for Mobile, Desktop and Web Apps" authors = ["Eugรฉn Cowie"] edition = "2021" rust-version = "1.88" -resolver = "3" -description = "Rust SDK for Aptabase: Open Source, Privacy-First and Simple Analytics for Mobile, Desktop and Web Apps" homepage = "https://github.com/eugencowie/aptabase-rs" repository = "https://github.com/eugencowie/aptabase-rs" -license = "MIT" keywords = ["analytics", "aptabase", "telemetry"] categories = ["api-bindings"] From bd328ff68720ca455b2902e0c3e000414299b564 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Wed, 29 Jul 2026 18:29:20 +0100 Subject: [PATCH 52/83] Move fork-only builder methods to a separate module - Shrink `lib.rs`'s diff against upstream - Keep the builder methods and their tests in `src/builder_ext.rs` --- src/builder_ext.rs | 63 ++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 57 +---------------------------------------- 2 files changed, 64 insertions(+), 56 deletions(-) create mode 100644 src/builder_ext.rs diff --git a/src/builder_ext.rs b/src/builder_ext.rs new file mode 100644 index 0000000..8fbbc05 --- /dev/null +++ b/src/builder_ext.rs @@ -0,0 +1,63 @@ +// Fork-only additions to `Builder`, kept separate to minimise the diff against +// upstream's `lib.rs`. + +use serde_json::json; + +use crate::Builder; + +impl Builder { + /// Sets the initial session ID. + pub fn with_session_id(mut self, session_id: impl Into) -> Self { + self.session_id = Some(session_id.into()); + self + } + + /// Sets whether polling should be enabled. + pub fn with_polling(mut self, enable: bool) -> Self { + self.enable_polling = enable; + self + } + + /// Enables the default panic hook. + pub fn with_default_panic_hook(self) -> Self { + self.with_panic_hook(Box::new(|client, info, message| { + let location = info + .location() + .map(|loc| format!("{}:{}:{}", loc.file(), loc.line(), loc.column())) + .unwrap_or_default(); + let _ = client.track_event( + "panic", + Some(json!({ + "info": format!("{} ({})", message, location), + })), + ); + })) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn builder_uses_supplied_session_id() { + // Act + let client = Builder::new("A-DEV-123", "test") + .with_session_id("persisted-session") + .build(); + + // Assert + assert_eq!(client.eval_session_id(), "persisted-session"); + } + + #[test] + fn builder_treats_empty_session_id_as_absent() { + // Act + let client = Builder::new("A-DEV-123", "test") + .with_session_id("") + .build(); + + // Assert + assert!(!client.eval_session_id().is_empty()); + } +} diff --git a/src/lib.rs b/src/lib.rs index bea3c1c..3b628b2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,4 @@ +mod builder_ext; mod client; mod config; mod dispatcher; @@ -7,7 +8,6 @@ use std::{panic::PanicHookInfo, sync::Arc, time::Duration}; pub use client::{new_session_id, AptabaseClient}; use config::Config; -use serde_json::json; #[derive(Default, Debug, Clone)] pub struct InitOptions { @@ -58,40 +58,12 @@ impl Builder { self } - /// Sets the initial session ID. - pub fn with_session_id(mut self, session_id: impl Into) -> Self { - self.session_id = Some(session_id.into()); - self - } - - /// Sets whether polling should be enabled. - pub fn with_polling(mut self, enable: bool) -> Self { - self.enable_polling = enable; - self - } - /// Sets a custom panic hook. pub fn with_panic_hook(mut self, hook: PanicHook) -> Self { self.panic_hook = Some(hook); self } - /// Enables the default panic hook. - pub fn with_default_panic_hook(self) -> Self { - self.with_panic_hook(Box::new(|client, info, message| { - let location = info - .location() - .map(|loc| format!("{}:{}:{}", loc.file(), loc.line(), loc.column())) - .unwrap_or_default(); - let _ = client.track_event( - "panic", - Some(json!({ - "info": format!("{} ({})", message, location), - })), - ); - })) - } - /// Builds and initializes the client pub fn build(self) -> Arc { let cfg = Config::new(self.app_key, self.session_id, self.options); @@ -115,30 +87,3 @@ impl Builder { client } } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn builder_uses_supplied_session_id() { - // Act - let client = Builder::new("A-DEV-123", "test") - .with_session_id("persisted-session") - .build(); - - // Assert - assert_eq!(client.eval_session_id(), "persisted-session"); - } - - #[test] - fn builder_treats_empty_session_id_as_absent() { - // Act - let client = Builder::new("A-DEV-123", "test") - .with_session_id("") - .build(); - - // Assert - assert!(!client.eval_session_id().is_empty()); - } -} From 574802d2de269d9a2c31f56d1e9c666b401c8f3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Wed, 29 Jul 2026 18:47:25 +0100 Subject: [PATCH 53/83] Concentrate custom session ID behind a child-module seam Return `config.rs` to upstream byte-for-byte and cut the session-related hunks in `client.rs` down to the module declaration. The feature was threaded through `Config` purely to carry a value from the builder to the client constructor, which forced a field, a `Config::new` parameter, a struct literal entry, and a `Default` entry into a shared file, plus a changed `TrackingSession::new` and two call sites in another. `src/client/session.rs` is a child module of `client`, so it can reach the private session state directly. It owns the client side of the feature: the public generator, the seeding seam, and the tests. Behaviour is unchanged; the "no session ID supplied" case gains the test it lacked. `Builder::build` now seeds after construction and before the client is shared, so the generated ID it replaces can never reach an event. `AptabaseClient::new` stays `pub(crate)` rather than matching upstream's `pub`. Upstream never exports `AptabaseClient`, but this crate does, so `pub` would let callers build a client that bypasses `Builder`. --- src/builder_ext.rs | 27 ---------------- src/client.rs | 28 +++++------------ src/client/session.rs | 71 +++++++++++++++++++++++++++++++++++++++++++ src/config.rs | 5 +-- src/lib.rs | 5 +-- 5 files changed, 82 insertions(+), 54 deletions(-) create mode 100644 src/client/session.rs diff --git a/src/builder_ext.rs b/src/builder_ext.rs index 8fbbc05..0acc377 100644 --- a/src/builder_ext.rs +++ b/src/builder_ext.rs @@ -34,30 +34,3 @@ impl Builder { })) } } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn builder_uses_supplied_session_id() { - // Act - let client = Builder::new("A-DEV-123", "test") - .with_session_id("persisted-session") - .build(); - - // Assert - assert_eq!(client.eval_session_id(), "persisted-session"); - } - - #[test] - fn builder_treats_empty_session_id_as_absent() { - // Act - let client = Builder::new("A-DEV-123", "test") - .with_session_id("") - .build(); - - // Assert - assert!(!client.eval_session_id().is_empty()); - } -} diff --git a/src/client.rs b/src/client.rs index 1ec47cc..cfee221 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,3 +1,5 @@ +pub(crate) mod session; + use rand::RngExt; use serde_json::{json, Value}; use std::time::{SystemTime, UNIX_EPOCH}; @@ -15,7 +17,7 @@ use crate::{ static SESSION_TIMEOUT: Duration = Duration::from_secs(4 * 60 * 60); -pub fn new_session_id() -> String { +fn new_session_id() -> String { let epoch_in_seconds = SystemTime::now() .duration_since(UNIX_EPOCH) .expect("time went backwards") @@ -37,11 +39,9 @@ pub struct TrackingSession { } impl TrackingSession { - fn new(id: Option) -> Self { + fn new() -> Self { Self { - id: id - .filter(|id| !id.is_empty()) - .unwrap_or_else(new_session_id), + id: new_session_id(), last_touch_ts: OffsetDateTime::now_utc(), } } @@ -67,7 +67,7 @@ impl AptabaseClient { Self { is_enabled, dispatcher, - session: SyncMutex::new(TrackingSession::new(config.session_id.clone())), + session: SyncMutex::new(TrackingSession::new()), app_version, sys_info, } @@ -91,7 +91,7 @@ impl AptabaseClient { let now = OffsetDateTime::now_utc(); if (now - session.last_touch_ts) > SESSION_TIMEOUT { - *session = TrackingSession::new(None); + *session = TrackingSession::new(); } else { session.last_touch_ts = now; } @@ -146,17 +146,3 @@ impl AptabaseClient { }); } } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn new_session_id_is_not_empty() { - // Act - let session_id = new_session_id(); - - // Assert - assert!(!session_id.is_empty()); - } -} diff --git a/src/client/session.rs b/src/client/session.rs new file mode 100644 index 0000000..f2f2a0c --- /dev/null +++ b/src/client/session.rs @@ -0,0 +1,71 @@ +// Fork-only session ID handling, kept separate to minimise the diff against +// upstream's `client.rs`. This is a child module of `client`, so it can reach +// the private session state that upstream's `AptabaseClient` keeps to itself. + +use super::AptabaseClient; + +/// Generates a new session ID. +pub fn new_session_id() -> String { + super::new_session_id() +} + +impl AptabaseClient { + /// Replaces the generated session ID with a caller-supplied one, before the + /// client is shared. An absent or empty ID leaves the generated one in + /// place. Timeout rotation is unaffected: the SDK does not persist session + /// IDs, so a supplied ID starts its `SESSION_TIMEOUT` window at construction. + pub(crate) fn seed_session_id(&self, id: Option) { + let Some(id) = id.filter(|id| !id.is_empty()) else { + return; + }; + + let mut session = self.session.lock().expect("could not lock session"); + session.id = id; + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::Builder; + + #[test] + fn new_session_id_is_not_empty() { + // Act + let session_id = new_session_id(); + + // Assert + assert!(!session_id.is_empty()); + } + + #[test] + fn builder_uses_supplied_session_id() { + // Act + let client = Builder::new("A-DEV-123", "test") + .with_session_id("persisted-session") + .build(); + + // Assert + assert_eq!(client.eval_session_id(), "persisted-session"); + } + + #[test] + fn builder_generates_session_id_when_none_supplied() { + // Act + let client = Builder::new("A-DEV-123", "test").build(); + + // Assert + assert!(!client.eval_session_id().is_empty()); + } + + #[test] + fn builder_treats_empty_session_id_as_absent() { + // Act + let client = Builder::new("A-DEV-123", "test") + .with_session_id("") + .build(); + + // Assert + assert!(!client.eval_session_id().is_empty()); + } +} diff --git a/src/config.rs b/src/config.rs index 1e5c1a1..1985f11 100644 --- a/src/config.rs +++ b/src/config.rs @@ -8,7 +8,6 @@ use crate::InitOptions; #[derive(Debug, Clone)] pub struct Config { pub app_key: String, - pub session_id: Option, pub ingest_api_url: Url, pub flush_interval: Duration, } @@ -26,7 +25,7 @@ static DEFAULT_FLUSH_INTERVAL: Duration = Duration::from_secs(2); const VALID_REGIONS: &[&str] = &["US", "EU", "DEV", "SH"]; impl Config { - pub fn new(app_key: String, session_id: Option, opts: InitOptions) -> Self { + pub fn new(app_key: String, opts: InitOptions) -> Self { let parts = app_key.split("-").collect::>(); if parts.len() != 3 || !VALID_REGIONS.contains(&parts[1]) { debug!( @@ -53,7 +52,6 @@ impl Config { Self { app_key, - session_id, ingest_api_url: format!("{}/api/v0/events", base_url).parse().unwrap(), flush_interval: opts.flush_interval.unwrap_or(DEFAULT_FLUSH_INTERVAL), } @@ -64,7 +62,6 @@ impl Default for Config { fn default() -> Self { Self { app_key: String::new(), - session_id: None, ingest_api_url: Url::parse(LOCAL).unwrap(), flush_interval: DEFAULT_FLUSH_INTERVAL, } diff --git a/src/lib.rs b/src/lib.rs index 3b628b2..8e34d33 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,7 +6,7 @@ mod sys; use std::{panic::PanicHookInfo, sync::Arc, time::Duration}; -pub use client::{new_session_id, AptabaseClient}; +pub use client::{session::new_session_id, AptabaseClient}; use config::Config; #[derive(Default, Debug, Clone)] @@ -66,8 +66,9 @@ impl Builder { /// Builds and initializes the client pub fn build(self) -> Arc { - let cfg = Config::new(self.app_key, self.session_id, self.options); + let cfg = Config::new(self.app_key, self.options); let client = Arc::new(AptabaseClient::new(&cfg, self.app_version)); + client.seed_session_id(self.session_id); if self.enable_polling { client.start_polling(cfg.flush_interval); From 7d1568ff0cefbe207ce53fa45458dac606daee59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Mon, 5 Oct 2026 18:40:07 +0100 Subject: [PATCH 54/83] Stop mise run installing unused tools in CI mise-action's install_args only limits its own install step. mise run then auto-installed the remaining four mise.toml tools before running the check task, which only needs rust. --- .github/workflows/ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 32cb65c..1a2fd0e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,6 +9,9 @@ jobs: check: name: Build & Check runs-on: ubuntu-latest + env: + # Stop `mise run` installing tools beyond what mise-action installed + MISE_TASK_RUN_AUTO_INSTALL: false steps: - uses: actions/checkout@v7 - uses: jdx/mise-action@v5 From 9759ab7fdf52c6c7d5202ed6c73f7f82075a6349 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Tue, 6 Oct 2026 12:07:38 +0100 Subject: [PATCH 55/83] Pin release-plz and git-cliff to mise.lock versions in CI The release-plz workflow ran tool versions mise.lock never saw: taiki-e installed the latest release-plz for `set-version`, release-plz/action binstalled its own default, and git-cliff-action downloaded its default git-cliff while the local changelog task uses the locked one. - both jobs: the shared mise-action step (now &install-tools, id mise) installs release-plz and git-cliff alongside Rust. mise-action only sets per-tool version outputs for installed tools - release-plz/action and git-cliff-action get their versions from those outputs - release-plz-pr: `set-version` runs mise's locked copy, so the taiki-e step is gone The actions still install their own copies, now the same versions: release-plz/action always reinstalls with `cargo-binstall --force`, and git-cliff-action always downloads its own binary. Ported from eugencowie/git-vmr#126. --- .github/workflows/release-plz.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/release-plz.yml b/.github/workflows/release-plz.yml index a82cfcb..fff2060 100644 --- a/.github/workflows/release-plz.yml +++ b/.github/workflows/release-plz.yml @@ -20,13 +20,15 @@ jobs: fetch-depth: 0 persist-credentials: true token: ${{ secrets.RELEASE_PLZ_TOKEN }} - - &install-rust - name: Install Rust toolchain + - &install-tools + name: Install tools + id: mise uses: jdx/mise-action@v5 - with: { install_args: rust } + with: { install_args: rust release-plz git-cliff } - name: Run release-plz uses: release-plz/action@v0.5 with: + version: ${{ steps.mise.outputs.release-plz }} command: release env: GITHUB_TOKEN: ${{ secrets.RELEASE_PLZ_TOKEN }} @@ -44,7 +46,7 @@ jobs: cancel-in-progress: false steps: - *checkout - - *install-rust + - *install-tools # Keep in sync with the changelog task in mise.toml. - name: Skip commits off the first-parent chain shell: bash @@ -53,14 +55,11 @@ jobs: id: git-cliff uses: orhun/git-cliff-action@v4 with: + version: v${{ steps.mise.outputs.git-cliff }} args: --bump env: OUTPUT: CHANGELOG.md GITHUB_REPO: ${{ github.repository }} - - name: Install release-plz - uses: taiki-e/install-action@v2 - with: - tool: release-plz - name: Set release version env: RELEASE_VERSION: ${{ steps.git-cliff.outputs.version }} @@ -68,6 +67,7 @@ jobs: - name: Run release-plz uses: release-plz/action@v0.5 with: + version: ${{ steps.mise.outputs.release-plz }} command: release-pr env: GITHUB_TOKEN: ${{ secrets.RELEASE_PLZ_TOKEN }} From 9599615b0a9e9b3effbf6a9e1e0b1c1abec03a97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Tue, 6 Oct 2026 12:14:42 +0100 Subject: [PATCH 56/83] Use block style for mise-action inputs in release-plz workflow --- .github/workflows/release-plz.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release-plz.yml b/.github/workflows/release-plz.yml index fff2060..a28741c 100644 --- a/.github/workflows/release-plz.yml +++ b/.github/workflows/release-plz.yml @@ -24,7 +24,8 @@ jobs: name: Install tools id: mise uses: jdx/mise-action@v5 - with: { install_args: rust release-plz git-cliff } + with: + install_args: rust release-plz git-cliff - name: Run release-plz uses: release-plz/action@v0.5 with: From a42256e4b6ec8fccc96b8fcabde37507c69628e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Fri, 9 Oct 2026 00:42:16 +0100 Subject: [PATCH 57/83] Declare the tokio features the crate uses `AptabaseClient::start_polling` calls `tokio::spawn` and `tokio::time::sleep`, which need tokio's `rt` and `time` features. The crate declared tokio with no features and only compiled because hyper-util and reqwest enable both. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index baffff6..c975db7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ keywords = ["analytics", "aptabase", "telemetry"] categories = ["api-bindings"] [dependencies] -tokio = "1.43.0" +tokio = { version = "1.43.0", features = ["rt", "time"] } futures = "0.3.31" serde = "1.0.217" serde_json = "1.0.138" From 3224b16a443abd5efc062c3566daf2f9f5c48498 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Fri, 9 Oct 2026 00:42:31 +0100 Subject: [PATCH 58/83] Add a glossary of the SDK's domain terms Names the concepts the README already uses (App Key, Region, Event, Session, Flush, Batch, Polling, Delivery) so code, tests and reviews share one vocabulary. The README said the session ID "rotates every 4 hours". It rotates after 4 hours of inactivity, so the README now says that, matching the glossary's Session. --- GLOSSARY.md | 41 +++++++++++++++++++++++++++++++++++++++++ README.md | 2 +- 2 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 GLOSSARY.md diff --git a/GLOSSARY.md b/GLOSSARY.md new file mode 100644 index 0000000..babe8c0 --- /dev/null +++ b/GLOSSARY.md @@ -0,0 +1,41 @@ +# Rust SDK for Aptabase + +A client that records analytics events in a Rust application and sends them to an Aptabase server. + +## Language + +### Core concepts + +**App Key**: +The identifier Aptabase issues to an application, which names the region its events go to. An invalid app key disables tracking. +_Avoid_: API key, token + +**Region**: +The Aptabase server an app key belongs to: `US` or `EU` cloud, `DEV` for a local server, or `SH` for a self-hosted server at a host the application supplies. +_Avoid_: Environment, server + +**Event**: +A single named occurrence in the application, with optional custom properties, enriched by the SDK with the session, app version and system details. +_Avoid_: Message, hit + +**Session**: +A run of activity that groups events, identified by a session ID. It ends after 4 hours without events, and on application restart unless the application supplies a persisted session ID. +_Avoid_: Visit + +### Event delivery + +**Flush**: +Sending every event not yet delivered to the server. +_Avoid_: Sync + +**Batch**: +The events sent to the server together in one request. +_Avoid_: Chunk, page + +**Polling**: +Flushing automatically at a fixed interval, as opt-in behaviour for long-running applications. +_Avoid_: Auto-flush, background sync + +**Delivery**: +How a flush gets events to the server: how events are grouped into batches, and which failed batches are kept for the next flush and which are dropped. +_Avoid_: Dispatch, upload diff --git a/README.md b/README.md index 5f721d3..f156929 100644 --- a/README.md +++ b/README.md @@ -91,7 +91,7 @@ async fn main() -> Result<(), Box> { ## Persisting sessions -By default, the SDK generates an in-memory session ID which rotates every 4 hours or whenever the app restarts. Applications which start and exit frequently may want to persist the session ID to disk so it can be reused across restarts. +By default, the SDK generates an in-memory session ID which rotates after 4 hours of inactivity or whenever the app restarts. Applications which start and exit frequently may want to persist the session ID to disk so it can be reused across restarts. To implement persistent sessions, generate a new session ID with `aptabase_rs::new_session_id()` and persist it to your application's storage (file, database, etc.). This can be loaded at startup and passed to the builder with `Builder::with_session_id()`. When you do this, your application is responsible for rotating the persisted session ID; if it keeps reusing the same stored ID, those runs will be grouped into the same session. From 94effdc76c2b91b0878fd2fe3958177cb6c012a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Fri, 9 Oct 2026 00:45:10 +0100 Subject: [PATCH 59/83] Test delivery against a local stand-in server The README's delivery behaviour had no tests. tests/delivery.rs drives the public interface (`Builder`, `track_event`, `flush`) with an `SH` app key pointed at a wiremock server, so no shared file changes. - batches of 25 - 5xx responses and transport failures keep events for the next Flush - 4xx responses drop them - wire format: headers, exact event and systemProps keys - an invalid app key sends nothing, even with a host - Polling flushes without a manual flush Event order after a retry is deliberately not asserted: the README doesn't promise one. The panic hook is out of scope, since it installs process-wide state. Each test was checked by mutating the code it covers and watching it fail. --- Cargo.lock | 131 +++++++++++++++++++++ Cargo.toml | 5 + tests/delivery.rs | 281 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 417 insertions(+) create mode 100644 tests/delivery.rs diff --git a/Cargo.lock b/Cargo.lock index 9637fc0..e80f1e0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,15 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "aho-corasick" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c982642fa9e8606056828ee9a8505737230110bb1099153c79efe865c59d12ba" +dependencies = [ + "memchr", +] + [[package]] name = "android_system_properties" version = "0.1.5" @@ -25,6 +34,17 @@ dependencies = [ "sys-locale", "time", "tokio", + "wiremock", +] + +[[package]] +name = "assert-json-diff" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47e4f2b81832e72834d7518d8487a0396a28cc408186a2e8854c0f98011faf12" +dependencies = [ + "serde", + "serde_json", ] [[package]] @@ -190,6 +210,24 @@ dependencies = [ "libc", ] +[[package]] +name = "deadpool" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0be2b1d1d6ec8d846f05e137292d0b89133caf95ef33695424c09568bdd39b1b" +dependencies = [ + "deadpool-runtime", + "lazy_static", + "num_cpus", + "tokio", +] + +[[package]] +name = "deadpool-runtime" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "092966b41edc516079bdf31ec78a2e0588d1d0c08f78b91d8307215928642b2b" + [[package]] name = "deranged" version = "0.5.8" @@ -441,6 +479,12 @@ version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" +[[package]] +name = "hermit-abi" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e17592d60ebacc7d5e169f4663c5f84f9161cc90328abcfe8456f41e4dfcb284" + [[package]] name = "http" version = "1.4.2" @@ -480,6 +524,12 @@ version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + [[package]] name = "hyper" version = "1.11.0" @@ -494,6 +544,7 @@ dependencies = [ "http", "http-body", "httparse", + "httpdate", "itoa", "pin-project-lite", "smallvec", @@ -752,6 +803,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "lazy_static" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20870f649af7073d53e38067b2a84312175d56ea15217e1b15bc83506ec50afb" + [[package]] name = "libc" version = "0.2.189" @@ -846,6 +903,16 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" +[[package]] +name = "num_cpus" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" +dependencies = [ + "hermit-abi", + "libc", +] + [[package]] name = "objc2" version = "0.6.4" @@ -1210,6 +1277,35 @@ dependencies = [ "rand_core", ] +[[package]] +name = "regex" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" + [[package]] name = "reqwest" version = "0.13.5" @@ -1690,9 +1786,21 @@ dependencies = [ "mio", "pin-project-lite", "socket2", + "tokio-macros", "windows-sys 0.61.2", ] +[[package]] +name = "tokio-macros" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78773a2a397f451582ce068015985c33193cf6dea8b74d2a639fe457b2f07b0e" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.3", +] + [[package]] name = "tokio-native-tls" version = "0.3.1" @@ -2068,6 +2176,29 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "wiremock" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08db1edfb05d9b3c1542e521aea074442088292f00b5f28e435c714a98f85031" +dependencies = [ + "assert-json-diff", + "base64 0.22.1", + "deadpool", + "futures", + "http", + "http-body-util", + "hyper", + "hyper-util", + "log", + "once_cell", + "regex", + "serde", + "serde_json", + "tokio", + "url", +] + [[package]] name = "writeable" version = "0.6.3" diff --git a/Cargo.toml b/Cargo.toml index c975db7..254c7bb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,11 @@ rand = "0.10.0" log = "0.4.25" sys-locale = "0.3.2" +[dev-dependencies] +tokio = { version = "1.43.0", features = ["macros", "rt-multi-thread"] } +time = { version = "0.3.37", features = ["parsing"] } +wiremock = "0.6.5" + [features] default = ["default-tls"] default-tls = ["reqwest/default-tls"] diff --git a/tests/delivery.rs b/tests/delivery.rs new file mode 100644 index 0000000..4539fa8 --- /dev/null +++ b/tests/delivery.rs @@ -0,0 +1,281 @@ +// Delivery behaviour, exercised through the public interface against a local +// stand-in for the Aptabase server. An `SH` App Key sends to whichever host +// the test supplies. + +use std::{net::TcpListener, sync::Arc, time::Duration}; + +use aptabase_rs::{AptabaseClient, Builder, InitOptions}; +use serde_json::{json, Value}; +use time::{format_description::well_known::Rfc3339, OffsetDateTime}; +use wiremock::{ + matchers::{method, path}, + Mock, MockServer, Request, ResponseTemplate, +}; + +const SH_APP_KEY: &str = "A-SH-1234567890"; +const APP_VERSION: &str = "1.2.3"; + +fn client_for(host: &str) -> Arc { + Builder::new(SH_APP_KEY, APP_VERSION) + .with_options(InitOptions { + host: Some(host.to_owned()), + flush_interval: None, + }) + .build() +} + +fn events_endpoint(status: u16) -> Mock { + Mock::given(method("POST")) + .and(path("/api/v0/events")) + .respond_with(ResponseTemplate::new(status)) +} + +fn batches(requests: &[Request]) -> Vec> { + requests + .iter() + .map(|request| request.body_json().unwrap()) + .collect() +} + +fn batch_sizes(requests: &[Request]) -> Vec { + batches(requests).iter().map(Vec::len).collect() +} + +fn event_names(requests: &[Request]) -> Vec { + let mut names: Vec = batches(requests) + .into_iter() + .flatten() + .map(|event| event["eventName"].as_str().unwrap().to_owned()) + .collect(); + names.sort(); + names +} + +fn sorted_keys(value: &Value) -> Vec<&str> { + let mut keys: Vec<&str> = value + .as_object() + .unwrap() + .keys() + .map(String::as_str) + .collect(); + keys.sort(); + keys +} + +#[tokio::test] +async fn flush_sends_events_in_batches_of_25() { + // Arrange + let server = MockServer::start().await; + let delivered = events_endpoint(200).mount_as_scoped(&server).await; + let client = client_for(&server.uri()); + for i in 0..60 { + client.track_event(&format!("event_{i}"), None).unwrap(); + } + + // Act + client.flush().await; + + // Assert + assert_eq!( + batch_sizes(&delivered.received_requests().await), + [25, 25, 10] + ); +} + +#[tokio::test] +async fn server_error_keeps_events_for_the_next_flush() { + // Arrange + let server = MockServer::start().await; + let failed = events_endpoint(503) + .up_to_n_times(1) + .mount_as_scoped(&server) + .await; + let delivered = events_endpoint(200).mount_as_scoped(&server).await; + let client = client_for(&server.uri()); + client.track_event("app_started", None).unwrap(); + client.track_event("app_exited", None).unwrap(); + + // Act + client.flush().await; + client.flush().await; + + // Assert + assert_eq!( + event_names(&failed.received_requests().await), + ["app_exited", "app_started"] + ); + assert_eq!( + event_names(&delivered.received_requests().await), + ["app_exited", "app_started"] + ); +} + +#[tokio::test] +async fn transport_failure_keeps_events_for_the_next_flush() { + // Arrange + let addr = TcpListener::bind("127.0.0.1:0") + .unwrap() + .local_addr() + .unwrap(); + let client = client_for(&format!("http://{addr}")); + client.track_event("app_started", None).unwrap(); + + // Act + // Nothing listens on the port during the first flush + client.flush().await; + let server = MockServer::builder() + .listener(TcpListener::bind(addr).unwrap()) + .start() + .await; + let delivered = events_endpoint(200).mount_as_scoped(&server).await; + client.flush().await; + + // Assert + assert_eq!( + event_names(&delivered.received_requests().await), + ["app_started"] + ); +} + +#[tokio::test] +async fn client_error_drops_events() { + // Arrange + let server = MockServer::start().await; + let rejected = events_endpoint(400) + .up_to_n_times(1) + .mount_as_scoped(&server) + .await; + let delivered = events_endpoint(200).mount_as_scoped(&server).await; + let client = client_for(&server.uri()); + client.track_event("app_started", None).unwrap(); + + // Act + client.flush().await; + + // Assert + assert_eq!( + event_names(&rejected.received_requests().await), + ["app_started"] + ); + + // Act + client.flush().await; + + // Assert + assert_eq!( + event_names(&delivered.received_requests().await), + Vec::::new() + ); +} + +#[tokio::test] +async fn events_are_sent_in_aptabase_format() { + // Arrange + let server = MockServer::start().await; + let delivered = events_endpoint(200).mount_as_scoped(&server).await; + let client = client_for(&server.uri()); + let props = json!({ "plan": "pro", "seats": 3 }); + client + .track_event("app_started", Some(props.clone())) + .unwrap(); + + // Act + client.flush().await; + + // Assert + let requests = delivered.received_requests().await; + let request = &requests[0]; + assert_eq!(request.headers["App-Key"], SH_APP_KEY); + assert_eq!(request.headers["Content-Type"], "application/json"); + + let event = &batches(&requests)[0][0]; + assert_eq!( + sorted_keys(event), + [ + "eventName", + "props", + "sessionId", + "systemProps", + "timestamp" + ] + ); + assert_eq!(event["eventName"], "app_started"); + assert_eq!(event["props"], props); + assert!(!event["sessionId"].as_str().unwrap().is_empty()); + OffsetDateTime::parse(event["timestamp"].as_str().unwrap(), &Rfc3339).unwrap(); + + let system = &event["systemProps"]; + assert_eq!( + sorted_keys(system), + [ + "appVersion", + "isDebug", + "locale", + "osName", + "osVersion", + "sdkVersion" + ] + ); + assert_eq!(system["appVersion"], APP_VERSION); + assert_eq!( + system["sdkVersion"], + concat!("aptabase-rs@", env!("CARGO_PKG_VERSION")) + ); + assert_eq!(system["isDebug"], cfg!(debug_assertions)); + assert!(system["osName"].is_string()); + assert!(system["osVersion"].is_string()); + assert!(system["locale"].is_string()); +} + +#[tokio::test] +async fn invalid_app_key_sends_nothing() { + // Arrange + let server = MockServer::start().await; + let delivered = events_endpoint(200).mount_as_scoped(&server).await; + let client = Builder::new("A-XX-1234567890", APP_VERSION) + .with_options(InitOptions { + host: Some(server.uri()), + flush_interval: None, + }) + .build(); + client.track_event("app_started", None).unwrap(); + + // Act + client.flush().await; + + // Assert + assert_eq!( + event_names(&delivered.received_requests().await), + Vec::::new() + ); +} + +#[tokio::test] +async fn polling_flushes_without_a_manual_flush() { + // Arrange + let server = MockServer::start().await; + let delivered = events_endpoint(200).mount_as_scoped(&server).await; + let client = Builder::new(SH_APP_KEY, APP_VERSION) + .with_polling(true) + .with_options(InitOptions { + host: Some(server.uri()), + flush_interval: Some(Duration::from_millis(50)), + }) + .build(); + + // Act + client.track_event("app_started", None).unwrap(); + + // Assert + let arrived = tokio::time::timeout(Duration::from_secs(5), async { + while delivered.received_requests().await.is_empty() { + tokio::time::sleep(Duration::from_millis(25)).await; + } + }) + .await; + assert!(arrived.is_ok(), "no flush within 5s"); + assert_eq!( + event_names(&delivered.received_requests().await), + ["app_started"] + ); +} From 4599b02a0fadb4952f19f07af1f5fcda85144f18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Sun, 11 Oct 2026 15:37:06 +0100 Subject: [PATCH 60/83] Switch from actionlint to jactionlint jactionlint is an actively maintained fork of actionlint that adds security and policy checks. shellcheck is pinned alongside it, so its `run:` script checks use a known version instead of whatever is on PATH. --- mise.lock | 155 ++++++++++++++++++++++++++++++++++-------------------- mise.toml | 3 +- 2 files changed, 99 insertions(+), 59 deletions(-) diff --git a/mise.lock b/mise.lock index f32ff8c..1e09b38 100644 --- a/mise.lock +++ b/mise.lock @@ -1,51 +1,5 @@ # @generated - this file is auto-generated by `mise lock` https://mise.jdx.dev/dev-tools/mise-lock.html -[[tools.actionlint]] -version = "1.7.12" -backend = "aqua:rhysd/actionlint" - -[tools.actionlint."platforms.linux-arm64"] -checksum = "sha256:325e971b6ba9bfa504672e29be93c24981eeb1c07576d730e9f7c8805afff0c6" -url = "https://github.com/rhysd/actionlint/releases/download/v1.7.12/actionlint_1.7.12_linux_arm64.tar.gz" -url_api = "https://api.github.com/repos/rhysd/actionlint/releases/assets/384924897" -provenance = "github-attestations" - -[tools.actionlint."platforms.linux-arm64-musl"] -checksum = "sha256:325e971b6ba9bfa504672e29be93c24981eeb1c07576d730e9f7c8805afff0c6" -url = "https://github.com/rhysd/actionlint/releases/download/v1.7.12/actionlint_1.7.12_linux_arm64.tar.gz" -url_api = "https://api.github.com/repos/rhysd/actionlint/releases/assets/384924897" -provenance = "github-attestations" - -[tools.actionlint."platforms.linux-x64"] -checksum = "sha256:8aca8db96f1b94770f1b0d72b6dddcb1ebb8123cb3712530b08cc387b349a3d8" -url = "https://github.com/rhysd/actionlint/releases/download/v1.7.12/actionlint_1.7.12_linux_amd64.tar.gz" -url_api = "https://api.github.com/repos/rhysd/actionlint/releases/assets/384924896" -provenance = "github-attestations" - -[tools.actionlint."platforms.linux-x64-musl"] -checksum = "sha256:8aca8db96f1b94770f1b0d72b6dddcb1ebb8123cb3712530b08cc387b349a3d8" -url = "https://github.com/rhysd/actionlint/releases/download/v1.7.12/actionlint_1.7.12_linux_amd64.tar.gz" -url_api = "https://api.github.com/repos/rhysd/actionlint/releases/assets/384924896" -provenance = "github-attestations" - -[tools.actionlint."platforms.macos-arm64"] -checksum = "sha256:aba9ced2dee8d27fecca3dc7feb1a7f9a52caefa1eb46f3271ea66b6e0e6953f" -url = "https://github.com/rhysd/actionlint/releases/download/v1.7.12/actionlint_1.7.12_darwin_arm64.tar.gz" -url_api = "https://api.github.com/repos/rhysd/actionlint/releases/assets/384924893" -provenance = "github-attestations" - -[tools.actionlint."platforms.macos-x64"] -checksum = "sha256:5b44c3bc2255115c9b69e30efc0fecdf498fdb63c5d58e17084fd5f16324c644" -url = "https://github.com/rhysd/actionlint/releases/download/v1.7.12/actionlint_1.7.12_darwin_amd64.tar.gz" -url_api = "https://api.github.com/repos/rhysd/actionlint/releases/assets/384924880" -provenance = "github-attestations" - -[tools.actionlint."platforms.windows-x64"] -checksum = "sha256:6e7241b51e6817ea6a047693d8e6fed13b31819c9a0dd6c5a726e1592d22f6e9" -url = "https://github.com/rhysd/actionlint/releases/download/v1.7.12/actionlint_1.7.12_windows_amd64.zip" -url_api = "https://api.github.com/repos/rhysd/actionlint/releases/assets/384924919" -provenance = "github-attestations" - [[tools.gh]] version = "2.96.0" backend = "aqua:cli/cli" @@ -97,9 +51,9 @@ version = "2.13.1" backend = "aqua:orhun/git-cliff" [tools.git-cliff."platforms.linux-arm64"] -checksum = "sha256:4054c124b926c117f3fa048939bc8be0a954f29f3b6f367627e8cb22c1971882" -url = "https://github.com/orhun/git-cliff/releases/download/v2.13.1/git-cliff-2.13.1-aarch64-unknown-linux-musl.tar.gz" -url_api = "https://api.github.com/repos/orhun/git-cliff/releases/assets/405850720" +checksum = "sha256:9619b7f0c584229f8a2331c1905afe88bd938bdc9102926c2073836a42f02455" +url = "https://github.com/orhun/git-cliff/releases/download/v2.13.1/git-cliff-2.13.1-aarch64-unknown-linux-gnu.tar.gz" +url_api = "https://api.github.com/repos/orhun/git-cliff/releases/assets/405850077" [tools.git-cliff."platforms.linux-arm64-musl"] checksum = "sha256:4054c124b926c117f3fa048939bc8be0a954f29f3b6f367627e8cb22c1971882" @@ -107,9 +61,9 @@ url = "https://github.com/orhun/git-cliff/releases/download/v2.13.1/git-cliff-2. url_api = "https://api.github.com/repos/orhun/git-cliff/releases/assets/405850720" [tools.git-cliff."platforms.linux-x64"] -checksum = "sha256:200d2535da6d9703f3bcc8a4d159c3b55eacdb01cf2148c55b3eee9dd04d5249" -url = "https://github.com/orhun/git-cliff/releases/download/v2.13.1/git-cliff-2.13.1-x86_64-unknown-linux-musl.tar.gz" -url_api = "https://api.github.com/repos/orhun/git-cliff/releases/assets/405850671" +checksum = "sha256:9a1263f24e59a2f508c7b3d3283c9dea94a8bf697f96dbc18cc783cac6284546" +url = "https://github.com/orhun/git-cliff/releases/download/v2.13.1/git-cliff-2.13.1-x86_64-unknown-linux-gnu.tar.gz" +url_api = "https://api.github.com/repos/orhun/git-cliff/releases/assets/405850439" [tools.git-cliff."platforms.linux-x64-musl"] checksum = "sha256:200d2535da6d9703f3bcc8a4d159c3b55eacdb01cf2148c55b3eee9dd04d5249" @@ -131,14 +85,60 @@ checksum = "sha256:3ae3a5549e85c7ad5b20192ebcfee4371269deca51255f6f2f2e051c6541f url = "https://github.com/orhun/git-cliff/releases/download/v2.13.1/git-cliff-2.13.1-x86_64-pc-windows-msvc.zip" url_api = "https://api.github.com/repos/orhun/git-cliff/releases/assets/405851866" +[[tools.jactionlint]] +version = "2.0.2" +backend = "github:jdx/jactionlint" + +[tools.jactionlint."platforms.linux-arm64"] +checksum = "sha256:978a9f32cc4ed1d81b94563fc49643b8b3a48dcba4d1ea089d9ba05f7e17e7a7" +url = "https://github.com/jdx/jactionlint/releases/download/v2.0.2/jactionlint_2.0.2_linux_arm64.tar.gz" +url_api = "https://api.github.com/repos/jdx/jactionlint/releases/assets/626020784" +provenance = "github-attestations" + +[tools.jactionlint."platforms.linux-arm64-musl"] +checksum = "sha256:978a9f32cc4ed1d81b94563fc49643b8b3a48dcba4d1ea089d9ba05f7e17e7a7" +url = "https://github.com/jdx/jactionlint/releases/download/v2.0.2/jactionlint_2.0.2_linux_arm64.tar.gz" +url_api = "https://api.github.com/repos/jdx/jactionlint/releases/assets/626020784" +provenance = "github-attestations" + +[tools.jactionlint."platforms.linux-x64"] +checksum = "sha256:026bce6d0ef196f5b22bf43acb1348d47c5d0a5ae61d6ec3bdba1229a823161b" +url = "https://github.com/jdx/jactionlint/releases/download/v2.0.2/jactionlint_2.0.2_linux_amd64.tar.gz" +url_api = "https://api.github.com/repos/jdx/jactionlint/releases/assets/626020780" +provenance = "github-attestations" + +[tools.jactionlint."platforms.linux-x64-musl"] +checksum = "sha256:026bce6d0ef196f5b22bf43acb1348d47c5d0a5ae61d6ec3bdba1229a823161b" +url = "https://github.com/jdx/jactionlint/releases/download/v2.0.2/jactionlint_2.0.2_linux_amd64.tar.gz" +url_api = "https://api.github.com/repos/jdx/jactionlint/releases/assets/626020780" +provenance = "github-attestations" + +[tools.jactionlint."platforms.macos-arm64"] +checksum = "sha256:1ad210bd21f4c70a81e0bffecc6d223daca8dd13e57fa6899787064f8b724c7c" +url = "https://github.com/jdx/jactionlint/releases/download/v2.0.2/jactionlint_2.0.2_darwin_arm64.tar.gz" +url_api = "https://api.github.com/repos/jdx/jactionlint/releases/assets/626020734" +provenance = "github-attestations" + +[tools.jactionlint."platforms.macos-x64"] +checksum = "sha256:03ccb52f3bf3c6b1b6358689a4d31e5f05d558b32c0b8edaeac7c452abe7fff4" +url = "https://github.com/jdx/jactionlint/releases/download/v2.0.2/jactionlint_2.0.2_darwin_amd64.tar.gz" +url_api = "https://api.github.com/repos/jdx/jactionlint/releases/assets/626020732" +provenance = "github-attestations" + +[tools.jactionlint."platforms.windows-x64"] +checksum = "sha256:988403508761b96fa506920b7db420e6e738247aade084f9b928f452340c8f8f" +url = "https://github.com/jdx/jactionlint/releases/download/v2.0.2/jactionlint_2.0.2_windows_amd64.zip" +url_api = "https://api.github.com/repos/jdx/jactionlint/releases/assets/626020828" +provenance = "github-attestations" + [[tools.release-plz]] version = "0.3.160" backend = "aqua:release-plz/release-plz" [tools.release-plz."platforms.linux-arm64"] -checksum = "sha256:43022dc9192f6da8d7f1e9157fc492a8339502b29623432b172f5353b0a4283a" -url = "https://github.com/release-plz/release-plz/releases/download/release-plz-v0.3.160/release-plz-aarch64-unknown-linux-musl.tar.gz" -url_api = "https://api.github.com/repos/release-plz/release-plz/releases/assets/476456802" +checksum = "sha256:38a64d9fd84083f352e29b628de7506eb0f3b6ca99b8a2305bd1e0fe747be754" +url = "https://github.com/release-plz/release-plz/releases/download/release-plz-v0.3.160/release-plz-aarch64-unknown-linux-gnu.tar.gz" +url_api = "https://api.github.com/repos/release-plz/release-plz/releases/assets/476457611" [tools.release-plz."platforms.linux-arm64-musl"] checksum = "sha256:43022dc9192f6da8d7f1e9157fc492a8339502b29623432b172f5353b0a4283a" @@ -146,9 +146,9 @@ url = "https://github.com/release-plz/release-plz/releases/download/release-plz- url_api = "https://api.github.com/repos/release-plz/release-plz/releases/assets/476456802" [tools.release-plz."platforms.linux-x64"] -checksum = "sha256:47a7ced8ce2870b767f47232adbfd5c561784af7cd0dbe1a83456977fe786d4a" -url = "https://github.com/release-plz/release-plz/releases/download/release-plz-v0.3.160/release-plz-x86_64-unknown-linux-musl.tar.gz" -url_api = "https://api.github.com/repos/release-plz/release-plz/releases/assets/476456450" +checksum = "sha256:2263c4f95eac1513da96a114a77fde20ea038742a8c8050f7514b8f93b828646" +url = "https://github.com/release-plz/release-plz/releases/download/release-plz-v0.3.160/release-plz-x86_64-unknown-linux-gnu.tar.gz" +url_api = "https://api.github.com/repos/release-plz/release-plz/releases/assets/476455741" [tools.release-plz."platforms.linux-x64-musl"] checksum = "sha256:47a7ced8ce2870b767f47232adbfd5c561784af7cd0dbe1a83456977fe786d4a" @@ -171,3 +171,42 @@ backend = "core:rust" [tools.rust.options] components = "clippy,rustfmt" + +[[tools.shellcheck]] +version = "0.11.0" +backend = "aqua:koalaman/shellcheck" + +[tools.shellcheck."platforms.linux-arm64"] +checksum = "sha256:12b331c1d2db6b9eb13cfca64306b1b157a86eb69db83023e261eaa7e7c14588" +url = "https://github.com/koalaman/shellcheck/releases/download/v0.11.0/shellcheck-v0.11.0.linux.aarch64.tar.xz" +url_api = "https://api.github.com/repos/koalaman/shellcheck/releases/assets/279056934" + +[tools.shellcheck."platforms.linux-arm64-musl"] +checksum = "sha256:12b331c1d2db6b9eb13cfca64306b1b157a86eb69db83023e261eaa7e7c14588" +url = "https://github.com/koalaman/shellcheck/releases/download/v0.11.0/shellcheck-v0.11.0.linux.aarch64.tar.xz" +url_api = "https://api.github.com/repos/koalaman/shellcheck/releases/assets/279056934" + +[tools.shellcheck."platforms.linux-x64"] +checksum = "sha256:8c3be12b05d5c177a04c29e3c78ce89ac86f1595681cab149b65b97c4e227198" +url = "https://github.com/koalaman/shellcheck/releases/download/v0.11.0/shellcheck-v0.11.0.linux.x86_64.tar.xz" +url_api = "https://api.github.com/repos/koalaman/shellcheck/releases/assets/279056942" + +[tools.shellcheck."platforms.linux-x64-musl"] +checksum = "sha256:8c3be12b05d5c177a04c29e3c78ce89ac86f1595681cab149b65b97c4e227198" +url = "https://github.com/koalaman/shellcheck/releases/download/v0.11.0/shellcheck-v0.11.0.linux.x86_64.tar.xz" +url_api = "https://api.github.com/repos/koalaman/shellcheck/releases/assets/279056942" + +[tools.shellcheck."platforms.macos-arm64"] +checksum = "sha256:56affdd8de5527894dca6dc3d7e0a99a873b0f004d7aabc30ae407d3f48b0a79" +url = "https://github.com/koalaman/shellcheck/releases/download/v0.11.0/shellcheck-v0.11.0.darwin.aarch64.tar.xz" +url_api = "https://api.github.com/repos/koalaman/shellcheck/releases/assets/279056932" + +[tools.shellcheck."platforms.macos-x64"] +checksum = "sha256:3c89db4edcab7cf1c27bff178882e0f6f27f7afdf54e859fa041fca10febe4c6" +url = "https://github.com/koalaman/shellcheck/releases/download/v0.11.0/shellcheck-v0.11.0.darwin.x86_64.tar.xz" +url_api = "https://api.github.com/repos/koalaman/shellcheck/releases/assets/279056930" + +[tools.shellcheck."platforms.windows-x64"] +checksum = "sha256:8a4e35ab0b331c85d73567b12f2a444df187f483e5079ceffa6bda1faa2e740e" +url = "https://github.com/koalaman/shellcheck/releases/download/v0.11.0/shellcheck-v0.11.0.zip" +url_api = "https://api.github.com/repos/koalaman/shellcheck/releases/assets/279056944" diff --git a/mise.toml b/mise.toml index 52f1549..f961ee4 100644 --- a/mise.toml +++ b/mise.toml @@ -1,8 +1,9 @@ [tools] -actionlint = "latest" gh = "latest" git-cliff = "latest" +jactionlint = "latest" release-plz = "latest" +shellcheck = "latest" rust = { version = "1.88", components = ["clippy", "rustfmt"] } # the MSRV [tasks.test] From 2790d6e9334750b7f16993a46824a69a8cfdd3e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Sun, 11 Oct 2026 15:37:06 +0100 Subject: [PATCH 61/83] Drop the CI feature matrix The TLS features only forward to reqwest's backends. No crate code sits behind `cfg(feature)`, so each matrix entry linted the same source, and a broken feature reference already fails dependency resolution in the default build. --- .github/workflows/ci.yml | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1a2fd0e..5c1c724 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,25 +19,6 @@ jobs: - uses: Swatinem/rust-cache@v2 - run: mise run check - features: - name: Clippy (${{ matrix.name }}) - runs-on: ubuntu-latest - strategy: - matrix: - include: - - name: native-tls - flags: --no-default-features --features native-tls - - name: rustls-tls - flags: --no-default-features --features rustls-tls - - name: no-tls - flags: --no-default-features - steps: - - uses: actions/checkout@v7 - - uses: jdx/mise-action@v5 - with: { install_args: rust } - - uses: Swatinem/rust-cache@v2 - - run: cargo clippy --all-targets ${{ matrix.flags }} - example: name: Example runs-on: ubuntu-latest From b5490a5f3414bb85a5882ed4294c83bc079678a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Sun, 11 Oct 2026 15:37:06 +0100 Subject: [PATCH 62/83] Rename the workflows and jobs The release workflow is now release.yml ("Release") with `release` and `release-pr` jobs, and the CI jobs are "Check" and "Check example". Step names that only repeated the action are dropped. No required status checks reference the old names. --- .github/workflows/ci.yml | 6 ++-- .../{release-plz.yml => release.yml} | 36 ++++++++----------- mise.toml | 2 +- 3 files changed, 18 insertions(+), 26 deletions(-) rename .github/workflows/{release-plz.yml => release.yml} (77%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5c1c724..4beefb2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,7 +7,7 @@ on: jobs: check: - name: Build & Check + name: Check runs-on: ubuntu-latest env: # Stop `mise run` installing tools beyond what mise-action installed @@ -19,8 +19,8 @@ jobs: - uses: Swatinem/rust-cache@v2 - run: mise run check - example: - name: Example + check-example: + name: Check example runs-on: ubuntu-latest defaults: run: diff --git a/.github/workflows/release-plz.yml b/.github/workflows/release.yml similarity index 77% rename from .github/workflows/release-plz.yml rename to .github/workflows/release.yml index a28741c..1f95c0e 100644 --- a/.github/workflows/release-plz.yml +++ b/.github/workflows/release.yml @@ -1,33 +1,29 @@ -name: Release-plz +name: Release on: push: - branches: - - main + branches: [main] jobs: - release-plz-release: - name: Release-plz release - runs-on: ubuntu-latest + release: if: ${{ github.repository_owner == 'eugencowie' }} + name: Release + runs-on: ubuntu-latest permissions: contents: write steps: - &checkout - name: Checkout repository uses: actions/checkout@v7 with: fetch-depth: 0 persist-credentials: true token: ${{ secrets.RELEASE_PLZ_TOKEN }} - &install-tools - name: Install tools id: mise uses: jdx/mise-action@v5 with: install_args: rust release-plz git-cliff - - name: Run release-plz - uses: release-plz/action@v0.5 + - uses: release-plz/action@v0.5 with: version: ${{ steps.mise.outputs.release-plz }} command: release @@ -35,13 +31,13 @@ jobs: GITHUB_TOKEN: ${{ secrets.RELEASE_PLZ_TOKEN }} CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} - release-plz-pr: - name: Release-plz PR - runs-on: ubuntu-latest + release-pr: if: ${{ github.repository_owner == 'eugencowie' }} + name: Release PR + runs-on: ubuntu-latest permissions: - pull-requests: write contents: write + pull-requests: write concurrency: group: release-plz-${{ github.ref }} cancel-in-progress: false @@ -49,11 +45,9 @@ jobs: - *checkout - *install-tools # Keep in sync with the changelog task in mise.toml. - - name: Skip commits off the first-parent chain - shell: bash + - shell: bash run: comm -23 <(git rev-list HEAD | sort) <(git rev-list --first-parent HEAD | sort) > .cliffignore - - name: Generate changelog - id: git-cliff + - id: git-cliff uses: orhun/git-cliff-action@v4 with: version: v${{ steps.mise.outputs.git-cliff }} @@ -61,12 +55,10 @@ jobs: env: OUTPUT: CHANGELOG.md GITHUB_REPO: ${{ github.repository }} - - name: Set release version - env: + - env: RELEASE_VERSION: ${{ steps.git-cliff.outputs.version }} run: release-plz set-version "${RELEASE_VERSION#v}" - - name: Run release-plz - uses: release-plz/action@v0.5 + - uses: release-plz/action@v0.5 with: version: ${{ steps.mise.outputs.release-plz }} command: release-pr diff --git a/mise.toml b/mise.toml index f961ee4..a58ba77 100644 --- a/mise.toml +++ b/mise.toml @@ -33,7 +33,7 @@ description = "Update changelog" shell = "bash -c" run = [ # Skip commits off HEAD's first-parent chain (branch and upstream commits), so - # run this on main. Keep in sync with the release-plz-pr job in release-plz.yml. + # run this on main. Keep in sync with the release-pr job in release.yml. "comm -23 <(git rev-list HEAD | sort) <(git rev-list --first-parent HEAD | sort) > .cliffignore", "git cliff --bump" ] From fbf6391f649fa43420ec8f7290a2fe0cd9840dfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Sun, 11 Oct 2026 15:37:06 +0100 Subject: [PATCH 63/83] Harden workflows against jactionlint's security checks - Pin every action to a commit SHA, with its version in a comment that Dependabot keeps up to date - Set timeout-minutes on every job: 15 for CI, 30 for releases - Give CI's GITHUB_TOKEN read-only contents permission, and don't leave it in the checkout where build scripts can read it. The release workflow keeps persist-credentials, because release-plz pushes. - Make Dependabot wait 7 days before proposing a new release --- .github/dependabot.yml | 4 ++++ .github/workflows/ci.yml | 19 +++++++++++++------ .github/workflows/release.yml | 12 +++++++----- 3 files changed, 24 insertions(+), 11 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 1a3d457..4f5dfb5 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -5,6 +5,8 @@ updates: directory: / schedule: interval: weekly + cooldown: + default-days: 7 commit-message: prefix: chore include: scope @@ -14,6 +16,8 @@ updates: directory: / schedule: interval: weekly + cooldown: + default-days: 7 commit-message: prefix: chore include: scope diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4beefb2..5ac66ff 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,31 +5,38 @@ on: branches: [main] pull_request: +permissions: + contents: read + jobs: check: name: Check runs-on: ubuntu-latest + timeout-minutes: 15 env: # Stop `mise run` installing tools beyond what mise-action installed MISE_TASK_RUN_AUTO_INSTALL: false steps: - - uses: actions/checkout@v7 - - uses: jdx/mise-action@v5 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: { persist-credentials: false } + - uses: jdx/mise-action@2d8d4cafcbd33be2ea37d2b6f5ad595363d1f1ca # v5.1.1 with: { install_args: rust } - - uses: Swatinem/rust-cache@v2 + - uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2 - run: mise run check check-example: name: Check example runs-on: ubuntu-latest + timeout-minutes: 15 defaults: run: working-directory: examples/helloworld steps: - - uses: actions/checkout@v7 - - uses: jdx/mise-action@v5 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: { persist-credentials: false } + - uses: jdx/mise-action@2d8d4cafcbd33be2ea37d2b6f5ad595363d1f1ca # v5.1.1 with: { install_args: rust } - - uses: Swatinem/rust-cache@v2 + - uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2 with: { workspaces: examples/helloworld } - run: cp .env.example .env - run: cargo clippy --all-targets diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1f95c0e..723609a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -9,21 +9,22 @@ jobs: if: ${{ github.repository_owner == 'eugencowie' }} name: Release runs-on: ubuntu-latest + timeout-minutes: 30 permissions: contents: write steps: - &checkout - uses: actions/checkout@v7 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 0 persist-credentials: true token: ${{ secrets.RELEASE_PLZ_TOKEN }} - &install-tools id: mise - uses: jdx/mise-action@v5 + uses: jdx/mise-action@2d8d4cafcbd33be2ea37d2b6f5ad595363d1f1ca # v5.1.1 with: install_args: rust release-plz git-cliff - - uses: release-plz/action@v0.5 + - uses: release-plz/action@f393a7b38aca9d9b4c58447b291dbac963901df6 # v0.5.141 with: version: ${{ steps.mise.outputs.release-plz }} command: release @@ -35,6 +36,7 @@ jobs: if: ${{ github.repository_owner == 'eugencowie' }} name: Release PR runs-on: ubuntu-latest + timeout-minutes: 30 permissions: contents: write pull-requests: write @@ -48,7 +50,7 @@ jobs: - shell: bash run: comm -23 <(git rev-list HEAD | sort) <(git rev-list --first-parent HEAD | sort) > .cliffignore - id: git-cliff - uses: orhun/git-cliff-action@v4 + uses: orhun/git-cliff-action@a9a95522b26fe6403f7bb24031f21fb573d0f5ff # v4.9.1 with: version: v${{ steps.mise.outputs.git-cliff }} args: --bump @@ -58,7 +60,7 @@ jobs: - env: RELEASE_VERSION: ${{ steps.git-cliff.outputs.version }} run: release-plz set-version "${RELEASE_VERSION#v}" - - uses: release-plz/action@v0.5 + - uses: release-plz/action@f393a7b38aca9d9b4c58447b291dbac963901df6 # v0.5.141 with: version: ${{ steps.mise.outputs.release-plz }} command: release-pr From 1ac4d35000393af1378334daef3222df540af1cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Sun, 11 Oct 2026 15:37:06 +0100 Subject: [PATCH 64/83] Cancel superseded CI runs and queue release runs A new push cancels CI already running for the same ref. The release workflow's concurrency group moves from the release-PR job to the workflow, so `release` runs queue too and two quick pushes can't publish the same version at once. Release runs are never cancelled, since that could leave a release half-published. --- .github/workflows/ci.yml | 4 ++++ .github/workflows/release.yml | 7 ++++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5ac66ff..069bd8e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,6 +8,10 @@ on: permissions: contents: read +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: check: name: Check diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 723609a..7b8e451 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -4,6 +4,10 @@ on: push: branches: [main] +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: false + jobs: release: if: ${{ github.repository_owner == 'eugencowie' }} @@ -40,9 +44,6 @@ jobs: permissions: contents: write pull-requests: write - concurrency: - group: release-plz-${{ github.ref }} - cancel-in-progress: false steps: - *checkout - *install-tools From 0a9395f3ddeeaae45d2dd571a3d7e0cfcf5946c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Sun, 11 Oct 2026 15:37:07 +0100 Subject: [PATCH 65/83] Add mise tasks for the example The example isn't a workspace member, so the root cargo commands never touch it. `example:build`, `example:format`, `example:lint` and `example:check` run in examples/helloworld. The example reads APTABASE_KEY from .env at compile time. A hidden `example:copy-env` task copies .env.example only when .env is missing, so a local key survives. CI's example job now runs `mise run example:check`, which adds a formatting check, so MISE_TASK_RUN_AUTO_INSTALL moves to the workflow. Also adds a root `build` task. --- .github/workflows/ci.yml | 13 +++++-------- mise.toml | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 069bd8e..0c59e3c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,14 +12,15 @@ concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true +env: + # Stop `mise run` installing tools beyond what mise-action installed + MISE_TASK_RUN_AUTO_INSTALL: false + jobs: check: name: Check runs-on: ubuntu-latest timeout-minutes: 15 - env: - # Stop `mise run` installing tools beyond what mise-action installed - MISE_TASK_RUN_AUTO_INSTALL: false steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: { persist-credentials: false } @@ -32,9 +33,6 @@ jobs: name: Check example runs-on: ubuntu-latest timeout-minutes: 15 - defaults: - run: - working-directory: examples/helloworld steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: { persist-credentials: false } @@ -42,5 +40,4 @@ jobs: with: { install_args: rust } - uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2 with: { workspaces: examples/helloworld } - - run: cp .env.example .env - - run: cargo clippy --all-targets + - run: mise run example:check diff --git a/mise.toml b/mise.toml index a58ba77..37cb19a 100644 --- a/mise.toml +++ b/mise.toml @@ -6,6 +6,10 @@ release-plz = "latest" shellcheck = "latest" rust = { version = "1.88", components = ["clippy", "rustfmt"] } # the MSRV +[tasks.build] +description = "Build project" +run = "cargo build" + [tasks.test] description = "Run tests" run = "cargo test" @@ -28,6 +32,35 @@ run = [ "cargo test" ] +[tasks."example:build"] +description = "Build example project" +depends = ["example:copy-env"] +dir = "examples/helloworld" +run = "cargo build" + +[tasks."example:format"] +description = "Fix formatting issues in example" +dir = "examples/helloworld" +run = "cargo fmt" + +[tasks."example:lint"] +description = "Fix linting issues in example" +depends = ["example:copy-env"] +dir = "examples/helloworld" +run = "cargo clippy --all-targets --fix --allow-dirty" + +[tasks."example:check"] +description = "Check formatting and linting in example" +depends = ["example:copy-env"] +dir = "examples/helloworld" +run = ["cargo fmt --check", "cargo clippy --all-targets"] + +[tasks."example:copy-env"] +hide = true +description = "Ensure .env is available at compile time" +dir = "examples/helloworld" +run = "test -f .env || cp .env.example .env" + [tasks.changelog] description = "Update changelog" shell = "bash -c" From 8d5918419946789baab1cac93ca4e15f4b895ec6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Sun, 11 Oct 2026 15:37:07 +0100 Subject: [PATCH 66/83] Move the toolchain guard into its own task `check` now depends on a hidden `check:toolchain` task, which fails when the pinned toolchain drifts from rust-version in Cargo.toml, and its cargo commands fit on one line. --- mise.toml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/mise.toml b/mise.toml index 37cb19a..e6d3ad2 100644 --- a/mise.toml +++ b/mise.toml @@ -23,14 +23,14 @@ description = "Fix linting issues" run = "cargo clippy --all-targets --fix --allow-dirty" [tasks.check] -description = "Check formatting/linting/tests" -run = [ - # Fail if the pinned toolchain has drifted from rust-version in Cargo.toml - 'grep -q "^rust-version = \"$(rustc -V | cut -d" " -f2 | cut -d. -f1,2)\"" Cargo.toml', - "cargo fmt --check", - "cargo clippy --all-targets", - "cargo test" -] +description = "Check formatting, linting, and tests" +depends = ["check:toolchain"] +run = ["cargo fmt --check", "cargo clippy --all-targets", "cargo test"] + +[tasks."check:toolchain"] +hide = true +description = "Fail if the pinned toolchain has drifted from rust-version in Cargo.toml" +run = 'grep -q "^rust-version = \"$(rustc -V | cut -d" " -f2 | cut -d. -f1,2)\"" Cargo.toml' [tasks."example:build"] description = "Build example project" From 9cdc1a6581d43037ca54b4426b371f745deb35c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Sun, 11 Oct 2026 15:37:07 +0100 Subject: [PATCH 67/83] Share the changelog ignore list with the release workflow The release-PR job ran its own copy of the command that writes .cliffignore, kept in sync by a comment. Both now run the hidden `changelog:build-ignore-list` task. MISE_TASK_RUN_AUTO_INSTALL is off so `mise run` installs nothing beyond what mise-action installed. --- .github/workflows/release.yml | 8 +++++--- mise.toml | 13 +++++++------ 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7b8e451..6259d46 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -8,6 +8,10 @@ concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: false +env: + # Stop `mise run` installing tools beyond what mise-action installed + MISE_TASK_RUN_AUTO_INSTALL: false + jobs: release: if: ${{ github.repository_owner == 'eugencowie' }} @@ -47,9 +51,7 @@ jobs: steps: - *checkout - *install-tools - # Keep in sync with the changelog task in mise.toml. - - shell: bash - run: comm -23 <(git rev-list HEAD | sort) <(git rev-list --first-parent HEAD | sort) > .cliffignore + - run: mise run changelog:build-ignore-list - id: git-cliff uses: orhun/git-cliff-action@a9a95522b26fe6403f7bb24031f21fb573d0f5ff # v4.9.1 with: diff --git a/mise.toml b/mise.toml index e6d3ad2..6ed3b99 100644 --- a/mise.toml +++ b/mise.toml @@ -63,13 +63,14 @@ run = "test -f .env || cp .env.example .env" [tasks.changelog] description = "Update changelog" +depends = ["changelog:build-ignore-list"] +run = "git cliff --bump" + +[tasks."changelog:build-ignore-list"] +hide = true +description = "Ignore commits off HEAD's first-parent chain (branch and upstream commits)" shell = "bash -c" -run = [ - # Skip commits off HEAD's first-parent chain (branch and upstream commits), so - # run this on main. Keep in sync with the release-pr job in release.yml. - "comm -23 <(git rev-list HEAD | sort) <(git rev-list --first-parent HEAD | sort) > .cliffignore", - "git cliff --bump" -] +run = "comm -23 <(git rev-list HEAD | sort) <(git rev-list --first-parent HEAD | sort) > .cliffignore" [tasks.release] description = "Update release versions and changelogs locally" From 38795fcd5a995cb06d68af17dbc70d281d6aad4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Sun, 11 Oct 2026 15:37:07 +0100 Subject: [PATCH 68/83] Lint workflows in mise run check A hidden `check:workflows` task runs jactionlint, and CI installs it in the check job. --- .github/workflows/ci.yml | 2 +- mise.toml | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0c59e3c..1eaf3e3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,7 +25,7 @@ jobs: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: { persist-credentials: false } - uses: jdx/mise-action@2d8d4cafcbd33be2ea37d2b6f5ad595363d1f1ca # v5.1.1 - with: { install_args: rust } + with: { install_args: rust jactionlint } - uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2 - run: mise run check diff --git a/mise.toml b/mise.toml index 6ed3b99..807a5a3 100644 --- a/mise.toml +++ b/mise.toml @@ -24,7 +24,7 @@ run = "cargo clippy --all-targets --fix --allow-dirty" [tasks.check] description = "Check formatting, linting, and tests" -depends = ["check:toolchain"] +depends = ["check:toolchain", "check:workflows"] run = ["cargo fmt --check", "cargo clippy --all-targets", "cargo test"] [tasks."check:toolchain"] @@ -32,6 +32,11 @@ hide = true description = "Fail if the pinned toolchain has drifted from rust-version in Cargo.toml" run = 'grep -q "^rust-version = \"$(rustc -V | cut -d" " -f2 | cut -d. -f1,2)\"" Cargo.toml' +[tasks."check:workflows"] +hide = true +description = "Check GitHub Actions workflows" +run = "jactionlint" + [tasks."example:build"] description = "Build example project" depends = ["example:copy-env"] From 35e48627bcf7573a996452e3522f1b9eadd697c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Sun, 11 Oct 2026 16:07:52 +0100 Subject: [PATCH 69/83] Install shellcheck for the workflow lint in CI jactionlint runs shellcheck on `run:` scripts. Installing it with mise-action pins the version from mise.lock instead of depending on whatever the runner has on PATH. --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1eaf3e3..5883b6b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,7 +25,7 @@ jobs: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: { persist-credentials: false } - uses: jdx/mise-action@2d8d4cafcbd33be2ea37d2b6f5ad595363d1f1ca # v5.1.1 - with: { install_args: rust jactionlint } + with: { install_args: rust jactionlint shellcheck } - uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2 - run: mise run check From 5e27090a5187cb883754d2e2121450602fa56db2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 11 Oct 2026 15:26:00 +0000 Subject: [PATCH 70/83] chore(deps): bump tokio from 1.53.1 to 1.53.2 Bumps [tokio](https://github.com/tokio-rs/tokio) from 1.53.1 to 1.53.2. - [Release notes](https://github.com/tokio-rs/tokio/releases) - [Commits](https://github.com/tokio-rs/tokio/compare/tokio-1.53.1...tokio-1.53.2) --- updated-dependencies: - dependency-name: tokio dependency-version: 1.53.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e80f1e0..e73f463 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1777,9 +1777,9 @@ checksum = "fd3ca314f692efd6c868f8408f53fe444634a845f96c028b97d35f6a1f79f0ee" [[package]] name = "tokio" -version = "1.53.1" +version = "1.53.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "202caea871b69668250d242070849eb495be178ed697a3e98aebce5bc81a0bed" +checksum = "e95f91fcc7a621e8b030f6aa23c71fe9838ae2fb4d8118b75602a328f5144044" dependencies = [ "bytes", "libc", From 8b71e975deecbc5b1fb3570220116364001a06e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Sun, 11 Oct 2026 16:37:56 +0100 Subject: [PATCH 71/83] Cache Rust builds with mbx mise's native mr-boxington integration wraps cargo in this project, so builds and tests reuse compiler outputs across target directories and worktrees. The Rust MSRV pin is unchanged. --- mise.lock | 34 ++++++++++++++++++++++++++++++++++ mise.toml | 3 ++- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/mise.lock b/mise.lock index 1e09b38..4355c7e 100644 --- a/mise.lock +++ b/mise.lock @@ -131,6 +131,40 @@ url = "https://github.com/jdx/jactionlint/releases/download/v2.0.2/jactionlint_2 url_api = "https://api.github.com/repos/jdx/jactionlint/releases/assets/626020828" provenance = "github-attestations" +[[tools.mr-boxington]] +version = "1.23.0" +backend = "packslip:github.com/jdx/mr-boxington" + +[tools.mr-boxington."platforms.linux-arm64"] +checksum = "sha256:7278068e1aa0c2acc9be675eae07584201c4bcb67f2b05b9952dc05979f3ddeb" +url = "https://github.com/jdx/mr-boxington/releases/download/v1.23.0/mbx-aarch64-unknown-linux-gnu.tar.gz" +signer = "sigstore-oidc:https://github.com/jdx/mr-boxington/.github/workflows/release.yml" + +[tools.mr-boxington."platforms.linux-arm64-musl"] +checksum = "sha256:2f4b304445843cf2d9626ce68a7d7923cc31f2ac924d26632dd6702c71f6df04" +url = "https://github.com/jdx/mr-boxington/releases/download/v1.23.0/mbx-aarch64-unknown-linux-musl.tar.gz" +signer = "sigstore-oidc:https://github.com/jdx/mr-boxington/.github/workflows/release.yml" + +[tools.mr-boxington."platforms.linux-x64"] +checksum = "sha256:417766e3286c9d669ebf9415f82a78114ac599a4644fda36223258090953d43f" +url = "https://github.com/jdx/mr-boxington/releases/download/v1.23.0/mbx-x86_64-unknown-linux-gnu.tar.gz" +signer = "sigstore-oidc:https://github.com/jdx/mr-boxington/.github/workflows/release.yml" + +[tools.mr-boxington."platforms.linux-x64-musl"] +checksum = "sha256:f50a708e7e3e67b054596729c7035b61bc872e1fce869d8101f45f8ab355f296" +url = "https://github.com/jdx/mr-boxington/releases/download/v1.23.0/mbx-x86_64-unknown-linux-musl.tar.gz" +signer = "sigstore-oidc:https://github.com/jdx/mr-boxington/.github/workflows/release.yml" + +[tools.mr-boxington."platforms.macos-arm64"] +checksum = "sha256:e836ef3ef83b15f2fce52a1a9ea2a82ddd8d2b2c8e107152fdc28098c8b41c60" +url = "https://github.com/jdx/mr-boxington/releases/download/v1.23.0/mbx-aarch64-apple-darwin.tar.gz" +signer = "sigstore-oidc:https://github.com/jdx/mr-boxington/.github/workflows/release.yml" + +[tools.mr-boxington."platforms.windows-x64"] +checksum = "sha256:ecd9a657cacd3e179f91a0a04ef32c458740365cd82f5a29734f8a95bed80710" +url = "https://github.com/jdx/mr-boxington/releases/download/v1.23.0/mbx-x86_64-pc-windows-msvc.zip" +signer = "sigstore-oidc:https://github.com/jdx/mr-boxington/.github/workflows/release.yml" + [[tools.release-plz]] version = "0.3.160" backend = "aqua:release-plz/release-plz" diff --git a/mise.toml b/mise.toml index 807a5a3..96002c7 100644 --- a/mise.toml +++ b/mise.toml @@ -2,9 +2,10 @@ gh = "latest" git-cliff = "latest" jactionlint = "latest" +mr-boxington = "latest" release-plz = "latest" shellcheck = "latest" -rust = { version = "1.88", components = ["clippy", "rustfmt"] } # the MSRV +rust = { version = "1.88", components = ["clippy", "rustfmt"], mr_boxington = true } # the MSRV [tasks.build] description = "Build project" From d089c55d52ff3d6d2b5aadc7f8d21936762ed1c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Sun, 11 Oct 2026 18:15:37 +0100 Subject: [PATCH 72/83] Cache CI builds with mr-boxington-action The action caches the same pruned target/ and registry as rust-cache, keyed on the rustc in use, and adds native link caching. It saves only from pushes to main, so PRs stop adding entries to an Actions cache that is already over its 10 GB limit. The example job gets its own cache generation so it never restores the root crate's target/. --- .github/workflows/ci.yml | 13 +++++++------ .github/workflows/release.yml | 1 - 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5883b6b..89b7db7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,7 +13,6 @@ concurrency: cancel-in-progress: true env: - # Stop `mise run` installing tools beyond what mise-action installed MISE_TASK_RUN_AUTO_INSTALL: false jobs: @@ -25,8 +24,8 @@ jobs: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: { persist-credentials: false } - uses: jdx/mise-action@2d8d4cafcbd33be2ea37d2b6f5ad595363d1f1ca # v5.1.1 - with: { install_args: rust jactionlint shellcheck } - - uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2 + with: { install_args: rust mr-boxington jactionlint shellcheck } + - uses: jdx/mr-boxington-action@47ce49b32025d93a7a2f4570815afaa2b7092240 # v1.7.2 - run: mise run check check-example: @@ -37,7 +36,9 @@ jobs: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: { persist-credentials: false } - uses: jdx/mise-action@2d8d4cafcbd33be2ea37d2b6f5ad595363d1f1ca # v5.1.1 - with: { install_args: rust } - - uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2 - with: { workspaces: examples/helloworld } + with: { install_args: rust mr-boxington } + - uses: jdx/mr-boxington-action@47ce49b32025d93a7a2f4570815afaa2b7092240 # v1.7.2 + with: + working-directory: examples/helloworld + cache-generation: example-v1 - run: mise run example:check diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6259d46..a4317db 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -9,7 +9,6 @@ concurrency: cancel-in-progress: false env: - # Stop `mise run` installing tools beyond what mise-action installed MISE_TASK_RUN_AUTO_INSTALL: false jobs: From da7f65bde606572ee509e54178eb806bdde61a1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Sun, 11 Oct 2026 20:58:14 +0100 Subject: [PATCH 73/83] Install mbx up front in release jobs Cargo runs through mise's mbx wrapper, so without mr-boxington in install_args mise installs it on demand partway through cargo publish. Install it with the other tools instead. --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a4317db..f6a3ac9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -30,7 +30,7 @@ jobs: id: mise uses: jdx/mise-action@2d8d4cafcbd33be2ea37d2b6f5ad595363d1f1ca # v5.1.1 with: - install_args: rust release-plz git-cliff + install_args: rust mr-boxington release-plz git-cliff - uses: release-plz/action@f393a7b38aca9d9b4c58447b291dbac963901df6 # v0.5.141 with: version: ${{ steps.mise.outputs.release-plz }} From 4b3b49c3ddb0c6889dfbbaf29635c11a1813f6a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Sun, 11 Oct 2026 23:35:51 +0100 Subject: [PATCH 74/83] Make one release:prepare task for CI and local releases The release-pr job rebuilt the changelog with git-cliff-action and set the version from its output, while `mise run release` ran `release-plz update`. That leaves CHANGELOG.md alone (changelog_update = false) and picks the version with release-plz's own rules over every commit, upstream ones included, so a local run could pick a different version than the release PR. And `mise run changelog` alone left CHANGELOG.md a version ahead of Cargo.toml. - release:prepare replaces the changelog, changelog:build-ignore-list and release tasks. It writes .cliffignore, updates CHANGELOG.md with `git cliff --bump`, and sets Cargo.toml to git-cliff's bumped version - the release-pr job runs it in place of the ignore-list step, git-cliff-action and `set-version`, so CI uses mise.lock's git-cliff rather than the action's own download - cliff.toml no longer sets `output`, so git-cliff writes CHANGELOG.md only when told to with `-o`. Before, every git-cliff run overwrote it, even `--bumped-version` --- .github/workflows/release.yml | 13 +------------ .gitignore | 2 +- cliff.toml | 1 - mise.toml | 21 ++++++++------------- 4 files changed, 10 insertions(+), 27 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f6a3ac9..85a8c43 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -50,18 +50,7 @@ jobs: steps: - *checkout - *install-tools - - run: mise run changelog:build-ignore-list - - id: git-cliff - uses: orhun/git-cliff-action@a9a95522b26fe6403f7bb24031f21fb573d0f5ff # v4.9.1 - with: - version: v${{ steps.mise.outputs.git-cliff }} - args: --bump - env: - OUTPUT: CHANGELOG.md - GITHUB_REPO: ${{ github.repository }} - - env: - RELEASE_VERSION: ${{ steps.git-cliff.outputs.version }} - run: release-plz set-version "${RELEASE_VERSION#v}" + - run: mise run release:prepare - uses: release-plz/action@f393a7b38aca9d9b4c58447b291dbac963901df6 # v0.5.141 with: version: ${{ steps.mise.outputs.release-plz }} diff --git a/.gitignore b/.gitignore index 606e3b4..b59f516 100644 --- a/.gitignore +++ b/.gitignore @@ -39,7 +39,7 @@ examples/*/Cargo.lock # git-cliff # -# Generated by `mise run changelog` +# Generated by `mise run release:prepare` .cliffignore # diff --git a/cliff.toml b/cliff.toml index 5d3320a..bdb76da 100644 --- a/cliff.toml +++ b/cliff.toml @@ -1,5 +1,4 @@ [changelog] -output = "CHANGELOG.md" header = "# Changelog" body = """ diff --git a/mise.toml b/mise.toml index 96002c7..9f3a9e3 100644 --- a/mise.toml +++ b/mise.toml @@ -67,17 +67,12 @@ description = "Ensure .env is available at compile time" dir = "examples/helloworld" run = "test -f .env || cp .env.example .env" -[tasks.changelog] -description = "Update changelog" -depends = ["changelog:build-ignore-list"] -run = "git cliff --bump" - -[tasks."changelog:build-ignore-list"] -hide = true -description = "Ignore commits off HEAD's first-parent chain (branch and upstream commits)" +[tasks."release:prepare"] +description = "Update CHANGELOG.md and Cargo.toml to the next version" shell = "bash -c" -run = "comm -23 <(git rev-list HEAD | sort) <(git rev-list --first-parent HEAD | sort) > .cliffignore" - -[tasks.release] -description = "Update release versions and changelogs locally" -run = "release-plz update" +run = [ + # Ignore commits off HEAD's first-parent chain (branch and upstream commits) + "comm -23 <(git rev-list HEAD | sort) <(git rev-list --first-parent HEAD | sort) > .cliffignore", + "git cliff --bump -o CHANGELOG.md", + 'version=$(git cliff --bumped-version) && release-plz set-version "${version#v}"', +] From 8d027d9194a5a59655cfa1cb1313efd7cfa89592 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Sun, 11 Oct 2026 23:36:03 +0100 Subject: [PATCH 75/83] Run mise's release-plz directly instead of release-plz/action release-plz/action reinstalled release-plz with cargo-binstall on every run, though mise had already installed mise.lock's version. Both jobs now call it directly, with GIT_TOKEN for the token and the repo URL and forge inferred from the checkout's remote. Of what else the action did: - its git identity step stays, as release-plz/git-config with gh from mise.lock, but only in release-pr: release-plz commits locally when it updates an existing release PR, while release creates tags and releases through the API - release-pr no longer gets CARGO_REGISTRY_TOKEN, as it never publishes The action also installed cargo-semver-checks, which isn't carried over as it never did anything here: release-plz skips its check because the release-pr job has already set the new version, and cargo-semver-checks 0.51 needs Rust 1.93 to run, above our pinned 1.88. --- .github/workflows/release.yml | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 85a8c43..6aa242d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -27,17 +27,13 @@ jobs: persist-credentials: true token: ${{ secrets.RELEASE_PLZ_TOKEN }} - &install-tools - id: mise uses: jdx/mise-action@2d8d4cafcbd33be2ea37d2b6f5ad595363d1f1ca # v5.1.1 with: - install_args: rust mr-boxington release-plz git-cliff - - uses: release-plz/action@f393a7b38aca9d9b4c58447b291dbac963901df6 # v0.5.141 - with: - version: ${{ steps.mise.outputs.release-plz }} - command: release - env: - GITHUB_TOKEN: ${{ secrets.RELEASE_PLZ_TOKEN }} + install_args: rust mr-boxington release-plz git-cliff gh + - env: + GIT_TOKEN: ${{ secrets.RELEASE_PLZ_TOKEN }} CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} + run: release-plz release release-pr: if: ${{ github.repository_owner == 'eugencowie' }} @@ -50,11 +46,11 @@ jobs: steps: - *checkout - *install-tools - - run: mise run release:prepare - - uses: release-plz/action@f393a7b38aca9d9b4c58447b291dbac963901df6 # v0.5.141 - with: - version: ${{ steps.mise.outputs.release-plz }} - command: release-pr + # release-plz commits locally when it updates an existing release PR + - uses: release-plz/git-config@59144859caf016f8b817a2ac9b051578729173c4 # v0.1.2 env: GITHUB_TOKEN: ${{ secrets.RELEASE_PLZ_TOKEN }} - CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} + - run: mise run release:prepare + - env: + GIT_TOKEN: ${{ secrets.RELEASE_PLZ_TOKEN }} + run: release-plz release-pr From 4cb75c1b9b39a75c30760590f4e8c149dc231c5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Sun, 11 Oct 2026 23:36:09 +0100 Subject: [PATCH 76/83] Add the changelog to release PR and GitHub release bodies Release PR and GitHub release bodies have always had an empty changelog. git-cliff writes CHANGELOG.md, so release-plz runs with changelog_update = false, and with that off it builds no changelog text for either body. Both now get the release's `## ` section of CHANGELOG.md, printed by a new release:notes task, which fails if the section is missing rather than posting an empty changelog. - release-pr: release-plz's default PR body renders an empty Changelog
, as its `{% if changes %}` sees leftover whitespace. The job reads the PR number and version from release-plz's JSON and puts the changelog before the block's closing

, failing if a release-plz upgrade drops it - release-notes.yml: a new workflow runs on each published release and sets its notes, so a failure can be retried with a rerun, which the release job couldn't do once release-plz had published. It uses GitHub's own token with contents: write, and only fires because release-plz creates releases with RELEASE_PLZ_TOKEN, as its comment notes. It takes the version from the tag, so needn't install Rust --- .github/workflows/release-notes.yml | 34 +++++++++++++++++++++++++++++ .github/workflows/release.yml | 16 +++++++++++++- mise.toml | 14 ++++++++++++ release-plz.toml | 3 +++ 4 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/release-notes.yml diff --git a/.github/workflows/release-notes.yml b/.github/workflows/release-notes.yml new file mode 100644 index 0000000..0e5c5a4 --- /dev/null +++ b/.github/workflows/release-notes.yml @@ -0,0 +1,34 @@ +name: Release notes + +# Fires for releases release-plz creates with RELEASE_PLZ_TOKEN. Events from +# GitHub's own token don't trigger workflows, so this relies on that token. +on: + release: + types: [published] + +concurrency: + group: ${{ github.workflow }}-${{ github.event.release.tag_name }} + cancel-in-progress: false + +env: + MISE_TASK_RUN_AUTO_INSTALL: false + +jobs: + release-notes: + if: ${{ github.repository_owner == 'eugencowie' }} + name: Release notes + runs-on: ubuntu-latest + timeout-minutes: 10 + permissions: + contents: write + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: { persist-credentials: false } + - uses: jdx/mise-action@2d8d4cafcbd33be2ea37d2b6f5ad595363d1f1ca # v5.1.1 + with: { install_args: gh, cache: false } + - env: + GH_TOKEN: ${{ github.token }} + TAG: ${{ github.event.release.tag_name }} + run: | + notes=$(mise run release:notes "${TAG#v}") + gh release edit "$TAG" --notes "$notes" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6aa242d..e1fe801 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -53,4 +53,18 @@ jobs: - run: mise run release:prepare - env: GIT_TOKEN: ${{ secrets.RELEASE_PLZ_TOKEN }} - run: release-plz release-pr + GH_TOKEN: ${{ secrets.RELEASE_PLZ_TOKEN }} + run: | + out=$(release-plz release-pr -o json) + pr=$(jq -r '.prs[0].number // empty' <<< "$out") + [ -n "$pr" ] || exit 0 + version=$(jq -r '.prs[0].releases[0].version' <<< "$out") + notes=$(mise run release:notes "$version") + # Fill the empty Changelog
in release-plz's default PR body + end='

' + body=$(gh pr view "$pr" --json body --jq .body) + if [[ "$body" != *"$end"* ]]; then + echo "::error::PR body has no $end to put the changelog before" + exit 1 + fi + gh pr edit "$pr" --body "${body/"$end"/"$notes"$'\n\n'"$end"}" diff --git a/mise.toml b/mise.toml index 9f3a9e3..6f807ef 100644 --- a/mise.toml +++ b/mise.toml @@ -76,3 +76,17 @@ run = [ "git cliff --bump -o CHANGELOG.md", 'version=$(git cliff --bumped-version) && release-plz set-version "${version#v}"', ] + +[tasks."release:notes"] +hide = true +description = "Print a version's changelog section" +usage = 'arg "" help="Version, without a leading v"' +quiet = true +run = ''' +awk -v heading="## $usage_version" ' + $0 == heading {in_section=1; next} + /^## / {in_section=0} + in_section {print; if (NF) found=1} + END {if (!found) {print "CHANGELOG.md has no " heading " section" > "/dev/stderr"; exit 1}} +' CHANGELOG.md +''' diff --git a/release-plz.toml b/release-plz.toml index e7cb105..a19cbeb 100644 --- a/release-plz.toml +++ b/release-plz.toml @@ -1,5 +1,8 @@ [workspace] allow_dirty = true +# git-cliff writes CHANGELOG.md, so release-plz leaves the changelog out of PR +# and release bodies. release.yml's release-pr job and release-notes.yml fill +# them in from CHANGELOG.md. changelog_update = false git_release_name = "{{ version }}" pr_name = "chore(release): Release {{ version }}" From 381d5d8c17fe8cb8597f66bddc543b6d817d1894 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Mon, 12 Oct 2026 01:00:39 +0100 Subject: [PATCH 77/83] chore(release): Release 0.3.1 --- CHANGELOG.md | 21 +++++++++++++++++++++ Cargo.lock | 2 +- Cargo.toml | 2 +- 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 11e4d65..6b96bc5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,26 @@ # Changelog +## 0.3.1 + +### ๐Ÿšœ Refactor + +- Move fork-only code out of upstream-shared files ([#25](https://github.com/eugencowie/aptabase-rs/pull/25)) + +### ๐Ÿงช Testing + +- Test event delivery against a local stand-in server ([#28](https://github.com/eugencowie/aptabase-rs/pull/28)) + +### โš™๏ธ Miscellaneous Tasks + +- Stop tracking the example's lockfile ([#23](https://github.com/eugencowie/aptabase-rs/pull/23)) +- Match upstream's Rust edition and require Rust 1.88 ([#21](https://github.com/eugencowie/aptabase-rs/pull/21)) +- Stop mise run from installing unused tools in CI ([#26](https://github.com/eugencowie/aptabase-rs/pull/26)) +- Pin release-plz and git-cliff to mise.lock versions ([#27](https://github.com/eugencowie/aptabase-rs/pull/27)) +- Switch to jactionlint and harden the workflows ([#29](https://github.com/eugencowie/aptabase-rs/pull/29)) +- Cache builds with mbx ([#31](https://github.com/eugencowie/aptabase-rs/pull/31)) +- Install mbx for release jobs ([#32](https://github.com/eugencowie/aptabase-rs/pull/32)) +- Add the changelog to release PR and GitHub release bodies ([#33](https://github.com/eugencowie/aptabase-rs/pull/33)) + ## 0.3.0 ### ๐Ÿš€ Features diff --git a/Cargo.lock b/Cargo.lock index e73f463..b1383d8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -22,7 +22,7 @@ dependencies = [ [[package]] name = "aptabase-rs" -version = "0.3.0" +version = "0.3.1" dependencies = [ "futures", "log", diff --git a/Cargo.toml b/Cargo.toml index 254c7bb..4c18c14 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "aptabase-rs" -version = "0.3.0" +version = "0.3.1" license = "MIT" description = "Rust SDK for Aptabase: Open Source, Privacy-First and Simple Analytics for Mobile, Desktop and Web Apps" authors = ["Eugรฉn Cowie"] From c751fd772f1a1d0b76d595c808d735622d0db9b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Mon, 12 Oct 2026 02:39:51 +0100 Subject: [PATCH 78/83] Install only the tools each release job uses Both release jobs shared one mise-action step through an anchor, so the release job installed git-cliff and gh it never runs. Each job now has its own step: prepare-release keeps all five tools, and release installs only rust, mr-boxington and release-plz, which cargo publish needs. release-plz creates the GitHub release through the API, and with changelog_update = false it doesn't use git-cliff. - the release-pr job is now prepare-release, and runs before release - the checkout anchor goes too, as the two jobs no longer share steps - one-line with and env blocks use flow style, and env follows run, to match ci.yml and release-notes.yml - the PR-body script checks for the Changelog
before reading the notes --- .github/workflows/ci.yml | 4 +- .github/workflows/release-notes.yml | 10 ++--- .github/workflows/release.yml | 69 +++++++++++++---------------- release-plz.toml | 3 -- 4 files changed, 36 insertions(+), 50 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 89b7db7..ac146e3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,7 +38,5 @@ jobs: - uses: jdx/mise-action@2d8d4cafcbd33be2ea37d2b6f5ad595363d1f1ca # v5.1.1 with: { install_args: rust mr-boxington } - uses: jdx/mr-boxington-action@47ce49b32025d93a7a2f4570815afaa2b7092240 # v1.7.2 - with: - working-directory: examples/helloworld - cache-generation: example-v1 + with: { working-directory: examples/helloworld, cache-generation: example-v1 } - run: mise run example:check diff --git a/.github/workflows/release-notes.yml b/.github/workflows/release-notes.yml index 0e5c5a4..4d76bd4 100644 --- a/.github/workflows/release-notes.yml +++ b/.github/workflows/release-notes.yml @@ -1,7 +1,5 @@ name: Release notes -# Fires for releases release-plz creates with RELEASE_PLZ_TOKEN. Events from -# GitHub's own token don't trigger workflows, so this relies on that token. on: release: types: [published] @@ -26,9 +24,9 @@ jobs: with: { persist-credentials: false } - uses: jdx/mise-action@2d8d4cafcbd33be2ea37d2b6f5ad595363d1f1ca # v5.1.1 with: { install_args: gh, cache: false } - - env: - GH_TOKEN: ${{ github.token }} - TAG: ${{ github.event.release.tag_name }} - run: | + - run: | notes=$(mise run release:notes "${TAG#v}") gh release edit "$TAG" --notes "$notes" + env: + GH_TOKEN: ${{ github.token }} + TAG: ${{ github.event.release.tag_name }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e1fe801..bb902ae 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,59 +12,52 @@ env: MISE_TASK_RUN_AUTO_INSTALL: false jobs: - release: - if: ${{ github.repository_owner == 'eugencowie' }} - name: Release - runs-on: ubuntu-latest - timeout-minutes: 30 - permissions: - contents: write - steps: - - &checkout - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - fetch-depth: 0 - persist-credentials: true - token: ${{ secrets.RELEASE_PLZ_TOKEN }} - - &install-tools - uses: jdx/mise-action@2d8d4cafcbd33be2ea37d2b6f5ad595363d1f1ca # v5.1.1 - with: - install_args: rust mr-boxington release-plz git-cliff gh - - env: - GIT_TOKEN: ${{ secrets.RELEASE_PLZ_TOKEN }} - CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} - run: release-plz release - - release-pr: + prepare-release: if: ${{ github.repository_owner == 'eugencowie' }} - name: Release PR + name: Prepare release runs-on: ubuntu-latest timeout-minutes: 30 permissions: contents: write pull-requests: write steps: - - *checkout - - *install-tools - # release-plz commits locally when it updates an existing release PR + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: { fetch-depth: 0, persist-credentials: true, token: "${{ secrets.RELEASE_PLZ_TOKEN }}" } + - uses: jdx/mise-action@2d8d4cafcbd33be2ea37d2b6f5ad595363d1f1ca # v5.1.1 + with: { install_args: rust mr-boxington release-plz git-cliff gh } - uses: release-plz/git-config@59144859caf016f8b817a2ac9b051578729173c4 # v0.1.2 - env: - GITHUB_TOKEN: ${{ secrets.RELEASE_PLZ_TOKEN }} + env: { GITHUB_TOKEN: "${{ secrets.RELEASE_PLZ_TOKEN }}" } - run: mise run release:prepare - - env: - GIT_TOKEN: ${{ secrets.RELEASE_PLZ_TOKEN }} - GH_TOKEN: ${{ secrets.RELEASE_PLZ_TOKEN }} - run: | + - run: | out=$(release-plz release-pr -o json) pr=$(jq -r '.prs[0].number // empty' <<< "$out") [ -n "$pr" ] || exit 0 - version=$(jq -r '.prs[0].releases[0].version' <<< "$out") - notes=$(mise run release:notes "$version") - # Fill the empty Changelog
in release-plz's default PR body - end='

' body=$(gh pr view "$pr" --json body --jq .body) + end='

' if [[ "$body" != *"$end"* ]]; then echo "::error::PR body has no $end to put the changelog before" exit 1 fi + version=$(jq -r '.prs[0].releases[0].version' <<< "$out") + notes=$(mise run release:notes "$version") gh pr edit "$pr" --body "${body/"$end"/"$notes"$'\n\n'"$end"}" + env: + GIT_TOKEN: ${{ secrets.RELEASE_PLZ_TOKEN }} + GH_TOKEN: ${{ secrets.RELEASE_PLZ_TOKEN }} + + release: + if: ${{ github.repository_owner == 'eugencowie' }} + name: Release + runs-on: ubuntu-latest + timeout-minutes: 30 + permissions: + contents: write + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: { fetch-depth: 0, persist-credentials: true, token: "${{ secrets.RELEASE_PLZ_TOKEN }}" } + - uses: jdx/mise-action@2d8d4cafcbd33be2ea37d2b6f5ad595363d1f1ca # v5.1.1 + with: { install_args: rust mr-boxington release-plz } + - run: release-plz release + env: + GIT_TOKEN: ${{ secrets.RELEASE_PLZ_TOKEN }} + CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} diff --git a/release-plz.toml b/release-plz.toml index a19cbeb..e7cb105 100644 --- a/release-plz.toml +++ b/release-plz.toml @@ -1,8 +1,5 @@ [workspace] allow_dirty = true -# git-cliff writes CHANGELOG.md, so release-plz leaves the changelog out of PR -# and release bodies. release.yml's release-pr job and release-notes.yml fill -# them in from CHANGELOG.md. changelog_update = false git_release_name = "{{ version }}" pr_name = "chore(release): Release {{ version }}" From 2f7d5b83188b5c948c35e5a9cb8da2819c28e70c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Mon, 12 Oct 2026 11:36:27 +0100 Subject: [PATCH 79/83] Name workflow steps and write them in block style Steps pinned to a SHA showed up in the Actions UI as "Run jdx/mise-action@2d8d4caf..." or as the first line of their script. Each step now has a name, and with/env blocks use block style so the ${{ }} expressions no longer need quoting. --- .github/workflows/ci.yml | 46 +++++++++++++++++++-------- .github/workflows/release-notes.yml | 18 ++++++++--- .github/workflows/release.yml | 49 +++++++++++++++++++++-------- 3 files changed, 82 insertions(+), 31 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ac146e3..5059ae3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,22 +21,42 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 15 steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: { persist-credentials: false } - - uses: jdx/mise-action@2d8d4cafcbd33be2ea37d2b6f5ad595363d1f1ca # v5.1.1 - with: { install_args: rust mr-boxington jactionlint shellcheck } - - uses: jdx/mr-boxington-action@47ce49b32025d93a7a2f4570815afaa2b7092240 # v1.7.2 - - run: mise run check + - name: Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - name: Install tools + uses: jdx/mise-action@2d8d4cafcbd33be2ea37d2b6f5ad595363d1f1ca # v5.1.1 + with: + install_args: rust mr-boxington jactionlint shellcheck + + - name: Set up build cache + uses: jdx/mr-boxington-action@47ce49b32025d93a7a2f4570815afaa2b7092240 # v1.7.2 + + - name: Run checks + run: mise run check check-example: name: Check example runs-on: ubuntu-latest timeout-minutes: 15 steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: { persist-credentials: false } - - uses: jdx/mise-action@2d8d4cafcbd33be2ea37d2b6f5ad595363d1f1ca # v5.1.1 - with: { install_args: rust mr-boxington } - - uses: jdx/mr-boxington-action@47ce49b32025d93a7a2f4570815afaa2b7092240 # v1.7.2 - with: { working-directory: examples/helloworld, cache-generation: example-v1 } - - run: mise run example:check + - name: Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - name: Install tools + uses: jdx/mise-action@2d8d4cafcbd33be2ea37d2b6f5ad595363d1f1ca # v5.1.1 + with: + install_args: rust mr-boxington + + - name: Set up build cache + uses: jdx/mr-boxington-action@47ce49b32025d93a7a2f4570815afaa2b7092240 # v1.7.2 + with: + working-directory: examples/helloworld + cache-generation: example-v1 + + - name: Run checks + run: mise run example:check diff --git a/.github/workflows/release-notes.yml b/.github/workflows/release-notes.yml index 4d76bd4..9eaa83d 100644 --- a/.github/workflows/release-notes.yml +++ b/.github/workflows/release-notes.yml @@ -20,11 +20,19 @@ jobs: permissions: contents: write steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: { persist-credentials: false } - - uses: jdx/mise-action@2d8d4cafcbd33be2ea37d2b6f5ad595363d1f1ca # v5.1.1 - with: { install_args: gh, cache: false } - - run: | + - name: Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - name: Install tools + uses: jdx/mise-action@2d8d4cafcbd33be2ea37d2b6f5ad595363d1f1ca # v5.1.1 + with: + install_args: gh + cache: false + + - name: Set release notes + run: | notes=$(mise run release:notes "${TAG#v}") gh release edit "$TAG" --notes "$notes" env: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index bb902ae..9b74d85 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -21,14 +21,28 @@ jobs: contents: write pull-requests: write steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: { fetch-depth: 0, persist-credentials: true, token: "${{ secrets.RELEASE_PLZ_TOKEN }}" } - - uses: jdx/mise-action@2d8d4cafcbd33be2ea37d2b6f5ad595363d1f1ca # v5.1.1 - with: { install_args: rust mr-boxington release-plz git-cliff gh } - - uses: release-plz/git-config@59144859caf016f8b817a2ac9b051578729173c4 # v0.1.2 - env: { GITHUB_TOKEN: "${{ secrets.RELEASE_PLZ_TOKEN }}" } - - run: mise run release:prepare - - run: | + - name: Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + fetch-depth: 0 + persist-credentials: true + token: ${{ secrets.RELEASE_PLZ_TOKEN }} + + - name: Install tools + uses: jdx/mise-action@2d8d4cafcbd33be2ea37d2b6f5ad595363d1f1ca # v5.1.1 + with: + install_args: rust mr-boxington release-plz git-cliff gh + + - name: Configure git + uses: release-plz/git-config@59144859caf016f8b817a2ac9b051578729173c4 # v0.1.2 + env: + GITHUB_TOKEN: ${{ secrets.RELEASE_PLZ_TOKEN }} + + - name: Update changelog and version + run: mise run release:prepare + + - name: Create or update release PR + run: | out=$(release-plz release-pr -o json) pr=$(jq -r '.prs[0].number // empty' <<< "$out") [ -n "$pr" ] || exit 0 @@ -53,11 +67,20 @@ jobs: permissions: contents: write steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: { fetch-depth: 0, persist-credentials: true, token: "${{ secrets.RELEASE_PLZ_TOKEN }}" } - - uses: jdx/mise-action@2d8d4cafcbd33be2ea37d2b6f5ad595363d1f1ca # v5.1.1 - with: { install_args: rust mr-boxington release-plz } - - run: release-plz release + - name: Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + fetch-depth: 0 + persist-credentials: true + token: ${{ secrets.RELEASE_PLZ_TOKEN }} + + - name: Install tools + uses: jdx/mise-action@2d8d4cafcbd33be2ea37d2b6f5ad595363d1f1ca # v5.1.1 + with: + install_args: rust mr-boxington release-plz + + - name: Publish release + run: release-plz release env: GIT_TOKEN: ${{ secrets.RELEASE_PLZ_TOKEN }} CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} From a900ec6f3d842579819ba690dfe04c1e2370a771 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Mon, 12 Oct 2026 15:51:44 +0100 Subject: [PATCH 80/83] Regenerate mise.lock in lockfile format v3 mise 2026.10 writes lockfile_version = 3, which records each tool's version specifier from mise.toml and the GitHub repository ID of mr-boxington's signed releases. Tool versions, URLs and checksums are unchanged. --- mise.lock | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/mise.lock b/mise.lock index 4355c7e..5b8ec86 100644 --- a/mise.lock +++ b/mise.lock @@ -1,8 +1,11 @@ # @generated - this file is auto-generated by `mise lock` https://mise.jdx.dev/dev-tools/mise-lock.html +lockfile_version = 3 + [[tools.gh]] version = "2.96.0" backend = "aqua:cli/cli" +specifiers = ["latest"] [tools.gh."platforms.linux-arm64"] checksum = "sha256:06f86ec7103d41993b76cd78072f43595c34aaa56506d971d9860e67140bf909" @@ -49,6 +52,7 @@ provenance = "github-attestations" [[tools.git-cliff]] version = "2.13.1" backend = "aqua:orhun/git-cliff" +specifiers = ["latest"] [tools.git-cliff."platforms.linux-arm64"] checksum = "sha256:9619b7f0c584229f8a2331c1905afe88bd938bdc9102926c2073836a42f02455" @@ -88,6 +92,7 @@ url_api = "https://api.github.com/repos/orhun/git-cliff/releases/assets/40585186 [[tools.jactionlint]] version = "2.0.2" backend = "github:jdx/jactionlint" +specifiers = ["latest"] [tools.jactionlint."platforms.linux-arm64"] checksum = "sha256:978a9f32cc4ed1d81b94563fc49643b8b3a48dcba4d1ea089d9ba05f7e17e7a7" @@ -134,40 +139,48 @@ provenance = "github-attestations" [[tools.mr-boxington]] version = "1.23.0" backend = "packslip:github.com/jdx/mr-boxington" +specifiers = ["latest"] [tools.mr-boxington."platforms.linux-arm64"] checksum = "sha256:7278068e1aa0c2acc9be675eae07584201c4bcb67f2b05b9952dc05979f3ddeb" url = "https://github.com/jdx/mr-boxington/releases/download/v1.23.0/mbx-aarch64-unknown-linux-gnu.tar.gz" signer = "sigstore-oidc:https://github.com/jdx/mr-boxington/.github/workflows/release.yml" +repository_ids = { repository = "1340721957" } [tools.mr-boxington."platforms.linux-arm64-musl"] checksum = "sha256:2f4b304445843cf2d9626ce68a7d7923cc31f2ac924d26632dd6702c71f6df04" url = "https://github.com/jdx/mr-boxington/releases/download/v1.23.0/mbx-aarch64-unknown-linux-musl.tar.gz" signer = "sigstore-oidc:https://github.com/jdx/mr-boxington/.github/workflows/release.yml" +repository_ids = { repository = "1340721957" } [tools.mr-boxington."platforms.linux-x64"] checksum = "sha256:417766e3286c9d669ebf9415f82a78114ac599a4644fda36223258090953d43f" url = "https://github.com/jdx/mr-boxington/releases/download/v1.23.0/mbx-x86_64-unknown-linux-gnu.tar.gz" signer = "sigstore-oidc:https://github.com/jdx/mr-boxington/.github/workflows/release.yml" +repository_ids = { repository = "1340721957" } [tools.mr-boxington."platforms.linux-x64-musl"] checksum = "sha256:f50a708e7e3e67b054596729c7035b61bc872e1fce869d8101f45f8ab355f296" url = "https://github.com/jdx/mr-boxington/releases/download/v1.23.0/mbx-x86_64-unknown-linux-musl.tar.gz" signer = "sigstore-oidc:https://github.com/jdx/mr-boxington/.github/workflows/release.yml" +repository_ids = { repository = "1340721957" } [tools.mr-boxington."platforms.macos-arm64"] checksum = "sha256:e836ef3ef83b15f2fce52a1a9ea2a82ddd8d2b2c8e107152fdc28098c8b41c60" url = "https://github.com/jdx/mr-boxington/releases/download/v1.23.0/mbx-aarch64-apple-darwin.tar.gz" signer = "sigstore-oidc:https://github.com/jdx/mr-boxington/.github/workflows/release.yml" +repository_ids = { repository = "1340721957" } [tools.mr-boxington."platforms.windows-x64"] checksum = "sha256:ecd9a657cacd3e179f91a0a04ef32c458740365cd82f5a29734f8a95bed80710" url = "https://github.com/jdx/mr-boxington/releases/download/v1.23.0/mbx-x86_64-pc-windows-msvc.zip" signer = "sigstore-oidc:https://github.com/jdx/mr-boxington/.github/workflows/release.yml" +repository_ids = { repository = "1340721957" } [[tools.release-plz]] version = "0.3.160" backend = "aqua:release-plz/release-plz" +specifiers = ["latest"] [tools.release-plz."platforms.linux-arm64"] checksum = "sha256:38a64d9fd84083f352e29b628de7506eb0f3b6ca99b8a2305bd1e0fe747be754" @@ -202,6 +215,7 @@ url_api = "https://api.github.com/repos/release-plz/release-plz/releases/assets/ [[tools.rust]] version = "1.88.0" backend = "core:rust" +specifiers = ["1.88"] [tools.rust.options] components = "clippy,rustfmt" @@ -209,6 +223,7 @@ components = "clippy,rustfmt" [[tools.shellcheck]] version = "0.11.0" backend = "aqua:koalaman/shellcheck" +specifiers = ["latest"] [tools.shellcheck."platforms.linux-arm64"] checksum = "sha256:12b331c1d2db6b9eb13cfca64306b1b157a86eb69db83023e261eaa7e7c14588" From 2b13a05e9ac7a0c7b9a2b7a26ead7cc063e8eb5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Mon, 12 Oct 2026 22:43:42 +0100 Subject: [PATCH 81/83] Drop personal-environment patterns from .gitignore The mise local config, agent/editor, Windows, macOS and Linux sections ignore files that come from each contributor's setup rather than from the project. Those belong in a global gitignore. The Rust, dotenv, examples and git-cliff patterns stay. --- .gitignore | 194 ----------------------------------------------------- 1 file changed, 194 deletions(-) diff --git a/.gitignore b/.gitignore index b59f516..fba9493 100644 --- a/.gitignore +++ b/.gitignore @@ -41,197 +41,3 @@ examples/*/Cargo.lock # Generated by `mise run release:prepare` .cliffignore - -# -# https://github.com/github/gitignore/blob/main/Global/mise.gitignore -# - -# https://mise.jdx.dev/configuration.html -# https://mise.jdx.dev/configuration/environments.html -.mise.*.local.toml -.mise.local.toml -mise.*.local.toml -mise.local.toml -.mise/*.local.toml -mise/*.local.toml - -# -# https://github.com/github/gitignore/blob/main/Global/Agents.gitignore -# - -.agents/ -CLAUDE.md -GEMINI.md -WARP.md -CRUSH.md -QWEN.md - -# OpenAI Codex -.codex/ - -# Aider -.aider.input.history -.aider.chat.history.md -.aider.llm.history -.aider.tags.cache.v* -.aiderignore - -# Claude Code -.claude/*.local.json -.claude/**/*.log -CLAUDE.local.md -.claude/ - -# Gemini CLI -gemini-debug.log -.gemini-clipboard/ -.gemini/ - -# Cursor AI -.cursorrules -.cursor/ -.cursor.json -.cursor-settings.yaml - -# Continue -.continue/ -.continuerc.json - -# Cline -.cline/ -.clinerules -cline.json - -# Other agent/editor project config -.warp/ -.crush/ -.codeium/ -.deepseek/ -.amazon-codewhisperer/ -.tabnineignore -.tabnine/ - -# GitHub Copilot -.github/copilot-instructions.md - -# Windsurf Editor -.windsurfrules -.windsurf/ - -# Replit AI Development -.replit -replit.nix - -# -# https://github.com/github/gitignore/blob/main/Global/Windows.gitignore -# - -# Windows thumbnail cache files -Thumbs.db -Thumbs.db:encryptable -ehthumbs.db -ehthumbs_vista.db - -# Dump file -*.stackdump - -# Folder config file -[Dd]esktop.ini - -# Recycle Bin used on file shares -$RECYCLE.BIN/ - -# Windows Installer files -*.cab -*.msi -*.msix -*.msm -*.msp - -# Windows shortcuts -*.lnk - -# -# https://github.com/github/gitignore/blob/main/Global/macOS.gitignore -# - -# General -.DS_Store -.localized -__MACOSX/ -.AppleDouble -.LSOverride -Icon[ -] - -# Resource forks -._* - -# Files and directories that might appear in the root of a volume -.DocumentRevisions-V100 -.fseventsd -.Spotlight-V100 -.TemporaryItems -.Trashes -.VolumeIcon.icns -.com.apple.timemachine.donotpresent -.com.apple.timemachine.supported -.PKInstallSandboxManager -.PKInstallSandboxManager-SystemSoftware -.hotfiles.btree -.vol -.file -.disk_label* -lost+found -.HFS+ Private Directory Data[ -] - -# Directories potentially created on remote AFP share -.AppleDB -.AppleDesktop -Network Trash Folder -Temporary Items -.apdisk - -# Mac OS 6 to 9 -Desktop DB -Desktop DF -TheFindByContentFolder -TheVolumeSettingsFolder -.FBCIndex -.FBCSemaphoreFile -.FBCLockFolder - -# Quota system -.quota.group -.quota.user -.quota.ops.group -.quota.ops.user - -# TimeMachine -Backups.backupdb -.MobileBackups -.MobileBackups.trash -MobileBackups.trash -tmbootpicker.efi - -# -# https://github.com/github/gitignore/blob/main/Global/Linux.gitignore -# - -*~ - -# temporary files which can be created if a process still has a handle open of a deleted file -.fuse_hidden* - -# Metadata left by Dolphin file manager, which comes with KDE Plasma -.directory - -# Linux trash folder which might appear on any partition or disk -.Trash-* - -# .nfs files are created when an open file is removed but is still being accessed -.nfs* - -# Log files created by default by the nohup command -nohup.out From 600ffa99adf49019d8b535c79e36612ede7625e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Mon, 12 Oct 2026 23:04:11 +0100 Subject: [PATCH 82/83] Standardise cliff.toml .cliffignore already removes branch commits and main only takes PR merges, so the PR-number filter is redundant. Dropping it fixes scoped commits being silently skipped and unrecognised types landing under Security, and adds an Other group so unknown types still show up. --- cliff.toml | 39 ++++++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/cliff.toml b/cliff.toml index bdb76da..046e0c7 100644 --- a/cliff.toml +++ b/cliff.toml @@ -1,5 +1,10 @@ +# git-cliff ~ configuration file +# https://git-cliff.org/docs/configuration + [changelog] +# Header template that will be rendered and added to the beginning of the changelog. header = "# Changelog" +# A Tera template to be rendered for each release in the changelog. body = """ {% if version %}\ @@ -16,30 +21,34 @@ body = """ {% endfor %} {% endfor %} """ -postprocessors = [ - { pattern = "\\(#([0-9]+)\\)", replace = "([#${1}](https://github.com/eugencowie/aptabase-rs/pull/${1}))" }, -] [git] -filter_commits = true +# Exclude tags that are not version-shaped. tag_pattern = "^v[0-9]" +# An array of regex based parsers to modify commit messages prior to further processing. +commit_preprocessors = [ + # Replace issue numbers with link templates to be updated in `changelog.postprocessors`. + { pattern = '\(#([0-9]+)\)', replace = "([#${1}](https://github.com/eugencowie/git-vmr/pull/${1}))" }, +] +# An array of regex based parsers for extracting data from the commit message. commit_parsers = [ - { message = "^feat!?: .+ \\(#[0-9]+\\)", group = "๐Ÿš€ Features" }, - { message = "^fix!?: .+ \\(#[0-9]+\\)", group = "๐Ÿ› Bug Fixes" }, - { message = "^docs?!?: .+ \\(#[0-9]+\\)", group = "๐Ÿ“š Documentation" }, - { message = "^perf!?: .+ \\(#[0-9]+\\)", group = "โšก Performance" }, - { message = "^refactor!?: .+ \\(#[0-9]+\\)", group = "๐Ÿšœ Refactor" }, - { message = "^style!?: .+ \\(#[0-9]+\\)", group = "๐ŸŽจ Styling" }, - { message = "^test!?: .+ \\(#[0-9]+\\)", group = "๐Ÿงช Testing" }, + { message = "^feat", group = "๐Ÿš€ Features" }, + { message = "^fix", group = "๐Ÿ› Bug Fixes" }, + { message = "^doc", group = "๐Ÿ“š Documentation" }, + { message = "^perf", group = "โšก Performance" }, + { message = "^refactor", group = "๐Ÿšœ Refactor" }, + { message = "^style", group = "๐ŸŽจ Styling" }, + { message = "^test", group = "๐Ÿงช Testing" }, { message = "^chore\\(release\\)", skip = true }, { message = "^chore\\(deps.*\\)", skip = true }, { message = "^chore\\(pr\\)", skip = true }, { message = "^chore\\(pull\\)", skip = true }, - { message = "^(chore|ci)!?: .+ \\(#[0-9]+\\)", group = "โš™๏ธ Miscellaneous Tasks" }, - { message = "^[a-z]+!?: .+ \\(#[0-9]+\\)", body = ".*security", group = "๐Ÿ›ก๏ธ Security" }, - { message = "^revert!?: .+ \\(#[0-9]+\\)", group = "โ—€๏ธ Revert" }, + { message = "^chore|^ci", group = "โš™๏ธ Miscellaneous Tasks" }, + { body = ".*security", group = "๐Ÿ›ก๏ธ Security" }, + { message = "^revert", group = "โ—€๏ธ Revert" }, + { message = ".*", group = "๐Ÿ’ผ Other" }, ] [bump] -# Breaking changes bump the minor version while pre-1.0 +# Breaking changes bump the minor version while pre-1.0. breaking_always_bump_major = false From 5a332a1aec6eed022d3ab3574230cef82dd5c7a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eug=C3=A9n=20Cowie?= Date: Tue, 13 Oct 2026 13:12:18 +0100 Subject: [PATCH 83/83] Fix repo link --- cliff.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cliff.toml b/cliff.toml index 046e0c7..f8e622f 100644 --- a/cliff.toml +++ b/cliff.toml @@ -28,7 +28,7 @@ tag_pattern = "^v[0-9]" # An array of regex based parsers to modify commit messages prior to further processing. commit_preprocessors = [ # Replace issue numbers with link templates to be updated in `changelog.postprocessors`. - { pattern = '\(#([0-9]+)\)', replace = "([#${1}](https://github.com/eugencowie/git-vmr/pull/${1}))" }, + { pattern = '\(#([0-9]+)\)', replace = "([#${1}](https://github.com/eugencowie/aptabase-rs/pull/${1}))" }, ] # An array of regex based parsers for extracting data from the commit message. commit_parsers = [