app

Local-first trade for farms and co-ops
git clone https://radroots.dev/git/app.git
Log | Files | Refs | README | LICENSE

commit 4b713d0798f1c9d588cb371131d30142d7be7b0f
parent 378652f863f3bb628a6690e6e307f7ae1aa98661
Author: triesap <137732411+triesap@users.noreply.github.com>
Date:   Mon, 10 Feb 2025 12:35:55 +0000

Add `model` crate with tables/database utils. Edit `core` adding keystore and nostr key utils. Edit `tauri` refactoring commands, utils, adding nostr key handlers. Refactor app client for svelte version 5, implement `@radroots/lib-app` view based routes.

Diffstat:
M.gitignore | 67+++++++++++++++++++++++++++++++++----------------------------------
M.gitmodules | 56+++++++++++++++++++++++++++++++++++++++++++++-----------
MCargo.lock | 2361+++++++++++++++++++++++++++++++++++++++++++++++--------------------------------
MCargo.toml | 1+
R.env.example -> app/.env.example | 0
Aapp/LICENSE | 675+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aapp/package.json | 45+++++++++++++++++++++++++++++++++++++++++++++
Aapp/postcss.config.js | 8++++++++
Aapp/src/app.css | 18++++++++++++++++++
Aapp/src/app.d.ts | 5+++++
Aapp/src/app.html | 25+++++++++++++++++++++++++
Aapp/src/lib/locale/en/common.json | 168+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aapp/src/lib/locale/en/error.json | 10++++++++++
Aapp/src/lib/locale/en/eula.json | 48++++++++++++++++++++++++++++++++++++++++++++++++
Aapp/src/lib/locale/en/icu.json | 85+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aapp/src/lib/locale/en/notify.json | 15+++++++++++++++
Aapp/src/lib/locale/i18n.ts | 30++++++++++++++++++++++++++++++
Aapp/src/lib/locale/locales.json | 4++++
Aapp/src/lib/util/conf.ts | 23+++++++++++++++++++++++
Aapp/src/lib/util/err.ts | 6++++++
Aapp/src/lib/util/index.ts | 65+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aapp/src/lib/util/model/location-gcs.ts | 68++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aapp/src/lib/util/nostr/nostr-sync.ts | 130+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aapp/src/lib/util/routes.ts | 14++++++++++++++
Aapp/src/routes/(app)/+error.svelte | 20++++++++++++++++++++
Aapp/src/routes/(app)/+layout.svelte | 69+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aapp/src/routes/(app)/+layout.ts | 19+++++++++++++++++++
Aapp/src/routes/(app)/+page.svelte | 27+++++++++++++++++++++++++++
Aapp/src/routes/(cfg)/+layout.svelte | 18++++++++++++++++++
Aapp/src/routes/(cfg)/+layout.ts | 21+++++++++++++++++++++
Aapp/src/routes/(cfg)/init/+page.svelte | 952+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aapp/src/routes/+layout.svelte | 49+++++++++++++++++++++++++++++++++++++++++++++++++
Aapp/src/routes/+layout.ts | 23+++++++++++++++++++++++
Aapp/static/assets | 1+
Aapp/static/geonames | 1+
Aapp/static/phosphor-icons | 1+
Aapp/static/stylesheets | 1+
Aapp/static/webfonts | 1+
Rsvelte.config.js -> app/svelte.config.js | 0
Aapp/tailwind.config.ts | 157+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Rtsconfig.json -> app/tsconfig.json | 0
Rvite.config.ts -> app/vite.config.ts | 0
Mcrates/core/Cargo.toml | 6++----
Acrates/core/LICENSE | 675+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dcrates/core/src/error.rs | 39---------------------------------------
Acrates/core/src/keystore.rs | 126+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mcrates/core/src/lib.rs | 6+++---
Dcrates/core/src/models/location_gcs.rs | 304-------------------------------------------------------------------------------
Dcrates/core/src/models/log_error.rs | 225-------------------------------------------------------------------------------
Dcrates/core/src/models/media_upload.rs | 277-------------------------------------------------------------------------------
Dcrates/core/src/models/mod.rs | 9---------
Dcrates/core/src/models/nostr_profile.rs | 289------------------------------------------------------------------------------
Dcrates/core/src/models/nostr_profile_relay.rs | 73-------------------------------------------------------------------------
Dcrates/core/src/models/nostr_relay.rs | 289------------------------------------------------------------------------------
Dcrates/core/src/models/trade_product.rs | 304-------------------------------------------------------------------------------
Dcrates/core/src/models/trade_product_location.rs | 73-------------------------------------------------------------------------
Dcrates/core/src/models/trade_product_media.rs | 73-------------------------------------------------------------------------
Acrates/core/src/nostr/keys.rs | 27+++++++++++++++++++++++++++
Acrates/core/src/nostr/mod.rs | 1+
Mcrates/core/src/types.rs | 19++++++++++++++-----
Acrates/core/src/util.rs | 37+++++++++++++++++++++++++++++++++++++
Dcrates/core/src/utils.rs | 11-----------
Acrates/model/Cargo.toml | 14++++++++++++++
Acrates/model/LICENSE | 675+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acrates/model/src/error.rs | 15+++++++++++++++
Acrates/model/src/lib.rs | 4++++
Acrates/model/src/tables/location_gcs.rs | 254+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acrates/model/src/tables/log_error.rs | 226+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acrates/model/src/tables/media_image.rs | 218+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acrates/model/src/tables/mod.rs | 21+++++++++++++++++++++
Acrates/model/src/tables/nostr_profile.rs | 234+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acrates/model/src/tables/nostr_profile_relay.rs | 58++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acrates/model/src/tables/nostr_relay.rs | 234+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acrates/model/src/tables/trade_product.rs | 260+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acrates/model/src/tables/trade_product_location.rs | 58++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acrates/model/src/tables/trade_product_media.rs | 58++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acrates/model/src/types.rs | 9+++++++++
Acrates/model/src/util.rs | 20++++++++++++++++++++
Mcrates/tauri/Cargo.toml | 31+++++++++++++------------------
Dcrates/tauri/Info.ios.plist | 29-----------------------------
Acrates/tauri/LICENSE | 675+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mcrates/tauri/capabilities/default.json | 19+++++++------------
Acrates/tauri/capabilities/desktop.json | 14++++++++++++++
Acrates/tauri/capabilities/mobile.json | 20++++++++++++++++++++
Mcrates/tauri/icons/icon.icns | 0
Acrates/tauri/icons/ios/AppIcon-20x20@1x.png | 0
Acrates/tauri/icons/ios/AppIcon-20x20@2x-1.png | 0
Acrates/tauri/icons/ios/AppIcon-20x20@2x.png | 0
Acrates/tauri/icons/ios/AppIcon-20x20@3x.png | 0
Acrates/tauri/icons/ios/AppIcon-29x29@1x.png | 0
Acrates/tauri/icons/ios/AppIcon-29x29@2x-1.png | 0
Acrates/tauri/icons/ios/AppIcon-29x29@2x.png | 0
Acrates/tauri/icons/ios/AppIcon-29x29@3x.png | 0
Acrates/tauri/icons/ios/AppIcon-40x40@1x.png | 0
Acrates/tauri/icons/ios/AppIcon-40x40@2x-1.png | 0
Acrates/tauri/icons/ios/AppIcon-40x40@2x.png | 0
Acrates/tauri/icons/ios/AppIcon-40x40@3x.png | 0
Acrates/tauri/icons/ios/AppIcon-512@2x.png | 0
Acrates/tauri/icons/ios/AppIcon-60x60@2x.png | 0
Acrates/tauri/icons/ios/AppIcon-60x60@3x.png | 0
Acrates/tauri/icons/ios/AppIcon-76x76@1x.png | 0
Acrates/tauri/icons/ios/AppIcon-76x76@2x.png | 0
Acrates/tauri/icons/ios/AppIcon-83.5x83.5@2x.png | 0
Dcrates/tauri/migrations/0001_location_gcs.sql | 21---------------------
Dcrates/tauri/migrations/0005_media_upload.sql | 12------------
Dcrates/tauri/migrations/0006_log_error.sql | 13-------------
Dcrates/tauri/migrations/0009_trade_product_media.sql | 8--------
Acrates/tauri/migrations/down/0001_location_gcs.sql | 2++
Acrates/tauri/migrations/down/0002_trade_product.sql | 2++
Acrates/tauri/migrations/down/0003_nostr_profile.sql | 2++
Acrates/tauri/migrations/down/0004_nostr_relay.sql | 2++
Acrates/tauri/migrations/down/0005_media_image.sql | 2++
Acrates/tauri/migrations/down/0006_log_error.sql | 2++
Acrates/tauri/migrations/down/0007_nostr_profile_relay.sql | 2++
Acrates/tauri/migrations/down/0008_trade_product_location.sql | 2++
Acrates/tauri/migrations/down/0009_trade_product_media.sql | 2++
Acrates/tauri/migrations/up/0001_location_gcs.sql | 21+++++++++++++++++++++
Rcrates/tauri/migrations/0002_trade_product.sql -> crates/tauri/migrations/up/0002_trade_product.sql | 0
Rcrates/tauri/migrations/0003_nostr_profile.sql -> crates/tauri/migrations/up/0003_nostr_profile.sql | 0
Rcrates/tauri/migrations/0004_nostr_relay.sql -> crates/tauri/migrations/up/0004_nostr_relay.sql | 0
Acrates/tauri/migrations/up/0005_media_image.sql | 12++++++++++++
Acrates/tauri/migrations/up/0006_log_error.sql | 14++++++++++++++
Rcrates/tauri/migrations/0007_nostr_profile_relay.sql -> crates/tauri/migrations/up/0007_nostr_profile_relay.sql | 0
Rcrates/tauri/migrations/0008_trade_product_location.sql -> crates/tauri/migrations/up/0008_trade_product_location.sql | 0
Acrates/tauri/migrations/up/0009_trade_product_media.sql | 8++++++++
Acrates/tauri/src/commands/keys.rs | 74++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acrates/tauri/src/commands/mod.rs | 2++
Acrates/tauri/src/commands/model/location_gcs.rs | 61+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acrates/tauri/src/commands/model/log_error.rs | 61+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acrates/tauri/src/commands/model/media_image.rs | 61+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acrates/tauri/src/commands/model/mod.rs | 21+++++++++++++++++++++
Acrates/tauri/src/commands/model/nostr_profile.rs | 61+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acrates/tauri/src/commands/model/nostr_profile_relay.rs | 29+++++++++++++++++++++++++++++
Acrates/tauri/src/commands/model/nostr_relay.rs | 61+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acrates/tauri/src/commands/model/trade_product.rs | 61+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acrates/tauri/src/commands/model/trade_product_location.rs | 29+++++++++++++++++++++++++++++
Acrates/tauri/src/commands/model/trade_product_media.rs | 29+++++++++++++++++++++++++++++
Dcrates/tauri/src/database.rs | 30------------------------------
Mcrates/tauri/src/lib.rs | 182+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
Dcrates/tauri/src/models/location_gcs.rs | 60------------------------------------------------------------
Dcrates/tauri/src/models/log_error.rs | 46----------------------------------------------
Dcrates/tauri/src/models/media_upload.rs | 60------------------------------------------------------------
Dcrates/tauri/src/models/mod.rs | 9---------
Dcrates/tauri/src/models/nostr_profile.rs | 60------------------------------------------------------------
Dcrates/tauri/src/models/nostr_profile_relay.rs | 45---------------------------------------------
Dcrates/tauri/src/models/nostr_relay.rs | 60------------------------------------------------------------
Dcrates/tauri/src/models/trade_product.rs | 60------------------------------------------------------------
Dcrates/tauri/src/models/trade_product_location.rs | 45---------------------------------------------
Dcrates/tauri/src/models/trade_product_media.rs | 45---------------------------------------------
Mcrates/tauri/src/radroots.rs | 23++++++++++++++++++++++-
Acrates/tauri/src/util.rs | 63+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mcrates/tauri/tauri.conf.json | 33++++++++++-----------------------
Mpackage.json | 66+++++++++++++++---------------------------------------------------
Apackages/client | 1+
Apackages/eslint | 1+
Apackages/geocoder | 1+
Apackages/lib-app | 1+
Apackages/models | 1+
Apackages/svelte-maplibre | 1+
Apackages/theme | 1+
Apackages/tsconfig | 1+
Apackages/util | 1+
Apnpm-lock.yaml | 7314+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apnpm-workspace.yaml | 3+++
Dpostcss.config.js | 7-------
Dsrc/app.css | 27---------------------------
Dsrc/app.d.ts | 138-------------------------------------------------------------------------------
Dsrc/app.html | 27---------------------------
Dsrc/lib/client.ts | 19-------------------
Dsrc/lib/components/image_upload_add_photo.svelte | 42------------------------------------------
Dsrc/lib/components/image_upload_control.svelte | 269-------------------------------------------------------------------------------
Dsrc/lib/components/image_upload_edit_envelope.svelte | 160-------------------------------------------------------------------------------
Dsrc/lib/components/line_entries_between.svelte | 23-----------------------
Dsrc/lib/components/line_entry_data.svelte | 17-----------------
Dsrc/lib/components/line_entry_label.svelte | 17-----------------
Dsrc/lib/components/map_marker_dot.svelte | 15---------------
Dsrc/lib/components/map_point_display.svelte | 24------------------------
Dsrc/lib/components/map_point_select.svelte | 80-------------------------------------------------------------------------------
Dsrc/lib/components/map_point_select_envelope.svelte | 153-------------------------------------------------------------------------------
Dsrc/lib/components/map_popup_point_geolocation.svelte | 87-------------------------------------------------------------------------------
Dsrc/lib/components/search_result_container.svelte | 16----------------
Dsrc/lib/components/search_result_display.svelte | 204-------------------------------------------------------------------------------
Dsrc/lib/components/trade_field_display_el.svelte | 59-----------------------------------------------------------
Dsrc/lib/components/trade_field_display_kv.svelte | 84-------------------------------------------------------------------------------
Dsrc/lib/components/trade_product_list_card.svelte | 350-------------------------------------------------------------------------------
Dsrc/lib/conf.ts | 88-------------------------------------------------------------------------------
Dsrc/lib/types.ts | 20--------------------
Dsrc/lib/util/client.ts | 89-------------------------------------------------------------------------------
Dsrc/lib/util/fetch-radroots-profile.ts | 129-------------------------------------------------------------------------------
Dsrc/lib/util/fetch-radroots-upload.ts | 52----------------------------------------------------
Dsrc/lib/util/fetch.ts | 73-------------------------------------------------------------------------
Dsrc/lib/util/geocode.ts | 52----------------------------------------------------
Dsrc/lib/util/kv.ts | 32--------------------------------
Dsrc/lib/util/models-location-gcs.ts | 72------------------------------------------------------------------------
Dsrc/lib/util/models-media-upload.ts | 116-------------------------------------------------------------------------------
Dsrc/lib/util/models-trade-product.ts | 123-------------------------------------------------------------------------------
Dsrc/lib/util/nostr-sync.ts | 147-------------------------------------------------------------------------------
Dsrc/routes/(app)/+layout.svelte | 108-------------------------------------------------------------------------------
Dsrc/routes/(app)/+layout.ts | 36------------------------------------
Dsrc/routes/(app)/+page.svelte | 67-------------------------------------------------------------------
Dsrc/routes/(app)/farm/land/+page.svelte | 136-------------------------------------------------------------------------------
Dsrc/routes/(app)/farm/land/add/+page.svelte | 462-------------------------------------------------------------------------------
Dsrc/routes/(app)/farm/land/view/+page.svelte | 90-------------------------------------------------------------------------------
Dsrc/routes/(app)/models/location-gcs/+page.svelte | 164-------------------------------------------------------------------------------
Dsrc/routes/(app)/models/nostr-profile/+page.svelte | 340-------------------------------------------------------------------------------
Dsrc/routes/(app)/models/nostr-profile/edit/field/+page.svelte | 243-------------------------------------------------------------------------------
Dsrc/routes/(app)/models/nostr-profile/view/+page.svelte | 500-------------------------------------------------------------------------------
Dsrc/routes/(app)/models/nostr-relay/+page.svelte | 217-------------------------------------------------------------------------------
Dsrc/routes/(app)/models/nostr-relay/view/+page.svelte | 363-------------------------------------------------------------------------------
Dsrc/routes/(app)/models/trade-product/+page.svelte | 109-------------------------------------------------------------------------------
Dsrc/routes/(app)/models/trade-product/add/+page.svelte | 1795-------------------------------------------------------------------------------
Dsrc/routes/(app)/models/trade-product/view/+page.svelte | 75---------------------------------------------------------------------------
Dsrc/routes/(app)/notifications/+page.svelte | 36------------------------------------
Dsrc/routes/(app)/search/+page.svelte | 118-------------------------------------------------------------------------------
Dsrc/routes/(app)/settings/+page.svelte | 424-------------------------------------------------------------------------------
Dsrc/routes/(app)/settings/nostr/+page.svelte | 142-------------------------------------------------------------------------------
Dsrc/routes/(app)/settings/profile/+page.svelte | 398-------------------------------------------------------------------------------
Dsrc/routes/(app)/settings/profile/edit/+page.svelte | 210-------------------------------------------------------------------------------
Dsrc/routes/(app)/test/+page.svelte | 4----
Dsrc/routes/(cfg)/cfg/+layout.ts | 27---------------------------
Dsrc/routes/(cfg)/cfg/error/+page.svelte | 25-------------------------
Dsrc/routes/(cfg)/cfg/init/+page.svelte | 1143-------------------------------------------------------------------------------
Dsrc/routes/+error.svelte | 29-----------------------------
Dsrc/routes/+layout.svelte | 123-------------------------------------------------------------------------------
Dsrc/routes/+layout.ts | 22----------------------
Dstatic/geonames | 1-
Dstatic/keyva.min.js | 1-
Dstatic/phosphor-icons | 1-
Dstatic/splashscreen.html | 27---------------------------
Dstatic/sql-wasm.wasm | 0
Dstatic/squircle.min.js | 2--
Dstatic/stylesheets | 1-
Dstatic/webfonts | 1-
Dtailwind.config.ts | 192-------------------------------------------------------------------------------
Aturbo.json | 36++++++++++++++++++++++++++++++++++++
235 files changed, 16461 insertions(+), 14204 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -1,36 +1,18 @@ -# dependencies node_modules -.pnp -.pnp.js - -# testing -coverage - -# svelte -.svelte-kit - -# misc -.DS_Store -*.pem - -# debug -npm-debug.log* -yarn-debug.log* -yarn-error.log* - -# local env files -.env.local -.env.development.local -.env.test.local -.env.production.local - -# turbo -.turbo # Output .output .vercel -/build +.netlify +.wrangler +.svelte-kit +build +.turbo +dist + +# OS +.DS_Store +Thumbs.db # Env .env @@ -42,13 +24,30 @@ yarn-error.log* vite.config.js.timestamp-* vite.config.ts.timestamp-* -_tmp +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# local +.tmp* +.backup* +.dev* .vscode notes*.txt -justfile -dist +notes*.md git-diff.txt +justfile +setup.py + target -tauri.conf.build* /crates/tauri/gen -.dev -\ No newline at end of file +/crates/tauri/tauri*.conf.json +/crates/tauri/conf +/crates/tauri/Entitlements*.plist +!/crates/tauri/tauri.conf.json +app*.png +\ No newline at end of file diff --git a/.gitmodules b/.gitmodules @@ -1,13 +1,47 @@ -[submodule "static/webfonts"] - path = static/webfonts - url = git@github.com:72-61-64-72-6f-6f-74-73/webfonts.git -[submodule "static/phosphor-icons"] - path = static/phosphor-icons - url = git@github.com:72-61-64-72-6f-6f-74-73/phosphor-icons.git -[submodule "static/stylesheets"] - path = static/stylesheets - url = git@github.com:72-61-64-72-6f-6f-74-73/stylesheets.git -[submodule "static/geonames"] - path = static/geonames +[submodule "packages/eslint"] + path = packages/eslint + url = git@github.com:72-61-64-72-6f-6f-74-73/packages-eslint.git +[submodule "packages/tsconfig"] + path = packages/tsconfig + url = git@github.com:72-61-64-72-6f-6f-74-73/packages-tsconfig.git +[submodule "packages/geocoder"] + path = packages/geocoder + url = git@github.com:72-61-64-72-6f-6f-74-73/packages-geocoder.git +[submodule "packages/client"] + path = packages/client + url = git@github.com:72-61-64-72-6f-6f-74-73/packages-client.git +[submodule "packages/models"] + path = packages/models + url = git@github.com:72-61-64-72-6f-6f-74-73/packages-models.git +[submodule "packages/theme"] + path = packages/theme + url = git@github.com:72-61-64-72-6f-6f-74-73/packages-theme.git +[submodule "packages/util"] + path = packages/util + url = git@github.com:72-61-64-72-6f-6f-74-73/packages-util.git +[submodule "packages/svelte-maplibre"] + path = packages/svelte-maplibre + url = git@github.com:72-61-64-72-6f-6f-74-73/svelte-maplibre.git +[submodule "packages/lib-app"] + path = packages/lib-app + url = git@github.com:72-61-64-72-6f-6f-74-73/packages-lib-app.git +[submodule "app/static/geonames"] + path = app/static/geonames url = git@github.com:72-61-64-72-6f-6f-74-73/geonames.git branch = database +[submodule "app/static/phosphor-icons"] + path = app/static/phosphor-icons + url = git@github.com:72-61-64-72-6f-6f-74-73/phosphor-icons.git + branch = app +[submodule "app/static/stylesheets"] + path = app/static/stylesheets + url = git@github.com:72-61-64-72-6f-6f-74-73/stylesheets.git + branch = master +[submodule "app/static/webfonts"] + path = app/static/webfonts + url = git@github.com:72-61-64-72-6f-6f-74-73/webfonts.git + branch = app +[submodule "app/static/assets"] + path = app/static/assets + url = git@github.com:72-61-64-72-6f-6f-74-73/assets.git + branch = app diff --git a/Cargo.lock b/Cargo.lock @@ -24,26 +24,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" [[package]] -name = "ahash" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" -dependencies = [ - "getrandom 0.2.15", - "once_cell", - "version_check", -] - -[[package]] -name = "ahash" -version = "0.8.11" +name = "aead" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" dependencies = [ - "cfg-if", - "once_cell", - "version_check", - "zerocopy", + "crypto-common", + "generic-array", ] [[package]] @@ -72,9 +59,9 @@ dependencies = [ [[package]] name = "allocator-api2" -version = "0.2.18" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "android-tzdata" @@ -83,23 +70,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" [[package]] -name = "android_log-sys" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ecc8056bf6ab9892dcd53216c83d1597487d7dacac16c8df6b877d127df9937" - -[[package]] -name = "android_logger" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05b07e8e73d720a1f2e4b6014766e6039fd2e96a4fa44e2a78d0e1fa2ff49826" -dependencies = [ - "android_log-sys", - "env_filter", - "log", -] - -[[package]] name = "android_system_properties" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -109,59 +79,10 @@ dependencies = [ ] [[package]] -name = "anstream" -version = "0.6.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" -dependencies = [ - "anstyle", - "anstyle-parse", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "is_terminal_polyfill", - "utf8parse", -] - -[[package]] -name = "anstyle" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" - -[[package]] -name = "anstyle-parse" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" -dependencies = [ - "utf8parse", -] - -[[package]] -name = "anstyle-query" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" -dependencies = [ - "windows-sys 0.52.0", -] - -[[package]] -name = "anstyle-wincon" -version = "3.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" -dependencies = [ - "anstyle", - "windows-sys 0.52.0", -] - -[[package]] name = "anyhow" -version = "1.0.89" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" +checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" [[package]] name = "arrayvec" @@ -171,9 +92,9 @@ checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" [[package]] name = "ashpd" -version = "0.9.2" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d43c03d9e36dd40cab48435be0b09646da362c278223ca535493877b2c1dee9" +checksum = "e9c39d707614dbcc6bed00015539f488d8e3fe3e66ed60961efc0c90f4b380b3" dependencies = [ "enumflags2", "futures-channel", @@ -187,14 +108,14 @@ dependencies = [ "wayland-backend", "wayland-client", "wayland-protocols", - "zbus", + "zbus 5.2.0", ] [[package]] name = "async-broadcast" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20cd0e2e25ea8e5f7e9df04578dc6cf5c83577fd09b1a46aaf5c85e1c33f2a7e" +checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532" dependencies = [ "event-listener", "event-listener-strategy", @@ -240,9 +161,9 @@ dependencies = [ [[package]] name = "async-io" -version = "2.3.4" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "444b0228950ee6501b3568d3c93bf1176a1fdbc3b758dcd9475046d30f4dc7e8" +checksum = "43a2b323ccce0a1d90b449fd71f2a06ca7faa7c54c2751f06c9bd851fc061059" dependencies = [ "async-lock", "cfg-if", @@ -295,7 +216,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.95", ] [[package]] @@ -324,20 +245,51 @@ checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" [[package]] name = "async-trait" -version = "0.1.83" +version = "0.1.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" +checksum = "1b1244b10dcd56c92219da4e14caa97e312079e185f04ba3eea25061561dc0a0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.95", +] + +[[package]] +name = "async-utility" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a349201d80b4aa18d17a34a182bdd7f8ddf845e9e57d2ea130a12e10ef1e3a47" +dependencies = [ + "futures-util", + "gloo-timers", + "tokio", + "wasm-bindgen-futures", +] + +[[package]] +name = "async-wsocket" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d50cb541e6d09e119e717c64c46ed33f49be7fa592fa805d56c11d6a7ff093c" +dependencies = [ + "async-utility", + "futures", + "futures-util", + "js-sys", + "tokio", + "tokio-rustls", + "tokio-socks", + "tokio-tungstenite", + "url", + "wasm-bindgen", + "web-sys", ] [[package]] name = "atk" -version = "0.18.0" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4af014b17dd80e8af9fa689b2d4a211ddba6eb583c1622f35d0cb543f6b17e4" +checksum = "241b621213072e993be4f6f3a9e4b45f65b7e6faad43001be957184b7bb1824b" dependencies = [ "atk-sys", "glib", @@ -346,9 +298,9 @@ dependencies = [ [[package]] name = "atk-sys" -version = "0.18.0" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "251e0b7d90e33e0ba930891a505a9a35ece37b2dd37a14f3ffc306c13b980009" +checksum = "c5e48b684b0ca77d2bbadeef17424c2ea3c897d44d566a1617e7e8f30614d086" dependencies = [ "glib-sys", "gobject-sys", @@ -366,6 +318,15 @@ dependencies = [ ] [[package]] +name = "atomic-destructor" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d919cb60ba95c87ba42777e9e246c4e8d658057299b437b7512531ce0a09a23" +dependencies = [ + "tracing", +] + +[[package]] name = "atomic-waker" version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -393,6 +354,16 @@ dependencies = [ ] [[package]] +name = "base58ck" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c8d66485a3a2ea485c1913c4572ce0256067a5377ac8c75c4960e1cda98605f" +dependencies = [ + "bitcoin-internals 0.3.0", + "bitcoin_hashes 0.14.0", +] + +[[package]] name = "base64" version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -411,6 +382,93 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" [[package]] +name = "bech32" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d965446196e3b7decd44aa7ee49e31d630118f90ef12f97900f262eb915c951d" + +[[package]] +name = "bip39" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33415e24172c1b7d6066f6d999545375ab8e1d95421d6784bdfff9496f292387" +dependencies = [ + "bitcoin_hashes 0.13.0", + "serde", + "unicode-normalization", +] + +[[package]] +name = "bitcoin" +version = "0.32.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6bc65742dea50536e35ad42492b234c27904a27f0abdcbce605015cb4ea026" +dependencies = [ + "base58ck", + "bech32", + "bitcoin-internals 0.3.0", + "bitcoin-io", + "bitcoin-units", + "bitcoin_hashes 0.14.0", + "hex-conservative 0.2.1", + "hex_lit", + "secp256k1", + "serde", +] + +[[package]] +name = "bitcoin-internals" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9425c3bf7089c983facbae04de54513cce73b41c7f9ff8c845b54e7bc64ebbfb" + +[[package]] +name = "bitcoin-internals" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30bdbe14aa07b06e6cfeffc529a1f099e5fbe249524f8125358604df99a4bed2" +dependencies = [ + "serde", +] + +[[package]] +name = "bitcoin-io" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b47c4ab7a93edb0c7198c5535ed9b52b63095f4e9b45279c6736cec4b856baf" + +[[package]] +name = "bitcoin-units" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5285c8bcaa25876d07f37e3d30c303f2609179716e11d688f51e8f1fe70063e2" +dependencies = [ + "bitcoin-internals 0.3.0", + "serde", +] + +[[package]] +name = "bitcoin_hashes" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1930a4dabfebb8d7d9992db18ebe3ae2876f0a305fab206fd168df931ede293b" +dependencies = [ + "bitcoin-internals 0.2.0", + "hex-conservative 0.1.2", +] + +[[package]] +name = "bitcoin_hashes" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb18c03d0db0247e147a21a6faafd5a7eb851c743db062de72018b6b7e8e4d16" +dependencies = [ + "bitcoin-io", + "hex-conservative 0.2.1", + "serde", +] + +[[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -426,18 +484,6 @@ dependencies = [ ] [[package]] -name = "bitvec" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" -dependencies = [ - "funty", - "radium", - "tap", - "wyz", -] - -[[package]] name = "block" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -453,6 +499,15 @@ dependencies = [ ] [[package]] +name = "block-padding" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" +dependencies = [ + "generic-array", +] + +[[package]] name = "block2" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -475,34 +530,10 @@ dependencies = [ ] [[package]] -name = "borsh" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d4d6dafc1a3bb54687538972158f07b2c948bc57d5890df22c0739098b3028" -dependencies = [ - "borsh-derive", - "cfg_aliases 0.1.1", -] - -[[package]] -name = "borsh-derive" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4918709cc4dd777ad2b6303ed03cb37f3ca0ccede8c1b0d28ac6db8f4710e0" -dependencies = [ - "once_cell", - "proc-macro-crate 2.0.2", - "proc-macro2", - "quote", - "syn 2.0.79", - "syn_derive", -] - -[[package]] name = "brotli" -version = "6.0.0" +version = "7.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74f7971dbd9326d58187408ab83117d8ac1bb9c17b085fdacd1cf2f598719b6b" +checksum = "cc97b8f16f944bba54f0433f07e30be199b6dc2bd25937444bbad560bcea29bd" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -526,43 +557,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] -name = "byte-unit" -version = "5.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1cd29c3c585209b0cbc7309bfe3ed7efd8c84c21b7af29c8bfae908f8777174" -dependencies = [ - "rust_decimal", - "serde", - "utf8-width", -] - -[[package]] -name = "bytecheck" -version = "0.6.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23cdc57ce23ac53c931e88a43d06d070a6fd142f2617be5855eb75efc9beb1c2" -dependencies = [ - "bytecheck_derive", - "ptr_meta", - "simdutf8", -] - -[[package]] -name = "bytecheck_derive" -version = "0.6.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3db406d29fbcd95542e92559bed4d8ad92636d1ca8b3b72ede10b4bcc010e659" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] name = "bytemuck" -version = "1.19.0" +version = "1.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8334215b81e418a0a7bdb8ef0849474f40bb10c8b71f1c4ed315cff49f32494d" +checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3" [[package]] name = "byteorder" @@ -572,9 +570,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.7.2" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" +checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" dependencies = [ "serde", ] @@ -590,7 +588,7 @@ dependencies = [ "glib", "libc", "once_cell", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -615,25 +613,25 @@ dependencies = [ [[package]] name = "cargo-platform" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24b1f0365a6c6bb4020cd05806fd0d33c44d38046b8bd7f0e40814b9763cabfc" +checksum = "e35af189006b9c0f00a064685c727031e3ed2d8020f7ba284d78cc2671bd36ea" dependencies = [ "serde", ] [[package]] name = "cargo_metadata" -version = "0.18.1" +version = "0.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" +checksum = "8769706aad5d996120af43197bf46ef6ad0fda35216b4505f926a365a232d924" dependencies = [ "camino", "cargo-platform", "semver", "serde", "serde_json", - "thiserror", + "thiserror 2.0.9", ] [[package]] @@ -643,14 +641,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a969e13a7589e9e3e4207e153bae624ade2b5622fb4684a4923b23ec3d57719" dependencies = [ "serde", - "toml 0.8.2", + "toml 0.8.19", +] + +[[package]] +name = "cbc" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6" +dependencies = [ + "cipher", ] [[package]] name = "cc" -version = "1.1.30" +version = "1.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b16803a61b81d9eabb7eae2588776c4c1e584b738ede45fdbb4c972cec1e9945" +checksum = "a012a0df96dd6d06ba9a1b29d6402d1a5d77c6befd2566afdc26e10603dc93d7" dependencies = [ "shlex", ] @@ -690,21 +697,39 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "cfg_aliases" -version = "0.1.1" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] -name = "cfg_aliases" -version = "0.2.1" +name = "chacha20" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "chacha20poly1305" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" +dependencies = [ + "aead", + "chacha20", + "cipher", + "poly1305", + "zeroize", +] [[package]] name = "chrono" -version = "0.4.38" +version = "0.4.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" dependencies = [ "android-tzdata", "iana-time-zone", @@ -716,6 +741,17 @@ dependencies = [ ] [[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", + "zeroize", +] + +[[package]] name = "cocoa" version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -746,12 +782,6 @@ dependencies = [ ] [[package]] -name = "colorchoice" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" - -[[package]] name = "combine" version = "4.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -795,12 +825,13 @@ dependencies = [ [[package]] name = "cookie_store" -version = "0.21.0" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4934e6b7e8419148b6ef56950d277af8561060b56afd59e2aadf98b59fce6baa" +checksum = "2eac901828f88a5241ee0600950ab981148a18f2f756900ffba1b125ca6a3ef9" dependencies = [ "cookie", - "idna 0.5.0", + "document-features", + "idna", "log", "publicsuffix", "serde", @@ -862,9 +893,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.14" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" dependencies = [ "libc", ] @@ -895,27 +926,27 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.13" +version = "0.5.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" +checksum = "06ba6d68e24814cb8de6bb986db8222d3a027d15872cabc0d18817bc3c0e4471" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-queue" -version = "0.3.11" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" +checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-utils" -version = "0.8.20" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crypto-common" @@ -924,6 +955,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", + "rand_core 0.6.4", "typenum", ] @@ -951,17 +983,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" dependencies = [ "quote", - "syn 2.0.79", + "syn 2.0.95", ] [[package]] name = "ctor" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edb49164822f3ee45b17acd4a208cfc1251410cf0cad9a833234c9890774dd9f" +checksum = "32a2785755761f3ddc1492979ce1e48d2c00d09311c39e4466429188f3dd6501" dependencies = [ "quote", - "syn 2.0.79", + "syn 2.0.95", ] [[package]] @@ -985,7 +1017,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.79", + "syn 2.0.95", ] [[package]] @@ -996,10 +1028,16 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", - "syn 2.0.79", + "syn 2.0.95", ] [[package]] +name = "data-encoding" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" + +[[package]] name = "data-url" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1027,17 +1065,6 @@ dependencies = [ ] [[package]] -name = "derivative" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] name = "derive_more" version = "0.99.18" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1047,7 +1074,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn 2.0.79", + "syn 2.0.95", ] [[package]] @@ -1111,12 +1138,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" [[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.95", +] + +[[package]] name = "dlib" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" dependencies = [ - "libloading", + "libloading 0.8.6", ] [[package]] @@ -1139,7 +1177,16 @@ checksum = "f2b99bf03862d7f545ebc28ddd33a665b50865f4dfd84031a393823879bd4c54" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.95", +] + +[[package]] +name = "document-features" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb6969eaabd2421f8a2775cfd2471a2b634372b4a25d41e3bd647b79912850a0" +dependencies = [ + "litrs", ] [[package]] @@ -1201,14 +1248,14 @@ dependencies = [ [[package]] name = "embed-resource" -version = "2.5.0" +version = "2.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4e24052d7be71f0efb50c201557f6fe7d237cfd5a64fd5bcd7fd8fe32dbbffa" +checksum = "b68b6f9f63a0b6a38bc447d4ce84e2b388f3ec95c99c641c8ff0dd3ef89a6379" dependencies = [ "cc", "memchr", "rustc_version", - "toml 0.8.2", + "toml 0.8.19", "vswhom", "winreg", ] @@ -1221,9 +1268,9 @@ checksum = "4ef6b89e5b37196644d8796de5268852ff179b44e96276cf4290264843743bb7" [[package]] name = "encoding_rs" -version = "0.8.34" +version = "0.8.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" dependencies = [ "cfg-if", ] @@ -1252,30 +1299,7 @@ checksum = "de0d48a183585823424a4ce1aa132d174a6a81bd540895822eb4c8373a8e49e8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", -] - -[[package]] -name = "env_filter" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f2c92ceda6ceec50f43169f9ee8424fe2db276791afde7b2cd8bc084cb376ab" -dependencies = [ - "log", - "regex", -] - -[[package]] -name = "env_logger" -version = "0.11.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13fa619b91fb2381732789fc5de83b45675e882f66623b7d8cb4f643017018d" -dependencies = [ - "anstream", - "anstyle", - "env_filter", - "humantime", - "log", + "syn 2.0.95", ] [[package]] @@ -1296,12 +1320,12 @@ dependencies = [ [[package]] name = "errno" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -1328,9 +1352,9 @@ dependencies = [ [[package]] name = "event-listener-strategy" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" +checksum = "3c3e4e0dd3673c1139bf041f3008816d9cf2946bbfac2945c09e523b8d7b05b2" dependencies = [ "event-listener", "pin-project-lite", @@ -1338,29 +1362,20 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.1.1" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "fdeflate" -version = "0.3.5" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8090f921a24b04994d9929e204f50b498a33ea6ba559ffaa05e04f7ee7fb5ab" +checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" dependencies = [ "simd-adler32", ] [[package]] -name = "fern" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69ff9c9d5fb3e6da8ac2f77ab76fe7e8087d512ce095200f8f29ac5b656cf6dc" -dependencies = [ - "log", -] - -[[package]] name = "field-offset" version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1372,28 +1387,19 @@ dependencies = [ [[package]] name = "flate2" -version = "1.0.34" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0" +checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" dependencies = [ "crc32fast", "miniz_oxide", ] [[package]] -name = "fluent-uri" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17c704e9dbe1ddd863da1e6ff3567795087b1eb201ce80d8fa81162e1516500d" -dependencies = [ - "bitflags 1.3.2", -] - -[[package]] name = "flume" -version = "0.11.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" +checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095" dependencies = [ "futures-core", "futures-sink", @@ -1407,6 +1413,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] +name = "foldhash" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" + +[[package]] name = "foreign-types" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1424,7 +1436,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.95", ] [[package]] @@ -1443,12 +1455,6 @@ dependencies = [ ] [[package]] -name = "funty" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" - -[[package]] name = "futf" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1519,9 +1525,9 @@ checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-lite" -version = "2.3.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" +checksum = "cef40d21ae2c515b51041df9ed313ed21e572df340ea58a922a0aefe7e8891a1" dependencies = [ "fastrand", "futures-core", @@ -1538,7 +1544,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.95", ] [[package]] @@ -1582,9 +1588,9 @@ dependencies = [ [[package]] name = "gdk" -version = "0.18.0" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5ba081bdef3b75ebcdbfc953699ed2d7417d6bd853347a42a37d76406a33646" +checksum = "d9f245958c627ac99d8e529166f9823fb3b838d1d41fd2b297af3075093c2691" dependencies = [ "cairo-rs", "gdk-pixbuf", @@ -1623,9 +1629,9 @@ dependencies = [ [[package]] name = "gdk-sys" -version = "0.18.0" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31ff856cb3386dae1703a920f803abafcc580e9b5f711ca62ed1620c25b51ff2" +checksum = "5c2d13f38594ac1e66619e188c6d5a1adb98d11b2fcf7894fc416ad76aa2f3f7" dependencies = [ "cairo-sys-rs", "gdk-pixbuf-sys", @@ -1640,9 +1646,9 @@ dependencies = [ [[package]] name = "gdkwayland-sys" -version = "0.18.0" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a90fbf5c033c65d93792192a49a8efb5bb1e640c419682a58bb96f5ae77f3d4a" +checksum = "140071d506d223f7572b9f09b5e155afbd77428cd5cc7af8f2694c41d98dfe69" dependencies = [ "gdk-sys", "glib-sys", @@ -1654,9 +1660,9 @@ dependencies = [ [[package]] name = "gdkx11" -version = "0.18.0" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2ea8a4909d530f79921290389cbd7c34cb9d623bfe970eaae65ca5f9cd9cce" +checksum = "3caa00e14351bebbc8183b3c36690327eb77c49abc2268dd4bd36b856db3fbfe" dependencies = [ "gdk", "gdkx11-sys", @@ -1668,9 +1674,9 @@ dependencies = [ [[package]] name = "gdkx11-sys" -version = "0.18.0" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fee8f00f4ee46cad2939b8990f5c70c94ff882c3028f3cc5abf950fa4ab53043" +checksum = "6e2e7445fe01ac26f11601db260dd8608fe172514eb63b3b5e261ea6b0f4428d" dependencies = [ "gdk-sys", "glib-sys", @@ -1717,8 +1723,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi 0.11.0+wasi-snapshot-preview1", + "wasm-bindgen", ] [[package]] @@ -1743,7 +1751,7 @@ dependencies = [ "once_cell", "pin-project-lite", "smallvec", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -1779,7 +1787,7 @@ dependencies = [ "memchr", "once_cell", "smallvec", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -1789,11 +1797,11 @@ 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-crate 2.0.0", "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.95", ] [[package]] @@ -1808,9 +1816,21 @@ dependencies = [ [[package]] name = "glob" -version = "0.3.1" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" + +[[package]] +name = "gloo-timers" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", +] [[package]] name = "gobject-sys" @@ -1825,9 +1845,9 @@ dependencies = [ [[package]] name = "gtk" -version = "0.18.1" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93c4f5e0e20b60e10631a5f06da7fe3dda744b05ad0ea71fee2f47adf865890c" +checksum = "fd56fb197bfc42bd5d2751f4f017d44ff59fbb58140c6b49f9b3b2bdab08506a" dependencies = [ "atk", "cairo-rs", @@ -1846,9 +1866,9 @@ dependencies = [ [[package]] name = "gtk-sys" -version = "0.18.0" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "771437bf1de2c1c0b496c11505bdf748e26066bbe942dfc8f614c9460f6d7722" +checksum = "8f29a1c21c59553eb7dd40e918be54dccd60c52b049b75119d5d96ce6b624414" dependencies = [ "atk-sys", "cairo-sys-rs", @@ -1864,22 +1884,22 @@ dependencies = [ [[package]] name = "gtk3-macros" -version = "0.18.0" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6063efb63db582968fb7df72e1ae68aa6360dcfb0a75143f34fc7d616bad75e" +checksum = "52ff3c5b21f14f0736fed6dcfc0bfb4225ebf5725f3c0209edeec181e4d73e9d" dependencies = [ "proc-macro-crate 1.3.1", "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.95", ] [[package]] name = "h2" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205" +checksum = "ccae279728d634d083c00f6099cb58f01cc99c145b84b8be2f6c74618d79922e" dependencies = [ "atomic-waker", "bytes", @@ -1887,7 +1907,7 @@ dependencies = [ "futures-core", "futures-sink", "http", - "indexmap 2.6.0", + "indexmap 2.7.0", "slab", "tokio", "tokio-util", @@ -1899,33 +1919,25 @@ name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -dependencies = [ - "ahash 0.7.8", -] [[package]] name = "hashbrown" -version = "0.14.5" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" dependencies = [ - "ahash 0.8.11", "allocator-api2", + "equivalent", + "foldhash", ] [[package]] -name = "hashbrown" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" - -[[package]] name = "hashlink" -version = "0.9.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af" +checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" dependencies = [ - "hashbrown 0.14.5", + "hashbrown 0.15.2", ] [[package]] @@ -1942,12 +1954,6 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" - -[[package]] -name = "hermit-abi" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" @@ -1959,6 +1965,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] +name = "hex-conservative" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "212ab92002354b4819390025006c897e8140934349e8635c9b077f47b4dcbd20" + +[[package]] +name = "hex-conservative" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5313b072ce3c597065a808dbf612c4c8e8590bdbf8b579508bf7a762c5eae6cd" +dependencies = [ + "arrayvec", +] + +[[package]] +name = "hex_lit" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3011d1213f159867b13cfd6ac92d2cd5f1345762c63be3554e84092d85a50bbd" + +[[package]] name = "hkdf" version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1978,11 +2005,11 @@ dependencies = [ [[package]] name = "home" -version = "0.5.9" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -2001,13 +2028,13 @@ dependencies = [ [[package]] name = "http" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea" dependencies = [ "bytes", "fnv", - "itoa 1.0.11", + "itoa 1.0.14", ] [[package]] @@ -2046,16 +2073,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" [[package]] -name = "humantime" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" - -[[package]] name = "hyper" -version = "1.4.1" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" +checksum = "256fb8d4bd6413123cc9d91832d78325c48ff41677595be797d90f42969beae0" dependencies = [ "bytes", "futures-channel", @@ -2064,7 +2085,7 @@ dependencies = [ "http", "http-body", "httparse", - "itoa 1.0.11", + "itoa 1.0.14", "pin-project-lite", "smallvec", "tokio", @@ -2073,9 +2094,9 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.27.3" +version = "0.27.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" +checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" dependencies = [ "futures-util", "http", @@ -2091,9 +2112,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41296eb09f183ac68eec06e03cdbea2e759633d4067b2f6552fc2e009bcad08b" +checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" dependencies = [ "bytes", "futures-channel", @@ -2142,6 +2163,124 @@ dependencies = [ ] [[package]] +name = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.95", +] + +[[package]] name = "ident_case" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2149,22 +2288,23 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "idna" -version = "0.3.0" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" dependencies = [ - "unicode-bidi", - "unicode-normalization", + "idna_adapter", + "smallvec", + "utf8_iter", ] [[package]] -name = "idna" -version = "0.5.0" +name = "idna_adapter" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" dependencies = [ - "unicode-bidi", - "unicode-normalization", + "icu_normalizer", + "icu_properties", ] [[package]] @@ -2180,12 +2320,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" +checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" dependencies = [ "equivalent", - "hashbrown 0.15.0", + "hashbrown 0.15.2", "serde", ] @@ -2199,12 +2339,25 @@ dependencies = [ ] [[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "block-padding", + "generic-array", +] + +[[package]] name = "instant" version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" dependencies = [ "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", ] [[package]] @@ -2233,12 +2386,6 @@ dependencies = [ ] [[package]] -name = "is_terminal_polyfill" -version = "1.70.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" - -[[package]] name = "itoa" version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2246,9 +2393,9 @@ checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" [[package]] name = "itoa" -version = "1.0.11" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" [[package]] name = "javascriptcore-rs" @@ -2284,7 +2431,7 @@ dependencies = [ "combine", "jni-sys", "log", - "thiserror", + "thiserror 1.0.69", "walkdir", "windows-sys 0.45.0", ] @@ -2297,32 +2444,32 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" [[package]] name = "js-sys" -version = "0.3.72" +version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" +checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7" dependencies = [ + "once_cell", "wasm-bindgen", ] [[package]] name = "json-patch" -version = "2.0.0" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b1fb8864823fad91877e6caea0baca82e49e8db50f8e5c9f9a453e27d3330fc" +checksum = "863726d7afb6bc2590eeff7135d923545e5e964f004c2ccf8716c25e70a86f08" dependencies = [ "jsonptr", "serde", "serde_json", - "thiserror", + "thiserror 1.0.69", ] [[package]] name = "jsonptr" -version = "0.4.7" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c6e529149475ca0b2820835d3dce8fcc41c6b943ca608d32f35b449255e4627" +checksum = "5dea2b27dd239b2556ed7a25ba842fe47fd602e7fc7433c2a8d6106d4d9edd70" dependencies = [ - "fluent-uri", "serde", "serde_json", ] @@ -2339,18 +2486,6 @@ dependencies = [ ] [[package]] -name = "keyring" -version = "3.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd3d701d3de5b9c4b0d9d077f8c2c66f0388d75e96932ebbb7cdff8713d7f7c6" -dependencies = [ - "byteorder", - "linux-keyutils", - "security-framework", - "windows-sys 0.59.0", -] - -[[package]] name = "kuchikiki" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2392,15 +2527,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e9ec52138abedcc58dc17a7c6c0c00a2bdb4f3427c7f63fa97fd0d859155caf" dependencies = [ "gtk-sys", - "libloading", + "libloading 0.7.4", "once_cell", ] [[package]] name = "libc" -version = "0.2.159" +version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" [[package]] name = "libloading" @@ -2413,10 +2548,20 @@ dependencies = [ ] [[package]] +name = "libloading" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" +dependencies = [ + "cfg-if", + "windows-targets 0.52.6", +] + +[[package]] name = "libm" -version = "0.2.8" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" +checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" [[package]] name = "libredox" @@ -2440,22 +2585,24 @@ dependencies = [ ] [[package]] -name = "linux-keyutils" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "761e49ec5fd8a5a463f9b84e877c373d888935b71c6be78f3767fe2ae6bed18e" -dependencies = [ - "bitflags 2.6.0", - "libc", -] - -[[package]] name = "linux-raw-sys" version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] +name = "litemap" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" + +[[package]] +name = "litrs" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" + +[[package]] name = "lock_api" version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2470,8 +2617,14 @@ name = "log" version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "lru" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" dependencies = [ - "value-bag", + "hashbrown 0.15.2", ] [[package]] @@ -2554,16 +2707,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] -name = "minimal-lexical" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" - -[[package]] name = "miniz_oxide" -version = "0.8.0" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +checksum = "4ffbe83022cedc1d264172192511ae958937694cd57ce297164951b8b3568394" dependencies = [ "adler2", "simd-adler32", @@ -2571,11 +2718,10 @@ dependencies = [ [[package]] name = "mio" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" dependencies = [ - "hermit-abi 0.3.9", "libc", "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys 0.52.0", @@ -2583,9 +2729,9 @@ dependencies = [ [[package]] name = "muda" -version = "0.15.1" +version = "0.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8123dfd4996055ac9b15a60ad263b44b01e539007523ad7a4a533a3d93b0591" +checksum = "fdae9c00e61cc0579bcac625e8ad22104c60548a025bfc972dc83868a28e1484" dependencies = [ "crossbeam-channel", "dpi", @@ -2597,7 +2743,7 @@ dependencies = [ "once_cell", "png", "serde", - "thiserror", + "thiserror 1.0.69", "windows-sys 0.59.0", ] @@ -2613,7 +2759,7 @@ dependencies = [ "ndk-sys", "num_enum", "raw-window-handle", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -2632,6 +2778,18 @@ dependencies = [ ] [[package]] +name = "negentropy" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e664971378a3987224f7a0e10059782035e89899ae403718ee07de85bec42afe" + +[[package]] +name = "negentropy" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a88da9dd148bbcdce323dd6ac47d369b4769d4a3b78c6c52389b9269f77932" + +[[package]] name = "new_debug_unreachable" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2639,12 +2797,13 @@ checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" [[package]] name = "nix" -version = "0.27.1" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ "bitflags 2.6.0", "cfg-if", + "cfg_aliases", "libc", "memoffset", ] @@ -2656,13 +2815,78 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" [[package]] -name = "nom" -version = "7.1.3" +name = "nostr" +version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +checksum = "8aad4b767bbed24ac5eb4465bfb83bc1210522eb99d67cf4e547ec2ec7e47786" dependencies = [ - "memchr", - "minimal-lexical", + "async-trait", + "base64 0.22.1", + "bech32", + "bip39", + "bitcoin", + "cbc", + "chacha20", + "chacha20poly1305", + "getrandom 0.2.15", + "instant", + "negentropy 0.3.1", + "negentropy 0.4.3", + "once_cell", + "scrypt", + "serde", + "serde_json", + "unicode-normalization", + "url", +] + +[[package]] +name = "nostr-database" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23696338d51e45cd44e061823847f4b0d1d362eca80d5033facf9c184149f72f" +dependencies = [ + "async-trait", + "lru", + "nostr", + "thiserror 1.0.69", + "tokio", + "tracing", +] + +[[package]] +name = "nostr-relay-pool" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15fcc6e3f0ca54d0fc779009bc5f2684cea9147be3b6aa68a7d301ea590f95f5" +dependencies = [ + "async-utility", + "async-wsocket", + "atomic-destructor", + "negentropy 0.3.1", + "negentropy 0.4.3", + "nostr", + "nostr-database", + "thiserror 1.0.69", + "tokio", + "tokio-stream", + "tracing", +] + +[[package]] +name = "nostr-sdk" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "491221fc89b1aa189a0de640127127d68b4e7c5c1d44371b04d9a6d10694b5af" +dependencies = [ + "async-utility", + "atomic-destructor", + "nostr", + "nostr-database", + "nostr-relay-pool", + "thiserror 1.0.69", + "tokio", + "tracing", ] [[package]] @@ -2675,7 +2899,7 @@ dependencies = [ "mac-notification-sys", "serde", "tauri-winrt-notification", - "zbus", + "zbus 4.4.0", ] [[package]] @@ -2746,19 +2970,10 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" dependencies = [ - "proc-macro-crate 2.0.2", + "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.79", -] - -[[package]] -name = "num_threads" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" -dependencies = [ - "libc", + "syn 2.0.95", ] [[package]] @@ -3011,9 +3226,9 @@ dependencies = [ [[package]] name = "object" -version = "0.36.5" +version = "0.36.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" dependencies = [ "memchr", ] @@ -3025,11 +3240,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + +[[package]] name = "open" -version = "5.3.0" +version = "5.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61a877bf6abd716642a53ef1b89fb498923a4afca5c754f9050b4d081c05c4b3" +checksum = "e2483562e62ea94312f3576a7aca397306df7990b8d89033e18766744377ef95" dependencies = [ + "dunce", "is-wsl", "libc", "pathdiff", @@ -3053,9 +3275,9 @@ dependencies = [ [[package]] name = "os_info" -version = "3.8.2" +version = "3.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae99c7fa6dd38c7cafe1ec085e804f8f555a2f8659b0dbe03f1f9963a9b51092" +checksum = "6e6520c8cc998c5741ee68ec1dc369fc47e5f0ea5320018ecf2a1ccd6328f48b" dependencies = [ "log", "serde", @@ -3127,16 +3349,37 @@ dependencies = [ ] [[package]] +name = "password-hash" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166" +dependencies = [ + "base64ct", + "rand_core 0.6.4", + "subtle", +] + +[[package]] name = "paste" version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pathdiff" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" [[package]] -name = "pathdiff" -version = "0.2.2" +name = "pbkdf2" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d61c5ce1153ab5b689d0c074c4e7fc613e942dfb7dd9eea5ab202d2ad91fe361" +checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" +dependencies = [ + "digest", + "hmac", +] [[package]] name = "pem-rfc7468" @@ -3257,7 +3500,7 @@ dependencies = [ "phf_shared 0.11.2", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.95", ] [[package]] @@ -3289,9 +3532,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" +checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" [[package]] name = "pin-utils" @@ -3344,7 +3587,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42cf17e9a1800f5f396bc67d193dc9411b59012a5876445ef450d449881e1016" dependencies = [ "base64 0.22.1", - "indexmap 2.6.0", + "indexmap 2.7.0", "quick-xml 0.32.0", "serde", "time", @@ -3352,9 +3595,9 @@ dependencies = [ [[package]] name = "png" -version = "0.17.14" +version = "0.17.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52f9d46a34a05a6a57566bc2bfae066ef07585a6e3fa30fbbdff5936380623f0" +checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" dependencies = [ "bitflags 1.3.2", "crc32fast", @@ -3365,13 +3608,13 @@ dependencies = [ [[package]] name = "polling" -version = "3.7.3" +version = "3.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc2790cd301dec6cd3b7a025e4815cf825724a51c98dccfe6a3e55f05ffb6511" +checksum = "a604568c3202727d1507653cb121dbd627a58684eb09a820fd746bee38b4442f" dependencies = [ "cfg-if", "concurrent-queue", - "hermit-abi 0.4.0", + "hermit-abi", "pin-project-lite", "rustix", "tracing", @@ -3379,6 +3622,17 @@ dependencies = [ ] [[package]] +name = "poly1305" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" +dependencies = [ + "cpufeatures", + "opaque-debug", + "universal-hash", +] + +[[package]] name = "powerfmt" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3411,12 +3665,20 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "2.0.2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b00f26d3400549137f92511a46ac1cd8ce37cb5598a96d382381458b992a5d24" +checksum = "7e8366a6159044a37876a2b9817124296703c586a5c92e2c53751fa06d8d43e8" dependencies = [ - "toml_datetime", - "toml_edit 0.20.2", + "toml_edit 0.20.7", +] + +[[package]] +name = "proc-macro-crate" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" +dependencies = [ + "toml_edit 0.22.22", ] [[package]] @@ -3451,9 +3713,9 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" [[package]] name = "proc-macro2" -version = "1.0.87" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3e4daa0dcf6feba26f985457cdf104d4b4256fc5a09547140f3631bb076b19a" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" dependencies = [ "unicode-ident", ] @@ -3465,32 +3727,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33cb294fe86a74cbcf50d4445b37da762029549ebeea341421c7c70370f86cac" [[package]] -name = "ptr_meta" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" -dependencies = [ - "ptr_meta_derive", -] - -[[package]] -name = "ptr_meta_derive" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] name = "publicsuffix" -version = "2.2.3" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96a8c1bda5ae1af7f99a2962e49df150414a43d62404644d98dd5c3a93d07457" +checksum = "6f42ea446cab60335f76979ec15e12619a2165b5ae2c12166bef27d283a9fadf" dependencies = [ - "idna 0.3.0", + "idna", "psl-types", ] @@ -3523,9 +3765,9 @@ dependencies = [ [[package]] name = "quinn" -version = "0.11.5" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c7c5fdde3cdae7203427dc4f0a68fe0ed09833edc525a03456b153b79828684" +checksum = "62e96808277ec6f97351a2380e6c25114bc9e67037775464979f3037c92d05ef" dependencies = [ "bytes", "pin-project-lite", @@ -3534,34 +3776,38 @@ dependencies = [ "rustc-hash", "rustls", "socket2", - "thiserror", + "thiserror 2.0.9", "tokio", "tracing", ] [[package]] name = "quinn-proto" -version = "0.11.8" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fadfaed2cd7f389d0161bb73eeb07b7b78f8691047a6f3e73caaeae55310a4a6" +checksum = "a2fe5ef3495d7d2e377ff17b1a8ce2ee2ec2a18cde8b6ad6619d65d0701c135d" dependencies = [ "bytes", + "getrandom 0.2.15", "rand 0.8.5", "ring", "rustc-hash", "rustls", + "rustls-pki-types", "slab", - "thiserror", + "thiserror 2.0.9", "tinyvec", "tracing", + "web-time", ] [[package]] name = "quinn-udp" -version = "0.5.5" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fe68c2e9e1a1234e218683dbdf9f9dfcb094113c5ac2b938dfcb9bab4c4140b" +checksum = "1c40286217b4ba3a71d644d752e6a0b71f13f1b6a2c5311acfcbe0c2418ed904" dependencies = [ + "cfg_aliases", "libc", "once_cell", "socket2", @@ -3571,27 +3817,19 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.37" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" dependencies = [ "proc-macro2", ] [[package]] -name = "radium" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" - -[[package]] name = "radroots" version = "0.0.1" dependencies = [ - "env_logger", - "keyring", - "log", "radroots_core", + "radroots_model", "serde", "serde_json", "sqlx", @@ -3601,8 +3839,6 @@ dependencies = [ "tauri-plugin-fs", "tauri-plugin-geolocation", "tauri-plugin-http", - "tauri-plugin-log", - "tauri-plugin-map-display", "tauri-plugin-notification", "tauri-plugin-os", "tauri-plugin-shell", @@ -3613,18 +3849,29 @@ dependencies = [ name = "radroots_core" version = "0.0.1" dependencies = [ + "base64 0.22.1", "chrono", - "futures", - "log", + "nostr-sdk", "regex", "serde", "serde_json", - "sqlx", - "thiserror", + "thiserror 1.0.69", "uuid", ] [[package]] +name = "radroots_model" +version = "0.0.1" +dependencies = [ + "futures", + "radroots_core", + "serde", + "serde_json", + "sqlx", + "thiserror 1.0.69", +] + +[[package]] name = "rand" version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3713,9 +3960,9 @@ checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" [[package]] name = "redox_syscall" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" +checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" dependencies = [ "bitflags 2.6.0", ] @@ -3728,14 +3975,14 @@ checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ "getrandom 0.2.15", "libredox", - "thiserror", + "thiserror 1.0.69", ] [[package]] name = "regex" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", @@ -3745,9 +3992,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", @@ -3761,19 +4008,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] -name = "rend" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71fe3824f5629716b1589be05dacd749f6aa084c87e00e016714a8cdfccc997c" -dependencies = [ - "bytecheck", -] - -[[package]] name = "reqwest" -version = "0.12.8" +version = "0.12.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f713147fbe92361e52392c73b8c9e48c04c6625bce969ef54dc901e58e042a7b" +checksum = "43e734407157c3c2034e0258f5e4473ddb361b1e85f95a66690d67264d7cd1da" dependencies = [ "base64 0.22.1", "bytes", @@ -3808,6 +4046,7 @@ dependencies = [ "tokio", "tokio-rustls", "tokio-util", + "tower", "tower-service", "url", "wasm-bindgen", @@ -3820,12 +4059,14 @@ dependencies = [ [[package]] name = "rfd" -version = "0.15.0" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8af382a047821a08aa6bfc09ab0d80ff48d45d8726f7cd8e44891f7cb4a4278e" +checksum = "6a24763657bff09769a8ccf12c8b8a50416fb035fe199263b4c5071e4e3f006f" dependencies = [ "ashpd", "block2", + "core-foundation 0.10.0", + "core-foundation-sys", "glib-sys", "gobject-sys", "gtk-sys", @@ -3838,7 +4079,7 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] @@ -3857,39 +4098,10 @@ dependencies = [ ] [[package]] -name = "rkyv" -version = "0.7.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9008cd6385b9e161d8229e1f6549dd23c3d022f132a2ea37ac3a10ac4935779b" -dependencies = [ - "bitvec", - "bytecheck", - "bytes", - "hashbrown 0.12.3", - "ptr_meta", - "rend", - "rkyv_derive", - "seahash", - "tinyvec", - "uuid", -] - -[[package]] -name = "rkyv_derive" -version = "0.7.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "503d1d27590a2b0a3a4ca4c94755aa2875657196ecbf401a42eff41d7de532c0" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] name = "rsa" -version = "0.9.6" +version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d0e5124fcb30e76a7e79bfee683a2746db83784b86289f6251b54b7950a0dfc" +checksum = "47c75d7c5c6b673e58bf54d8544a9f432e3a925b0e80f7cd3602ab5c50c55519" dependencies = [ "const-oid", "digest", @@ -3906,22 +4118,6 @@ dependencies = [ ] [[package]] -name = "rust_decimal" -version = "1.36.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b082d80e3e3cc52b2ed634388d436fe1f4de6af5786cc2de9ba9737527bdf555" -dependencies = [ - "arrayvec", - "borsh", - "bytes", - "num-traits", - "rand 0.8.5", - "rkyv", - "serde", - "serde_json", -] - -[[package]] name = "rustc-demangle" version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3929,9 +4125,9 @@ checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustc-hash" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" +checksum = "c7fb8039b3032c191086b10f11f319a6e99e1e82889c5cc6046f515c9db1d497" [[package]] name = "rustc_version" @@ -3944,22 +4140,22 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.37" +version = "0.38.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" +checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85" dependencies = [ "bitflags 2.6.0", "errno", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "rustls" -version = "0.23.14" +version = "0.23.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "415d9944693cb90382053259f89fbb077ea730ad7273047ec63b19bc9b160ba8" +checksum = "5065c3f250cbd332cd894be57c40fa52387247659b14a2d6041d121547903b1b" dependencies = [ "once_cell", "ring", @@ -3980,9 +4176,12 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.9.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e696e35370c65c9c541198af4543ccd580cf17fc25d8e05c5a242b202488c55" +checksum = "d2bf47e6ff922db3825eb750c4e2ff784c6ff8fb9e13046ef6a1d1c5401b0b37" +dependencies = [ + "web-time", +] [[package]] name = "rustls-webpki" @@ -4002,6 +4201,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] +name = "salsa20" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213" +dependencies = [ + "cipher", +] + +[[package]] name = "same-file" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -4034,7 +4242,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.79", + "syn 2.0.95", ] [[package]] @@ -4050,32 +4258,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] -name = "seahash" -version = "4.1.0" +name = "scrypt" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" +checksum = "0516a385866c09368f0b5bcd1caff3366aace790fcd46e2bb032697bb172fd1f" +dependencies = [ + "password-hash", + "pbkdf2", + "salsa20", + "sha2", +] [[package]] -name = "security-framework" -version = "3.0.0" +name = "secp256k1" +version = "0.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9d0283c0a4a22a0f1b0e4edca251aa20b92fc96eaa09b84bec052f9415e9d71" +checksum = "9465315bc9d4566e1724f0fffcbcc446268cb522e60f9a27bcded6b19c108113" dependencies = [ - "bitflags 2.6.0", - "core-foundation 0.10.0", - "core-foundation-sys", - "libc", - "security-framework-sys", + "bitcoin_hashes 0.14.0", + "rand 0.8.5", + "secp256k1-sys", + "serde", ] [[package]] -name = "security-framework-sys" -version = "2.12.0" +name = "secp256k1-sys" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea4a292869320c0272d7bc55a5a6aafaff59b4f63404a003887b679a2e05b4b6" +checksum = "d4387882333d3aa8cb20530a17c69a3752e97837832f34f6dccc760e715001d9" dependencies = [ - "core-foundation-sys", - "libc", + "cc", ] [[package]] @@ -4100,18 +4312,18 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.23" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" +checksum = "3cb6eb87a131f756572d7fb904f6e7b68633f09cca868c5df1c4b8d1a694bbba" dependencies = [ "serde", ] [[package]] name = "serde" -version = "1.0.210" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" dependencies = [ "serde_derive", ] @@ -4129,13 +4341,13 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.210" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.95", ] [[package]] @@ -4146,16 +4358,17 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.95", ] [[package]] name = "serde_json" -version = "1.0.128" +version = "1.0.134" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" +checksum = "d00f4175c42ee48b15416f6193a959ba3a0d67fc699a0db9ad12df9f83991c7d" dependencies = [ - "itoa 1.0.11", + "indexmap 2.7.0", + "itoa 1.0.14", "memchr", "ryu", "serde", @@ -4169,7 +4382,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.95", ] [[package]] @@ -4188,22 +4401,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" dependencies = [ "form_urlencoded", - "itoa 1.0.11", + "itoa 1.0.14", "ryu", "serde", ] [[package]] name = "serde_with" -version = "3.11.0" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e28bdad6db2b8340e449f7108f020b3b092e8583a9e3fb82713e1d4e71fe817" +checksum = "d6b6f7f2fcb69f747921f79f3926bd1e203fce4fef62c268dd3abfb6d86029aa" dependencies = [ "base64 0.22.1", "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.6.0", + "indexmap 2.7.0", "serde", "serde_derive", "serde_json", @@ -4213,14 +4426,14 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.11.0" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d846214a9854ef724f3da161b426242d8de7c1fc7de2f89bb1efcb154dca79d" +checksum = "8d00caa5193a3c8362ac2b73be6b9e768aa5a4b2f721d8f4b339600c3cb51f8e" dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.95", ] [[package]] @@ -4319,12 +4532,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" [[package]] -name = "simdutf8" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" - -[[package]] name = "siphasher" version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -4350,9 +4557,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" dependencies = [ "libc", "windows-sys 0.52.0", @@ -4365,7 +4572,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "18051cdd562e792cad055119e0cdb2cfc137e44e3987532e0f9659a77931bb08" dependencies = [ "bytemuck", - "cfg_aliases 0.2.1", + "cfg_aliases", "core-graphics", "foreign-types", "js-sys", @@ -4414,7 +4621,7 @@ checksum = "4ccbb212565d2dc177bc15ecb7b039d66c4490da892436a4eee5b394d620c9bc" dependencies = [ "paste", "specta-macros", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -4426,7 +4633,19 @@ dependencies = [ "Inflector", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.95", +] + +[[package]] +name = "specta-util" +version = "0.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8113d65b58a6de3184b01d6df9e50b6d4bbe7f724251f576d84f23228824456" +dependencies = [ + "ctor", + "serde", + "specta", + "specta-macros", ] [[package]] @@ -4449,20 +4668,10 @@ dependencies = [ ] [[package]] -name = "sqlformat" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bba3a93db0cc4f7bdece8bb09e77e2e785c20bfebf79eb8340ed80708048790" -dependencies = [ - "nom", - "unicode_categories", -] - -[[package]] name = "sqlx" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93334716a037193fac19df402f8571269c84a00852f6a7066b5d2616dcd64d3e" +checksum = "4410e73b3c0d8442c5f99b425d7a435b5ee0ae4167b3196771dd3f7a01be745f" dependencies = [ "sqlx-core", "sqlx-macros", @@ -4473,37 +4682,31 @@ dependencies = [ [[package]] name = "sqlx-core" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4d8060b456358185f7d50c55d9b5066ad956956fddec42ee2e8567134a8936e" +checksum = "6a007b6936676aa9ab40207cde35daab0a04b823be8ae004368c0793b96a61e0" dependencies = [ - "atoi", - "byteorder", "bytes", "crc", "crossbeam-queue", "either", "event-listener", - "futures-channel", "futures-core", "futures-intrusive", "futures-io", "futures-util", - "hashbrown 0.14.5", + "hashbrown 0.15.2", "hashlink", - "hex", - "indexmap 2.6.0", + "indexmap 2.7.0", "log", "memchr", "once_cell", - "paste", "percent-encoding", "serde", "serde_json", "sha2", "smallvec", - "sqlformat", - "thiserror", + "thiserror 2.0.9", "tokio", "tokio-stream", "tracing", @@ -4512,22 +4715,22 @@ dependencies = [ [[package]] name = "sqlx-macros" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cac0692bcc9de3b073e8d747391827297e075c7710ff6276d9f7a1f3d58c6657" +checksum = "3112e2ad78643fef903618d78cf0aec1cb3134b019730edb039b69eaf531f310" dependencies = [ "proc-macro2", "quote", "sqlx-core", "sqlx-macros-core", - "syn 2.0.79", + "syn 2.0.95", ] [[package]] name = "sqlx-macros-core" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1804e8a7c7865599c9c79be146dc8a9fd8cc86935fa641d3ea58e5f0688abaa5" +checksum = "4e9f90acc5ab146a99bf5061a7eb4976b573f560bc898ef3bf8435448dd5e7ad" dependencies = [ "dotenvy", "either", @@ -4543,7 +4746,7 @@ dependencies = [ "sqlx-mysql", "sqlx-postgres", "sqlx-sqlite", - "syn 2.0.79", + "syn 2.0.95", "tempfile", "tokio", "url", @@ -4551,9 +4754,9 @@ dependencies = [ [[package]] name = "sqlx-mysql" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64bb4714269afa44aef2755150a0fc19d756fb580a67db8885608cf02f47d06a" +checksum = "4560278f0e00ce64938540546f59f590d60beee33fffbd3b9cd47851e5fff233" dependencies = [ "atoi", "base64 0.22.1", @@ -4572,7 +4775,7 @@ dependencies = [ "hex", "hkdf", "hmac", - "itoa 1.0.11", + "itoa 1.0.14", "log", "md-5", "memchr", @@ -4586,16 +4789,16 @@ dependencies = [ "smallvec", "sqlx-core", "stringprep", - "thiserror", + "thiserror 2.0.9", "tracing", "whoami", ] [[package]] name = "sqlx-postgres" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fa91a732d854c5d7726349bb4bb879bb9478993ceb764247660aee25f67c2f8" +checksum = "c5b98a57f363ed6764d5b3a12bfedf62f07aa16e1856a7ddc2a0bb190a959613" dependencies = [ "atoi", "base64 0.22.1", @@ -4606,13 +4809,12 @@ dependencies = [ "etcetera", "futures-channel", "futures-core", - "futures-io", "futures-util", "hex", "hkdf", "hmac", "home", - "itoa 1.0.11", + "itoa 1.0.14", "log", "md-5", "memchr", @@ -4624,16 +4826,16 @@ dependencies = [ "smallvec", "sqlx-core", "stringprep", - "thiserror", + "thiserror 2.0.9", "tracing", "whoami", ] [[package]] name = "sqlx-sqlite" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5b2cf34a45953bfd3daaf3db0f7a7878ab9b7a6b91b422d24a7a9e4c857b680" +checksum = "f85ca71d3a5b24e64e1d08dd8fe36c6c95c339a896cc33068148906784620540" dependencies = [ "atoi", "flume", @@ -4737,9 +4939,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.79" +version = "2.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" +checksum = "46f71c0377baf4ef1cc3e3402ded576dccc315800fbc62dfc7fe04b009773b4a" dependencies = [ "proc-macro2", "quote", @@ -4747,31 +4949,30 @@ dependencies = [ ] [[package]] -name = "syn_derive" -version = "0.1.8" +name = "sync_wrapper" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1329189c02ff984e9736652b1631330da25eaa6bc639089ed4915d25446cbe7b" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" dependencies = [ - "proc-macro-error", - "proc-macro2", - "quote", - "syn 2.0.79", + "futures-core", ] [[package]] -name = "sync_wrapper" -version = "1.0.1" +name = "synstructure" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ - "futures-core", + "proc-macro2", + "quote", + "syn 2.0.95", ] [[package]] name = "sys-locale" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e801cf239ecd6ccd71f03d270d67dd53d13e90aab208bf4b8fe4ad957ea949b0" +checksum = "8eab9a99a024a169fe8a903cf9d4a3b3601109bcc13bd9e3c6fff259138626c4" dependencies = [ "libc", ] @@ -4806,15 +5007,15 @@ dependencies = [ "cfg-expr", "heck 0.5.0", "pkg-config", - "toml 0.8.2", + "toml 0.8.19", "version-compare", ] [[package]] name = "tao" -version = "0.30.3" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0dbbebe82d02044dfa481adca1550d6dd7bd16e086bc34fa0fbecceb5a63751" +checksum = "3731d04d4ac210cd5f344087733943b9bfb1a32654387dad4d1c70de21aee2c9" dependencies = [ "bitflags 2.6.0", "cocoa", @@ -4827,7 +5028,6 @@ dependencies = [ "gdkwayland-sys", "gdkx11-sys", "gtk", - "instant", "jni", "lazy_static", "libc", @@ -4857,16 +5057,10 @@ checksum = "f4e16beb8b2ac17db28eab8bca40e62dbfbb34c0fcdc6d9826b11b7b5d047dfd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.95", ] [[package]] -name = "tap" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" - -[[package]] name = "target-lexicon" version = "0.12.16" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -4874,9 +5068,9 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tauri" -version = "2.0.5" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ce2818e803ce3097987296623ed8c0d9f65ed93b4137ff9a83e168bdbf62932" +checksum = "2e2e3349fbb2be7af9fad1b43d61ac83ba55ab48d47fbe1b2732f0c8211610a9" dependencies = [ "anyhow", "bytes", @@ -4907,13 +5101,14 @@ dependencies = [ "serde_repr", "serialize-to-javascript", "specta", + "specta-util", "swift-rs", "tauri-build", "tauri-macros", "tauri-runtime", "tauri-runtime-wry", "tauri-utils", - "thiserror", + "thiserror 2.0.9", "tokio", "tray-icon", "url", @@ -4926,9 +5121,9 @@ dependencies = [ [[package]] name = "tauri-build" -version = "2.0.1" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "935f9b3c49b22b3e2e485a57f46d61cd1ae07b1cbb2ba87387a387caf2d8c4e7" +checksum = "b274ec7239ada504deb615f1c8abd7ba99631e879709e6f10e5d17217058d976" dependencies = [ "anyhow", "cargo_toml", @@ -4942,15 +5137,15 @@ dependencies = [ "serde_json", "tauri-utils", "tauri-winres", - "toml 0.8.2", + "toml 0.8.19", "walkdir", ] [[package]] name = "tauri-codegen" -version = "2.0.1" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95d7443dd4f0b597704b6a14b964ee2ed16e99928d8e6292ae9825f09fbcd30e" +checksum = "f77894f9ddb5cb6c04fcfe8c8869ebe0aded4dabf19917118d48be4a95599ab5" dependencies = [ "base64 0.22.1", "brotli", @@ -4964,9 +5159,9 @@ dependencies = [ "serde", "serde_json", "sha2", - "syn 2.0.79", + "syn 2.0.95", "tauri-utils", - "thiserror", + "thiserror 2.0.9", "time", "url", "uuid", @@ -4975,23 +5170,23 @@ dependencies = [ [[package]] name = "tauri-macros" -version = "2.0.1" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d2c0963ccfc3f5194415f2cce7acc975942a8797fbabfb0aa1ed6f59326ae7f" +checksum = "3240a5caed760a532e8f687be6f05b2c7d11a1d791fb53ccc08cfeb3e5308736" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.95", "tauri-codegen", "tauri-utils", ] [[package]] name = "tauri-plugin" -version = "2.0.1" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2e6660a409963e4d57b9bfab4addd141eeff41bd3a7fb14e13004a832cf7ef6" +checksum = "5841b9a0200e954ef7457f8d327091424328891e267a97b641dc246cc54d0dec" dependencies = [ "anyhow", "glob", @@ -5000,15 +5195,15 @@ dependencies = [ "serde", "serde_json", "tauri-utils", - "toml 0.8.2", + "toml 0.8.19", "walkdir", ] [[package]] name = "tauri-plugin-dialog" -version = "2.0.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddb2fe88b602461c118722c574e2775ab26a4e68886680583874b2f6520608b7" +checksum = "8b59fd750551b1066744ab956a1cd6b1ea3e1b3763b0b9153ac27a044d596426" dependencies = [ "log", "raw-window-handle", @@ -5018,15 +5213,15 @@ dependencies = [ "tauri", "tauri-plugin", "tauri-plugin-fs", - "thiserror", + "thiserror 2.0.9", "url", ] [[package]] name = "tauri-plugin-fs" -version = "2.0.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab300488ebec3487ca5f56289692e7e45feb07eea8d5e1dba497f7dc9dd9c407" +checksum = "a1a1edf18000f02903a7c2e5997fb89aca455ecbc0acc15c6535afbb883be223" dependencies = [ "anyhow", "dunce", @@ -5038,31 +5233,32 @@ dependencies = [ "serde_repr", "tauri", "tauri-plugin", - "thiserror", + "tauri-utils", + "thiserror 2.0.9", + "toml 0.8.19", "url", "uuid", ] [[package]] name = "tauri-plugin-geolocation" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14553ada3bcb836d710e8cb9013bcc98508b48ba9b0dc166e7510552bd05cf9" +version = "2.2.0" dependencies = [ "log", "serde", "serde_json", "specta", + "specta-util", "tauri", "tauri-plugin", - "thiserror", + "thiserror 2.0.9", ] [[package]] name = "tauri-plugin-http" -version = "2.0.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "784333f1632d96c94346e8145bfe52970923a38a0e6eacd3dccaa12289275acf" +checksum = "e62a9bde54d6a0218b63f5a248f02056ad4316ba6ad81dfb9e4f73715df5deb1" dependencies = [ "data-url", "http", @@ -5074,50 +5270,17 @@ dependencies = [ "tauri", "tauri-plugin", "tauri-plugin-fs", - "thiserror", + "thiserror 2.0.9", "tokio", "url", "urlpattern", ] [[package]] -name = "tauri-plugin-log" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8aa13d15daf90230ba26d5a9b4a4612975fa64ce17290cb7f6e0f89bb6997d82" -dependencies = [ - "android_logger", - "byte-unit", - "cocoa", - "fern", - "log", - "objc", - "serde", - "serde_json", - "serde_repr", - "swift-rs", - "tauri", - "tauri-plugin", - "thiserror", - "time", -] - -[[package]] -name = "tauri-plugin-map-display" -version = "0.1.0" -source = "git+https://github.com/inkibra/tauri-plugins?tag=@inkibra/tauri-plugin-map-display@0.2.0#31aa60a18d19f35828649e84c36597d18661b7e9" -dependencies = [ - "serde", - "tauri", - "tauri-plugin", - "thiserror", -] - -[[package]] name = "tauri-plugin-notification" -version = "2.0.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef492a2d19b6376bb4c9e0c4fab3f3bf8a220ea112d24f35027b737ff55de20c" +checksum = "46ab803095f14ac6521fdb6477210a49e86fed6623c3c97d8e4b2b35e045e922" dependencies = [ "log", "notify-rust", @@ -5127,16 +5290,16 @@ dependencies = [ "serde_repr", "tauri", "tauri-plugin", - "thiserror", + "thiserror 2.0.9", "time", "url", ] [[package]] name = "tauri-plugin-os" -version = "2.0.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbc5f23a86f37687c7f4fecfdc706b279087bc44f7a46702f7307ff1551ee03a" +checksum = "dda2d571a9baf0664c1f2088db227e3072f9028602fafa885deade7547c3b738" dependencies = [ "gethostname", "log", @@ -5147,14 +5310,14 @@ dependencies = [ "sys-locale", "tauri", "tauri-plugin", - "thiserror", + "thiserror 2.0.9", ] [[package]] name = "tauri-plugin-shell" -version = "2.0.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "371fb9aca2823990a2d0db7970573be5fdf07881fcaa2b835b29631feb84aec1" +checksum = "bb2c50a63e60fb8925956cc5b7569f4b750ac197a4d39f13b8dd46ea8e2bad79" dependencies = [ "encoding_rs", "log", @@ -5167,31 +5330,31 @@ dependencies = [ "shared_child", "tauri", "tauri-plugin", - "thiserror", + "thiserror 2.0.9", "tokio", ] [[package]] name = "tauri-plugin-store" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9a580be53f04bb62422d239aa798e88522877f58a0d4a0e745f030055a51bb4" +checksum = "1c0c08fae6995909f5e9a0da6038273b750221319f2c0f3b526d6de1cde21505" dependencies = [ "dunce", - "log", "serde", "serde_json", "tauri", "tauri-plugin", - "thiserror", + "thiserror 2.0.9", "tokio", + "tracing", ] [[package]] name = "tauri-runtime" -version = "2.1.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8f437293d6f5e5dce829250f4dbdce4e0b52905e297a6689cc2963eb53ac728" +checksum = "2274ef891ccc0a8d318deffa9d70053f947664d12d58b9c0d1ae5e89237e01f7" dependencies = [ "dpi", "gtk", @@ -5201,16 +5364,16 @@ dependencies = [ "serde", "serde_json", "tauri-utils", - "thiserror", + "thiserror 2.0.9", "url", "windows 0.58.0", ] [[package]] name = "tauri-runtime-wry" -version = "2.1.1" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1431602bcc71f2f840ad623915c9842ecc32999b867c4a787d975a17a9625cc6" +checksum = "3707b40711d3b9f6519150869e358ffbde7c57567fb9b5a8b51150606939b2a0" dependencies = [ "gtk", "http", @@ -5234,9 +5397,9 @@ dependencies = [ [[package]] name = "tauri-utils" -version = "2.0.1" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c38b0230d6880cf6dd07b6d7dd7789a0869f98ac12146e0d18d1c1049215a045" +checksum = "96fb10e7cc97456b2d5b9c03e335b5de5da982039a303a20d10006885e4523a0" dependencies = [ "brotli", "cargo_metadata", @@ -5244,6 +5407,7 @@ dependencies = [ "dunce", "glob", "html5ever", + "http", "infer", "json-patch", "kuchikiki", @@ -5260,8 +5424,8 @@ dependencies = [ "serde_json", "serde_with", "swift-rs", - "thiserror", - "toml 0.8.2", + "thiserror 2.0.9", + "toml 0.8.19", "url", "urlpattern", "uuid", @@ -5291,12 +5455,13 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.13.0" +version = "3.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" +checksum = "9a8a559c81686f576e8cd0290cd2a24a2a9ad80c98b3478856500fcbd7acd704" dependencies = [ "cfg-if", "fastrand", + "getrandom 0.2.15", "once_cell", "rustix", "windows-sys 0.59.0", @@ -5317,39 +5482,57 @@ dependencies = [ name = "thin-slice" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eaa81235c7058867fa8c0e7314f33dcce9c215f535d1913822a2b3f5e289f3c" +checksum = "8eaa81235c7058867fa8c0e7314f33dcce9c215f535d1913822a2b3f5e289f3c" + +[[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.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f072643fd0190df67a8bab670c20ef5d8737177d6ac6b2e9a236cb096206b2cc" +dependencies = [ + "thiserror-impl 2.0.9", +] [[package]] -name = "thiserror" -version = "1.0.64" +name = "thiserror-impl" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ - "thiserror-impl", + "proc-macro2", + "quote", + "syn 2.0.95", ] [[package]] name = "thiserror-impl" -version = "1.0.64" +version = "2.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" +checksum = "7b50fa271071aae2e6ee85f842e2e28ba8cd2c5fb67f11fcb1fd70b276f9e7d4" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.95", ] [[package]] name = "time" -version = "0.3.36" +version = "0.3.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21" dependencies = [ "deranged", - "itoa 1.0.11", - "libc", + "itoa 1.0.14", "num-conv", - "num_threads", "powerfmt", "serde", "time-core", @@ -5364,19 +5547,29 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de" dependencies = [ "num-conv", "time-core", ] [[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] name = "tinyvec" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +checksum = "022db8904dfa342efe721985167e9fcd16c29b226db4397ed752a761cfce81e8" dependencies = [ "tinyvec_macros", ] @@ -5389,9 +5582,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.40.0" +version = "1.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" +checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551" dependencies = [ "backtrace", "bytes", @@ -5413,25 +5606,36 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.95", ] [[package]] name = "tokio-rustls" -version = "0.26.0" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" +checksum = "5f6d0975eaace0cf0fcadee4e4aaa5da15b5c079146f2cffb67c113be122bf37" dependencies = [ "rustls", - "rustls-pki-types", + "tokio", +] + +[[package]] +name = "tokio-socks" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d4770b8024672c1101b3f6733eab95b18007dbe0847a8afe341fcf79e06043f" +dependencies = [ + "either", + "futures-util", + "thiserror 1.0.69", "tokio", ] [[package]] name = "tokio-stream" -version = "0.1.16" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f4e6ce100d0eb49a2734f8c0812bcd324cf357d21810932c5df6b96ef2b86f1" +checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" dependencies = [ "futures-core", "pin-project-lite", @@ -5439,10 +5643,26 @@ dependencies = [ ] [[package]] +name = "tokio-tungstenite" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edc5f74e248dc973e0dbb7b74c7e0d6fcc301c694ff50049504004ef4d0cdcd9" +dependencies = [ + "futures-util", + "log", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tungstenite", + "webpki-roots", +] + +[[package]] name = "tokio-util" -version = "0.7.12" +version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" +checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078" dependencies = [ "bytes", "futures-core", @@ -5465,21 +5685,21 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.2" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "185d8ab0dfbb35cf1399a6344d8484209c088f75f8f68230da55d48d95d43e3d" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.20.2", + "toml_edit 0.22.22", ] [[package]] name = "toml_datetime" -version = "0.6.3" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" dependencies = [ "serde", ] @@ -5490,27 +5710,59 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.6.0", + "indexmap 2.7.0", "serde", "serde_spanned", "toml_datetime", - "winnow", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.20.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81" +dependencies = [ + "indexmap 2.7.0", + "toml_datetime", + "winnow 0.5.40", ] [[package]] name = "toml_edit" -version = "0.20.2" +version = "0.22.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ - "indexmap 2.6.0", + "indexmap 2.7.0", "serde", "serde_spanned", "toml_datetime", - "winnow", + "winnow 0.6.22", +] + +[[package]] +name = "tower" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", ] [[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" @@ -5518,9 +5770,9 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" -version = "0.1.40" +version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ "log", "pin-project-lite", @@ -5530,29 +5782,29 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.27" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.95", ] [[package]] name = "tracing-core" -version = "0.1.32" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" dependencies = [ "once_cell", ] [[package]] name = "tray-icon" -version = "0.19.0" +version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "533fc2d4105e0e3d96ce1c71f2d308c9fbbe2ef9c587cab63dd627ab5bde218f" +checksum = "d48a05076dd272615d03033bf04f480199f7d1b66a8ac64d75c625fc4a70c06b" dependencies = [ "core-graphics", "crossbeam-channel", @@ -5565,7 +5817,7 @@ dependencies = [ "once_cell", "png", "serde", - "thiserror", + "thiserror 1.0.69", "windows-sys 0.59.0", ] @@ -5576,6 +5828,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] +name = "tungstenite" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18e5b8366ee7a95b16d32197d0b2604b43a0be89dc5fac9f8e96ccafbaedda8a" +dependencies = [ + "byteorder", + "bytes", + "data-encoding", + "http", + "httparse", + "log", + "rand 0.8.5", + "rustls", + "rustls-pki-types", + "sha1", + "thiserror 1.0.69", + "utf-8", +] + +[[package]] name = "typeid" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -5641,15 +5913,15 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.17" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" +checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" [[package]] name = "unicode-ident" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" [[package]] name = "unicode-normalization" @@ -5673,10 +5945,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" [[package]] -name = "unicode_categories" -version = "0.1.1" +name = "universal-hash" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +dependencies = [ + "crypto-common", + "subtle", +] [[package]] name = "untrusted" @@ -5686,12 +5962,12 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.2" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" dependencies = [ "form_urlencoded", - "idna 0.5.0", + "idna", "percent-encoding", "serde", ] @@ -5715,34 +5991,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" [[package]] -name = "utf8-width" -version = "0.1.7" +name = "utf16_iter" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86bd8d4e895da8537e5315b8254664e6b769c4ff3db18321b297a1e7004392e3" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" [[package]] -name = "utf8parse" -version = "0.2.2" +name = "utf8_iter" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "uuid" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" +checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" dependencies = [ "getrandom 0.2.15", "serde", ] [[package]] -name = "value-bag" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ef4c4aa54d5d05a279399bfa921ec387b7aba77caf7a682ae8d86785b8fdad2" - -[[package]] name = "vcpkg" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -5819,9 +6089,9 @@ checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" [[package]] name = "wasm-bindgen" -version = "0.2.95" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" +checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396" dependencies = [ "cfg-if", "once_cell", @@ -5830,36 +6100,36 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.95" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" +checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79" dependencies = [ "bumpalo", "log", - "once_cell", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.95", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.45" +version = "0.4.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc7ec4f8827a71586374db3e87abdb5a2bb3a15afed140221307c3ec06b1f63b" +checksum = "38176d9b44ea84e9184eff0bc34cc167ed044f816accfe5922e54d84cf48eca2" dependencies = [ "cfg-if", "js-sys", + "once_cell", "wasm-bindgen", "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.95" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" +checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -5867,28 +6137,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.95" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" +checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.95", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.95" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" +checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" [[package]] name = "wasm-streams" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e072d4e72f700fb3443d8fe94a39315df013eef1104903cdb0a2abd322bbecd" +checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" dependencies = [ "futures-util", "js-sys", @@ -5913,9 +6183,9 @@ dependencies = [ [[package]] name = "wayland-client" -version = "0.31.6" +version = "0.31.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3f45d1222915ef1fd2057220c1d9d9624b7654443ea35c3877f7a52bd0a5a2d" +checksum = "b66249d3fc69f76fd74c82cc319300faa554e9d865dab1f7cd66cc20db10b280" dependencies = [ "bitflags 2.6.0", "rustix", @@ -5925,9 +6195,9 @@ dependencies = [ [[package]] name = "wayland-protocols" -version = "0.32.4" +version = "0.32.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b5755d77ae9040bb872a25026555ce4cb0ae75fd923e90d25fba07d81057de0" +checksum = "7cd0ade57c4e6e9a8952741325c30bf82f4246885dca8bf561898b86d0c1f58e" dependencies = [ "bitflags 2.6.0", "wayland-backend", @@ -5959,9 +6229,19 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.72" +version = "0.3.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04dd7223427d52553d3702c004d3b2fe07c148165faa56313cb00211e31c12bc" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" dependencies = [ "js-sys", "wasm-bindgen", @@ -6013,18 +6293,18 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.26.6" +version = "0.26.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841c67bff177718f1d4dfefde8d8f0e78f9b6589319ba88312f567fc5841a958" +checksum = "5d642ff16b7e79272ae451b7322067cdc17cadf68c23264be9d94a32319efe7e" dependencies = [ "rustls-pki-types", ] [[package]] name = "webview2-com" -version = "0.33.0" +version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f61ff3d9d0ee4efcb461b14eb3acfda2702d10dc329f339303fc3e57215ae2c" +checksum = "823e7ebcfaea51e78f72c87fc3b65a1e602c321f407a0b36dbb327d7bb7cd921" dependencies = [ "webview2-com-macros", "webview2-com-sys", @@ -6042,16 +6322,16 @@ checksum = "1d228f15bba3b9d56dde8bddbee66fa24545bd17b48d5128ccf4a8742b18e431" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.95", ] [[package]] name = "webview2-com-sys" -version = "0.33.0" +version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3a3e2eeb58f82361c93f9777014668eb3d07e7d174ee4c819575a9208011886" +checksum = "7a82bce72db6e5ee83c68b5de1e2cd6ea195b9fbff91cb37df5884cbe3222df4" dependencies = [ - "thiserror", + "thiserror 1.0.69", "windows 0.58.0", "windows-core 0.58.0", ] @@ -6173,7 +6453,7 @@ checksum = "f6fc35f58ecd95a9b71c4f2329b911016e6bec66b3f2e6a4aad86bd2e99e2f9b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.95", ] [[package]] @@ -6184,7 +6464,7 @@ checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.95", ] [[package]] @@ -6195,7 +6475,7 @@ checksum = "08990546bf4edef8f431fa6326e032865f27138718c587dc21bc0265bbcb57cc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.95", ] [[package]] @@ -6206,7 +6486,7 @@ checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.95", ] [[package]] @@ -6481,6 +6761,15 @@ dependencies = [ ] [[package]] +name = "winnow" +version = "0.6.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39281189af81c07ec09db316b302a3e67bf9bd7cbf6c820b50e35fee9c2fa980" +dependencies = [ + "memchr", +] + +[[package]] name = "winreg" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -6491,13 +6780,26 @@ dependencies = [ ] [[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + +[[package]] name = "wry" -version = "0.46.3" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd5cdf57c66813d97601181349c63b96994b3074fc3d7a31a8cce96e968e3bbd" +checksum = "1e644bf458e27b11b0ecafc9e5633d1304fdae82baca1d42185669752fe6ca4f" dependencies = [ "base64 0.22.1", "block2", + "cookie", "crossbeam-channel", "dpi", "dunce", @@ -6521,7 +6823,8 @@ dependencies = [ "sha2", "soup3", "tao-macros", - "thiserror", + "thiserror 2.0.9", + "url", "webkit2gtk", "webkit2gtk-sys", "webview2-com", @@ -6532,15 +6835,6 @@ dependencies = [ ] [[package]] -name = "wyz" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" -dependencies = [ - "tap", -] - -[[package]] name = "x11" version = "2.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -6572,10 +6866,34 @@ dependencies = [ ] [[package]] +name = "yoke" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.95", + "synstructure", +] + +[[package]] name = "zbus" -version = "4.0.1" +version = "4.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b8e3d6ae3342792a6cc2340e4394334c7402f3d793b390d2c5494a4032b3030" +checksum = "bb97012beadd29e654708a0fdb4c84bc046f537aecfde2c3ee0a9e4b4d48c725" dependencies = [ "async-broadcast", "async-executor", @@ -6587,7 +6905,6 @@ dependencies = [ "async-task", "async-trait", "blocking", - "derivative", "enumflags2", "event-listener", "futures-core", @@ -6601,28 +6918,71 @@ dependencies = [ "serde_repr", "sha1", "static_assertions", - "tokio", "tracing", "uds_windows", "windows-sys 0.52.0", "xdg-home", - "zbus_macros", - "zbus_names", - "zvariant", + "zbus_macros 4.4.0", + "zbus_names 3.0.0", + "zvariant 4.2.0", +] + +[[package]] +name = "zbus" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb67eadba43784b6fb14857eba0d8fc518686d3ee537066eb6086dc318e2c8a1" +dependencies = [ + "async-broadcast", + "async-recursion", + "async-trait", + "enumflags2", + "event-listener", + "futures-core", + "futures-util", + "hex", + "nix", + "ordered-stream", + "serde", + "serde_repr", + "static_assertions", + "tokio", + "tracing", + "uds_windows", + "windows-sys 0.59.0", + "winnow 0.6.22", + "xdg-home", + "zbus_macros 5.2.0", + "zbus_names 4.1.0", + "zvariant 5.1.0", ] [[package]] name = "zbus_macros" -version = "4.0.1" +version = "4.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7a3e850ff1e7217a3b7a07eba90d37fe9bb9e89a310f718afcde5885ca9b6d7" +checksum = "267db9407081e90bbfa46d841d3cbc60f59c0351838c4bc65199ecd79ab1983e" dependencies = [ - "proc-macro-crate 1.3.1", + "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "regex", - "syn 1.0.109", - "zvariant_utils", + "syn 2.0.95", + "zvariant_utils 2.1.0", +] + +[[package]] +name = "zbus_macros" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d49ebc960ceb660f2abe40a5904da975de6986f2af0d7884b39eec6528c57" +dependencies = [ + "proc-macro-crate 3.2.0", + "proc-macro2", + "quote", + "syn 2.0.95", + "zbus_names 4.1.0", + "zvariant 5.1.0", + "zvariant_utils 3.0.2", ] [[package]] @@ -6633,7 +6993,19 @@ checksum = "4b9b1fef7d021261cc16cba64c351d291b715febe0fa10dc3a443ac5a5022e6c" dependencies = [ "serde", "static_assertions", - "zvariant", + "zvariant 4.2.0", +] + +[[package]] +name = "zbus_names" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "856b7a38811f71846fd47856ceee8bccaec8399ff53fb370247e66081ace647b" +dependencies = [ + "serde", + "static_assertions", + "winnow 0.6.22", + "zvariant 5.1.0", ] [[package]] @@ -6654,7 +7026,28 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.95", +] + +[[package]] +name = "zerofrom" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.95", + "synstructure", ] [[package]] @@ -6664,39 +7057,103 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" [[package]] +name = "zerovec" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.95", +] + +[[package]] +name = "zvariant" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2084290ab9a1c471c38fc524945837734fbf124487e105daec2bb57fd48c81fe" +dependencies = [ + "endi", + "enumflags2", + "serde", + "static_assertions", + "zvariant_derive 4.2.0", +] + +[[package]] name = "zvariant" -version = "4.0.0" +version = "5.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e09e8be97d44eeab994d752f341e67b3b0d80512a8b315a0671d47232ef1b65" +checksum = "a1200ee6ac32f1e5a312e455a949a4794855515d34f9909f4a3e082d14e1a56f" dependencies = [ "endi", "enumflags2", "serde", "static_assertions", "url", - "zvariant_derive", + "winnow 0.6.22", + "zvariant_derive 5.1.0", + "zvariant_utils 3.0.2", ] [[package]] name = "zvariant_derive" -version = "4.0.0" +version = "4.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72a5857e2856435331636a9fbb415b09243df4521a267c5bedcd5289b4d5799e" +checksum = "73e2ba546bda683a90652bac4a279bc146adad1386f25379cf73200d2002c449" dependencies = [ - "proc-macro-crate 1.3.1", + "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 1.0.109", - "zvariant_utils", + "syn 2.0.95", + "zvariant_utils 2.1.0", +] + +[[package]] +name = "zvariant_derive" +version = "5.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "687e3b97fae6c9104fbbd36c73d27d149abf04fb874e2efbd84838763daa8916" +dependencies = [ + "proc-macro-crate 3.2.0", + "proc-macro2", + "quote", + "syn 2.0.95", + "zvariant_utils 3.0.2", ] [[package]] name = "zvariant_utils" -version = "1.1.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00bedb16a193cc12451873fee2a1bc6550225acece0e36f333e68326c73c8172" +checksum = "c51bcff7cc3dbb5055396bcf774748c3dab426b4b8659046963523cee4808340" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.95", +] + +[[package]] +name = "zvariant_utils" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20d1d011a38f12360e5fcccceeff5e2c42a8eb7f27f0dcba97a0862ede05c9c6" +dependencies = [ + "proc-macro2", + "quote", + "serde", + "static_assertions", + "syn 2.0.95", + "winnow 0.6.22", ] diff --git a/Cargo.toml b/Cargo.toml @@ -2,5 +2,6 @@ resolver = "2" members = [ "crates/core", + "crates/model", "crates/tauri", ] diff --git a/.env.example b/app/.env.example diff --git a/app/LICENSE b/app/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + <program> Copyright (C) <year> <name of author> + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +<https://www.gnu.org/licenses/>. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +<https://www.gnu.org/licenses/why-not-lgpl.html>. +\ No newline at end of file diff --git a/app/package.json b/app/package.json @@ -0,0 +1,44 @@ +{ + "name": "app", + "private": true, + "version": "0.0.0", + "license": "GPLv3", + "type": "module", + "scripts": { + "build": "vite build --mode production", + "build:dev": "vite build --mode development", + "dev:hmr": "vite dev --mode development --debug hmr", + "dev": "", + "preview": "vite preview", + "prepare": "svelte-kit sync || echo ''", + "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", + "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch" + }, + "devDependencies": { + "@sveltejs/adapter-static": "^3.0.0", + "@sveltejs/kit": "^2.16.0", + "@sveltejs/vite-plugin-svelte": "^5.0.0", + "autoprefixer": "^10.4.20", + "daisyui": "^4.10.0", + "postcss": "^8.5.1", + "postcss-import": "^16.1.0", + "svelte": "^5.0.0", + "svelte-check": "^4.0.0", + "tailwindcss": "^3.4.3", + "typescript": "^5.0.0", + "vite": "^6.0.0" + }, + "dependencies": { + "@nostr-dev-kit/ndk": "^2.10.6", + "@nostr-dev-kit/ndk-svelte": "^2.3.1", + "@radroots/client": "workspace:*", + "@radroots/geocoder": "workspace:*", + "@radroots/lib-app": "workspace:*", + "@radroots/models": "workspace:*", + "@radroots/svelte-maplibre": "workspace:*", + "@radroots/theme": "workspace:*", + "@radroots/util": "workspace:*", + "chart.js": "^4.4.5", + "css-paint-polyfill": "^3.4.0" + } +} +\ No newline at end of file diff --git a/app/postcss.config.js b/app/postcss.config.js @@ -0,0 +1,7 @@ +export default { + plugins: { + 'postcss-import': {}, + tailwindcss: {}, + autoprefixer: {}, + }, +} +\ No newline at end of file diff --git a/app/src/app.css b/app/src/app.css @@ -0,0 +1,17 @@ +@import "/static/stylesheets/tailwindcss-app-carousel.css"; +@import "/static/stylesheets/tailwindcss-app-form.css"; +@import "/static/stylesheets/tailwindcss-app-styles.css"; +@import "/static/stylesheets/tailwindcss-app.css"; +@import "/static/stylesheets/tailwindcss-spinner.css"; +@import "/static/stylesheets/tailwindcss-app-layer-1.css"; + +@import "/static/webfonts/sf-pro-rounded/styles.css"; +@import "/static/webfonts/sf-pro-display/styles.css"; + +@tailwind base; +@tailwind components; +@tailwind utilities; + +@layer base {} + +@layer components {} +\ No newline at end of file diff --git a/app/src/app.d.ts b/app/src/app.d.ts @@ -0,0 +1,5 @@ +declare global { + namespace App { } +} + +export { }; diff --git a/app/src/app.html b/app/src/app.html @@ -0,0 +1,24 @@ +<!doctype html> +<html lang="en" data-theme=""> + +<head> + <meta charset="utf-8" /> + + <link rel="stylesheet" type="text/css" href="/phosphor-icons/bold.css" /> + <link rel="stylesheet" type="text/css" href="/phosphor-icons/fill.css" /> + <link rel="stylesheet" type="text/css" href="/stylesheets/styles-maplibre-gl.css" /> + <link rel="stylesheet" type="text/css" href="/stylesheets/styles-superellipse.css" /> + + <script src="/assets/keyva.min.js"></script> + <script src="/assets/squircle.min.js"></script> + + <meta name="viewport" + content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no viewport-fit=cover" /> + %sveltekit.head% +</head> + +<body data-sveltekit-preload-data="hover"> + <div style="display: contents">%sveltekit.body%</div> +</body> + +</html> +\ No newline at end of file diff --git a/app/src/lib/locale/en/common.json b/app/src/lib/locale/en/common.json @@ -0,0 +1,167 @@ +{ + "stop": "Stop", + "this_action_is_irreversible": "This action is irreversible", + "notifications": "Notifications", + "accept": "Accept", + "activation": "Activation", + "active": "Active", + "add": "Add", + "add_current_location": "Add current location", + "add_map_location": "Add map location", + "add_new": "Add new", + "add_new_location": "Add new location", + "agree": "Agree", + "all_accounts": "All accounts", + "area": "Area", + "at": "At", + "authenticated": "Authenticated", + "available_balance": "Available balance", + "back": "Back", + "bag": "Bag", + "bags": "Bags", + "bank_account": "Bank account", + "banner_photo": "Banner photo", + "bio": "Bio", + "business_name": "Business name", + "cancel": "Cancel", + "choose_a_profile_name": "Choose a profile name", + "climate": "Climate", + "close": "Close", + "color_mode": "Color mode", + "complete": "Complete", + "configure": "Configure", + "configure_your_device": "Configure your device", + "connect": "Connect", + "connected": "Connected", + "connection": "Connection", + "continue": "Continue", + "coordinates": "Coordinates", + "current_location": "Current location", + "dark": "Dark", + "date_created": "Date created", + "date_modified": "Date modified", + "delete": "Delete", + "description": "Description", + "details": "Details", + "device": "Device", + "disagree": "Disagree", + "disconnect": "Disconnect", + "do_you_want_to_continue_q": "Do you want to continue?", + "done": "Done", + "edit": "Edit", + "elevation": "Elevation", + "end_date": "End date", + "endpoint": "Endpoint", + "estate": "Estate", + "failure_to_process_the_request": "Failure to process the request", + "farm": "Farm", + "farm_land": "Farm land", + "file_name": "File name", + "file_size": "File size", + "filters": "Filters", + "from": "From", + "general": "General", + "hex": "Hex", + "highest_price": "Highest price", + "home": "Home", + "inbox": "Inbox", + "inflows": "Inflows", + "items": "Items", + "key": "Key", + "keypair": "Keypair", + "keys": "Keys", + "land_area": "Land area", + "land_plot": "Land plot", + "latitude": "Latitude", + "light": "Light", + "list": "List", + "listing": "Listing", + "location": "Location", + "locations": "Locations", + "longitude": "Longitude", + "lot": "Lot", + "lot_name": "Lot name", + "lowest_price": "Lowest price", + "make_primary": "Make primary", + "map": "Map", + "market": "Market", + "message": "Message", + "messages": "Messages", + "month": "Month", + "month_to_date": "Month to date", + "name": "Name", + "name_of_farm_or_estate": "Name of farm or estate", + "new": "New", + "no": "No", + "no_items_to_display": "No items to display", + "no_locations_saved": "No locations saved", + "nostr": "Nostr", + "not_connected": "Not connected", + "npub": "Npub", + "nsec": "Nsec", + "optional": "Optional", + "options": "Options", + "options_list": "Options list", + "order": "Order", + "origin": "Origin", + "other": "Other", + "outflows": "Outflows", + "overview": "Overview", + "page": "Page", + "per": "Per", + "personal": "Personal", + "photo": "Photo", + "photo_hosting": "Photo hosting", + "photos": "Photos", + "post": "Post", + "preview": "Preview", + "price": "Price", + "process": "Process", + "product": "Product", + "product_location": "Product location", + "product_name": "Product name", + "products": "Products", + "profile": "Profile", + "profile_name": "Profile name", + "profile_photo": "Profile photo", + "profiles": "Profiles", + "public_key": "Public key", + "publish": "Publish", + "quantity": "Quantity", + "quit": "Quit", + "reading": "Reading", + "relay": "Relay", + "relays": "Relays", + "reset": "Reset", + "return": "Return", + "review": "Review", + "search": "Search", + "secret_key": "Secret key", + "settings": "Settings", + "setup": "Setup", + "setup_for_farmer": "Setup for Farmer", + "skip": "Skip", + "socials": "Socials", + "soil": "Soil", + "start_date": "Start date", + "status": "Status", + "subject": "Subject", + "submit": "Submit", + "summary": "Summary", + "terms_of_use_agreement": "Terms of Use agreement", + "title": "Title", + "to": "To", + "unlock": "Unlock", + "update": "Update", + "upload_url": "Upload url", + "url": "url", + "username": "Username", + "using_public_key": "Using public key", + "value": "Value", + "view": "View", + "wallet": "Wallet", + "website": "Website", + "year": "Year", + "yes": "Yes", + "your_name": "Your name" +} +\ No newline at end of file diff --git a/app/src/lib/locale/en/error.json b/app/src/lib/locale/en/error.json @@ -0,0 +1,9 @@ +{ + "device": { + "configuration_failure": "There was an error configuring the device", + "public_key_not_derived": "Error deriving public key" + }, + "radroots": { + "profile_registered": "The profile has been registered" + } +} +\ No newline at end of file diff --git a/app/src/lib/locale/en/eula.json b/app/src/lib/locale/en/eula.json @@ -0,0 +1,47 @@ +{ + "acceptance_of_terms": { + "body": "By using our Application, you signify your acceptance of this EULA. If you do not agree to this EULA, you may not use our Application.", + "title": "Acceptance of Terms" + }, + "changes": { + "body": "We reserve the right to update or modify this EULA at any time and without prior notice. Your continued use of our application following any changes to this EULA will be deemed to be your acceptance of such changes.", + "title": "Changes to EULA" + }, + "consequences_of_violation": { + "body": "Any violation of this EULA, including the prohibited content and conduct outlined above, may result in the termination of your access to our application.", + "title": "Consequences of Violation" + }, + "contact": { + "body": "If you have any questions about this EULA, please contact us at info@radroots.market", + "title": "Contact Information" + }, + "disclaimer": { + "body": "Our application is provided \"as is\" and \"as available\" without warranty of any kind, either express or implied, including but not limited to the implied warranties of merchantability and fitness for a particular purpose. We do not guarantee that our application will be uninterrupted or error-free. In no event shall Radroots Inc. be liable for any damages whatsoever, including but not limited to direct, indirect, special, incidental, or consequential damages, arising out of or in connection with the use or inability to use our application.", + "title": "Disclaimer of Warranties and Limitation of Liability" + }, + "error": { + "required": "The user agreement is required to use the application" + }, + "introduction": { + "body": "This End User License Agreement (\"EULA\") is a legal agreement between you and Radroots Inc. for the use of our mobile application Radroots. By installing, accessing, or using our application, you agree to be bound by the terms and conditions of this EULA.", + "title": "Introduction" + }, + "prohibited_conduct": { + "body_li_0_0": "Harasses or bullies others", + "body_li_0_1": "Impersonates others", + "body_li_0_2": "Is intended to intimidate or threaten others", + "body_li_0_3": "Is intended to promote or incite violence", + "title": "You also agree not to engage in any conduct that:" + }, + "prohibited_content": { + "body_0_title": "You agree not to use our application to create, upload, post, send, or store any content that:", + "body_li_0_0": "Is illegal, infringing, or fraudulent", + "body_li_0_1": "Is pornographic, obscene, or offensive", + "body_li_0_2": "Is discriminatory or promotes hate speech", + "body_li_0_3": "Is harmful to minors", + "body_li_0_4": "Is intended to harass or bully others", + "body_li_0_5": "Is intended to impersonate others", + "title": "Prohibited Content and Conduct" + }, + "title": "End User License Agreement" +} +\ No newline at end of file diff --git a/app/src/lib/locale/en/icu.json b/app/src/lib/locale/en/icu.json @@ -0,0 +1,84 @@ +{ + "*_as": "{value} as", + "*_available": "{value} Available", + "*_copied": "{value} copied", + "*_description": "{value} description", + "*_details": "{value} details", + "*_failure": "{value} failure", + "*_list": "{value} list", + "*_month": "{value} month", + "*_name": "{value} name", + "*_order": "{value} order", + "*_price": "{value} price", + "*_quantity": "{value} quantity", + "*_sold": "{value} sold", + "*_summary": "{value} summary", + "*_the": "{value} the", + "*_title": "{value} title", + "*_total": "{value} total", + "*_your_device": "{value} your device", + "a_*_is_required": "A {value} is required", + "add_*": "Add {value}", + "add_a_*": "Add a {value}", + "add_existing_*": "Add existing {value}", + "as_*": "As {value}", + "choose_*": "Choose {value}", + "choose_a_*": "Choose a {}", + "choose_on_*": "Choose on {value}", + "click_to_*": "Click to {value}", + "click_to_add_a_*": "Click to add a {value}", + "configure_*": "Configure {value}", + "connect_*": "Connect {value}", + "connected_*": "Connected {value}", + "create_new_*": "Create new {value}", + "default_*": "Default {value}", + "delete_*": "Delete {value}", + "disconnect_*": "Disconnect {value}", + "edit_*": "Edit {value}", + "enter_*": "Enter {value}", + "enter_*_per_order": "Enter {value} per order", + "enter_a_*": "Enter a {value}", + "enter_new_*": "Enter new {value}", + "enter_the_*": "Enter the {value}", + "error_loading_*": "Error loading {value}", + "failure_*": "Failure {value}", + "failure_saving_*_to_the_database": "Failure saving {value} to the database", + "go_*": "Go {value}", + "invalid_*": "Invalid {value}", + "invalid_*_entry": "Invalid {value} entry", + "last_*": "Last {value}", + "listing_*": "Listing {value}", + "month_of_*": "Month of {value}", + "name_your_*": "Name your {value}", + "new_*": "New {value}", + "no_*": "No {value}", + "no_*_published": "No {value} published", + "no_*_selected": "No {value} selected", + "no_*_to_display": "No {value} to display", + "nostr_*": "Nostr {value}", + "post_*": "Post {value}", + "primary_*": "Primary {value}", + "reading_*": "Reading {value}", + "set_as_*": "Set as {value}", + "show_*": "Show {value}", + "the_*": "The {value}", + "the_*_is_available": "The {value} is available", + "the_*_is_incomplete": "The {value} is incomplete", + "the_*_is_missing": "The {value} is missing", + "the_*_is_registered": "The {value} is registered", + "the_*_is_required": "The {value} is missing", + "the_current_entry_*_will_be_deleted": "The current entry \"{value}\" will be deleted", + "the_listing_will_be_created_without_a_*": "The listing will be created without a {value}", + "there_was_a_failure_while_*": "There was a failure while {value}", + "this_*": "This {value}", + "toggle_*": "Toggle {value}", + "total_*": "Total {value}", + "unable_to_save_*": "Unable to save {value}", + "unconnected_*": "Unconnected {value}", + "uploading_*_photos": "Uploading {value} photos", + "use_existing_*": "Use existing {value}", + "valid_*": "Valid {value}", + "view_*": "View {value}", + "view_the_*": "View the {value}", + "your_*": "Your {value}" +} +\ No newline at end of file diff --git a/app/src/lib/locale/en/notify.json b/app/src/lib/locale/en/notify.json @@ -0,0 +1,14 @@ +{ + "message": { + "init_greeting": "Welcome to Radroots!", + "init_details": "Your device will be configured by the setup wizard", + "init_welcome": "Welcome! Your device was configured. To view or change your configuration go to Settings > Configuration", + "no_profile_config": "Your profile will be created without a name. You can change this later in Settings > Configuration > Profile" + }, + "device": { + "reset": "You are about to reset this device, which will erase all data including keys, profiles, and personal files" + }, + "profile": { + "name_update": "Updating your username will result in public links on your profile being updated" + } +} +\ No newline at end of file diff --git a/app/src/lib/locale/i18n.ts b/app/src/lib/locale/i18n.ts @@ -0,0 +1,30 @@ +import { i18n_conf } from '@radroots/util'; +import locales_keys from './locales.json'; + +export type Locale = keyof typeof locales_keys; + +const translations: Record<Locale, any> = { + en: { locales_keys }, +}; + +const i18n = i18n_conf<Locale>({ + default_locale: `en`, + translations, + loaders: [ + ...Object.keys(translations).map((locale) => [`common`, `error`, `eula`, `icu`, `notify`].map(key => ({ + locale, + key, + loader: async () => (await import(`./${locale}/${key}.json`)).default + }))), + ].flat() +}); + +const { + t: ls, + loading: translations_loading, + locales, + locale, + loadTranslations: load_translations +} = i18n; + +export { load_translations, locale, locales, ls, translations, translations_loading }; diff --git a/app/src/lib/locale/locales.json b/app/src/lib/locale/locales.json @@ -0,0 +1,3 @@ +{ + "en": "English" +} +\ No newline at end of file diff --git a/app/src/lib/util/conf.ts b/app/src/lib/util/conf.ts @@ -0,0 +1,23 @@ +import { PUBLIC_RADROOTS_NOSTR_PUBKEY, PUBLIC_RADROOTS_RELAY_URL } from "$env/static/public"; +import { root_symbol, type NostrEventTagClient } from "@radroots/util"; + +export const cfg_delay = { + load: 321, + notify: 123, + mount_el: 500, + nostr_relay_poll_document: 3000, + entry_focus: 2000, + load_notify: 3000, +}; + +export const cfg_nostr = { + relay_url: PUBLIC_RADROOTS_RELAY_URL, + relay_pubkey: PUBLIC_RADROOTS_NOSTR_PUBKEY, + relay_polling_count_max: 10, +}; + +export const cfg_nostr_client: NostrEventTagClient = { + name: root_symbol, + pubkey: cfg_nostr.relay_pubkey, + relay: cfg_nostr.relay_url +}; diff --git a/app/src/lib/util/err.ts b/app/src/lib/util/err.ts @@ -0,0 +1,5 @@ +export const err = { + nostr: { + no_relays: `error.nostr.no_relays_connected` + } +} +\ No newline at end of file diff --git a/app/src/lib/util/index.ts b/app/src/lib/util/index.ts @@ -0,0 +1,64 @@ +import { goto } from "$app/navigation"; +import { PUBLIC_RADROOTS_URL } from "$env/static/public"; +import { ls } from "$lib/locale/i18n"; +import { TauriClientDatabase, TauriClientDatastore, TauriClientFs, TauriClientGeolocation, TauriClientGui, TauriClientHttp, TauriClientKeys, TauriClientRadroots } from "@radroots/client"; +import { Geocoder } from "@radroots/geocoder"; +import { app_notify, get_store, handle_err } from "@radroots/lib-app"; +import { encode_qp_route, NostrEventUtil, NostrKeyUtil, type CallbackPromise, type NavigationParamTuple } from "@radroots/util"; +import type { NavigationRoute } from "./routes"; + +export const db = new TauriClientDatabase(); +export const datastore = new TauriClientDatastore(); +export const fs = new TauriClientFs(); +export const geol = new TauriClientGeolocation(); +export const gui = new TauriClientGui(); +export const http = new TauriClientHttp(); +export const keys = new TauriClientKeys(); +export const radroots = new TauriClientRadroots(PUBLIC_RADROOTS_URL); + +export const geoc = new Geocoder(); +export const nostrkey = new NostrKeyUtil(); +export const nostre = new NostrEventUtil(); + +export const route = async (nav_route: NavigationRoute, params: NavigationParamTuple[] = []): Promise<void> => { + try { + if (params.length) await goto(encode_qp_route<NavigationRoute>(nav_route, params)); + else await goto(nav_route); + } catch (e) { + await handle_err(e, `route`); + }; +}; + +export const restart = async (opts?: { + notify_message?: string; + route?: NavigationRoute; +}): Promise<void> => { + try { + goto(opts?.route || `/`); + if (opts?.notify_message) app_notify.set(opts.notify_message); + location.reload(); + } catch (e) { + await handle_err(e, `restart`); + } +}; + +export const reset = async (): Promise<void> => { + try { + const $ls = get_store(ls); + const confirm = await gui.confirm({ + message: `${$ls(`notify.device.reset`)}. ${$ls(`common.this_action_is_irreversible`)}. ${$ls(`common.do_you_want_to_continue_q`)}` + }); + if (!confirm) return; + await keys.nostr_keystore_reset(); + await db.reset(); + goto(`/`) + app_notify.set(`Device reset complete.`); + } catch (e) { + await handle_err(e, `reset`); + } +}; + +export const message_callback = async (message: string, callback: CallbackPromise): Promise<void> => { + gui.alert(message); + await callback(); +}; +\ No newline at end of file diff --git a/app/src/lib/util/model/location-gcs.ts b/app/src/lib/util/model/location-gcs.ts @@ -0,0 +1,68 @@ +import { handle_err, } from "@radroots/lib-app"; +import type { ILocationGcsCreateResolve, LocationGcsFormFields } from "@radroots/models"; +import { err_msg, location_geohash, type GeocoderReverseResult, type GeolocationCoordinatesPoint, type GeolocationPoint } from "@radroots/util"; +import { db, geoc } from ".."; + +export const model_location_gcs_create_geol_p = async (opts: { + label?: string; + kind: string; + geol_p: GeolocationPoint; +}): Promise<ILocationGcsCreateResolve<string>> => { + try { + const { label, geol_p } = opts; + const fields: LocationGcsFormFields = { + lat: geol_p.lat.toString(), + lng: geol_p.lng.toString(), + geohash: location_geohash(geol_p), + kind: opts.kind, + } + if (label) fields.label = label; + const geoc_rev = await geoc.reverse({ + lat: geol_p.lat, + lng: geol_p.lng + }); + if (`results` in geoc_rev && geoc_rev.results.length > 0) { + const geoc_res = geoc_rev.results[0]; + fields.gc_id = geoc_res.id.toString(); + fields.gc_name = geoc_res.name; + fields.gc_admin1_id = geoc_res.admin1_id.toString(); + fields.gc_admin1_name = geoc_res.admin1_name; + fields.gc_country_id = geoc_res.country_id; + fields.gc_country_name = geoc_res.country_name; + }; + const res = await db.location_gcs_create(fields); + return res; + } catch (e) { + await handle_err(e, `model_location_gcs_add_position`); + return err_msg(`*`) + } +}; + +export const model_location_gcs_create_geoc_r = async (opts: { + label?: string; + kind: string; + geoc_r: GeocoderReverseResult; + geol_p: GeolocationCoordinatesPoint; +}): Promise<ILocationGcsCreateResolve<string>> => { + try { + const { label, geoc_r, geol_p } = opts; + const fields: LocationGcsFormFields = { + lat: geol_p.lat.toString(), + lng: geol_p.lng.toString(), + geohash: location_geohash(geol_p), + kind: opts.kind, + gc_id: geoc_r.id.toString(), + gc_name: geoc_r.name, + gc_admin1_id: geoc_r.admin1_id.toString(), + gc_admin1_name: geoc_r.admin1_name, + gc_country_id: geoc_r.country_id, + gc_country_name: geoc_r.country_name, + }; + if (label) fields.label = label; + const res = await db.location_gcs_create(fields); + return res; + } catch (e) { + await handle_err(e, `model_location_gcs_add_geocode`); + return err_msg(`*`) + } +}; diff --git a/app/src/lib/util/nostr/nostr-sync.ts b/app/src/lib/util/nostr/nostr-sync.ts @@ -0,0 +1,130 @@ +import { ls } from "$lib/locale/i18n"; +import { db, gui, nostre } from "$lib/util"; +import { cfg_nostr_client } from "$lib/util/conf"; +import { NDKKind } from "@nostr-dev-kit/ndk"; +import { get_store, handle_err, key_nostr, ndk, ndk_user, nostr_sync_prevent } from "@radroots/lib-app"; +import type { NostrRelay } from "@radroots/models"; +import { ndk_event, ndk_event_metadata, num_str, throw_err } from "@radroots/util"; +import { err } from "../err"; + +export const nostr_sync_metadata = async (): Promise<void> => { + try { + const $ndk = get_store(ndk); + const $ndk_user = get_store(ndk_user); + const $key_nostr = get_store(key_nostr); + const tbl_nostr_profile = await db.nostr_profile_read({ + public_key: $key_nostr + }); + if (`err` in tbl_nostr_profile) return throw_err(tbl_nostr_profile); + const ev_metadata = await ndk_event_metadata({ + $ndk, + $ndk_user, + metadata: tbl_nostr_profile.result + }); + if (ev_metadata) await ev_metadata.publish(); + } catch (e) { + await handle_err(e, `nostr_sync_metadata`); + } +}; + +export const nostr_sync_classified = async (nostr_relays: NostrRelay[]): Promise<void> => { + try { + const $ndk = get_store(ndk); + const $ndk_user = get_store(ndk_user); + const tbl_trade_products = await db.trade_product_read_list(); + if (`err` in tbl_trade_products) return throw_err(tbl_trade_products); + for (const trade_product of tbl_trade_products.results) { + const tbl_location_gcss = await db.location_gcs_read_list({ + table: [`on_trade_product`, { id: trade_product.id }], + }); + if (`err` in tbl_location_gcss) return throw_err(tbl_location_gcss); + const trade_product_location = tbl_location_gcss.results[0]; + const tbl_media_uploads = await db.media_image_read_list({ + table: [`on_trade_product`, { id: trade_product.id }], + }); + if (`err` in tbl_media_uploads) return throw_err(tbl_media_uploads); + const ev = await ndk_event({ + $ndk, + $ndk_user, + basis: { + kind: NDKKind.Classified, + content: ``, + tags: nostre.fmt_tags_basis_nip99({ + d_tag: trade_product.id, + client: cfg_nostr_client, + listing: { + key: trade_product.key, + category: trade_product.category, + title: trade_product.title, + summary: trade_product.summary, + process: trade_product.process, + lot: trade_product.lot, + profile: trade_product.profile, + year: num_str(trade_product.year), + }, + quantity: { + amt: num_str(trade_product.qty_amt), + unit: trade_product.qty_unit, + label: trade_product.qty_label + }, + price: { + amt: num_str(trade_product.price_amt), + currency: trade_product.price_currency, + qty_amt: num_str(trade_product.price_qty_amt), + qty_unit: trade_product.price_qty_unit, + }, + location: { + city: trade_product_location.gc_name, + region: trade_product_location.gc_admin1_name, + region_code: trade_product_location.gc_admin1_id, + country: trade_product_location.gc_country_name, + country_code: trade_product_location.gc_country_id, + lat: trade_product_location.lat, + lng: trade_product_location.lng, + geohash: trade_product_location.geohash, + }, + images: tbl_media_uploads.results.length ? tbl_media_uploads.results.map(i => ({ url: `${i.res_base}/${i.res_path}.${i.mime_type}` })) : undefined + }), + }, + }); + if (ev) { + ev.content = `radroots:[nostr:${nostre.nevent_encode({ + id: ev.id, + author: ev.pubkey, + relays: nostr_relays.map(i => i.url), + kind: NDKKind.Classified, + })}]` + await ev.publish(); + } + } + } catch (e) { + await handle_err(e, `nostr_sync_classified`); + } +}; + +export const nostr_sync = async (): Promise<void> => { + try { + const $nostr_sync_prevent = get_store(nostr_sync_prevent); + const $lls = get_store(ls); + const $key_nostr = get_store(key_nostr); + if ($nostr_sync_prevent) { + const confirm = await gui.confirm({ + message: `${$lls(`error.client.nostr_sync_disabled`)}`, + }); + if (confirm) { + nostr_sync_prevent.set(false); + await nostr_sync(); + } + return; + } + const tbl_nostr_relays = await db.nostr_relay_read_list({ + table: [`on_profile`, { public_key: $key_nostr }], + }); + if (`err` in tbl_nostr_relays) return throw_err(tbl_nostr_relays); + if (!tbl_nostr_relays.results.length) return throw_err(err.nostr.no_relays); + await nostr_sync_metadata(); + await nostr_sync_classified(tbl_nostr_relays.results); + } catch (e) { + await handle_err(e, `nostr_sync`); + } +}; diff --git a/app/src/lib/util/routes.ts b/app/src/lib/util/routes.ts @@ -0,0 +1,13 @@ +export type NavigationRoute = + | "/" + | "/init"; + +export function parse_route(route: string): NavigationRoute { + switch (route) { + case "/": + case "/init": + return route; + default: + return "/"; + }; +}; +\ No newline at end of file diff --git a/app/src/routes/(app)/+error.svelte b/app/src/routes/(app)/+error.svelte @@ -0,0 +1,20 @@ +<script lang="ts"> + import { goto } from "$app/navigation"; + import { page } from "$app/state"; +</script> + +<div class={`flex flex-col w-full pt-20 justify-center items-center`}> + <p class={`font-sans font-[400] text-layer-0-glyph`}> + {`error: ${page.error?.message || `no message`}`} + </p> + <button + class={`flex flex-row justify-center items-center`} + onclick={async () => { + await goto(`/`); + }} + > + <p class={`font-sans font-[400] text-layer-0-glyph`}> + {`reset`} + </p> + </button> +</div> diff --git a/app/src/routes/(app)/+layout.svelte b/app/src/routes/(app)/+layout.svelte @@ -0,0 +1,69 @@ +<script lang="ts"> + import { datastore, db, http, keys } from "$lib/util"; + import { + app_splash, + handle_err, + key_nostr, + kv_init, + ndk, + ndk_user, + nostr_ndk_configured, + } from "@radroots/lib-app"; + import { ndk_init, throw_err } from "@radroots/util"; + import { onMount } from "svelte"; + import type { LayoutProps } from "./$types"; + + let { children }: LayoutProps = $props(); + + onMount(async () => { + try { + await init(); + } catch (e) { + handle_err(e, `on_mount`); + } finally { + app_splash.set(false); + } + }); + + const init = async (): Promise<void> => { + try { + if (`paintWorklet` in CSS) + (CSS as any).paintWorklet.addModule(`/assets/squircle.min.js`); + await http.init(); + await datastore.init(); + await kv_init(); + await nostr_init(); + } catch (e) { + await handle_err(e, `init`); + } + }; + + const nostr_init = async (): Promise<void> => { + try { + if (!$key_nostr) return void throw_err(`*-key_nostr`); + const keys_nostr_read = await keys.nostr_read($key_nostr); + if (`err` in keys_nostr_read) + return void throw_err(keys_nostr_read.err); + const tb_nostr_relays = await db.nostr_relay_read_list({ + table: [`on_profile`, { public_key: $key_nostr }], + }); + if (`err` in tb_nostr_relays) + return void throw_err(tb_nostr_relays.err); + for (const { url } of tb_nostr_relays.results) + $ndk.addExplicitRelay(url); + await $ndk.connect(); + const _ndk_user = await ndk_init({ + $ndk, + secret_key: keys_nostr_read.secret_key, + }); + nostr_ndk_configured.set(!!_ndk_user); + if (!_ndk_user) return; + $ndk_user = _ndk_user; + $ndk_user.ndk = $ndk; + } catch (e) { + await handle_err(e, `nostr_init`); + } + }; +</script> + +{@render children()} diff --git a/app/src/routes/(app)/+layout.ts b/app/src/routes/(app)/+layout.ts @@ -0,0 +1,19 @@ + +import { datastore, keys, route } from '$lib/util'; +import { handle_err, key_nostr } from '@radroots/lib-app'; +import type { LayoutLoad, LayoutLoadEvent } from '../$types'; + +export const load: LayoutLoad = async (_: LayoutLoadEvent) => { + try { + await datastore.init(); + const ks_keynostr = await datastore.get(`key_nostr`); + if (`err` in ks_keynostr) return void await route(`/init`); + const nostrkey = await keys.nostr_read(ks_keynostr.result); + if (`err` in nostrkey) return void await route(`/init`); + key_nostr.set(ks_keynostr.result); + } catch (e) { + await handle_err(e, `(app)load`) + } finally { + return {}; + }; +}; diff --git a/app/src/routes/(app)/+page.svelte b/app/src/routes/(app)/+page.svelte @@ -0,0 +1,27 @@ +<script lang="ts"> + import { gui, route } from "$lib/util"; + import { handle_err, Home } from "@radroots/lib-app"; + import type { ResolveAccountInfo } from "@radroots/util"; + + let data: ResolveAccountInfo | undefined = $state(undefined); +</script> + +<Home + basis={{ + data: data, + lc_handle_farms: async () => { + try { + await route(`/`); + } catch (e) { + await handle_err(e, `lc_handle_farms`); + } + }, + lc_handle_products: async () => { + try { + await gui.alert(`@todo`); + } catch (e) { + await handle_err(e, `lc_handle_products`); + } + }, + }} +/> diff --git a/app/src/routes/(cfg)/+layout.svelte b/app/src/routes/(cfg)/+layout.svelte @@ -0,0 +1,18 @@ +<script lang="ts"> + import { app_splash, handle_err } from "@radroots/lib-app"; + import { onMount } from "svelte"; + import type { LayoutProps } from "./$types"; + + let { children }: LayoutProps = $props(); + + onMount(async () => { + try { + } catch (e) { + handle_err(e, `on_mount`); + } finally { + app_splash.set(false); + } + }); +</script> + +{@render children()} diff --git a/app/src/routes/(cfg)/+layout.ts b/app/src/routes/(cfg)/+layout.ts @@ -0,0 +1,21 @@ + +import { datastore, keys, route } from '$lib/util'; +import { handle_err, key_nostr } from '@radroots/lib-app'; +import type { LayoutLoad, LayoutLoadEvent } from './$types'; + +export const load: LayoutLoad = async (_: LayoutLoadEvent) => { + try { + await datastore.init(); + const ks_keynostr = await datastore.get(`key_nostr`); + if (`result` in ks_keynostr) { + const nostrkey = await keys.nostr_read(ks_keynostr.result); + if (`result` in nostrkey) return void await route(`/`); + await datastore.remove(`key_nostr`); + } + key_nostr.set(``); + } catch (e) { + await handle_err(e, `(cfg)load`) + } finally { + return {}; + }; +}; diff --git a/app/src/routes/(cfg)/init/+page.svelte b/app/src/routes/(cfg)/init/+page.svelte @@ -0,0 +1,952 @@ +<script lang="ts"> + import { goto } from "$app/navigation"; + import { PUBLIC_NOSTR_RELAY_DEFAULTS } from "$env/static/public"; + import { ls } from "$lib/locale/i18n"; + import { datastore, db, gui, keys, nostrkey, radroots } from "$lib/util"; + import { cfg_delay } from "$lib/util/conf"; + import { + app_lo, + app_loading, + app_notify, + ButtonLayoutPair, + carousel_dec, + carousel_inc, + carousel_index, + carousel_index_max, + EntryLineIdb, + fmt_id, + handle_err, + idb, + Input, + KvLib, + LabelDisplay, + LoadSymbol, + LogoCircle, + view_effect, + } from "@radroots/lib-app"; + import { el_id, form_fields, sleep } from "@radroots/util"; + import { onMount } from "svelte"; + + type KvKey = `nostr:key:add` | `nostr:profile` | `#key_nostrp`; + const kv = new KvLib<KvKey>(idb); + + const page_carousel: Record<View, { max_index: number }> = { + cfg_key: { + max_index: 2, + }, + cfg_profile: { + max_index: 2, + }, + eula: { + max_index: 1, + }, + }; + + let el_eula_scrolled = $state(false); + type View = `cfg_key` | `cfg_profile` | `eula`; + let initial_view: View = `cfg_key`; + let view: View = $state(initial_view); + + $effect(() => { + view_effect<View>(view); + }); + + type CfgKeyOpt = `cfg_keygen` | `cfg_keyadd`; + let cgf_keyopt: CfgKeyOpt | undefined = $state(undefined); + + type CfgRole = `farmer` | `personal`; + let cfg_role: CfgRole | undefined = $state(undefined); + + let cfg_profile_nostr_publickey = $state(``); + const cfg_profile_nostr_publickey_npub = $derived( + cfg_profile_nostr_publickey + ? nostrkey.npub(cfg_profile_nostr_publickey) || `` + : ``, + ); + + let cfg_profile_profilename_valid = $state(false); + let cfg_profile_profilename_loading = $state(false); + + let loading_submit = $state(false); + + onMount(async () => { + try { + await init(); + } catch (e) { + handle_err(e, `on_mount`); + } + }); + + const init = async (): Promise<void> => { + const nostrkey_all = await keys.nostr_read_all(); + if (`results` in nostrkey_all && nostrkey_all.results.length) { + handle_view(`eula`); + } else { + handle_view(view); + } + await kv.init(); + }; + + const handle_view = (new_view: View): void => { + if (new_view === `cfg_key` && view === `cfg_profile`) { + const offset = cgf_keyopt === `cfg_keygen` ? 1 : 0; + carousel_index.set(page_carousel[new_view].max_index - offset); + } else { + carousel_index.set(0); + carousel_index_max.set(page_carousel[new_view].max_index); + } + view = new_view; + }; + + const on_scroll_eula = async ({ + currentTarget: el, + }: { + currentTarget: EventTarget & HTMLDivElement; + }): Promise<void> => { + const client_h = el?.clientHeight; + const scroll_h = el?.scrollHeight; + const scroll_top = el?.scrollTop; + if (scroll_top + client_h >= scroll_h) el_eula_scrolled = true; + }; + + const reset = async ( + alert_message?: string, + prevent_loading?: boolean, + ): Promise<void> => { + try { + app_loading.set(!prevent_loading); + handle_view(`cfg_key`); + if (alert_message) await gui.alert(alert_message); + await sleep(cfg_delay.load); + await keys.nostr_keystore_reset(); + await db.reset(); + } catch (e) { + await handle_err(e, `reset`); + } finally { + app_loading.set(false); + } + }; + + const key_add_keystore = async (public_key: string): Promise<void> => { + const ks_key_nostr_add = await datastore.set(`key_nostr`, public_key); + if (`err` in ks_key_nostr_add) + return void (await reset( + `${$ls(`error.device.configuration_failure`)}`, + )); + }; + + const key_gen = async (): Promise<void> => { + const keys_nostr_create = await keys.nostr_gen(); + if (`err` in keys_nostr_create) + return void (await reset( + `${$ls(`error.device.configuration_failure`)}`, + )); + await kv.save(`#key_nostrp`, keys_nostr_create.public_key); + }; + + const key_add = async (secret_key: string): Promise<void> => { + const keys_nostr_add = await keys.nostr_add(secret_key); + if (`err` in keys_nostr_add) + return void (await gui.alert( + `${$ls(`icu.invalid_*`, { value: `${$ls(`common.key`)}`.toLowerCase() })}`, + )); + await kv.save(`#key_nostrp`, keys_nostr_add.public_key); + }; + + const configure_device = async ( + public_key: string, + profile_name?: string, + ): Promise<void> => { + const nostr_profile_add = await db.nostr_profile_create({ + public_key, + name: profile_name ? profile_name : undefined, + }); + if (`err` in nostr_profile_add || `err_s` in nostr_profile_add) + return void (await gui.alert( + `${$ls(`icu.failure_saving_*_to_the_database`, { value: `${$ls(`common.profile`)}`.toLowerCase() })}`, + )); + for (const url of Array.from( + new Set([...PUBLIC_NOSTR_RELAY_DEFAULTS.split(",")]), + )) { + const nostr_relay_add = await db.nostr_relay_create({ url }); + if (`err` in nostr_relay_add || `err_s` in nostr_relay_add) + return void (await gui.alert( + `${$ls(`icu.failure_saving_*_to_the_database`, { value: `${$ls(`icu.default_*`, { value: `${$ls(`common.relays`)}` })}`.toLowerCase() })}`, + )); + await db.nostr_profile_relay_set({ + nostr_profile: { + id: nostr_profile_add.id, + }, + nostr_relay: { + id: nostr_relay_add.id, + }, + }); + } + await key_add_keystore(public_key); + }; + + const handle_choose_key_gen_or_add = async (): Promise<void> => { + try { + if (cgf_keyopt === `cfg_keyadd`) + return void (await carousel_inc(view)); + await key_gen(); + handle_view(`cfg_profile`); + } catch (e) { + await handle_err(e, `handle_choose_key_gen_or_add`); + } + }; + + const handle_submit_key_add = async (): Promise<void> => { + try { + const nostrkey_add = await kv.read(`nostr:key:add`); + if (!nostrkey_add) + return void (await gui.alert( + `${$ls(`icu.enter_a_*`, { value: `${$ls(`icu.valid_*`, { value: `${$ls(`common.key`)}` })}`.toLowerCase() })}`, + )); + const key_nostr_read = await keys.nostr_read(nostrkey_add); + if (`err` in key_nostr_read) + return void (await reset( + `${$ls(`error.device.configuration_failure`)}`, + )); + await key_add(key_nostr_read.secret_key); + kv.del(`nostr:key:add`); + return void handle_view(`cfg_profile`); + } catch (e) { + await handle_err(e, `handle_submit_key_add`); + } + }; + + const handle_profile_add = async (): Promise<void> => { + try { + if (cfg_profile_profilename_loading) return; + const kv_keynostrp = await kv.read(`#key_nostrp`); + if (!kv_keynostrp) + return void (await reset( + `${$ls(`error.device.configuration_failure`)}`, + )); //@todo + const key_nostr_read = await keys.nostr_read(kv_keynostrp); + if (`err` in key_nostr_read) + return void (await reset( + `${$ls(`error.device.configuration_failure`)}`, + )); //@todo + const kv_profilename = await kv.read(`nostr:profile`); + if (!kv_profilename) + return void (await gui.alert( + `${$ls(`icu.enter_a_*`, { value: `${$ls(`common.profile_name`)}`.toLowerCase() })}`, + )); + cfg_profile_profilename_loading = true; + const profile_req = await radroots.fetch_profile_request({ + profile_name: kv_profilename, + secret_key: key_nostr_read.secret_key, + }); + if (`err` in profile_req) { + cfg_profile_profilename_loading = false; + return void (await gui.alert(profile_req.err)); + } + const confirm = await gui.confirm({ + message: `${`${$ls(`icu.the_*_is_available`, { value: `${$ls(`common.profile_name`).toLowerCase()} "${kv_profilename}"` })}`}. Would you like to use it?`, //@todo + cancel: `${$ls(`common.no`)}`, + ok: `${$ls(`common.yes`)}`, + }); + if (!confirm) { + cfg_profile_profilename_loading = false; + return; + } + const profile_create = await radroots.fetch_profile_create({ + tok: profile_req.result, + secret_key: key_nostr_read.secret_key, + }); + cfg_profile_profilename_loading = false; + if (`err` in profile_create) + return void (await gui.alert(profile_create.err)); + await datastore.setp( + `radroots_profile`, + kv_keynostrp, + profile_create.result, + ); + handle_view(`eula`); + } catch (e) { + await handle_err(e, `handle_profile_add`); + } finally { + cfg_profile_profilename_loading = false; + } + }; + + const handle_set_role = async (): Promise<void> => { + if (!cfg_role) cfg_role = `personal`; + await datastore.set(`role`, cfg_role); + handle_view(`eula`); + }; + + const handle_continue = async (): Promise<void> => { + switch (view) { + case `cfg_key`: + switch ($carousel_index) { + case 0: + return await carousel_inc(view); + case 1: + return await handle_choose_key_gen_or_add(); + case 2: + return await handle_submit_key_add(); + } + break; + case `cfg_profile`: + switch ($carousel_index) { + case 0: + return await handle_profile_add(); + case 1: + return await handle_set_role(); + } + break; + } + }; + + const handle_back = async (): Promise<void> => { + switch (view) { + case `cfg_key`: + switch ($carousel_index) { + case 1: { + cgf_keyopt = undefined; + return await carousel_dec(view); + } + case 2: + return await carousel_dec(view); + } + break; + case `cfg_profile`: + switch ($carousel_index) { + case 0: + return handle_view(`cfg_key`); + case 1: + return carousel_dec(view); + } + break; + } + }; + + const submit = async (): Promise<void> => { + try { + loading_submit = true; + const kv_keynostrp = await kv.read(`#key_nostrp`); + if (!kv_keynostrp) + return void (await reset( + `${$ls(`error.device.configuration_failure`)}`, + )); //@todo + const key_nostr_read = await keys.nostr_read(kv_keynostrp); + if (`err` in key_nostr_read) + return void (await reset( + `${$ls(`error.device.configuration_failure`)}`, + )); //@todo + const radroots_profile = await datastore.getp( + `radroots_profile`, + kv_keynostrp, + ); + if (`result` in radroots_profile) { + await radroots.fetch_profile_activate({ + id: radroots_profile.result, + secret_key: key_nostr_read.secret_key, + }); //@todo + } + await configure_device( + kv_keynostrp, + await kv.read(`nostr:profile`), + ); + app_notify.set(`${$ls(`notify.message.init_welcome`)}`); + } catch (e) { + await handle_err(e, `submit`); + } finally { + loading_submit = false; + } + }; +</script> + +<div + data-view={`cfg_key`} + class={`flex flex-col h-full w-full justify-start items-center`} +> + <div + class={`z-10 absolute max-m_0:bottom-0 bottom-10 left-0 flex flex-col w-full justify-center items-center`} + > + <ButtonLayoutPair + basis={{ + continue: { + label: `${$ls(`common.continue`)}`, + disabled: $carousel_index === 1 && !cgf_keyopt, + callback: async () => await handle_continue(), + }, + back: { + label: `${$ls(`common.back`)}`, + visible: $carousel_index > 0, + callback: async () => await handle_back(), + }, + }} + /> + </div> + <div + data-carousel-container={`cfg_key`} + class={`carousel-container flex h-full w-full`} + > + <div + data-carousel-item={`cfg_key`} + class={`carousel-item flex flex-col h-full w-full justify-center items-center`} + > + <div + class={`relative flex flex-col h-full w-full justify-center items-center`} + > + <div + class={`flex flex-row w-full justify-start items-center -translate-y-16`} + > + <button + class={`flex flex-row w-full justify-center items-center`} + onclick={async () => { + await goto(`/`); + }} + > + <LogoCircle /> + </button> + </div> + <div + class={`absolute bottom-0 left-0 flex flex-col h-[20rem] w-full px-10 gap-2 justify-start items-center`} + > + <div + class={`flex flex-row w-full justify-start items-center`} + > + <p + class={`font-sans font-[400] text-sm text-layer-0-glyph-label uppercase`} + > + {`${$ls(`common.configure`)}`} + </p> + </div> + <div + class={`flex flex-col w-full gap-2 justify-start items-center`} + > + <div + class={`flex flex-row w-full justify-start items-center`} + > + <p + class={`font-mono font-[400] text-[1.1rem] text-layer-0-glyph`} + > + {`${$ls(`notify.message.init_greeting`)}`} + </p> + </div> + <div + class={`flex flex-row w-full justify-start items-center`} + > + <p + class={`font-mono font-[400] text-[1.1rem] text-layer-0-glyph`} + > + {`${$ls(`notify.message.init_details`)}.`} + </p> + </div> + </div> + </div> + </div> + </div> + <div + data-carousel-item={`cfg_key`} + class={`carousel-item flex flex-col h-full w-full justify-center items-center`} + role="button" + tabindex="0" + onclick={async () => { + cgf_keyopt = undefined; + }} + onkeydown={null} + > + <div + class={`flex flex-col h-[16rem] gap-8 w-full justify-start items-center`} + > + <div class={`flex flex-row w-full justify-center items-center`}> + <p + class={`font-sans font-[600] text-layer-0-glyph text-3xl`} + > + {`${$ls(`icu.configure_*`, { value: `${$ls(`common.device`)}` })}`} + </p> + </div> + <div + class={`flex flex-col w-full gap-6 justify-center items-center`} + > + <button + class={`flex flex-col h-bold_button w-lo_${$app_lo} justify-center items-center rounded-touch ${cgf_keyopt === `cfg_keygen` ? `layer-1-surface-apply-active layer-1-raise-apply layer-1-ring-apply` : `bg-layer-1-surface`} el-re`} + onclick={async (ev) => { + ev.stopPropagation(); + cgf_keyopt = `cfg_keygen`; + }} + > + <p + class={`font-sans font-[600] text-layer-0-glyph text-xl`} + > + {`${$ls(`icu.create_new_*`, { value: `${$ls(`common.keypair`)}`.toLowerCase() })}`} + </p> + </button> + <button + class={`flex flex-col h-bold_button w-lo_${$app_lo} justify-center items-center rounded-touch ${cgf_keyopt === `cfg_keyadd` ? `layer-1-surface-apply-active layer-1-raise-apply layer-1-ring-apply` : `bg-layer-1-surface`} el-re`} + onclick={async (ev) => { + ev.stopPropagation(); + cgf_keyopt = `cfg_keyadd`; + }} + > + <p + class={`font-sans font-[600] text-layer-0-glyph text-xl`} + > + {`${$ls(`icu.use_existing_*`, { + value: `${$ls(`common.keypair`)}`.toLowerCase(), + })}`} + </p> + </button> + </div> + </div> + </div> + <div + data-carousel-item={`cfg_key`} + class={`carousel-item flex flex-col h-full w-full justify-center items-center`} + > + <div + class={`flex flex-col w-full gap-8 justify-start items-center`} + > + <div + class={`flex flex-col w-full gap-6 justify-center items-center`} + > + {#if cfg_profile_nostr_publickey} + <p + class={`font-sans font-[600] text-layer-0-glyph text-3xl`} + > + {`${$ls(`common.using_public_key`)}`} + </p> + <LabelDisplay + basis={{ + classes: `w-lo_${$app_lo}`, + label: { + classes: `pl-4 font-mono text-lg text-start truncate`, + value: + cfg_profile_nostr_publickey_npub || + cfg_profile_nostr_publickey, + }, + style: `guide`, + }} + /> + {:else} + <p + class={`font-sans font-[600] text-layer-0-glyph text-3xl capitalize`} + > + {`${$ls(`icu.add_existing_*`, { value: `${$ls(`common.key`)}`.toLowerCase() })}`} + </p> + <Input + basis={{ + classes: `h-entry_guide w-lo_${$app_lo} bg-layer-1-surface layer-1-focus-surface rounded-touch font-mono text-lg placeholder:opacity-60 items-end text-center`, + id: fmt_id(`nostr:key:add`), + sync: true, + placeholder: `${$ls(`icu.enter_*`, { value: `nostr nsec/hex` })}`, + field: form_fields.profile_name, + callback_keydown: async ({ key_s, el }) => { + if (key_s) { + el.blur(); + await handle_continue(); + } + }, + }} + /> + {/if} + </div> + </div> + </div> + </div> +</div> +<div + data-view={`cfg_profile`} + class={`hidden flex flex-col h-full w-full justify-start items-center`} +> + <div + data-carousel-container={`cfg_profile`} + class={`carousel-container flex h-full w-full`} + > + <div + data-carousel-item={`cfg_profile`} + class={`carousel-item flex flex-col h-full w-full justify-center items-center`} + > + <div + class={`flex flex-col h-[16rem] w-full px-4 gap-10 justify-start items-center`} + > + <p class={`font-sans font-[600] text-layer-0-glyph text-3xl`}> + {`${$ls(`icu.add_*`, { value: `${$ls(`common.profile`)}` })}`} + </p> + <EntryLineIdb + basis={{ + loading: cfg_profile_profilename_loading, + wrap: { + layer: 1, + classes: `w-lo_${$app_lo}`, + style: `guide`, + }, + el: { + classes: `font-sans text-[1.25rem] text-center placeholder:opacity-60`, + id: fmt_id(`nostr:profile`), + sync: true, + layer: 1, + placeholder: `${$ls(`icu.enter_*`, { value: `${$ls(`common.profile_name`)}`.toLowerCase() })}`, + field: form_fields.profile_name, + callback: async ({ pass }) => { + cfg_profile_profilename_valid = pass; + }, + callback_keydown: async ({ key_s, el }) => { + if (key_s) { + el.blur(); + await handle_continue(); + } + }, + }, + }} + /> + </div> + </div> + <div + data-carousel-item={`cfg_profile`} + class={`carousel-item flex flex-col h-full w-full justify-center items-center`} + role="button" + tabindex="0" + onclick={async () => { + cfg_role = undefined; + }} + onkeydown={null} + > + <div + class={`flex flex-col h-[16rem] w-full gap-10 justify-start items-center`} + > + <div class={`flex flex-row w-full justify-center items-center`}> + <p + class={`font-sans font-[600] text-layer-0-glyph text-3xl`} + > + {`${$ls(`common.setup_for_farmer`)}`} + </p> + </div> + <div + class={`flex flex-col w-full gap-5 justify-center items-center`} + > + <button + class={`flex flex-col h-bold_button w-lo_${$app_lo} justify-center items-center rounded-touch ${cfg_role === `farmer` ? `layer-1-surface-apply-active layer-1-raise-apply layer-1-ring-apply` : `bg-layer-1-surface`} el-re`} + onclick={async (ev) => { + ev.stopPropagation(); + cfg_role = `farmer`; + }} + > + <p + class={`font-sans font-[600] text-layer-0-glyph text-xl`} + > + {`${$ls(`common.yes`)}`} + </p> + </button> + <button + class={`flex flex-col h-bold_button w-lo_${$app_lo} justify-center items-center rounded-touch ${cfg_role === `personal` ? `layer-1-surface-apply-active layer-1-raise-apply layer-1-ring-apply` : `bg-layer-1-surface`} el-re`} + onclick={async (ev) => { + ev.stopPropagation(); + cfg_role = `personal`; + }} + > + <p + class={`font-sans font-[600] text-layer-0-glyph text-xl`} + > + {`${$ls(`common.no`)}`} + </p> + </button> + </div> + </div> + </div> + </div> + + <div + class={`absolute max-m_0:bottom-0 bottom-10 left-0 flex flex-col w-full justify-center items-center`} + > + <ButtonLayoutPair + basis={{ + continue: { + label: `${$ls(`common.continue`)}`, + disabled: + ($carousel_index === 0 && + !cfg_profile_profilename_valid) || + ($carousel_index === 1 && !cfg_role), + callback: async () => await handle_continue(), + }, + back: { + visible: true, + label: + $carousel_index === 0 + ? `${$ls(`common.skip`)}` + : `${$ls(`common.back`)}`, + callback: async () => { + if ($carousel_index === 0) { + const confirm = await gui.confirm({ + message: `${$ls(`notify.message.no_profile_config`)}`, + cancel: `${$ls(`icu.add_*`, { value: `${$ls(`common.profile`)}` })}`, + ok: `${$ls(`common.continue`)}`, + }); + if (confirm === false) + return void el_id( + fmt_id(`nostr:profile`), + )?.focus(); + return void carousel_inc(view); + } + await handle_back(); + }, + }, + }} + /> + </div> +</div> +<div + data-view={`eula`} + class={`hidden flex flex-col h-full w-full max-m_0:pt-12 pt-24 justify-start items-center`} +> + <div + data-carousel-container={`eula`} + class={`carousel-container flex h-full w-full rounded-2xl scroll-hide`} + > + <div + data-carousel-item={`eula`} + class={`carousel-item flex flex-col w-full max-m_0:pt-16 justify-start items-center`} + > + <div + class={`flex flex-col w-full px-4 pb-2 justify-start items-center ${view === `eula` ? `fade-in-long` : ``} overflow-hidden`} + > + <div + class={`flex flex-col w-full px-4 gap-4 justify-start items-center`} + > + <div + class={`flex flex-row w-full justify-center items-center`} + > + <p + class={`font-mono font-[600] text-layer-0-glyph text-2xl`} + > + {`${$ls(`eula.title`)}`} + </p> + </div> + <div + onscroll={on_scroll_eula} + class={`flex flex-col h-[34rem] w-full gap-6 justify-start items-center overflow-y-scroll scroll-hide`} + > + <div + class={`flex flex-col w-full gap-2 justify-start items-start`} + > + <p + class={`font-mono font-[600] text-layer-0-glyph`} + > + {`**${$ls(`eula.introduction.title`)}**`} + </p> + <p + class={`font-mono font-[500] text-layer-0-glyph text-sm text-justify break-word`} + > + {`${$ls(`eula.introduction.body`)}`} + </p> + </div> + <div + class={`flex flex-col w-full gap-2 justify-start items-start`} + > + <p + class={`font-mono font-[600] text-layer-0-glyph`} + > + {`**${$ls(`eula.prohibited_content.title`)}**`} + </p> + <p + class={`font-mono font-[500] text-sm text-layer-0-glyph text-justify break-word`} + > + {`${$ls(`eula.prohibited_content.body_0_title`)}`} + </p> + <div + class={`flex flex-col w-full justify-start items-start`} + > + {#each [0, 1, 2, 3, 4, 5] as li} + <div + class={`flex flex-row w-full justify-start items-center`} + > + <div + class={`flex flex-row h-full w-8 justify-start items-start`} + > + <p + class={` font-mono font-[500] text-sm text-layer-0-glyph text-justify break-word`} + > + {`*`} + </p> + </div> + <div + class={`flex flex-row h-full w-full justify-start items-start`} + > + <p + class={`col-span-10 font-mono font-[500] text-sm text-layer-0-glyph text-justify break-word`} + > + {`${$ls(`eula.prohibited_content.body_li_0_${li}`)}`} + </p> + </div> + </div> + {/each} + </div> + </div> + <div + class={`flex flex-col w-full gap-2 justify-start items-start`} + > + <p + class={`font-mono font-[600] text-layer-0-glyph`} + > + {`**${$ls(`eula.prohibited_conduct.title`)}**`} + </p> + <div + class={`flex flex-col w-full justify-start items-start`} + > + {#each [0, 1, 2, 3] as li} + <div + class={`flex flex-row w-full justify-start items-center`} + > + <div + class={`flex flex-row h-full w-8 justify-start items-start`} + > + <p + class={` font-mono font-[500] text-sm text-layer-0-glyph text-justify break-word`} + > + {`*`} + </p> + </div> + <div + class={`flex flex-row h-full w-full justify-start items-start`} + > + <p + class={`col-span-10 font-mono font-[500] text-sm text-layer-0-glyph text-justify break-word`} + > + {`${$ls(`eula.prohibited_conduct.body_li_0_${li}`)}`} + </p> + </div> + </div> + {/each} + </div> + </div> + <div + class={`flex flex-col w-full gap-2 justify-start items-start`} + > + <p + class={`font-mono font-[600] text-layer-0-glyph`} + > + {`**${$ls(`eula.consequences_of_violation.title`)}**`} + </p> + <p + class={`font-mono font-[500] text-layer-0-glyph text-sm text-justify break-word`} + > + {`${$ls(`eula.consequences_of_violation.body`)}`} + </p> + </div> + <div + class={`flex flex-col w-full gap-2 justify-start items-start`} + > + <p + class={`font-mono font-[600] text-layer-0-glyph`} + > + {`**${$ls(`eula.disclaimer.title`)}**`} + </p> + <p + class={`font-mono font-[500] text-layer-0-glyph text-sm text-justify break-word`} + > + {`${$ls(`eula.disclaimer.body`)}`} + </p> + </div> + <div + class={`flex flex-col w-full gap-2 justify-start items-start`} + > + <p + class={`font-mono font-[600] text-layer-0-glyph`} + > + {`**${$ls(`eula.changes.title`)}**`} + </p> + <p + class={`font-mono font-[500] text-layer-0-glyph text-sm text-justify break-word`} + > + {`${$ls(`eula.changes.body`)}`} + </p> + </div> + <div + class={`flex flex-col w-full gap-2 justify-start items-start`} + > + <p + class={`font-mono font-[600] text-layer-0-glyph`} + > + {`**${$ls(`eula.contact.title`)}**`} + </p> + <p + class={`font-mono font-[500] text-layer-0-glyph text-sm text-justify break-word`} + > + {`${$ls(`eula.contact.body`)}`} + </p> + </div> + <div + class={`flex flex-col w-full gap-2 justify-start items-start`} + > + <p + class={`font-mono font-[600] text-layer-0-glyph`} + > + {`**${$ls(`eula.acceptance_of_terms.title`)}**`} + </p> + <p + class={`font-mono font-[500] text-layer-0-glyph text-sm text-justify break-word`} + > + {`${$ls(`eula.acceptance_of_terms.body`)}`} + </p> + </div> + </div> + </div> + <div + class={`flex flex-row w-full pt-6 justify-center items-center`} + > + <button + class={`group flex flex-row basis-1/2 gap-4 justify-center items-center ${el_eula_scrolled ? `` : `opacity-80`}`} + onclick={async () => { + const confirm = await gui.confirm({ + message: `${$ls(`eula.error.required`)}`, + cancel: `${$ls(`common.quit`)}`, + }); + if (confirm === false) await reset(undefined, true); + }} + > + <p + class={`font-mono font-[400] text-sm text-layer-0-glyph group-active:text-layer-0-glyph el-re`} + > + {`-`} + </p> + <p + class={`font-mono font-[400] text-sm text-layer-0-glyph group-active:text-layer-0-glyph el-re`} + > + {`${`${$ls(`common.disagree`)}`}`} + </p> + <p + class={`font-mono font-[400] text-sm text-layer-0-glyph group-active:text-layer-0-glyph el-re`} + > + {`-`} + </p> + </button> + <button + class={`relative group flex flex-row basis-1/2 gap-4 justify-center items-center el-re ${el_eula_scrolled ? `` : `opacity-40`}`} + onclick={async () => { + if (el_eula_scrolled) await submit(); + }} + > + <p + class={`font-mono font-[400] text-sm text-layer-0-glyph-hl group-active:text-layer-0-glyph-hl/80 el-re`} + > + {`-`} + </p> + <p + class={`font-mono font-[400] text-sm text-layer-0-glyph-hl group-active:text-layer-0-glyph-hl/80 el-re`} + > + {`${`${$ls(`common.agree`)}`}`} + </p> + <p + class={`font-mono font-[400] text-sm text-layer-0-glyph-hl group-active:text-layer-0-glyph-hl/80 el-re`} + > + {`- `} + </p> + {#if loading_submit} + <div + class={`absolute right-3 flex flex-row justify-start items-center`} + > + <LoadSymbol basis={{ dim: `xs` }} /> + </div> + {/if} + </button> + </div> + </div> + </div> + </div> +</div> diff --git a/app/src/routes/+layout.svelte b/app/src/routes/+layout.svelte @@ -0,0 +1,49 @@ +<script lang="ts"> + import { gui, route } from "$lib/util"; + import { cfg_delay } from "$lib/util/conf"; + import { + app_lo, + app_loading, + app_notify, + app_th, + app_thc, + app_win, + LayoutWindow, + theme_set, + } from "@radroots/lib-app"; + import { parse_color_mode, parse_theme_key } from "@radroots/theme"; + import { cfg_app, sleep } from "@radroots/util"; + import "css-paint-polyfill"; + import "../app.css"; + import type { LayoutProps } from "./$types"; + + let { children }: LayoutProps = $props(); + + app_thc.subscribe((_app_thc) => + theme_set(parse_theme_key($app_th), parse_color_mode(_app_thc)), + ); + + app_th.subscribe((_app_th) => + theme_set(parse_theme_key(_app_th), parse_color_mode($app_thc)), + ); + + app_win.subscribe((_app_win) => { + // @todo android layout + if (_app_win.h > cfg_app.layout.ios1.h) app_lo.set(`ios1`); + else app_lo.set(`ios0`); + }); + + app_notify.subscribe(async (_app_notify) => { + if (!_app_notify) return; + app_loading.set(true); + await sleep(cfg_delay.notify); + route(`/`); + app_loading.set(false); + await gui.alert(_app_notify); + app_notify.set(``); + }); +</script> + +<LayoutWindow> + {@render children()} +</LayoutWindow> diff --git a/app/src/routes/+layout.ts b/app/src/routes/+layout.ts @@ -0,0 +1,23 @@ +import { load_translations, locales, translations_loading } from '$lib/locale/i18n'; +import { handle_err } from '@radroots/lib-app'; +import type { LayoutLoad, LayoutLoadEvent } from './$types'; + +export const prerender = true; +export const ssr = false; +export const trailingSlash = 'always'; + +export const load: LayoutLoad = async ({ url }: LayoutLoadEvent) => { + try { + const { language: nav_locale } = navigator; + let locale = `en`; + const locales_avail = locales.get(); + if (locales_avail.some(i => i === nav_locale.toLowerCase())) locale = navigator.language; + else if (locales_avail.some(i => i === nav_locale.slice(0, 2).toLowerCase())) locale = nav_locale.slice(0, 2); + await load_translations(locale.toLowerCase(), url.pathname); + await translations_loading.toPromise(); + } catch (e) { + await handle_err(e, `(root)load`) + } finally { + return {}; + }; +}; diff --git a/app/static/assets b/app/static/assets @@ -0,0 +1 @@ +Subproject commit f52f247cd90f6f8a4eb61201a3ac4648daa3bdb9 diff --git a/app/static/geonames b/app/static/geonames @@ -0,0 +1 @@ +Subproject commit 49ba0e0ee53d69636c8d991fa1448890405b4bb0 diff --git a/app/static/phosphor-icons b/app/static/phosphor-icons @@ -0,0 +1 @@ +Subproject commit 3495be6ae1dfd19459d6d5caac1ffc4ee4c5c57f diff --git a/app/static/stylesheets b/app/static/stylesheets @@ -0,0 +1 @@ +Subproject commit 3527d0f511d7e2244a4822227a3c2827db91ca45 diff --git a/app/static/webfonts b/app/static/webfonts @@ -0,0 +1 @@ +Subproject commit e7bab1604df604e470bbf97c5c9c03f60e91fdc3 diff --git a/svelte.config.js b/app/svelte.config.js diff --git a/app/tailwind.config.ts b/app/tailwind.config.ts @@ -0,0 +1,157 @@ +import { theme_colors, themes } from "@radroots/theme"; +import { type AppHeightsResponsiveIOS, type AppWidthsResponsiveIOS, cfg_app } from "@radroots/util"; +import daisyui from "daisyui"; +import type { Config } from "tailwindcss"; +import tailwind_default from "tailwindcss/defaultTheme"; +const { fontFamily: tw_font, screens: tw_screens } = tailwind_default; + +const heights_responsive: Record<AppHeightsResponsiveIOS, string> = { + nav_tabs_ios0: `80px`, + nav_tabs_ios1: `120px`, + nav_page_toolbar_ios0: `72px`, + nav_page_toolbar_ios1: `54px`, + nav_page_header_ios0: `62px`, + nav_page_header_ios1: `54px`, + lo_bottom_button_ios0: `90px`, + lo_bottom_button_ios1: `90px`, +}; + +const heights: Record<string, string> = { + ...heights_responsive, + line: `46px`, + line_button: `3.25rem`, + touch_guide: `3.4rem`, + entry_line: `48px`, + bold_button: `4.25rem`, +}; + +const widths_responsive: Record<AppWidthsResponsiveIOS, string> = { + lo_ios0: `340px`, + lo_ios1: `345px`, + lo_textdesc_ios0: `312px`, + lo_textdesc_ios1: `312px`, +}; + +const widths: Record<string, string> = { + ...widths_responsive, + trellis_line: `349px`, + trellis_value: `180px`, + trellis_display: `286px`, +}; + +const dimensions_responsive: Record<string, string> = { + ios0: `340px`, + ios1: `345px` +}; + +const spacing: Record<string, string> = { + line: `1px`, + edge: `2px` +}; + +const dimensions: Record<string, string> = { + ...dimensions_responsive +}; + +const config: Config = { + content: [ + `src/**/*.{ts,svelte}`, + `../packages/lib-app/src/**/*.{ts,svelte}`, + ], + theme: { + screens: { + ios1: { raw: `(orientation: portrait) and (min-height: ${cfg_app.layout.ios1.h}px)` }, + ios0: { raw: `(orientation: portrait) and (max-height: ${cfg_app.layout.ios0.h}px)` }, + ...tw_screens + }, + extend: { + colors: { + ...theme_colors, + }, + fontFamily: { + sans: [`SF Pro Rounded`, ...tw_font.sans], + sansd: [`SF Pro Display`], + serif: [...tw_font.serif], + mono: [...tw_font.mono], + }, + fontSize: { + guide: [`1.25rem`, { lineHeight: `1.25rem` }], + form_base: `1.08rem`, + line_label: [`1.3rem`, { lineHeight: `1.3rem` }], + trellis_ti: [`0.8rem`, { lineHeight: `1rem`, fontWeight: 300 }], + line_d: [`1.05rem`, { lineHeight: `1.33rem`, fontWeight: 400 }], + nav_prev: [`1.09rem`, { lineHeight: `1.33rem`, fontWeight: 400 }], + nav_curr: [`1.09rem`, { lineHeight: `1.33rem`, fontWeight: 500 }], + env_ti: [`1.05rem`, { lineHeight: `1.75rem`, fontWeight: 600 }], + env_btnc: [`1.1rem`, { lineHeight: `1.75rem`, fontWeight: 600 }], + env_btnl: [`1.1rem`, { lineHeight: `1.75rem`, fontWeight: 500 }], + }, + gridTemplateColumns: { + '16': `repeat(16, minmax(0, 1fr))`, + '24': `repeat(24, minmax(0, 1fr))`, + }, + height: { + ...heights, + ...dimensions, + }, + width: { + ...widths, + ...dimensions, + }, + minHeight: { + ...heights + }, + minWidth: { + ...widths + }, + maxHeight: { + ...heights + }, + maxWidth: { + ...widths + }, + padding: { + ...Object.fromEntries(Object.entries(heights).map(([k, v]) => [`h_${k}`, v])), + ...Object.fromEntries(Object.entries(widths).map(([k, v]) => [`w_${k}`, v])), + ...Object.fromEntries(Object.entries(dimensions).map(([k, v]) => [`dim_${k}`, v])), + }, + translate: { + ...Object.fromEntries(Object.entries(heights).map(([k, v]) => [`h_${k}`, v])), + ...Object.fromEntries(Object.entries(widths).map(([k, v]) => [`w_${k}`, v])), + }, + spacing: { + ...spacing, + ...Object.fromEntries(Object.entries(dimensions).map(([k, v]) => [`dim_${k}`, v])), + }, + borderWidth: { + line: `1px`, + edge: `2px` + }, + borderRadius: { + input_form: `8px`, + entry: `1.05rem`, + touch: `1.25rem` + }, + animation: { + 'spin-slow': 'spin 3s linear infinite', + }, + keyframes: { + spin: { + '0%': { transform: 'rotate(0deg)' }, + '100%': { transform: 'rotate(360deg)' }, + }, + }, + }, + }, + plugins: [ + daisyui, + ], + daisyui: { + themes: [ + themes.theme_os_light, + themes.theme_os_dark, + ], + }, +}; + +export default config; diff --git a/tsconfig.json b/app/tsconfig.json diff --git a/vite.config.ts b/app/vite.config.ts diff --git a/crates/core/Cargo.toml b/crates/core/Cargo.toml @@ -7,10 +7,9 @@ edition = "2021" [dependencies] serde_json = "1.0" serde = { version = "1.0", features = ["derive"] } -sqlx = { version = "0.8.2", features = ["sqlite", "runtime-tokio"] } thiserror = "1.0.64" uuid = { version = "1", features = ["v4"] } chrono = "0.4" regex = "1.11.0" -futures = "0.3.31" -log = "0.4" -\ No newline at end of file +base64 = "0.22" +nostr-sdk = { version = "0.37", features = [] } diff --git a/crates/core/LICENSE b/crates/core/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + <program> Copyright (C) <year> <name of author> + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +<https://www.gnu.org/licenses/>. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +<https://www.gnu.org/licenses/why-not-lgpl.html>. +\ No newline at end of file diff --git a/crates/core/src/error.rs b/crates/core/src/error.rs @@ -1,39 +0,0 @@ -use thiserror::Error; - -#[derive(Error, Debug, Clone)] -pub enum ModelError { - #[error("Invalid argument: {0}")] - InvalidArgument(String), - #[error("{0} not found")] - NotFound(String), - #[error("Serialization error: {0}")] - SerializationError(String), - #[error("Invalid query: {0}")] - InvalidQuery(String), - #[error("Internal error")] - Internal, -} - -impl From<ModelError> for String { - fn from(err: ModelError) -> Self { - err.to_string() - } -} - -#[derive(Error, Debug, Clone)] -pub enum KeyringError { - #[error("Internal error")] - Internal, - #[error("Keyring error: {0}")] - KeyringError(String), - #[error("Parsing error: {0}")] - ParsingError(String), - #[error("Missing secret key")] - MissingSecretKey, -} - -impl From<KeyringError> for String { - fn from(err: KeyringError) -> Self { - err.to_string() - } -} diff --git a/crates/core/src/keystore.rs b/crates/core/src/keystore.rs @@ -0,0 +1,126 @@ +use base64::{engine::general_purpose, Engine as _}; +use serde_json::json; +use thiserror::Error; +use uuid::Uuid; + +use crate::util::json_keys; + +#[derive(Error, Debug)] +pub enum KeystoreError { + #[error("Failed to parse JSON: {0}")] + JsonError(#[from] serde_json::Error), + #[error("UUID error: {0}")] + UuidError(#[from] uuid::Error), + #[error("File error: {0}")] + FileError(#[from] std::io::Error), + #[error("Base64 error: {0}")] + Base64Error(#[from] base64::DecodeError), + #[error("UTF-8 error: {0}")] + Utf8Error(#[from] std::string::FromUtf8Error), + #[error("Key error: {0}")] + KeyError(#[from] nostr_sdk::key::Error), + #[error("Key not found")] + KeyNotFound, +} + +pub type KeystoreResult<T> = std::result::Result<T, KeystoreError>; + +fn get_keystore_path(data_dir: &std::path::Path) -> std::path::PathBuf { + data_dir.join("radroots.json") +} + +fn get_keystore_key(data_dir: &std::path::Path) -> Vec<u8> { + let keystore_file = data_dir.join("radroots_keystore"); + let keystore_key = if keystore_file.exists() { + std::fs::read_to_string(&keystore_file) + .map_err(KeystoreError::FileError) + .and_then(|s| s.parse::<Uuid>().map_err(KeystoreError::UuidError)) + } else { + let new_keystore_key = Uuid::new_v4(); + let _ = std::fs::create_dir_all(data_dir).map_err(KeystoreError::FileError); + let _ = std::fs::write(keystore_file, new_keystore_key.to_string()) + .map_err(KeystoreError::FileError); + Ok(new_keystore_key) + }; + keystore_key + .expect("Couldn't unwrap keystore key") + .as_bytes() + .to_vec() +} + +fn obfuscate(data: &str, data_dir: &std::path::Path) -> String { + let keystore_key = get_keystore_key(data_dir); + let xored: Vec<u8> = data + .as_bytes() + .iter() + .zip(keystore_key.iter().cycle()) + .map(|(&x1, &x2)| x1 ^ x2) + .collect(); + general_purpose::STANDARD_NO_PAD.encode(xored) +} + +fn deobfuscate(data: &str, data_dir: &std::path::Path) -> KeystoreResult<String> { + let keystore_key = get_keystore_key(data_dir); + let decoded = general_purpose::STANDARD_NO_PAD + .decode(data) + .map_err(KeystoreError::Base64Error)?; + let xored: Vec<u8> = decoded + .iter() + .zip(keystore_key.iter().cycle()) + .map(|(&x1, &x2)| x1 ^ x2) + .collect(); + String::from_utf8(xored).map_err(KeystoreError::Utf8Error) +} + +fn read_keystore_file(data_dir: &std::path::Path) -> KeystoreResult<serde_json::Value> { + let content = match std::fs::read_to_string(get_keystore_path(data_dir)) { + Ok(content) => content, + Err(e) if e.kind() == std::io::ErrorKind::NotFound => String::from("{}"), + Err(e) => return Err(e.into()), + }; + Ok(serde_json::from_str(&content)?) +} + +fn write_keystore_file( + data_dir: &std::path::Path, + secrets: &serde_json::Value, +) -> KeystoreResult<()> { + let content = serde_json::to_string_pretty(secrets)?; + std::fs::write(get_keystore_path(data_dir), content)?; + Ok(()) +} + +pub fn key_add(keys: &nostr_sdk::Keys, data_dir: &std::path::Path) -> KeystoreResult<()> { + let mut secrets = read_keystore_file(data_dir).unwrap_or(json!({})); + let obfuscated_key = obfuscate(keys.secret_key().to_secret_hex().as_str(), data_dir); + secrets[keys.public_key().to_hex()] = json!(obfuscated_key); + write_keystore_file(data_dir, &secrets)?; + Ok(()) +} + +pub fn key_read(public_key: &str, data_dir: &std::path::Path) -> KeystoreResult<nostr_sdk::Keys> { + let secrets = read_keystore_file(data_dir)?; + let obfuscated_key = secrets[public_key] + .as_str() + .ok_or(KeystoreError::KeyNotFound)?; + let secret_key = deobfuscate(obfuscated_key, data_dir)?; + nostr_sdk::Keys::parse(&secret_key).map_err(KeystoreError::KeyError) +} + +pub fn key_delete(public_key: &str, data_dir: &std::path::Path) -> KeystoreResult<()> { + let mut secrets = read_keystore_file(data_dir)?; + secrets.as_object_mut().map(|obj| obj.remove(public_key)); + write_keystore_file(data_dir, &secrets)?; + Ok(()) +} + +pub fn keys_read_all(data_dir: &std::path::Path) -> KeystoreResult<Vec<String>> { + let secrets = read_keystore_file(data_dir)?; + let results = json_keys(&secrets); + Ok(results) +} + +pub fn reset(data_dir: &std::path::Path) -> KeystoreResult<()> { + write_keystore_file(data_dir, &json!({}))?; + Ok(()) +} diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs @@ -1,4 +1,4 @@ -pub mod error; -pub mod models; +pub mod keystore; +pub mod nostr; pub mod types; -pub mod utils; +pub mod util; diff --git a/crates/core/src/models/location_gcs.rs b/crates/core/src/models/location_gcs.rs @@ -1,304 +0,0 @@ -use crate::{ - error::ModelError, - types::{IModelsId, IModelsQueryBindValue, IModelsQueryBindValueTuple, IModelsResults}, - utils::{time_created_on, uuidv4}, -}; -use futures::TryStreamExt; - -#[derive(Debug, serde::Serialize, serde::Deserialize, sqlx::FromRow)] -pub struct LocationGcs { - id: String, - created_at: String, - updated_at: String, - lat: f64, - lng: f64, - geohash: String, - kind: String, - label: Option<String>, - area: Option<f64>, - elevation: Option<i32>, - soil: Option<String>, - climate: Option<String>, - gc_id: Option<String>, - gc_name: Option<String>, - gc_admin1_id: Option<String>, - gc_admin1_name: Option<String>, - gc_country_id: Option<String>, - gc_country_name: Option<String>, -} - -#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] -pub struct ILocationGcsFields { - pub lat: String, - pub lng: String, - pub geohash: String, - pub kind: String, - pub label: Option<String>, - pub area: Option<String>, - pub elevation: Option<String>, - pub soil: Option<String>, - pub climate: Option<String>, - pub gc_id: Option<String>, - pub gc_name: Option<String>, - pub gc_admin1_id: Option<String>, - pub gc_admin1_name: Option<String>, - pub gc_country_id: Option<String>, - pub gc_country_name: Option<String>, -} - -#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] -pub struct ILocationGcsFieldsUpdate { - pub lat: Option<String>, - pub lng: Option<String>, - pub geohash: Option<String>, - pub kind: Option<String>, - pub label: Option<String>, - pub area: Option<String>, - pub elevation: Option<String>, - pub soil: Option<String>, - pub climate: Option<String>, - pub gc_id: Option<String>, - pub gc_name: Option<String>, - pub gc_admin1_id: Option<String>, - pub gc_admin1_name: Option<String>, - pub gc_country_id: Option<String>, - pub gc_country_name: Option<String>, -} - -#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] -#[serde(rename_all = "snake_case")] -pub enum LocationGcsSort { - Newest, - Oldest, -} - -#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] -#[serde(rename_all = "snake_case")] -pub enum LocationGcsQueryBindValues { - Id(IModelsQueryBindValue), - Geohash(IModelsQueryBindValue), -} - -#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] -#[serde(rename_all = "snake_case")] -pub enum LocationGcsQueryListOf { - All(IModelsQueryBindValue), - OnTradeProduct(IModelsQueryBindValue), - OffTradeProduct(IModelsQueryBindValue), -} - -#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] -pub struct ILocationGcsQueryGetList { - pub of: LocationGcsQueryListOf, - pub sort: Option<LocationGcsSort>, -} - -#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] -pub struct ILocationGcsQueryGet { - pub on: Option<LocationGcsQueryBindValues>, - pub list: Option<ILocationGcsQueryGetList>, -} - -#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] -pub struct ILocationGcsQueryUpdate { - pub on: LocationGcsQueryBindValues, - pub fields: ILocationGcsFieldsUpdate, -} - -pub type ILocationGcsAdd = ILocationGcsFields; -pub type ILocationGcsAddResolve = IModelsId; -pub type ILocationGcsGet = ILocationGcsQueryGet; -pub type ILocationGcsGetResolve = IModelsResults<LocationGcs>; -pub type ILocationGcsDelete = LocationGcsQueryBindValues; -pub type ILocationGcsDeleteResolve = (); -pub type ILocationGcsUpdate = ILocationGcsQueryUpdate; -pub type ILocationGcsUpdateResolve = (); - -pub fn location_gcs_query_bind_values(opts: LocationGcsQueryBindValues) -> IModelsQueryBindValueTuple { - match opts { - LocationGcsQueryBindValues::Id(id) => ("id".to_string(), id), - LocationGcsQueryBindValues::Geohash(geohash) => ("geohash".to_string(), geohash), - } -} - -pub fn location_gcs_query_get_list(opts: ILocationGcsQueryGetList) -> IModelsQueryBindValueTuple { - let query_sort = match opts.sort { - Some(LocationGcsSort::Newest) => " ORDER BY lg.created_at DESC", - Some(LocationGcsSort::Oldest) => " ORDER BY lg.created_at ASC", - None => "", - }; - match opts.of { - LocationGcsQueryListOf::All(_) => (format!("SELECT lg.* FROM location_gcs lg{}", query_sort), "".to_string()), - LocationGcsQueryListOf::OnTradeProduct(id) => (format!("SELECT lg.* FROM location_gcs lg JOIN trade_product_location tp_lg ON lg.id = tp_lg.tb_lg WHERE tp_lg.tb_tp = ?1{}", query_sort), id), - LocationGcsQueryListOf::OffTradeProduct(id) => (format!("SELECT lg.* FROM location_gcs lg WHERE NOT EXISTS (SELECT 1 FROM trade_product_location tp_lg WHERE tp_lg.tb_lg = lg.id AND tp_lg.tb_tp = ?1){}", query_sort), id), - } -} - -fn location_gcs_fields_bind_values( - opts: ILocationGcsFields, -) -> Result<Vec<IModelsQueryBindValueTuple>, ModelError> { - let bind_values = serde_json::to_value(&opts) - .map_err(|err| ModelError::SerializationError(err.to_string()))? - .as_object() - .ok_or_else(|| ModelError::InvalidArgument("model.error.object_invalid".to_string()))? - .iter() - .filter_map(|(key, value)| value.as_str().map(|v| (key.clone(), v.to_string()))) - .collect::<Vec<_>>(); - Ok(bind_values) -} - -fn location_gcs_fields_update_bind_values( - opts: ILocationGcsFieldsUpdate, -) -> Result<Vec<IModelsQueryBindValueTuple>, ModelError> { - let bind_values = serde_json::to_value(&opts) - .map_err(|err| ModelError::SerializationError(err.to_string()))? - .as_object() - .ok_or_else(|| ModelError::InvalidArgument("model.error.object_invalid".to_string()))? - .iter() - .filter_map(|(key, value)| value.as_str().map(|v| (key.clone(), v.to_string()))) - .collect::<Vec<_>>(); - Ok(bind_values) -} - -pub async fn lib_model_location_gcs_add( - db: &sqlx::Pool<sqlx::Sqlite>, - opts: ILocationGcsAdd, -) -> Result<ILocationGcsAddResolve, ModelError> { - let id = uuidv4(); - let created_at = time_created_on(); - let updated_at = created_at.clone(); - let bind_values = location_gcs_fields_bind_values(opts) - .map_err(|e| ModelError::InvalidArgument(e.to_string()))?; - let mut query_col = vec![ - "id".to_string(), - "created_at".to_string(), - "updated_at".to_string(), - ]; - let mut query_pl = vec!["?1".to_string(), "?2".to_string(), "?3".to_string()]; - let mut query_vals: Vec<String> = vec![id.to_string(), created_at.clone(), updated_at.clone()]; - for (k, v) in bind_values.iter() { - query_col.push(k.clone()); - query_pl.push(format!("?{}", query_col.len())); - query_vals.push(v.clone()); - } - let query = format!( - "INSERT INTO location_gcs ({}) VALUES ({});", - query_col.join(", "), - query_pl.join(", ") - ); - let mut query_builder = sqlx::query(&query); - for value in query_vals.iter() { - query_builder = query_builder.bind(value); - } - query_builder - .execute(db) - .await - .map_err(|e| ModelError::InvalidQuery(e.to_string()))?; - Ok(IModelsId { id }) -} - -fn location_gcs_query_get( - opts: ILocationGcsGet, -) -> Result<(String, Vec<IModelsQueryBindValue>), ModelError> { - match opts { - ILocationGcsQueryGet { - list: Some(opts_list), - .. - } => { - let (query, bv) = location_gcs_query_get_list(opts_list); - Ok((query, vec![bv])) - } - ILocationGcsQueryGet { - on: Some(opts_on), .. - } => { - let (bv_k, bv) = location_gcs_query_bind_values(opts_on); - let query = format!("SELECT * FROM location_gcs WHERE {} = ?1;", bv_k); - Ok((query, vec![bv])) - } - _ => Err(ModelError::InvalidQuery( - "model.location_gcs.error.query_invalid".to_string(), - )), - } -} - -pub async fn lib_model_location_gcs_get( - db: &sqlx::Pool<sqlx::Sqlite>, - opts: ILocationGcsQueryGet, -) -> Result<ILocationGcsGetResolve, ModelError> { - let (query, bind_values) = location_gcs_query_get(opts)?; - let mut query_builder = sqlx::query_as::<_, LocationGcs>(&query); - for value in bind_values.iter() { - query_builder = query_builder.bind(value); - } - let results = query_builder - .fetch(db) - .try_collect() - .await - .map_err(|e: sqlx::Error| ModelError::InvalidQuery(e.to_string()))?; - Ok(IModelsResults { results }) -} - -pub async fn lib_model_location_gcs_delete( - db: &sqlx::Pool<sqlx::Sqlite>, - opts: ILocationGcsDelete, -) -> Result<ILocationGcsDeleteResolve, ModelError> { - let (bv_k, bv) = location_gcs_query_bind_values(opts); - let query = format!("DELETE FROM location_gcs WHERE {} = ?1;", bv_k); - let result = sqlx::query(&query) - .bind(bv) - .execute(db) - .await - .map_err(|e: sqlx::Error| ModelError::InvalidQuery(e.to_string()))?; - println!("{:?}", result); - if result.rows_affected() > 0 { - Ok(()) - } else { - Err(ModelError::InvalidQuery( - "models.error.model_not_found".to_string(), - )) - } -} - -pub async fn lib_model_location_gcs_update( - db: &sqlx::Pool<sqlx::Sqlite>, - opts: ILocationGcsUpdate, -) -> Result<ILocationGcsUpdateResolve, ModelError> { - let (bv_k, bv) = location_gcs_query_bind_values(opts.on); - let bind_values = location_gcs_fields_update_bind_values(opts.fields) - .map_err(|e| ModelError::InvalidArgument(e.to_string()))?; - let updated_at = time_created_on(); - let mut query_col = vec!["updated_at".to_string()]; - let mut query_pl = vec!["?2".to_string()]; - let mut query_vals = vec![bv, updated_at]; - for (k, v) in bind_values.iter() { - query_col.push(k.clone()); - query_pl.push(format!("?{}", query_col.len() + 1)); - query_vals.push(v.clone()); - } - let query = format!( - "UPDATE location_gcs SET {} WHERE {} = ?1;", - query_col - .iter() - .enumerate() - .map(|(i, col)| format!("{} = {}", col, query_pl[i])) - .collect::<Vec<_>>() - .join(", "), - bv_k - ); - let mut query_builder = sqlx::query(&query); - for value in query_vals.iter() { - query_builder = query_builder.bind(value); - } - let result = query_builder - .execute(db) - .await - .map_err(|e| ModelError::InvalidQuery(e.to_string()))?; - println!("{:?}", result); - if result.rows_affected() > 0 { - Ok(()) - } else { - Err(ModelError::InvalidQuery( - "models.error.model_not_found".to_string(), - )) - } -} diff --git a/crates/core/src/models/log_error.rs b/crates/core/src/models/log_error.rs @@ -1,225 +0,0 @@ -use crate::{ - error::ModelError, - types::{IModelsId, IModelsQueryBindValue, IModelsQueryBindValueTuple, IModelsResults}, - utils::{time_created_on, uuidv4}, -}; -use futures::TryStreamExt; - -#[derive(Debug, serde::Serialize, serde::Deserialize, sqlx::FromRow)] -pub struct LogError { - id: String, - created_at: String, - updated_at: String, - error: String, - message: String, - stack_trace: Option<String>, - cause: Option<String>, - app_system: String, - app_version: String, - nostr_pubkey: String, - data: Option<String>, -} - -#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] -pub struct ILogErrorFields { - pub error: String, - pub message: String, - pub stack_trace: Option<String>, - pub cause: Option<String>, - pub app_system: String, - pub app_version: String, - pub nostr_pubkey: String, - pub data: Option<String>, -} - -#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] -pub struct ILogErrorFieldsUpdate { - pub error: Option<String>, - pub message: Option<String>, - pub stack_trace: Option<String>, - pub cause: Option<String>, - pub app_system: Option<String>, - pub app_version: Option<String>, - pub nostr_pubkey: Option<String>, - pub data: Option<String>, -} - -#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] -#[serde(rename_all = "snake_case")] -pub enum LogErrorSort { - Newest, - Oldest, -} - -#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] -#[serde(rename_all = "snake_case")] -pub enum LogErrorQueryBindValues { - Id(IModelsQueryBindValue), -} - -#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] -#[serde(rename_all = "snake_case")] -pub enum LogErrorQueryListOf { - All(IModelsQueryBindValue), -} - -#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] -pub struct ILogErrorQueryGetList { - pub of: LogErrorQueryListOf, - pub sort: Option<LogErrorSort>, -} - -#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] -pub struct ILogErrorQueryGet { - pub on: Option<LogErrorQueryBindValues>, - pub list: Option<ILogErrorQueryGetList>, -} - -pub type ILogErrorAdd = ILogErrorFields; -pub type ILogErrorAddResolve = IModelsId; -pub type ILogErrorGet = ILogErrorQueryGet; -pub type ILogErrorGetResolve = IModelsResults<LogError>; -pub type ILogErrorDelete = LogErrorQueryBindValues; -pub type ILogErrorDeleteResolve = (); - -pub fn log_error_query_bind_values(opts: LogErrorQueryBindValues) -> IModelsQueryBindValueTuple { - match opts { - LogErrorQueryBindValues::Id(id) => ("id".to_string(), id), - } -} - -pub fn log_error_query_get_list(opts: ILogErrorQueryGetList) -> IModelsQueryBindValueTuple { - let query_sort = match opts.sort { - Some(LogErrorSort::Newest) => " ORDER BY le.created_at DESC", - Some(LogErrorSort::Oldest) => " ORDER BY le.created_at ASC", - None => "", - }; - match opts.of { - LogErrorQueryListOf::All(_) => (format!("SELECT le.* FROM log_error le{}", query_sort), "".to_string()), - } -} - -fn log_error_fields_bind_values( - opts: ILogErrorFields, -) -> Result<Vec<IModelsQueryBindValueTuple>, ModelError> { - let bind_values = serde_json::to_value(&opts) - .map_err(|err| ModelError::SerializationError(err.to_string()))? - .as_object() - .ok_or_else(|| ModelError::InvalidArgument("model.error.object_invalid".to_string()))? - .iter() - .filter_map(|(key, value)| value.as_str().map(|v| (key.clone(), v.to_string()))) - .collect::<Vec<_>>(); - Ok(bind_values) -} - -fn log_error_fields_update_bind_values( - opts: ILogErrorFieldsUpdate, -) -> Result<Vec<IModelsQueryBindValueTuple>, ModelError> { - let bind_values = serde_json::to_value(&opts) - .map_err(|err| ModelError::SerializationError(err.to_string()))? - .as_object() - .ok_or_else(|| ModelError::InvalidArgument("model.error.object_invalid".to_string()))? - .iter() - .filter_map(|(key, value)| value.as_str().map(|v| (key.clone(), v.to_string()))) - .collect::<Vec<_>>(); - Ok(bind_values) -} - -pub async fn lib_model_log_error_add( - db: &sqlx::Pool<sqlx::Sqlite>, - opts: ILogErrorAdd, -) -> Result<ILogErrorAddResolve, ModelError> { - let id = uuidv4(); - let created_at = time_created_on(); - let updated_at = created_at.clone(); - let bind_values = log_error_fields_bind_values(opts) - .map_err(|e| ModelError::InvalidArgument(e.to_string()))?; - let mut query_col = vec![ - "id".to_string(), - "created_at".to_string(), - "updated_at".to_string(), - ]; - let mut query_pl = vec!["?1".to_string(), "?2".to_string(), "?3".to_string()]; - let mut query_vals: Vec<String> = vec![id.to_string(), created_at.clone(), updated_at.clone()]; - for (k, v) in bind_values.iter() { - query_col.push(k.clone()); - query_pl.push(format!("?{}", query_col.len())); - query_vals.push(v.clone()); - } - let query = format!( - "INSERT INTO log_error ({}) VALUES ({});", - query_col.join(", "), - query_pl.join(", ") - ); - let mut query_builder = sqlx::query(&query); - for value in query_vals.iter() { - query_builder = query_builder.bind(value); - } - query_builder - .execute(db) - .await - .map_err(|e| ModelError::InvalidQuery(e.to_string()))?; - Ok(IModelsId { id }) -} - -fn log_error_query_get( - opts: ILogErrorGet, -) -> Result<(String, Vec<IModelsQueryBindValue>), ModelError> { - match opts { - ILogErrorQueryGet { - list: Some(opts_list), - .. - } => { - let (query, bv) = log_error_query_get_list(opts_list); - Ok((query, vec![bv])) - } - ILogErrorQueryGet { - on: Some(opts_on), .. - } => { - let (bv_k, bv) = log_error_query_bind_values(opts_on); - let query = format!("SELECT * FROM log_error WHERE {} = ?1;", bv_k); - Ok((query, vec![bv])) - } - _ => Err(ModelError::InvalidQuery( - "model.log_error.error.query_invalid".to_string(), - )), - } -} - -pub async fn lib_model_log_error_get( - db: &sqlx::Pool<sqlx::Sqlite>, - opts: ILogErrorQueryGet, -) -> Result<ILogErrorGetResolve, ModelError> { - let (query, bind_values) = log_error_query_get(opts)?; - let mut query_builder = sqlx::query_as::<_, LogError>(&query); - for value in bind_values.iter() { - query_builder = query_builder.bind(value); - } - let results = query_builder - .fetch(db) - .try_collect() - .await - .map_err(|e: sqlx::Error| ModelError::InvalidQuery(e.to_string()))?; - Ok(IModelsResults { results }) -} - -pub async fn lib_model_log_error_delete( - db: &sqlx::Pool<sqlx::Sqlite>, - opts: ILogErrorDelete, -) -> Result<ILogErrorDeleteResolve, ModelError> { - let (bv_k, bv) = log_error_query_bind_values(opts); - let query = format!("DELETE FROM log_error WHERE {} = ?1;", bv_k); - let result = sqlx::query(&query) - .bind(bv) - .execute(db) - .await - .map_err(|e: sqlx::Error| ModelError::InvalidQuery(e.to_string()))?; - println!("{:?}", result); - if result.rows_affected() > 0 { - Ok(()) - } else { - Err(ModelError::InvalidQuery( - "models.error.model_not_found".to_string(), - )) - } -} diff --git a/crates/core/src/models/media_upload.rs b/crates/core/src/models/media_upload.rs @@ -1,277 +0,0 @@ -use crate::{ - error::ModelError, - types::{IModelsId, IModelsQueryBindValue, IModelsQueryBindValueTuple, IModelsResults}, - utils::{time_created_on, uuidv4}, -}; -use futures::TryStreamExt; - -#[derive(Debug, serde::Serialize, serde::Deserialize, sqlx::FromRow)] -pub struct MediaUpload { - id: String, - created_at: String, - updated_at: String, - file_path: String, - mime_type: String, - res_base: String, - res_path: String, - label: Option<String>, - description: Option<String>, -} - -#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] -pub struct IMediaUploadFields { - pub file_path: String, - pub mime_type: String, - pub res_base: String, - pub res_path: String, - pub label: Option<String>, - pub description: Option<String>, -} - -#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] -pub struct IMediaUploadFieldsUpdate { - pub file_path: Option<String>, - pub mime_type: Option<String>, - pub res_base: Option<String>, - pub res_path: Option<String>, - pub label: Option<String>, - pub description: Option<String>, -} - -#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] -#[serde(rename_all = "snake_case")] -pub enum MediaUploadSort { - Newest, - Oldest, -} - -#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] -#[serde(rename_all = "snake_case")] -pub enum MediaUploadQueryBindValues { - Id(IModelsQueryBindValue), - FilePath(IModelsQueryBindValue), -} - -#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] -#[serde(rename_all = "snake_case")] -pub enum MediaUploadQueryListOf { - All(IModelsQueryBindValue), - OnTradeProduct(IModelsQueryBindValue), - OffTradeProduct(IModelsQueryBindValue), -} - -#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] -pub struct IMediaUploadQueryGetList { - pub of: MediaUploadQueryListOf, - pub sort: Option<MediaUploadSort>, -} - -#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] -pub struct IMediaUploadQueryGet { - pub on: Option<MediaUploadQueryBindValues>, - pub list: Option<IMediaUploadQueryGetList>, -} - -#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] -pub struct IMediaUploadQueryUpdate { - pub on: MediaUploadQueryBindValues, - pub fields: IMediaUploadFieldsUpdate, -} - -pub type IMediaUploadAdd = IMediaUploadFields; -pub type IMediaUploadAddResolve = IModelsId; -pub type IMediaUploadGet = IMediaUploadQueryGet; -pub type IMediaUploadGetResolve = IModelsResults<MediaUpload>; -pub type IMediaUploadDelete = MediaUploadQueryBindValues; -pub type IMediaUploadDeleteResolve = (); -pub type IMediaUploadUpdate = IMediaUploadQueryUpdate; -pub type IMediaUploadUpdateResolve = (); - -pub fn media_upload_query_bind_values(opts: MediaUploadQueryBindValues) -> IModelsQueryBindValueTuple { - match opts { - MediaUploadQueryBindValues::Id(id) => ("id".to_string(), id), - MediaUploadQueryBindValues::FilePath(file_path) => ("file_path".to_string(), file_path), - } -} - -pub fn media_upload_query_get_list(opts: IMediaUploadQueryGetList) -> IModelsQueryBindValueTuple { - let query_sort = match opts.sort { - Some(MediaUploadSort::Newest) => " ORDER BY mu.created_at DESC", - Some(MediaUploadSort::Oldest) => " ORDER BY mu.created_at ASC", - None => "", - }; - match opts.of { - MediaUploadQueryListOf::All(_) => (format!("SELECT mu.* FROM media_upload mu{}", query_sort), "".to_string()), - MediaUploadQueryListOf::OnTradeProduct(id) => (format!("SELECT mu.* FROM media_upload mu JOIN trade_product_media tp_lg ON mu.id = tp_lg.tb_mu WHERE tp_lg.tb_tp = ?1{}", query_sort), id), - MediaUploadQueryListOf::OffTradeProduct(id) => (format!("SELECT mu.* FROM media_upload mu WHERE NOT EXISTS (SELECT 1 FROM trade_product_media tp_lg WHERE tp_lg.tb_mu = mu.id AND tp_lg.tb_tp = ?1){}", query_sort), id), - } -} - -fn media_upload_fields_bind_values( - opts: IMediaUploadFields, -) -> Result<Vec<IModelsQueryBindValueTuple>, ModelError> { - let bind_values = serde_json::to_value(&opts) - .map_err(|err| ModelError::SerializationError(err.to_string()))? - .as_object() - .ok_or_else(|| ModelError::InvalidArgument("model.error.object_invalid".to_string()))? - .iter() - .filter_map(|(key, value)| value.as_str().map(|v| (key.clone(), v.to_string()))) - .collect::<Vec<_>>(); - Ok(bind_values) -} - -fn media_upload_fields_update_bind_values( - opts: IMediaUploadFieldsUpdate, -) -> Result<Vec<IModelsQueryBindValueTuple>, ModelError> { - let bind_values = serde_json::to_value(&opts) - .map_err(|err| ModelError::SerializationError(err.to_string()))? - .as_object() - .ok_or_else(|| ModelError::InvalidArgument("model.error.object_invalid".to_string()))? - .iter() - .filter_map(|(key, value)| value.as_str().map(|v| (key.clone(), v.to_string()))) - .collect::<Vec<_>>(); - Ok(bind_values) -} - -pub async fn lib_model_media_upload_add( - db: &sqlx::Pool<sqlx::Sqlite>, - opts: IMediaUploadAdd, -) -> Result<IMediaUploadAddResolve, ModelError> { - let id = uuidv4(); - let created_at = time_created_on(); - let updated_at = created_at.clone(); - let bind_values = media_upload_fields_bind_values(opts) - .map_err(|e| ModelError::InvalidArgument(e.to_string()))?; - let mut query_col = vec![ - "id".to_string(), - "created_at".to_string(), - "updated_at".to_string(), - ]; - let mut query_pl = vec!["?1".to_string(), "?2".to_string(), "?3".to_string()]; - let mut query_vals: Vec<String> = vec![id.to_string(), created_at.clone(), updated_at.clone()]; - for (k, v) in bind_values.iter() { - query_col.push(k.clone()); - query_pl.push(format!("?{}", query_col.len())); - query_vals.push(v.clone()); - } - let query = format!( - "INSERT INTO media_upload ({}) VALUES ({});", - query_col.join(", "), - query_pl.join(", ") - ); - let mut query_builder = sqlx::query(&query); - for value in query_vals.iter() { - query_builder = query_builder.bind(value); - } - query_builder - .execute(db) - .await - .map_err(|e| ModelError::InvalidQuery(e.to_string()))?; - Ok(IModelsId { id }) -} - -fn media_upload_query_get( - opts: IMediaUploadGet, -) -> Result<(String, Vec<IModelsQueryBindValue>), ModelError> { - match opts { - IMediaUploadQueryGet { - list: Some(opts_list), - .. - } => { - let (query, bv) = media_upload_query_get_list(opts_list); - Ok((query, vec![bv])) - } - IMediaUploadQueryGet { - on: Some(opts_on), .. - } => { - let (bv_k, bv) = media_upload_query_bind_values(opts_on); - let query = format!("SELECT * FROM media_upload WHERE {} = ?1;", bv_k); - Ok((query, vec![bv])) - } - _ => Err(ModelError::InvalidQuery( - "model.media_upload.error.query_invalid".to_string(), - )), - } -} - -pub async fn lib_model_media_upload_get( - db: &sqlx::Pool<sqlx::Sqlite>, - opts: IMediaUploadQueryGet, -) -> Result<IMediaUploadGetResolve, ModelError> { - let (query, bind_values) = media_upload_query_get(opts)?; - let mut query_builder = sqlx::query_as::<_, MediaUpload>(&query); - for value in bind_values.iter() { - query_builder = query_builder.bind(value); - } - let results = query_builder - .fetch(db) - .try_collect() - .await - .map_err(|e: sqlx::Error| ModelError::InvalidQuery(e.to_string()))?; - Ok(IModelsResults { results }) -} - -pub async fn lib_model_media_upload_delete( - db: &sqlx::Pool<sqlx::Sqlite>, - opts: IMediaUploadDelete, -) -> Result<IMediaUploadDeleteResolve, ModelError> { - let (bv_k, bv) = media_upload_query_bind_values(opts); - let query = format!("DELETE FROM media_upload WHERE {} = ?1;", bv_k); - let result = sqlx::query(&query) - .bind(bv) - .execute(db) - .await - .map_err(|e: sqlx::Error| ModelError::InvalidQuery(e.to_string()))?; - println!("{:?}", result); - if result.rows_affected() > 0 { - Ok(()) - } else { - Err(ModelError::InvalidQuery( - "models.error.model_not_found".to_string(), - )) - } -} - -pub async fn lib_model_media_upload_update( - db: &sqlx::Pool<sqlx::Sqlite>, - opts: IMediaUploadUpdate, -) -> Result<IMediaUploadUpdateResolve, ModelError> { - let (bv_k, bv) = media_upload_query_bind_values(opts.on); - let bind_values = media_upload_fields_update_bind_values(opts.fields) - .map_err(|e| ModelError::InvalidArgument(e.to_string()))?; - let updated_at = time_created_on(); - let mut query_col = vec!["updated_at".to_string()]; - let mut query_pl = vec!["?2".to_string()]; - let mut query_vals = vec![bv, updated_at]; - for (k, v) in bind_values.iter() { - query_col.push(k.clone()); - query_pl.push(format!("?{}", query_col.len() + 1)); - query_vals.push(v.clone()); - } - let query = format!( - "UPDATE media_upload SET {} WHERE {} = ?1;", - query_col - .iter() - .enumerate() - .map(|(i, col)| format!("{} = {}", col, query_pl[i])) - .collect::<Vec<_>>() - .join(", "), - bv_k - ); - let mut query_builder = sqlx::query(&query); - for value in query_vals.iter() { - query_builder = query_builder.bind(value); - } - let result = query_builder - .execute(db) - .await - .map_err(|e| ModelError::InvalidQuery(e.to_string()))?; - println!("{:?}", result); - if result.rows_affected() > 0 { - Ok(()) - } else { - Err(ModelError::InvalidQuery( - "models.error.model_not_found".to_string(), - )) - } -} diff --git a/crates/core/src/models/mod.rs b/crates/core/src/models/mod.rs @@ -1,9 +0,0 @@ -pub mod location_gcs; -pub mod log_error; -pub mod media_upload; -pub mod nostr_profile; -pub mod nostr_profile_relay; -pub mod nostr_relay; -pub mod trade_product; -pub mod trade_product_location; -pub mod trade_product_media; diff --git a/crates/core/src/models/nostr_profile.rs b/crates/core/src/models/nostr_profile.rs @@ -1,289 +0,0 @@ -use crate::{ - error::ModelError, - types::{IModelsId, IModelsQueryBindValue, IModelsQueryBindValueTuple, IModelsResults}, - utils::{time_created_on, uuidv4}, -}; -use futures::TryStreamExt; - -#[derive(Debug, serde::Serialize, serde::Deserialize, sqlx::FromRow)] -pub struct NostrProfile { - id: String, - created_at: String, - updated_at: String, - public_key: String, - name: Option<String>, - display_name: Option<String>, - about: Option<String>, - website: Option<String>, - picture: Option<String>, - banner: Option<String>, - nip05: Option<String>, - lud06: Option<String>, - lud16: Option<String>, -} - -#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] -pub struct INostrProfileFields { - pub public_key: String, - pub name: Option<String>, - pub display_name: Option<String>, - pub about: Option<String>, - pub website: Option<String>, - pub picture: Option<String>, - pub banner: Option<String>, - pub nip05: Option<String>, - pub lud06: Option<String>, - pub lud16: Option<String>, -} - -#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] -pub struct INostrProfileFieldsUpdate { - pub public_key: Option<String>, - pub name: Option<String>, - pub display_name: Option<String>, - pub about: Option<String>, - pub website: Option<String>, - pub picture: Option<String>, - pub banner: Option<String>, - pub nip05: Option<String>, - pub lud06: Option<String>, - pub lud16: Option<String>, -} - -#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] -#[serde(rename_all = "snake_case")] -pub enum NostrProfileSort { - Newest, - Oldest, -} - -#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] -#[serde(rename_all = "snake_case")] -pub enum NostrProfileQueryBindValues { - Id(IModelsQueryBindValue), - PublicKey(IModelsQueryBindValue), -} - -#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] -#[serde(rename_all = "snake_case")] -pub enum NostrProfileQueryListOf { - All(IModelsQueryBindValue), - OnRelay(IModelsQueryBindValue), - OffRelay(IModelsQueryBindValue), -} - -#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] -pub struct INostrProfileQueryGetList { - pub of: NostrProfileQueryListOf, - pub sort: Option<NostrProfileSort>, -} - -#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] -pub struct INostrProfileQueryGet { - pub on: Option<NostrProfileQueryBindValues>, - pub list: Option<INostrProfileQueryGetList>, -} - -#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] -pub struct INostrProfileQueryUpdate { - pub on: NostrProfileQueryBindValues, - pub fields: INostrProfileFieldsUpdate, -} - -pub type INostrProfileAdd = INostrProfileFields; -pub type INostrProfileAddResolve = IModelsId; -pub type INostrProfileGet = INostrProfileQueryGet; -pub type INostrProfileGetResolve = IModelsResults<NostrProfile>; -pub type INostrProfileDelete = NostrProfileQueryBindValues; -pub type INostrProfileDeleteResolve = (); -pub type INostrProfileUpdate = INostrProfileQueryUpdate; -pub type INostrProfileUpdateResolve = (); - -pub fn nostr_profile_query_bind_values(opts: NostrProfileQueryBindValues) -> IModelsQueryBindValueTuple { - match opts { - NostrProfileQueryBindValues::Id(id) => ("id".to_string(), id), - NostrProfileQueryBindValues::PublicKey(public_key) => ("public_key".to_string(), public_key), - } -} - -pub fn nostr_profile_query_get_list(opts: INostrProfileQueryGetList) -> IModelsQueryBindValueTuple { - let query_sort = match opts.sort { - Some(NostrProfileSort::Newest) => " ORDER BY pr.created_at DESC", - Some(NostrProfileSort::Oldest) => " ORDER BY pr.created_at ASC", - None => "", - }; - match opts.of { - NostrProfileQueryListOf::All(_) => (format!("SELECT pr.* FROM nostr_profile pr{}", query_sort), "".to_string()), - NostrProfileQueryListOf::OnRelay(id) => (format!("SELECT pr.* FROM nostr_profile pr JOIN nostr_profile_relay pr_rl ON pr.id = pr_rl.tb_pr WHERE pr_rl.tb_rl = ?1{}", query_sort), id), - NostrProfileQueryListOf::OffRelay(id) => (format!("SELECT pr.* FROM nostr_profile pr WHERE NOT EXISTS (SELECT 1 FROM nostr_profile_relay pr_rl WHERE pr_rl.tb_pr = pr.id AND pr_rl.tb_rl = ?1){}", query_sort), id), - } -} - -fn nostr_profile_fields_bind_values( - opts: INostrProfileFields, -) -> Result<Vec<IModelsQueryBindValueTuple>, ModelError> { - let bind_values = serde_json::to_value(&opts) - .map_err(|err| ModelError::SerializationError(err.to_string()))? - .as_object() - .ok_or_else(|| ModelError::InvalidArgument("model.error.object_invalid".to_string()))? - .iter() - .filter_map(|(key, value)| value.as_str().map(|v| (key.clone(), v.to_string()))) - .collect::<Vec<_>>(); - Ok(bind_values) -} - -fn nostr_profile_fields_update_bind_values( - opts: INostrProfileFieldsUpdate, -) -> Result<Vec<IModelsQueryBindValueTuple>, ModelError> { - let bind_values = serde_json::to_value(&opts) - .map_err(|err| ModelError::SerializationError(err.to_string()))? - .as_object() - .ok_or_else(|| ModelError::InvalidArgument("model.error.object_invalid".to_string()))? - .iter() - .filter_map(|(key, value)| value.as_str().map(|v| (key.clone(), v.to_string()))) - .collect::<Vec<_>>(); - Ok(bind_values) -} - -pub async fn lib_model_nostr_profile_add( - db: &sqlx::Pool<sqlx::Sqlite>, - opts: INostrProfileAdd, -) -> Result<INostrProfileAddResolve, ModelError> { - let id = uuidv4(); - let created_at = time_created_on(); - let updated_at = created_at.clone(); - let bind_values = nostr_profile_fields_bind_values(opts) - .map_err(|e| ModelError::InvalidArgument(e.to_string()))?; - let mut query_col = vec![ - "id".to_string(), - "created_at".to_string(), - "updated_at".to_string(), - ]; - let mut query_pl = vec!["?1".to_string(), "?2".to_string(), "?3".to_string()]; - let mut query_vals: Vec<String> = vec![id.to_string(), created_at.clone(), updated_at.clone()]; - for (k, v) in bind_values.iter() { - query_col.push(k.clone()); - query_pl.push(format!("?{}", query_col.len())); - query_vals.push(v.clone()); - } - let query = format!( - "INSERT INTO nostr_profile ({}) VALUES ({});", - query_col.join(", "), - query_pl.join(", ") - ); - let mut query_builder = sqlx::query(&query); - for value in query_vals.iter() { - query_builder = query_builder.bind(value); - } - query_builder - .execute(db) - .await - .map_err(|e| ModelError::InvalidQuery(e.to_string()))?; - Ok(IModelsId { id }) -} - -fn nostr_profile_query_get( - opts: INostrProfileGet, -) -> Result<(String, Vec<IModelsQueryBindValue>), ModelError> { - match opts { - INostrProfileQueryGet { - list: Some(opts_list), - .. - } => { - let (query, bv) = nostr_profile_query_get_list(opts_list); - Ok((query, vec![bv])) - } - INostrProfileQueryGet { - on: Some(opts_on), .. - } => { - let (bv_k, bv) = nostr_profile_query_bind_values(opts_on); - let query = format!("SELECT * FROM nostr_profile WHERE {} = ?1;", bv_k); - Ok((query, vec![bv])) - } - _ => Err(ModelError::InvalidQuery( - "model.nostr_profile.error.query_invalid".to_string(), - )), - } -} - -pub async fn lib_model_nostr_profile_get( - db: &sqlx::Pool<sqlx::Sqlite>, - opts: INostrProfileQueryGet, -) -> Result<INostrProfileGetResolve, ModelError> { - let (query, bind_values) = nostr_profile_query_get(opts)?; - let mut query_builder = sqlx::query_as::<_, NostrProfile>(&query); - for value in bind_values.iter() { - query_builder = query_builder.bind(value); - } - let results = query_builder - .fetch(db) - .try_collect() - .await - .map_err(|e: sqlx::Error| ModelError::InvalidQuery(e.to_string()))?; - Ok(IModelsResults { results }) -} - -pub async fn lib_model_nostr_profile_delete( - db: &sqlx::Pool<sqlx::Sqlite>, - opts: INostrProfileDelete, -) -> Result<INostrProfileDeleteResolve, ModelError> { - let (bv_k, bv) = nostr_profile_query_bind_values(opts); - let query = format!("DELETE FROM nostr_profile WHERE {} = ?1;", bv_k); - let result = sqlx::query(&query) - .bind(bv) - .execute(db) - .await - .map_err(|e: sqlx::Error| ModelError::InvalidQuery(e.to_string()))?; - println!("{:?}", result); - if result.rows_affected() > 0 { - Ok(()) - } else { - Err(ModelError::InvalidQuery( - "models.error.model_not_found".to_string(), - )) - } -} - -pub async fn lib_model_nostr_profile_update( - db: &sqlx::Pool<sqlx::Sqlite>, - opts: INostrProfileUpdate, -) -> Result<INostrProfileUpdateResolve, ModelError> { - let (bv_k, bv) = nostr_profile_query_bind_values(opts.on); - let bind_values = nostr_profile_fields_update_bind_values(opts.fields) - .map_err(|e| ModelError::InvalidArgument(e.to_string()))?; - let updated_at = time_created_on(); - let mut query_col = vec!["updated_at".to_string()]; - let mut query_pl = vec!["?2".to_string()]; - let mut query_vals = vec![bv, updated_at]; - for (k, v) in bind_values.iter() { - query_col.push(k.clone()); - query_pl.push(format!("?{}", query_col.len() + 1)); - query_vals.push(v.clone()); - } - let query = format!( - "UPDATE nostr_profile SET {} WHERE {} = ?1;", - query_col - .iter() - .enumerate() - .map(|(i, col)| format!("{} = {}", col, query_pl[i])) - .collect::<Vec<_>>() - .join(", "), - bv_k - ); - let mut query_builder = sqlx::query(&query); - for value in query_vals.iter() { - query_builder = query_builder.bind(value); - } - let result = query_builder - .execute(db) - .await - .map_err(|e| ModelError::InvalidQuery(e.to_string()))?; - println!("{:?}", result); - if result.rows_affected() > 0 { - Ok(()) - } else { - Err(ModelError::InvalidQuery( - "models.error.model_not_found".to_string(), - )) - } -} diff --git a/crates/core/src/models/nostr_profile_relay.rs b/crates/core/src/models/nostr_profile_relay.rs @@ -1,73 +0,0 @@ -use crate::{ - error::ModelError, - types::IModelsResults, - models::nostr_profile::{nostr_profile_query_bind_values, NostrProfileQueryBindValues}, - models::nostr_relay::{nostr_relay_query_bind_values, NostrRelayQueryBindValues}, -}; -use futures::TryStreamExt; - -#[derive(Debug, serde::Serialize, serde::Deserialize, sqlx::FromRow)] -pub struct NostrProfileRelay { - tb_pr: String, - tb_rl: String, -} - -pub type INostrProfileRelayRelationResolve = bool; -pub type INostrProfileRelayRelationResolveGetAll = IModelsResults<NostrProfileRelay>; - -#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] -pub struct INostrProfileRelayRelation { - pub nostr_profile: NostrProfileQueryBindValues, - pub nostr_relay: NostrRelayQueryBindValues, -} - -pub async fn lib_model_nostr_profile_relay_set( - db: &sqlx::Pool<sqlx::Sqlite>, - opts: INostrProfileRelayRelation, -) -> Result<INostrProfileRelayRelationResolve, ModelError> { - let (bv_pr_k, bv_pr) = nostr_profile_query_bind_values(opts.nostr_profile); - let (bv_rl_k, bv_rl) = nostr_relay_query_bind_values(opts.nostr_relay); - let query_vals = vec![bv_pr, bv_rl]; - let query = format!("INSERT INTO nostr_profile_relay (tb_pr, tb_rl) VALUES ((SELECT id FROM nostr_profile WHERE {} = ?1), (SELECT id FROM nostr_relay WHERE {} = ?2));", bv_pr_k, bv_rl_k); - let mut query_builder = sqlx::query(&query); - for value in query_vals.iter() { - query_builder = query_builder.bind(value); - } - query_builder - .execute(db) - .await - .map_err(|e| ModelError::InvalidQuery(e.to_string()))?; - Ok(true) -} - -pub async fn lib_model_nostr_profile_relay_unset( - db: &sqlx::Pool<sqlx::Sqlite>, - opts: INostrProfileRelayRelation, -) -> Result<INostrProfileRelayRelationResolve, ModelError> { - let (bv_pr_k, bv_pr) = nostr_profile_query_bind_values(opts.nostr_profile); - let (bv_rl_k, bv_rl) = nostr_relay_query_bind_values(opts.nostr_relay); - let query_vals = vec![bv_pr, bv_rl]; - let query = format!("DELETE FROM nostr_profile_relay WHERE tb_pr = (SELECT id FROM nostr_profile WHERE {} = ?1) AND tb_rl = (SELECT id FROM nostr_relay WHERE {} = ?2);", bv_pr_k, bv_rl_k); - let mut query_builder = sqlx::query(&query); - for value in query_vals.iter() { - query_builder = query_builder.bind(value); - } - query_builder - .execute(db) - .await - .map_err(|e| ModelError::InvalidQuery(e.to_string()))?; - Ok(true) -} - -pub async fn lib_model_nostr_profile_relay_get_all( - db: &sqlx::Pool<sqlx::Sqlite>, -) -> Result<INostrProfileRelayRelationResolveGetAll, ModelError> { - let query = format!("SELECT * FROM nostr_profile_relay;"); - let query_builder = sqlx::query_as::<_, NostrProfileRelay>(&query); - let results = query_builder - .fetch(db) - .try_collect() - .await - .map_err(|e: sqlx::Error| ModelError::InvalidQuery(e.to_string()))?; - Ok(IModelsResults { results }) -} diff --git a/crates/core/src/models/nostr_relay.rs b/crates/core/src/models/nostr_relay.rs @@ -1,289 +0,0 @@ -use crate::{ - error::ModelError, - types::{IModelsId, IModelsQueryBindValue, IModelsQueryBindValueTuple, IModelsResults}, - utils::{time_created_on, uuidv4}, -}; -use futures::TryStreamExt; - -#[derive(Debug, serde::Serialize, serde::Deserialize, sqlx::FromRow)] -pub struct NostrRelay { - id: String, - created_at: String, - updated_at: String, - url: String, - relay_id: Option<String>, - name: Option<String>, - description: Option<String>, - pubkey: Option<String>, - contact: Option<String>, - supported_nips: Option<String>, - software: Option<String>, - version: Option<String>, - data: Option<String>, -} - -#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] -pub struct INostrRelayFields { - pub url: String, - pub relay_id: Option<String>, - pub name: Option<String>, - pub description: Option<String>, - pub pubkey: Option<String>, - pub contact: Option<String>, - pub supported_nips: Option<String>, - pub software: Option<String>, - pub version: Option<String>, - pub data: Option<String>, -} - -#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] -pub struct INostrRelayFieldsUpdate { - pub url: Option<String>, - pub relay_id: Option<String>, - pub name: Option<String>, - pub description: Option<String>, - pub pubkey: Option<String>, - pub contact: Option<String>, - pub supported_nips: Option<String>, - pub software: Option<String>, - pub version: Option<String>, - pub data: Option<String>, -} - -#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] -#[serde(rename_all = "snake_case")] -pub enum NostrRelaySort { - Newest, - Oldest, -} - -#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] -#[serde(rename_all = "snake_case")] -pub enum NostrRelayQueryBindValues { - Id(IModelsQueryBindValue), - Url(IModelsQueryBindValue), -} - -#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] -#[serde(rename_all = "snake_case")] -pub enum NostrRelayQueryListOf { - All(IModelsQueryBindValue), - OnProfile(IModelsQueryBindValue), - OffProfile(IModelsQueryBindValue), -} - -#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] -pub struct INostrRelayQueryGetList { - pub of: NostrRelayQueryListOf, - pub sort: Option<NostrRelaySort>, -} - -#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] -pub struct INostrRelayQueryGet { - pub on: Option<NostrRelayQueryBindValues>, - pub list: Option<INostrRelayQueryGetList>, -} - -#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] -pub struct INostrRelayQueryUpdate { - pub on: NostrRelayQueryBindValues, - pub fields: INostrRelayFieldsUpdate, -} - -pub type INostrRelayAdd = INostrRelayFields; -pub type INostrRelayAddResolve = IModelsId; -pub type INostrRelayGet = INostrRelayQueryGet; -pub type INostrRelayGetResolve = IModelsResults<NostrRelay>; -pub type INostrRelayDelete = NostrRelayQueryBindValues; -pub type INostrRelayDeleteResolve = (); -pub type INostrRelayUpdate = INostrRelayQueryUpdate; -pub type INostrRelayUpdateResolve = (); - -pub fn nostr_relay_query_bind_values(opts: NostrRelayQueryBindValues) -> IModelsQueryBindValueTuple { - match opts { - NostrRelayQueryBindValues::Id(id) => ("id".to_string(), id), - NostrRelayQueryBindValues::Url(url) => ("url".to_string(), url), - } -} - -pub fn nostr_relay_query_get_list(opts: INostrRelayQueryGetList) -> IModelsQueryBindValueTuple { - let query_sort = match opts.sort { - Some(NostrRelaySort::Newest) => " ORDER BY rl.created_at DESC", - Some(NostrRelaySort::Oldest) => " ORDER BY rl.created_at ASC", - None => "", - }; - match opts.of { - NostrRelayQueryListOf::All(_) => (format!("SELECT rl.* FROM nostr_relay rl{}", query_sort), "".to_string()), - NostrRelayQueryListOf::OnProfile(public_key) => (format!("SELECT rl.* FROM nostr_relay rl JOIN nostr_profile_relay pr_rl ON rl.id = pr_rl.tb_rl JOIN nostr_profile pr ON pr.id = pr_rl.tb_pr WHERE pr.public_key = ?1{}", query_sort), public_key), - NostrRelayQueryListOf::OffProfile(public_key) => (format!("SELECT rl.* FROM nostr_relay rl LEFT JOIN nostr_profile_relay pr_rl ON rl.id = pr_rl.tb_rl LEFT JOIN nostr_profile pr ON pr.id = pr_rl.tb_pr WHERE pr.public_key <> ?1{}", query_sort), public_key), - } -} - -fn nostr_relay_fields_bind_values( - opts: INostrRelayFields, -) -> Result<Vec<IModelsQueryBindValueTuple>, ModelError> { - let bind_values = serde_json::to_value(&opts) - .map_err(|err| ModelError::SerializationError(err.to_string()))? - .as_object() - .ok_or_else(|| ModelError::InvalidArgument("model.error.object_invalid".to_string()))? - .iter() - .filter_map(|(key, value)| value.as_str().map(|v| (key.clone(), v.to_string()))) - .collect::<Vec<_>>(); - Ok(bind_values) -} - -fn nostr_relay_fields_update_bind_values( - opts: INostrRelayFieldsUpdate, -) -> Result<Vec<IModelsQueryBindValueTuple>, ModelError> { - let bind_values = serde_json::to_value(&opts) - .map_err(|err| ModelError::SerializationError(err.to_string()))? - .as_object() - .ok_or_else(|| ModelError::InvalidArgument("model.error.object_invalid".to_string()))? - .iter() - .filter_map(|(key, value)| value.as_str().map(|v| (key.clone(), v.to_string()))) - .collect::<Vec<_>>(); - Ok(bind_values) -} - -pub async fn lib_model_nostr_relay_add( - db: &sqlx::Pool<sqlx::Sqlite>, - opts: INostrRelayAdd, -) -> Result<INostrRelayAddResolve, ModelError> { - let id = uuidv4(); - let created_at = time_created_on(); - let updated_at = created_at.clone(); - let bind_values = nostr_relay_fields_bind_values(opts) - .map_err(|e| ModelError::InvalidArgument(e.to_string()))?; - let mut query_col = vec![ - "id".to_string(), - "created_at".to_string(), - "updated_at".to_string(), - ]; - let mut query_pl = vec!["?1".to_string(), "?2".to_string(), "?3".to_string()]; - let mut query_vals: Vec<String> = vec![id.to_string(), created_at.clone(), updated_at.clone()]; - for (k, v) in bind_values.iter() { - query_col.push(k.clone()); - query_pl.push(format!("?{}", query_col.len())); - query_vals.push(v.clone()); - } - let query = format!( - "INSERT INTO nostr_relay ({}) VALUES ({});", - query_col.join(", "), - query_pl.join(", ") - ); - let mut query_builder = sqlx::query(&query); - for value in query_vals.iter() { - query_builder = query_builder.bind(value); - } - query_builder - .execute(db) - .await - .map_err(|e| ModelError::InvalidQuery(e.to_string()))?; - Ok(IModelsId { id }) -} - -fn nostr_relay_query_get( - opts: INostrRelayGet, -) -> Result<(String, Vec<IModelsQueryBindValue>), ModelError> { - match opts { - INostrRelayQueryGet { - list: Some(opts_list), - .. - } => { - let (query, bv) = nostr_relay_query_get_list(opts_list); - Ok((query, vec![bv])) - } - INostrRelayQueryGet { - on: Some(opts_on), .. - } => { - let (bv_k, bv) = nostr_relay_query_bind_values(opts_on); - let query = format!("SELECT * FROM nostr_relay WHERE {} = ?1;", bv_k); - Ok((query, vec![bv])) - } - _ => Err(ModelError::InvalidQuery( - "model.nostr_relay.error.query_invalid".to_string(), - )), - } -} - -pub async fn lib_model_nostr_relay_get( - db: &sqlx::Pool<sqlx::Sqlite>, - opts: INostrRelayQueryGet, -) -> Result<INostrRelayGetResolve, ModelError> { - let (query, bind_values) = nostr_relay_query_get(opts)?; - let mut query_builder = sqlx::query_as::<_, NostrRelay>(&query); - for value in bind_values.iter() { - query_builder = query_builder.bind(value); - } - let results = query_builder - .fetch(db) - .try_collect() - .await - .map_err(|e: sqlx::Error| ModelError::InvalidQuery(e.to_string()))?; - Ok(IModelsResults { results }) -} - -pub async fn lib_model_nostr_relay_delete( - db: &sqlx::Pool<sqlx::Sqlite>, - opts: INostrRelayDelete, -) -> Result<INostrRelayDeleteResolve, ModelError> { - let (bv_k, bv) = nostr_relay_query_bind_values(opts); - let query = format!("DELETE FROM nostr_relay WHERE {} = ?1;", bv_k); - let result = sqlx::query(&query) - .bind(bv) - .execute(db) - .await - .map_err(|e: sqlx::Error| ModelError::InvalidQuery(e.to_string()))?; - println!("{:?}", result); - if result.rows_affected() > 0 { - Ok(()) - } else { - Err(ModelError::InvalidQuery( - "models.error.model_not_found".to_string(), - )) - } -} - -pub async fn lib_model_nostr_relay_update( - db: &sqlx::Pool<sqlx::Sqlite>, - opts: INostrRelayUpdate, -) -> Result<INostrRelayUpdateResolve, ModelError> { - let (bv_k, bv) = nostr_relay_query_bind_values(opts.on); - let bind_values = nostr_relay_fields_update_bind_values(opts.fields) - .map_err(|e| ModelError::InvalidArgument(e.to_string()))?; - let updated_at = time_created_on(); - let mut query_col = vec!["updated_at".to_string()]; - let mut query_pl = vec!["?2".to_string()]; - let mut query_vals = vec![bv, updated_at]; - for (k, v) in bind_values.iter() { - query_col.push(k.clone()); - query_pl.push(format!("?{}", query_col.len() + 1)); - query_vals.push(v.clone()); - } - let query = format!( - "UPDATE nostr_relay SET {} WHERE {} = ?1;", - query_col - .iter() - .enumerate() - .map(|(i, col)| format!("{} = {}", col, query_pl[i])) - .collect::<Vec<_>>() - .join(", "), - bv_k - ); - let mut query_builder = sqlx::query(&query); - for value in query_vals.iter() { - query_builder = query_builder.bind(value); - } - let result = query_builder - .execute(db) - .await - .map_err(|e| ModelError::InvalidQuery(e.to_string()))?; - println!("{:?}", result); - if result.rows_affected() > 0 { - Ok(()) - } else { - Err(ModelError::InvalidQuery( - "models.error.model_not_found".to_string(), - )) - } -} diff --git a/crates/core/src/models/trade_product.rs b/crates/core/src/models/trade_product.rs @@ -1,304 +0,0 @@ -use crate::{ - error::ModelError, - types::{IModelsId, IModelsQueryBindValue, IModelsQueryBindValueTuple, IModelsResults}, - utils::{time_created_on, uuidv4}, -}; -use futures::TryStreamExt; - -#[derive(Debug, serde::Serialize, serde::Deserialize, sqlx::FromRow)] -pub struct TradeProduct { - id: String, - created_at: String, - updated_at: String, - key: String, - category: String, - title: String, - summary: String, - process: String, - lot: String, - profile: String, - year: i32, - qty_amt: i32, - qty_unit: String, - qty_label: Option<String>, - qty_avail: Option<i32>, - price_amt: f64, - price_currency: String, - price_qty_amt: i32, - price_qty_unit: String, - notes: Option<String>, -} - -#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] -pub struct ITradeProductFields { - pub key: String, - pub category: String, - pub title: String, - pub summary: String, - pub process: String, - pub lot: String, - pub profile: String, - pub year: String, - pub qty_amt: String, - pub qty_unit: String, - pub qty_label: Option<String>, - pub qty_avail: Option<String>, - pub price_amt: String, - pub price_currency: String, - pub price_qty_amt: String, - pub price_qty_unit: String, - pub notes: Option<String>, -} - -#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] -pub struct ITradeProductFieldsUpdate { - pub key: Option<String>, - pub category: Option<String>, - pub title: Option<String>, - pub summary: Option<String>, - pub process: Option<String>, - pub lot: Option<String>, - pub profile: Option<String>, - pub year: Option<String>, - pub qty_amt: Option<String>, - pub qty_unit: Option<String>, - pub qty_label: Option<String>, - pub qty_avail: Option<String>, - pub price_amt: Option<String>, - pub price_currency: Option<String>, - pub price_qty_amt: Option<String>, - pub price_qty_unit: Option<String>, - pub notes: Option<String>, -} - -#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] -#[serde(rename_all = "snake_case")] -pub enum TradeProductSort { - Newest, - Oldest, -} - -#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] -#[serde(rename_all = "snake_case")] -pub enum TradeProductQueryBindValues { - Id(IModelsQueryBindValue), -} - -#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] -#[serde(rename_all = "snake_case")] -pub enum TradeProductQueryListOf { - All(IModelsQueryBindValue), -} - -#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] -pub struct ITradeProductQueryGetList { - pub of: TradeProductQueryListOf, - pub sort: Option<TradeProductSort>, -} - -#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] -pub struct ITradeProductQueryGet { - pub on: Option<TradeProductQueryBindValues>, - pub list: Option<ITradeProductQueryGetList>, -} - -#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] -pub struct ITradeProductQueryUpdate { - pub on: TradeProductQueryBindValues, - pub fields: ITradeProductFieldsUpdate, -} - -pub type ITradeProductAdd = ITradeProductFields; -pub type ITradeProductAddResolve = IModelsId; -pub type ITradeProductGet = ITradeProductQueryGet; -pub type ITradeProductGetResolve = IModelsResults<TradeProduct>; -pub type ITradeProductDelete = TradeProductQueryBindValues; -pub type ITradeProductDeleteResolve = (); -pub type ITradeProductUpdate = ITradeProductQueryUpdate; -pub type ITradeProductUpdateResolve = (); - -pub fn trade_product_query_bind_values(opts: TradeProductQueryBindValues) -> IModelsQueryBindValueTuple { - match opts { - TradeProductQueryBindValues::Id(id) => ("id".to_string(), id), - } -} - -pub fn trade_product_query_get_list(opts: ITradeProductQueryGetList) -> IModelsQueryBindValueTuple { - let query_sort = match opts.sort { - Some(TradeProductSort::Newest) => " ORDER BY tp.created_at DESC", - Some(TradeProductSort::Oldest) => " ORDER BY tp.created_at ASC", - None => "", - }; - match opts.of { - TradeProductQueryListOf::All(_) => (format!("SELECT tp.* FROM trade_product tp{}", query_sort), "".to_string()), - } -} - -fn trade_product_fields_bind_values( - opts: ITradeProductFields, -) -> Result<Vec<IModelsQueryBindValueTuple>, ModelError> { - let bind_values = serde_json::to_value(&opts) - .map_err(|err| ModelError::SerializationError(err.to_string()))? - .as_object() - .ok_or_else(|| ModelError::InvalidArgument("model.error.object_invalid".to_string()))? - .iter() - .filter_map(|(key, value)| value.as_str().map(|v| (key.clone(), v.to_string()))) - .collect::<Vec<_>>(); - Ok(bind_values) -} - -fn trade_product_fields_update_bind_values( - opts: ITradeProductFieldsUpdate, -) -> Result<Vec<IModelsQueryBindValueTuple>, ModelError> { - let bind_values = serde_json::to_value(&opts) - .map_err(|err| ModelError::SerializationError(err.to_string()))? - .as_object() - .ok_or_else(|| ModelError::InvalidArgument("model.error.object_invalid".to_string()))? - .iter() - .filter_map(|(key, value)| value.as_str().map(|v| (key.clone(), v.to_string()))) - .collect::<Vec<_>>(); - Ok(bind_values) -} - -pub async fn lib_model_trade_product_add( - db: &sqlx::Pool<sqlx::Sqlite>, - opts: ITradeProductAdd, -) -> Result<ITradeProductAddResolve, ModelError> { - let id = uuidv4(); - let created_at = time_created_on(); - let updated_at = created_at.clone(); - let bind_values = trade_product_fields_bind_values(opts) - .map_err(|e| ModelError::InvalidArgument(e.to_string()))?; - let mut query_col = vec![ - "id".to_string(), - "created_at".to_string(), - "updated_at".to_string(), - ]; - let mut query_pl = vec!["?1".to_string(), "?2".to_string(), "?3".to_string()]; - let mut query_vals: Vec<String> = vec![id.to_string(), created_at.clone(), updated_at.clone()]; - for (k, v) in bind_values.iter() { - query_col.push(k.clone()); - query_pl.push(format!("?{}", query_col.len())); - query_vals.push(v.clone()); - } - let query = format!( - "INSERT INTO trade_product ({}) VALUES ({});", - query_col.join(", "), - query_pl.join(", ") - ); - let mut query_builder = sqlx::query(&query); - for value in query_vals.iter() { - query_builder = query_builder.bind(value); - } - query_builder - .execute(db) - .await - .map_err(|e| ModelError::InvalidQuery(e.to_string()))?; - Ok(IModelsId { id }) -} - -fn trade_product_query_get( - opts: ITradeProductGet, -) -> Result<(String, Vec<IModelsQueryBindValue>), ModelError> { - match opts { - ITradeProductQueryGet { - list: Some(opts_list), - .. - } => { - let (query, bv) = trade_product_query_get_list(opts_list); - Ok((query, vec![bv])) - } - ITradeProductQueryGet { - on: Some(opts_on), .. - } => { - let (bv_k, bv) = trade_product_query_bind_values(opts_on); - let query = format!("SELECT * FROM trade_product WHERE {} = ?1;", bv_k); - Ok((query, vec![bv])) - } - _ => Err(ModelError::InvalidQuery( - "model.trade_product.error.query_invalid".to_string(), - )), - } -} - -pub async fn lib_model_trade_product_get( - db: &sqlx::Pool<sqlx::Sqlite>, - opts: ITradeProductQueryGet, -) -> Result<ITradeProductGetResolve, ModelError> { - let (query, bind_values) = trade_product_query_get(opts)?; - let mut query_builder = sqlx::query_as::<_, TradeProduct>(&query); - for value in bind_values.iter() { - query_builder = query_builder.bind(value); - } - let results = query_builder - .fetch(db) - .try_collect() - .await - .map_err(|e: sqlx::Error| ModelError::InvalidQuery(e.to_string()))?; - Ok(IModelsResults { results }) -} - -pub async fn lib_model_trade_product_delete( - db: &sqlx::Pool<sqlx::Sqlite>, - opts: ITradeProductDelete, -) -> Result<ITradeProductDeleteResolve, ModelError> { - let (bv_k, bv) = trade_product_query_bind_values(opts); - let query = format!("DELETE FROM trade_product WHERE {} = ?1;", bv_k); - let result = sqlx::query(&query) - .bind(bv) - .execute(db) - .await - .map_err(|e: sqlx::Error| ModelError::InvalidQuery(e.to_string()))?; - println!("{:?}", result); - if result.rows_affected() > 0 { - Ok(()) - } else { - Err(ModelError::InvalidQuery( - "models.error.model_not_found".to_string(), - )) - } -} - -pub async fn lib_model_trade_product_update( - db: &sqlx::Pool<sqlx::Sqlite>, - opts: ITradeProductUpdate, -) -> Result<ITradeProductUpdateResolve, ModelError> { - let (bv_k, bv) = trade_product_query_bind_values(opts.on); - let bind_values = trade_product_fields_update_bind_values(opts.fields) - .map_err(|e| ModelError::InvalidArgument(e.to_string()))?; - let updated_at = time_created_on(); - let mut query_col = vec!["updated_at".to_string()]; - let mut query_pl = vec!["?2".to_string()]; - let mut query_vals = vec![bv, updated_at]; - for (k, v) in bind_values.iter() { - query_col.push(k.clone()); - query_pl.push(format!("?{}", query_col.len() + 1)); - query_vals.push(v.clone()); - } - let query = format!( - "UPDATE trade_product SET {} WHERE {} = ?1;", - query_col - .iter() - .enumerate() - .map(|(i, col)| format!("{} = {}", col, query_pl[i])) - .collect::<Vec<_>>() - .join(", "), - bv_k - ); - let mut query_builder = sqlx::query(&query); - for value in query_vals.iter() { - query_builder = query_builder.bind(value); - } - let result = query_builder - .execute(db) - .await - .map_err(|e| ModelError::InvalidQuery(e.to_string()))?; - println!("{:?}", result); - if result.rows_affected() > 0 { - Ok(()) - } else { - Err(ModelError::InvalidQuery( - "models.error.model_not_found".to_string(), - )) - } -} diff --git a/crates/core/src/models/trade_product_location.rs b/crates/core/src/models/trade_product_location.rs @@ -1,73 +0,0 @@ -use crate::{ - error::ModelError, - types::IModelsResults, - models::trade_product::{trade_product_query_bind_values, TradeProductQueryBindValues}, - models::location_gcs::{location_gcs_query_bind_values, LocationGcsQueryBindValues}, -}; -use futures::TryStreamExt; - -#[derive(Debug, serde::Serialize, serde::Deserialize, sqlx::FromRow)] -pub struct TradeProductLocation { - tb_tp: String, - tb_lg: String, -} - -pub type ITradeProductLocationRelationResolve = bool; -pub type ITradeProductLocationRelationResolveGetAll = IModelsResults<TradeProductLocation>; - -#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] -pub struct ITradeProductLocationRelation { - pub trade_product: TradeProductQueryBindValues, - pub location_gcs: LocationGcsQueryBindValues, -} - -pub async fn lib_model_trade_product_location_set( - db: &sqlx::Pool<sqlx::Sqlite>, - opts: ITradeProductLocationRelation, -) -> Result<ITradeProductLocationRelationResolve, ModelError> { - let (bv_tp_k, bv_tp) = trade_product_query_bind_values(opts.trade_product); - let (bv_lg_k, bv_lg) = location_gcs_query_bind_values(opts.location_gcs); - let query_vals = vec![bv_tp, bv_lg]; - let query = format!("INSERT INTO trade_product_location (tb_tp, tb_lg) VALUES ((SELECT id FROM trade_product WHERE {} = ?1), (SELECT id FROM location_gcs WHERE {} = ?2));", bv_tp_k, bv_lg_k); - let mut query_builder = sqlx::query(&query); - for value in query_vals.iter() { - query_builder = query_builder.bind(value); - } - query_builder - .execute(db) - .await - .map_err(|e| ModelError::InvalidQuery(e.to_string()))?; - Ok(true) -} - -pub async fn lib_model_trade_product_location_unset( - db: &sqlx::Pool<sqlx::Sqlite>, - opts: ITradeProductLocationRelation, -) -> Result<ITradeProductLocationRelationResolve, ModelError> { - let (bv_tp_k, bv_tp) = trade_product_query_bind_values(opts.trade_product); - let (bv_lg_k, bv_lg) = location_gcs_query_bind_values(opts.location_gcs); - let query_vals = vec![bv_tp, bv_lg]; - let query = format!("DELETE FROM trade_product_location WHERE tb_tp = (SELECT id FROM trade_product WHERE {} = ?1) AND tb_lg = (SELECT id FROM location_gcs WHERE {} = ?2);", bv_tp_k, bv_lg_k); - let mut query_builder = sqlx::query(&query); - for value in query_vals.iter() { - query_builder = query_builder.bind(value); - } - query_builder - .execute(db) - .await - .map_err(|e| ModelError::InvalidQuery(e.to_string()))?; - Ok(true) -} - -pub async fn lib_model_trade_product_location_get_all( - db: &sqlx::Pool<sqlx::Sqlite>, -) -> Result<ITradeProductLocationRelationResolveGetAll, ModelError> { - let query = format!("SELECT * FROM trade_product_location;"); - let query_builder = sqlx::query_as::<_, TradeProductLocation>(&query); - let results = query_builder - .fetch(db) - .try_collect() - .await - .map_err(|e: sqlx::Error| ModelError::InvalidQuery(e.to_string()))?; - Ok(IModelsResults { results }) -} diff --git a/crates/core/src/models/trade_product_media.rs b/crates/core/src/models/trade_product_media.rs @@ -1,73 +0,0 @@ -use crate::{ - error::ModelError, - types::IModelsResults, - models::trade_product::{trade_product_query_bind_values, TradeProductQueryBindValues}, - models::media_upload::{media_upload_query_bind_values, MediaUploadQueryBindValues}, -}; -use futures::TryStreamExt; - -#[derive(Debug, serde::Serialize, serde::Deserialize, sqlx::FromRow)] -pub struct TradeProductMedia { - tb_tp: String, - tb_mu: String, -} - -pub type ITradeProductMediaRelationResolve = bool; -pub type ITradeProductMediaRelationResolveGetAll = IModelsResults<TradeProductMedia>; - -#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] -pub struct ITradeProductMediaRelation { - pub trade_product: TradeProductQueryBindValues, - pub media_upload: MediaUploadQueryBindValues, -} - -pub async fn lib_model_trade_product_media_set( - db: &sqlx::Pool<sqlx::Sqlite>, - opts: ITradeProductMediaRelation, -) -> Result<ITradeProductMediaRelationResolve, ModelError> { - let (bv_tp_k, bv_tp) = trade_product_query_bind_values(opts.trade_product); - let (bv_mu_k, bv_mu) = media_upload_query_bind_values(opts.media_upload); - let query_vals = vec![bv_tp, bv_mu]; - let query = format!("INSERT INTO trade_product_media (tb_tp, tb_mu) VALUES ((SELECT id FROM trade_product WHERE {} = ?1), (SELECT id FROM media_upload WHERE {} = ?2));", bv_tp_k, bv_mu_k); - let mut query_builder = sqlx::query(&query); - for value in query_vals.iter() { - query_builder = query_builder.bind(value); - } - query_builder - .execute(db) - .await - .map_err(|e| ModelError::InvalidQuery(e.to_string()))?; - Ok(true) -} - -pub async fn lib_model_trade_product_media_unset( - db: &sqlx::Pool<sqlx::Sqlite>, - opts: ITradeProductMediaRelation, -) -> Result<ITradeProductMediaRelationResolve, ModelError> { - let (bv_tp_k, bv_tp) = trade_product_query_bind_values(opts.trade_product); - let (bv_mu_k, bv_mu) = media_upload_query_bind_values(opts.media_upload); - let query_vals = vec![bv_tp, bv_mu]; - let query = format!("DELETE FROM trade_product_media WHERE tb_tp = (SELECT id FROM trade_product WHERE {} = ?1) AND tb_mu = (SELECT id FROM media_upload WHERE {} = ?2);", bv_tp_k, bv_mu_k); - let mut query_builder = sqlx::query(&query); - for value in query_vals.iter() { - query_builder = query_builder.bind(value); - } - query_builder - .execute(db) - .await - .map_err(|e| ModelError::InvalidQuery(e.to_string()))?; - Ok(true) -} - -pub async fn lib_model_trade_product_media_get_all( - db: &sqlx::Pool<sqlx::Sqlite>, -) -> Result<ITradeProductMediaRelationResolveGetAll, ModelError> { - let query = format!("SELECT * FROM trade_product_media;"); - let query_builder = sqlx::query_as::<_, TradeProductMedia>(&query); - let results = query_builder - .fetch(db) - .try_collect() - .await - .map_err(|e: sqlx::Error| ModelError::InvalidQuery(e.to_string()))?; - Ok(IModelsResults { results }) -} diff --git a/crates/core/src/nostr/keys.rs b/crates/core/src/nostr/keys.rs @@ -0,0 +1,27 @@ +use nostr_sdk::prelude::*; + +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum NostrKeyError { + #[error("Key error: {0}")] + KeyError(#[from] nostr_sdk::key::Error), +} + +pub type NostrKeyResult<T> = std::result::Result<T, NostrKeyError>; + +pub fn lib_nostr_keys_gen() -> nostr_sdk::Keys { + Keys::generate() +} + +pub fn lib_nostr_keys_parse(secret_key: String) -> NostrKeyResult<nostr_sdk::Keys> { + Keys::parse(&secret_key).map_err(NostrKeyError::KeyError) +} + +pub fn lib_nostr_secret_key_hex(keys: nostr_sdk::Keys) -> String { + keys.secret_key().to_secret_hex() +} + +pub fn lib_nostr_public_key_hex(keys: nostr_sdk::Keys) -> String { + keys.public_key().to_hex() +} diff --git a/crates/core/src/nostr/mod.rs b/crates/core/src/nostr/mod.rs @@ -0,0 +1 @@ +pub mod keys; diff --git a/crates/core/src/types.rs b/crates/core/src/types.rs @@ -1,12 +1,21 @@ -pub type IModelsQueryBindValue = String; -pub type IModelsQueryBindValueTuple = (String, IModelsQueryBindValue); +pub type IModelsQueryBindValueTuple = (String, String); #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -pub struct IModelsId { - pub id: String, +pub struct IResult<T> { + pub result: T, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -pub struct IModelsResults<T> { +pub struct IResultList<T> { pub results: Vec<T>, } + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +pub struct IResultPass { + pub pass: bool, +} + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +pub struct IError { + pub err: String, +} diff --git a/crates/core/src/util.rs b/crates/core/src/util.rs @@ -0,0 +1,37 @@ +use chrono::Utc; +use uuid::Uuid; + +pub fn time_created_on() -> String { + let now: chrono::DateTime<Utc> = Utc::now(); + now.to_rfc3339_opts(chrono::SecondsFormat::Millis, true) +} + +pub fn uuidv4() -> String { + Uuid::new_v4().to_string() +} + +pub fn json_values(obj: &serde_json::Value) -> Vec<serde_json::Value> { + if let serde_json::Value::Object(map) = obj { + map.values().cloned().collect() + } else { + Vec::new() + } +} + +pub fn json_values_str(obj: &serde_json::Value) -> Vec<String> { + if let serde_json::Value::Object(map) = obj { + map.values() + .filter_map(|v| v.as_str().map(String::from)) + .collect() + } else { + Vec::new() + } +} + +pub fn json_keys(obj: &serde_json::Value) -> Vec<String> { + if let serde_json::Value::Object(map) = obj { + map.keys().cloned().collect() + } else { + Vec::new() + } +} diff --git a/crates/core/src/utils.rs b/crates/core/src/utils.rs @@ -1,11 +0,0 @@ -use chrono::Utc; -use uuid::Uuid; - -pub fn time_created_on() -> String { - let now: chrono::DateTime<Utc> = Utc::now(); - now.to_rfc3339_opts(chrono::SecondsFormat::Millis, true) -} - -pub fn uuidv4() -> String { - Uuid::new_v4().to_string() -} diff --git a/crates/model/Cargo.toml b/crates/model/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "radroots_model" +version = "0.0.1" +license = "GPLv3" +edition = "2021" + +[dependencies] +serde_json = "1.0" +serde = { version = "1.0", features = ["derive"] } +sqlx = { version = "0.8.2", features = ["sqlite", "runtime-tokio"] } +thiserror = "1.0.64" +futures = "0.3.31" + +radroots_core = { path = "../core" } diff --git a/crates/model/LICENSE b/crates/model/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + <program> Copyright (C) <year> <name of author> + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +<https://www.gnu.org/licenses/>. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +<https://www.gnu.org/licenses/why-not-lgpl.html>. +\ No newline at end of file diff --git a/crates/model/src/error.rs b/crates/model/src/error.rs @@ -0,0 +1,15 @@ +use thiserror::Error; + +#[derive(Error, Debug, Clone)] +pub enum ModelError { + #[error("Invalid argument: {0}")] + InvalidArgument(String), + #[error("{0} not found")] + NotFound(String), + #[error("Serialization error: {0}")] + SerializationError(String), + #[error("Invalid query: {0}")] + InvalidQuery(String), + #[error("Internal error")] + Internal, +} diff --git a/crates/model/src/lib.rs b/crates/model/src/lib.rs @@ -0,0 +1,4 @@ +pub mod error; +pub mod tables; +pub mod types; +pub mod util; diff --git a/crates/model/src/tables/location_gcs.rs b/crates/model/src/tables/location_gcs.rs @@ -0,0 +1,254 @@ +use futures::TryStreamExt; +use radroots_core::{ + types::{IModelsQueryBindValueTuple, IResult, IResultList, IResultPass}, + util::{time_created_on, uuidv4}, +}; + +use crate::{error::ModelError, types::DatabaseConnection, util::parse_query_value}; + +#[derive(Debug, serde::Serialize, serde::Deserialize, sqlx::FromRow)] +pub struct LocationGcs { + id: String, + created_at: String, + updated_at: String, + lat: f64, + lng: f64, + geohash: String, + kind: String, + label: Option<String>, + area: Option<f64>, + elevation: Option<u32>, + soil: Option<String>, + climate: Option<String>, + gc_id: Option<String>, + gc_name: Option<String>, + gc_admin1_id: Option<String>, + gc_admin1_name: Option<String>, + gc_country_id: Option<String>, + gc_country_name: Option<String>, +} + +#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] +pub struct ILocationGcsFields { + pub lat: f64, + pub lng: f64, + pub geohash: String, + pub kind: String, + pub label: Option<String>, + pub area: Option<f64>, + pub elevation: Option<u32>, + pub soil: Option<String>, + pub climate: Option<String>, + pub gc_id: Option<String>, + pub gc_name: Option<String>, + pub gc_admin1_id: Option<String>, + pub gc_admin1_name: Option<String>, + pub gc_country_id: Option<String>, + pub gc_country_name: Option<String>, +} + +#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] +pub struct ILocationGcsFieldsPartial { + pub lat: Option<f64>, + pub lng: Option<f64>, + pub geohash: Option<String>, + pub kind: Option<String>, + pub label: Option<String>, + pub area: Option<f64>, + pub elevation: Option<u32>, + pub soil: Option<String>, + pub climate: Option<String>, + pub gc_id: Option<String>, + pub gc_name: Option<String>, + pub gc_admin1_id: Option<String>, + pub gc_admin1_name: Option<String>, + pub gc_country_id: Option<String>, + pub gc_country_name: Option<String>, +} + +#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] +pub struct ILocationGcsFieldsFilter { + pub created_at: Option<String>, + pub updated_at: Option<String>, + pub lat: Option<String>, + pub lng: Option<String>, + pub geohash: Option<String>, + pub kind: Option<String>, + pub label: Option<String>, + pub area: Option<String>, + pub elevation: Option<String>, + pub soil: Option<String>, + pub climate: Option<String>, + pub gc_id: Option<String>, + pub gc_name: Option<String>, + pub gc_admin1_id: Option<String>, + pub gc_admin1_name: Option<String>, + pub gc_country_id: Option<String>, + pub gc_country_name: Option<String>, +} + +#[derive(Clone, serde::Serialize, serde::Deserialize)] +#[serde(untagged)] +pub enum LocationGcsQueryBindValues { + Id { id: String }, + Geohash { geohash: String }, +} + +pub fn location_gcs_query_bind_values(opts: LocationGcsQueryBindValues) -> IModelsQueryBindValueTuple { + match opts { + LocationGcsQueryBindValues::Id { id } => ("id".to_string(), id), + LocationGcsQueryBindValues::Geohash { geohash } => ("geohash".to_string(), geohash), + } +} + +fn lib_table_location_gcs_parse_fields( + opts: ILocationGcsFields, +) -> Result<serde_json::Map<String, serde_json::Value>, ModelError> { + let fields = serde_json::to_value(opts) + .map_err(|err| ModelError::SerializationError(err.to_string()))? + .as_object() + .ok_or_else(|| ModelError::SerializationError("Expected an object".to_string()))? + .clone(); + Ok(fields) +} + +pub type ILocationGcsCreate = ILocationGcsFields; +pub type ILocationGcsCreateResolve = IResult<String>; + +pub async fn lib_model_location_gcs_create( + db: &DatabaseConnection, + opts: ILocationGcsCreate, +) -> Result<ILocationGcsCreateResolve, ModelError> { + let id: String = uuidv4(); + let created_at: String = time_created_on(); + let updated_at: String = created_at.clone(); + let fields = lib_table_location_gcs_parse_fields(opts)?; + + let query = format!( + "INSERT INTO location_gcs (id, created_at, updated_at, {}) VALUES (?, ?, ?, {});", + fields + .keys() + .map(|k| k.to_string()) + .collect::<Vec<String>>() + .join(","), + (0..fields.len()) + .map(|_| "?") + .collect::<Vec<&str>>() + .join(",") + ); + + let mut query_builder = sqlx::query(&query); + query_builder = query_builder.bind(&id); + query_builder = query_builder.bind(&created_at); + query_builder = query_builder.bind(&updated_at); + for (_, value) in fields.iter() { + match value { + serde_json::Value::Bool(b) => { + let bool_str = if *b { "1" } else { "0" }; + query_builder = query_builder.bind(bool_str); + } + serde_json::Value::Number(n) => { + if let Some(f) = n.as_f64() { + query_builder = query_builder.bind(f); + } else if let Some(i64) = n.as_i64() { + query_builder = query_builder.bind(i64); + } else if let Some(u64) = n.as_u64() { + if u64 <= u32::MAX as u64 { + query_builder = query_builder.bind(u64 as u32); + } + } + } + serde_json::Value::String(s) => { + query_builder = query_builder.bind(s); + } + _ => { + query_builder = query_builder.bind::<Option<serde_json::Value>>(None); + } + } + } + query_builder + .execute(db) + .await + .map_err(|e| ModelError::InvalidQuery(e.to_string()))?; + Ok(IResult { result: id }) +} + +#[derive(serde::Serialize, serde::Deserialize)] +pub struct ILocationGcsQueryRead { + pub query: String, + pub bind_values: Vec<serde_json::Value>, +} + +pub type ILocationGcsRead = ILocationGcsQueryRead; +pub type ILocationGcsReadResolve = IResultList<LocationGcs>; + +pub async fn lib_model_location_gcs_read( + db: &DatabaseConnection, + opts: ILocationGcsRead, +) -> Result<ILocationGcsReadResolve, ModelError> { + let mut query_builder = sqlx::query_as::<_, LocationGcs>(&opts.query); + for value in opts.bind_values.iter() { + query_builder = query_builder.bind(parse_query_value(value)?); + } + let results = query_builder + .fetch(db) + .try_collect() + .await + .map_err(|e: sqlx::Error| ModelError::InvalidQuery(e.to_string()))?; + Ok(IResultList { results }) +} + +#[derive(serde::Serialize, serde::Deserialize)] +pub struct ILocationGcsQueryReadList { + pub query: String, + pub bind_values: Vec<serde_json::Value>, +} + +pub type ILocationGcsReadList = ILocationGcsQueryReadList; +pub type ILocationGcsReadListResolve = IResultList<LocationGcs>; + +pub async fn lib_model_location_gcs_read_list( + db: &DatabaseConnection, + opts: ILocationGcsReadList, +) -> Result<ILocationGcsReadListResolve, ModelError> { + let mut query_builder = sqlx::query_as::<_, LocationGcs>(&opts.query); + for value in opts.bind_values.iter() { + query_builder = query_builder.bind(parse_query_value(value)?); + } + let results = query_builder + .fetch(db) + .try_collect() + .await + .map_err(|e: sqlx::Error| ModelError::InvalidQuery(e.to_string()))?; + Ok(IResultList { results }) +} + +pub type ILocationGcsUpdate = LocationGcsQueryBindValues; +pub type ILocationGcsUpdateResolve = (); + +pub async fn lib_model_location_gcs_update( + _db: &sqlx::Pool<sqlx::Sqlite>, + _opts: ILocationGcsUpdate, +) -> Result<ILocationGcsUpdateResolve, ModelError> { + Ok(()) +} +pub type ILocationGcsDelete = LocationGcsQueryBindValues; +pub type ILocationGcsDeleteResolve = IResultPass; + +pub async fn lib_model_location_gcs_delete( + db: &DatabaseConnection, + opts: ILocationGcsDelete, +) -> Result<ILocationGcsDeleteResolve, ModelError> { + let (bv_k, bv) = location_gcs_query_bind_values(opts); + let query = format!("DELETE FROM location_gcs WHERE {} = ?1;", bv_k); + let result = sqlx::query(&query) + .bind(bv) + .execute(db) + .await + .map_err(|e: sqlx::Error| ModelError::InvalidQuery(e.to_string()))?; + if result.rows_affected() > 0 { + Ok(IResultPass { pass: true }) + } else { + Err(ModelError::NotFound("model.location_gcs.name".to_string())) + } +} diff --git a/crates/model/src/tables/log_error.rs b/crates/model/src/tables/log_error.rs @@ -0,0 +1,226 @@ +use futures::TryStreamExt; +use radroots_core::{ + types::{IModelsQueryBindValueTuple, IResult, IResultList, IResultPass}, + util::{time_created_on, uuidv4}, +}; + +use crate::{error::ModelError, types::DatabaseConnection, util::parse_query_value}; + +#[derive(Debug, serde::Serialize, serde::Deserialize, sqlx::FromRow)] +pub struct LogError { + id: String, + created_at: String, + updated_at: String, + error: String, + message: String, + stack_trace: Option<String>, + cause: Option<String>, + app_system: String, + app_version: String, + nostr_pubkey: String, + data: Option<String>, +} + +#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] +pub struct ILogErrorFields { + pub error: String, + pub message: String, + pub stack_trace: Option<String>, + pub cause: Option<String>, + pub app_system: String, + pub app_version: String, + pub nostr_pubkey: String, + pub data: Option<String>, +} + +#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] +pub struct ILogErrorFieldsPartial { + pub error: Option<String>, + pub message: Option<String>, + pub stack_trace: Option<String>, + pub cause: Option<String>, + pub app_system: Option<String>, + pub app_version: Option<String>, + pub nostr_pubkey: Option<String>, + pub data: Option<String>, +} + +#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] +pub struct ILogErrorFieldsFilter { + pub created_at: Option<String>, + pub updated_at: Option<String>, + pub error: Option<String>, + pub message: Option<String>, + pub stack_trace: Option<String>, + pub cause: Option<String>, + pub app_system: Option<String>, + pub app_version: Option<String>, + pub nostr_pubkey: Option<String>, + pub data: Option<String>, +} + +#[derive(Clone, serde::Serialize, serde::Deserialize)] +#[serde(untagged)] +pub enum LogErrorQueryBindValues { + Id { id: String }, + NostrPubkey { nostr_pubkey: String }, +} + +pub fn log_error_query_bind_values(opts: LogErrorQueryBindValues) -> IModelsQueryBindValueTuple { + match opts { + LogErrorQueryBindValues::Id { id } => ("id".to_string(), id), + LogErrorQueryBindValues::NostrPubkey { nostr_pubkey } => ("nostr_pubkey".to_string(), nostr_pubkey), + } +} + +fn lib_table_log_error_parse_fields( + opts: ILogErrorFields, +) -> Result<serde_json::Map<String, serde_json::Value>, ModelError> { + let fields = serde_json::to_value(opts) + .map_err(|err| ModelError::SerializationError(err.to_string()))? + .as_object() + .ok_or_else(|| ModelError::SerializationError("Expected an object".to_string()))? + .clone(); + Ok(fields) +} + +pub type ILogErrorCreate = ILogErrorFields; +pub type ILogErrorCreateResolve = IResult<String>; + +pub async fn lib_model_log_error_create( + db: &DatabaseConnection, + opts: ILogErrorCreate, +) -> Result<ILogErrorCreateResolve, ModelError> { + let id: String = uuidv4(); + let created_at: String = time_created_on(); + let updated_at: String = created_at.clone(); + let fields = lib_table_log_error_parse_fields(opts)?; + + let query = format!( + "INSERT INTO log_error (id, created_at, updated_at, {}) VALUES (?, ?, ?, {});", + fields + .keys() + .map(|k| k.to_string()) + .collect::<Vec<String>>() + .join(","), + (0..fields.len()) + .map(|_| "?") + .collect::<Vec<&str>>() + .join(",") + ); + + let mut query_builder = sqlx::query(&query); + query_builder = query_builder.bind(&id); + query_builder = query_builder.bind(&created_at); + query_builder = query_builder.bind(&updated_at); + for (_, value) in fields.iter() { + match value { + serde_json::Value::Bool(b) => { + let bool_str = if *b { "1" } else { "0" }; + query_builder = query_builder.bind(bool_str); + } + serde_json::Value::Number(n) => { + if let Some(f) = n.as_f64() { + query_builder = query_builder.bind(f); + } else if let Some(i64) = n.as_i64() { + query_builder = query_builder.bind(i64); + } else if let Some(u64) = n.as_u64() { + if u64 <= u32::MAX as u64 { + query_builder = query_builder.bind(u64 as u32); + } + } + } + serde_json::Value::String(s) => { + query_builder = query_builder.bind(s); + } + _ => { + query_builder = query_builder.bind::<Option<serde_json::Value>>(None); + } + } + } + query_builder + .execute(db) + .await + .map_err(|e| ModelError::InvalidQuery(e.to_string()))?; + Ok(IResult { result: id }) +} + +#[derive(serde::Serialize, serde::Deserialize)] +pub struct ILogErrorQueryRead { + pub query: String, + pub bind_values: Vec<serde_json::Value>, +} + +pub type ILogErrorRead = ILogErrorQueryRead; +pub type ILogErrorReadResolve = IResultList<LogError>; + +pub async fn lib_model_log_error_read( + db: &DatabaseConnection, + opts: ILogErrorRead, +) -> Result<ILogErrorReadResolve, ModelError> { + let mut query_builder = sqlx::query_as::<_, LogError>(&opts.query); + for value in opts.bind_values.iter() { + query_builder = query_builder.bind(parse_query_value(value)?); + } + let results = query_builder + .fetch(db) + .try_collect() + .await + .map_err(|e: sqlx::Error| ModelError::InvalidQuery(e.to_string()))?; + Ok(IResultList { results }) +} + +#[derive(serde::Serialize, serde::Deserialize)] +pub struct ILogErrorQueryReadList { + pub query: String, + pub bind_values: Vec<serde_json::Value>, +} + +pub type ILogErrorReadList = ILogErrorQueryReadList; +pub type ILogErrorReadListResolve = IResultList<LogError>; + +pub async fn lib_model_log_error_read_list( + db: &DatabaseConnection, + opts: ILogErrorReadList, +) -> Result<ILogErrorReadListResolve, ModelError> { + let mut query_builder = sqlx::query_as::<_, LogError>(&opts.query); + for value in opts.bind_values.iter() { + query_builder = query_builder.bind(parse_query_value(value)?); + } + let results = query_builder + .fetch(db) + .try_collect() + .await + .map_err(|e: sqlx::Error| ModelError::InvalidQuery(e.to_string()))?; + Ok(IResultList { results }) +} + +pub type ILogErrorUpdate = LogErrorQueryBindValues; +pub type ILogErrorUpdateResolve = (); + +pub async fn lib_model_log_error_update( + _db: &sqlx::Pool<sqlx::Sqlite>, + _opts: ILogErrorUpdate, +) -> Result<ILogErrorUpdateResolve, ModelError> { + Ok(()) +} +pub type ILogErrorDelete = LogErrorQueryBindValues; +pub type ILogErrorDeleteResolve = IResultPass; + +pub async fn lib_model_log_error_delete( + db: &DatabaseConnection, + opts: ILogErrorDelete, +) -> Result<ILogErrorDeleteResolve, ModelError> { + let (bv_k, bv) = log_error_query_bind_values(opts); + let query = format!("DELETE FROM log_error WHERE {} = ?1;", bv_k); + let result = sqlx::query(&query) + .bind(bv) + .execute(db) + .await + .map_err(|e: sqlx::Error| ModelError::InvalidQuery(e.to_string()))?; + if result.rows_affected() > 0 { + Ok(IResultPass { pass: true }) + } else { + Err(ModelError::NotFound("model.log_error.name".to_string())) + } +} diff --git a/crates/model/src/tables/media_image.rs b/crates/model/src/tables/media_image.rs @@ -0,0 +1,218 @@ +use futures::TryStreamExt; +use radroots_core::{ + types::{IModelsQueryBindValueTuple, IResult, IResultList, IResultPass}, + util::{time_created_on, uuidv4}, +}; + +use crate::{error::ModelError, types::DatabaseConnection, util::parse_query_value}; + +#[derive(Debug, serde::Serialize, serde::Deserialize, sqlx::FromRow)] +pub struct MediaImage { + id: String, + created_at: String, + updated_at: String, + file_path: String, + mime_type: String, + res_base: String, + res_path: String, + label: Option<String>, + description: Option<String>, +} + +#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] +pub struct IMediaImageFields { + pub file_path: String, + pub mime_type: String, + pub res_base: String, + pub res_path: String, + pub label: Option<String>, + pub description: Option<String>, +} + +#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] +pub struct IMediaImageFieldsPartial { + pub file_path: Option<String>, + pub mime_type: Option<String>, + pub res_base: Option<String>, + pub res_path: Option<String>, + pub label: Option<String>, + pub description: Option<String>, +} + +#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] +pub struct IMediaImageFieldsFilter { + pub created_at: Option<String>, + pub updated_at: Option<String>, + pub file_path: Option<String>, + pub mime_type: Option<String>, + pub res_base: Option<String>, + pub res_path: Option<String>, + pub label: Option<String>, + pub description: Option<String>, +} + +#[derive(Clone, serde::Serialize, serde::Deserialize)] +#[serde(untagged)] +pub enum MediaImageQueryBindValues { + Id { id: String }, + FilePath { file_path: String }, +} + +pub fn media_image_query_bind_values(opts: MediaImageQueryBindValues) -> IModelsQueryBindValueTuple { + match opts { + MediaImageQueryBindValues::Id { id } => ("id".to_string(), id), + MediaImageQueryBindValues::FilePath { file_path } => ("file_path".to_string(), file_path), + } +} + +fn lib_table_media_image_parse_fields( + opts: IMediaImageFields, +) -> Result<serde_json::Map<String, serde_json::Value>, ModelError> { + let fields = serde_json::to_value(opts) + .map_err(|err| ModelError::SerializationError(err.to_string()))? + .as_object() + .ok_or_else(|| ModelError::SerializationError("Expected an object".to_string()))? + .clone(); + Ok(fields) +} + +pub type IMediaImageCreate = IMediaImageFields; +pub type IMediaImageCreateResolve = IResult<String>; + +pub async fn lib_model_media_image_create( + db: &DatabaseConnection, + opts: IMediaImageCreate, +) -> Result<IMediaImageCreateResolve, ModelError> { + let id: String = uuidv4(); + let created_at: String = time_created_on(); + let updated_at: String = created_at.clone(); + let fields = lib_table_media_image_parse_fields(opts)?; + + let query = format!( + "INSERT INTO media_image (id, created_at, updated_at, {}) VALUES (?, ?, ?, {});", + fields + .keys() + .map(|k| k.to_string()) + .collect::<Vec<String>>() + .join(","), + (0..fields.len()) + .map(|_| "?") + .collect::<Vec<&str>>() + .join(",") + ); + + let mut query_builder = sqlx::query(&query); + query_builder = query_builder.bind(&id); + query_builder = query_builder.bind(&created_at); + query_builder = query_builder.bind(&updated_at); + for (_, value) in fields.iter() { + match value { + serde_json::Value::Bool(b) => { + let bool_str = if *b { "1" } else { "0" }; + query_builder = query_builder.bind(bool_str); + } + serde_json::Value::Number(n) => { + if let Some(f) = n.as_f64() { + query_builder = query_builder.bind(f); + } else if let Some(i64) = n.as_i64() { + query_builder = query_builder.bind(i64); + } else if let Some(u64) = n.as_u64() { + if u64 <= u32::MAX as u64 { + query_builder = query_builder.bind(u64 as u32); + } + } + } + serde_json::Value::String(s) => { + query_builder = query_builder.bind(s); + } + _ => { + query_builder = query_builder.bind::<Option<serde_json::Value>>(None); + } + } + } + query_builder + .execute(db) + .await + .map_err(|e| ModelError::InvalidQuery(e.to_string()))?; + Ok(IResult { result: id }) +} + +#[derive(serde::Serialize, serde::Deserialize)] +pub struct IMediaImageQueryRead { + pub query: String, + pub bind_values: Vec<serde_json::Value>, +} + +pub type IMediaImageRead = IMediaImageQueryRead; +pub type IMediaImageReadResolve = IResultList<MediaImage>; + +pub async fn lib_model_media_image_read( + db: &DatabaseConnection, + opts: IMediaImageRead, +) -> Result<IMediaImageReadResolve, ModelError> { + let mut query_builder = sqlx::query_as::<_, MediaImage>(&opts.query); + for value in opts.bind_values.iter() { + query_builder = query_builder.bind(parse_query_value(value)?); + } + let results = query_builder + .fetch(db) + .try_collect() + .await + .map_err(|e: sqlx::Error| ModelError::InvalidQuery(e.to_string()))?; + Ok(IResultList { results }) +} + +#[derive(serde::Serialize, serde::Deserialize)] +pub struct IMediaImageQueryReadList { + pub query: String, + pub bind_values: Vec<serde_json::Value>, +} + +pub type IMediaImageReadList = IMediaImageQueryReadList; +pub type IMediaImageReadListResolve = IResultList<MediaImage>; + +pub async fn lib_model_media_image_read_list( + db: &DatabaseConnection, + opts: IMediaImageReadList, +) -> Result<IMediaImageReadListResolve, ModelError> { + let mut query_builder = sqlx::query_as::<_, MediaImage>(&opts.query); + for value in opts.bind_values.iter() { + query_builder = query_builder.bind(parse_query_value(value)?); + } + let results = query_builder + .fetch(db) + .try_collect() + .await + .map_err(|e: sqlx::Error| ModelError::InvalidQuery(e.to_string()))?; + Ok(IResultList { results }) +} + +pub type IMediaImageUpdate = MediaImageQueryBindValues; +pub type IMediaImageUpdateResolve = (); + +pub async fn lib_model_media_image_update( + _db: &sqlx::Pool<sqlx::Sqlite>, + _opts: IMediaImageUpdate, +) -> Result<IMediaImageUpdateResolve, ModelError> { + Ok(()) +} +pub type IMediaImageDelete = MediaImageQueryBindValues; +pub type IMediaImageDeleteResolve = IResultPass; + +pub async fn lib_model_media_image_delete( + db: &DatabaseConnection, + opts: IMediaImageDelete, +) -> Result<IMediaImageDeleteResolve, ModelError> { + let (bv_k, bv) = media_image_query_bind_values(opts); + let query = format!("DELETE FROM media_image WHERE {} = ?1;", bv_k); + let result = sqlx::query(&query) + .bind(bv) + .execute(db) + .await + .map_err(|e: sqlx::Error| ModelError::InvalidQuery(e.to_string()))?; + if result.rows_affected() > 0 { + Ok(IResultPass { pass: true }) + } else { + Err(ModelError::NotFound("model.media_image.name".to_string())) + } +} diff --git a/crates/model/src/tables/mod.rs b/crates/model/src/tables/mod.rs @@ -0,0 +1,21 @@ +use crate::{error::ModelError, types::DatabaseConnection}; +use radroots_core::types::IResultPass; + +pub mod location_gcs; +pub mod log_error; +pub mod media_image; +pub mod nostr_profile; +pub mod nostr_profile_relay; +pub mod nostr_relay; +pub mod trade_product; +pub mod trade_product_location; +pub mod trade_product_media; + +pub async fn lib_model_tables_reset(db: &DatabaseConnection) -> Result<IResultPass, ModelError> { + let query = format!("DELETE FROM location_gcs; DELETE FROM trade_product; DELETE FROM nostr_profile; DELETE FROM nostr_relay; DELETE FROM media_image; DELETE FROM log_error;DELETE FROM nostr_profile_relay; DELETE FROM trade_product_location; DELETE FROM trade_product_media;"); + sqlx::query(&query) + .execute(db) + .await + .map_err(|e: sqlx::Error| ModelError::InvalidQuery(e.to_string()))?; + Ok(IResultPass { pass: true }) +} diff --git a/crates/model/src/tables/nostr_profile.rs b/crates/model/src/tables/nostr_profile.rs @@ -0,0 +1,234 @@ +use futures::TryStreamExt; +use radroots_core::{ + types::{IModelsQueryBindValueTuple, IResult, IResultList, IResultPass}, + util::{time_created_on, uuidv4}, +}; + +use crate::{error::ModelError, types::DatabaseConnection, util::parse_query_value}; + +#[derive(Debug, serde::Serialize, serde::Deserialize, sqlx::FromRow)] +pub struct NostrProfile { + id: String, + created_at: String, + updated_at: String, + public_key: String, + name: Option<String>, + display_name: Option<String>, + about: Option<String>, + website: Option<String>, + picture: Option<String>, + banner: Option<String>, + nip05: Option<String>, + lud06: Option<String>, + lud16: Option<String>, +} + +#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] +pub struct INostrProfileFields { + pub public_key: String, + pub name: Option<String>, + pub display_name: Option<String>, + pub about: Option<String>, + pub website: Option<String>, + pub picture: Option<String>, + pub banner: Option<String>, + pub nip05: Option<String>, + pub lud06: Option<String>, + pub lud16: Option<String>, +} + +#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] +pub struct INostrProfileFieldsPartial { + pub public_key: Option<String>, + pub name: Option<String>, + pub display_name: Option<String>, + pub about: Option<String>, + pub website: Option<String>, + pub picture: Option<String>, + pub banner: Option<String>, + pub nip05: Option<String>, + pub lud06: Option<String>, + pub lud16: Option<String>, +} + +#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] +pub struct INostrProfileFieldsFilter { + pub created_at: Option<String>, + pub updated_at: Option<String>, + pub public_key: Option<String>, + pub name: Option<String>, + pub display_name: Option<String>, + pub about: Option<String>, + pub website: Option<String>, + pub picture: Option<String>, + pub banner: Option<String>, + pub nip05: Option<String>, + pub lud06: Option<String>, + pub lud16: Option<String>, +} + +#[derive(Clone, serde::Serialize, serde::Deserialize)] +#[serde(untagged)] +pub enum NostrProfileQueryBindValues { + Id { id: String }, + PublicKey { public_key: String }, +} + +pub fn nostr_profile_query_bind_values(opts: NostrProfileQueryBindValues) -> IModelsQueryBindValueTuple { + match opts { + NostrProfileQueryBindValues::Id { id } => ("id".to_string(), id), + NostrProfileQueryBindValues::PublicKey { public_key } => ("public_key".to_string(), public_key), + } +} + +fn lib_table_nostr_profile_parse_fields( + opts: INostrProfileFields, +) -> Result<serde_json::Map<String, serde_json::Value>, ModelError> { + let fields = serde_json::to_value(opts) + .map_err(|err| ModelError::SerializationError(err.to_string()))? + .as_object() + .ok_or_else(|| ModelError::SerializationError("Expected an object".to_string()))? + .clone(); + Ok(fields) +} + +pub type INostrProfileCreate = INostrProfileFields; +pub type INostrProfileCreateResolve = IResult<String>; + +pub async fn lib_model_nostr_profile_create( + db: &DatabaseConnection, + opts: INostrProfileCreate, +) -> Result<INostrProfileCreateResolve, ModelError> { + let id: String = uuidv4(); + let created_at: String = time_created_on(); + let updated_at: String = created_at.clone(); + let fields = lib_table_nostr_profile_parse_fields(opts)?; + + let query = format!( + "INSERT INTO nostr_profile (id, created_at, updated_at, {}) VALUES (?, ?, ?, {});", + fields + .keys() + .map(|k| k.to_string()) + .collect::<Vec<String>>() + .join(","), + (0..fields.len()) + .map(|_| "?") + .collect::<Vec<&str>>() + .join(",") + ); + + let mut query_builder = sqlx::query(&query); + query_builder = query_builder.bind(&id); + query_builder = query_builder.bind(&created_at); + query_builder = query_builder.bind(&updated_at); + for (_, value) in fields.iter() { + match value { + serde_json::Value::Bool(b) => { + let bool_str = if *b { "1" } else { "0" }; + query_builder = query_builder.bind(bool_str); + } + serde_json::Value::Number(n) => { + if let Some(f) = n.as_f64() { + query_builder = query_builder.bind(f); + } else if let Some(i64) = n.as_i64() { + query_builder = query_builder.bind(i64); + } else if let Some(u64) = n.as_u64() { + if u64 <= u32::MAX as u64 { + query_builder = query_builder.bind(u64 as u32); + } + } + } + serde_json::Value::String(s) => { + query_builder = query_builder.bind(s); + } + _ => { + query_builder = query_builder.bind::<Option<serde_json::Value>>(None); + } + } + } + query_builder + .execute(db) + .await + .map_err(|e| ModelError::InvalidQuery(e.to_string()))?; + Ok(IResult { result: id }) +} + +#[derive(serde::Serialize, serde::Deserialize)] +pub struct INostrProfileQueryRead { + pub query: String, + pub bind_values: Vec<serde_json::Value>, +} + +pub type INostrProfileRead = INostrProfileQueryRead; +pub type INostrProfileReadResolve = IResultList<NostrProfile>; + +pub async fn lib_model_nostr_profile_read( + db: &DatabaseConnection, + opts: INostrProfileRead, +) -> Result<INostrProfileReadResolve, ModelError> { + let mut query_builder = sqlx::query_as::<_, NostrProfile>(&opts.query); + for value in opts.bind_values.iter() { + query_builder = query_builder.bind(parse_query_value(value)?); + } + let results = query_builder + .fetch(db) + .try_collect() + .await + .map_err(|e: sqlx::Error| ModelError::InvalidQuery(e.to_string()))?; + Ok(IResultList { results }) +} + +#[derive(serde::Serialize, serde::Deserialize)] +pub struct INostrProfileQueryReadList { + pub query: String, + pub bind_values: Vec<serde_json::Value>, +} + +pub type INostrProfileReadList = INostrProfileQueryReadList; +pub type INostrProfileReadListResolve = IResultList<NostrProfile>; + +pub async fn lib_model_nostr_profile_read_list( + db: &DatabaseConnection, + opts: INostrProfileReadList, +) -> Result<INostrProfileReadListResolve, ModelError> { + let mut query_builder = sqlx::query_as::<_, NostrProfile>(&opts.query); + for value in opts.bind_values.iter() { + query_builder = query_builder.bind(parse_query_value(value)?); + } + let results = query_builder + .fetch(db) + .try_collect() + .await + .map_err(|e: sqlx::Error| ModelError::InvalidQuery(e.to_string()))?; + Ok(IResultList { results }) +} + +pub type INostrProfileUpdate = NostrProfileQueryBindValues; +pub type INostrProfileUpdateResolve = (); + +pub async fn lib_model_nostr_profile_update( + _db: &sqlx::Pool<sqlx::Sqlite>, + _opts: INostrProfileUpdate, +) -> Result<INostrProfileUpdateResolve, ModelError> { + Ok(()) +} +pub type INostrProfileDelete = NostrProfileQueryBindValues; +pub type INostrProfileDeleteResolve = IResultPass; + +pub async fn lib_model_nostr_profile_delete( + db: &DatabaseConnection, + opts: INostrProfileDelete, +) -> Result<INostrProfileDeleteResolve, ModelError> { + let (bv_k, bv) = nostr_profile_query_bind_values(opts); + let query = format!("DELETE FROM nostr_profile WHERE {} = ?1;", bv_k); + let result = sqlx::query(&query) + .bind(bv) + .execute(db) + .await + .map_err(|e: sqlx::Error| ModelError::InvalidQuery(e.to_string()))?; + if result.rows_affected() > 0 { + Ok(IResultPass { pass: true }) + } else { + Err(ModelError::NotFound("model.nostr_profile.name".to_string())) + } +} diff --git a/crates/model/src/tables/nostr_profile_relay.rs b/crates/model/src/tables/nostr_profile_relay.rs @@ -0,0 +1,58 @@ +use super::{ + nostr_profile::{nostr_profile_query_bind_values, NostrProfileQueryBindValues}, + nostr_relay::{nostr_relay_query_bind_values, NostrRelayQueryBindValues}, +}; +use crate::{error::ModelError, types::DatabaseConnection}; +use radroots_core::types::IResultPass; + +#[derive(serde::Serialize, serde::Deserialize, sqlx::FromRow)] +pub struct NostrProfileRelay { + tb_pr: String, + tb_rl: String, +} + +#[derive(serde::Serialize, serde::Deserialize)] +pub struct INostrProfileRelayTables { + pub nostr_profile: NostrProfileQueryBindValues, + pub nostr_relay: NostrRelayQueryBindValues, +} + +pub type INostrProfileRelayRelation = INostrProfileRelayTables; +pub type INostrProfileRelayResolve = IResultPass; + +pub async fn lib_model_nostr_profile_relay_set( + db: &DatabaseConnection, + opts: INostrProfileRelayRelation, +) -> Result<INostrProfileRelayResolve, ModelError> { + let (bv_pr_k, bv_pr) = nostr_profile_query_bind_values(opts.nostr_profile); + let (bv_rl_k, bv_rl) = nostr_relay_query_bind_values(opts.nostr_relay); + let query_vals = vec![bv_pr, bv_rl]; + let query = format!("INSERT INTO nostr_profile_relay (tb_pr, tb_rl) VALUES ((SELECT id FROM nostr_profile WHERE {} = ?), (SELECT id FROM nostr_relay WHERE {} = ?));", bv_pr_k, bv_rl_k); + let mut query_builder = sqlx::query(&query); + for value in query_vals.iter() { + query_builder = query_builder.bind(value); + } + query_builder + .execute(db) + .await + .map_err(|e| ModelError::InvalidQuery(e.to_string()))?; + Ok(IResultPass { pass: true }) +} +pub async fn lib_model_nostr_profile_relay_unset( + db: &DatabaseConnection, + opts: INostrProfileRelayRelation, +) -> Result<INostrProfileRelayResolve, ModelError> { + let (bv_pr_k, bv_pr) = nostr_profile_query_bind_values(opts.nostr_profile); + let (bv_rl_k, bv_rl) = nostr_relay_query_bind_values(opts.nostr_relay); + let query_vals = vec![bv_pr, bv_rl]; + let query = format!("DELETE FROM nostr_profile_relay WHERE tb_pr = (SELECT id FROM nostr_profile WHERE {} = ?) AND tb_rl = (SELECT id FROM nostr_relay WHERE {} = ?);", bv_pr_k, bv_rl_k); + let mut query_builder = sqlx::query(&query); + for value in query_vals.iter() { + query_builder = query_builder.bind(value); + } + query_builder + .execute(db) + .await + .map_err(|e| ModelError::InvalidQuery(e.to_string()))?; + Ok(IResultPass { pass: true }) +} diff --git a/crates/model/src/tables/nostr_relay.rs b/crates/model/src/tables/nostr_relay.rs @@ -0,0 +1,234 @@ +use futures::TryStreamExt; +use radroots_core::{ + types::{IModelsQueryBindValueTuple, IResult, IResultList, IResultPass}, + util::{time_created_on, uuidv4}, +}; + +use crate::{error::ModelError, types::DatabaseConnection, util::parse_query_value}; + +#[derive(Debug, serde::Serialize, serde::Deserialize, sqlx::FromRow)] +pub struct NostrRelay { + id: String, + created_at: String, + updated_at: String, + url: String, + relay_id: Option<String>, + name: Option<String>, + description: Option<String>, + pubkey: Option<String>, + contact: Option<String>, + supported_nips: Option<String>, + software: Option<String>, + version: Option<String>, + data: Option<String>, +} + +#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] +pub struct INostrRelayFields { + pub url: String, + pub relay_id: Option<String>, + pub name: Option<String>, + pub description: Option<String>, + pub pubkey: Option<String>, + pub contact: Option<String>, + pub supported_nips: Option<String>, + pub software: Option<String>, + pub version: Option<String>, + pub data: Option<String>, +} + +#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] +pub struct INostrRelayFieldsPartial { + pub url: Option<String>, + pub relay_id: Option<String>, + pub name: Option<String>, + pub description: Option<String>, + pub pubkey: Option<String>, + pub contact: Option<String>, + pub supported_nips: Option<String>, + pub software: Option<String>, + pub version: Option<String>, + pub data: Option<String>, +} + +#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] +pub struct INostrRelayFieldsFilter { + pub created_at: Option<String>, + pub updated_at: Option<String>, + pub url: Option<String>, + pub relay_id: Option<String>, + pub name: Option<String>, + pub description: Option<String>, + pub pubkey: Option<String>, + pub contact: Option<String>, + pub supported_nips: Option<String>, + pub software: Option<String>, + pub version: Option<String>, + pub data: Option<String>, +} + +#[derive(Clone, serde::Serialize, serde::Deserialize)] +#[serde(untagged)] +pub enum NostrRelayQueryBindValues { + Id { id: String }, + Url { url: String }, +} + +pub fn nostr_relay_query_bind_values(opts: NostrRelayQueryBindValues) -> IModelsQueryBindValueTuple { + match opts { + NostrRelayQueryBindValues::Id { id } => ("id".to_string(), id), + NostrRelayQueryBindValues::Url { url } => ("url".to_string(), url), + } +} + +fn lib_table_nostr_relay_parse_fields( + opts: INostrRelayFields, +) -> Result<serde_json::Map<String, serde_json::Value>, ModelError> { + let fields = serde_json::to_value(opts) + .map_err(|err| ModelError::SerializationError(err.to_string()))? + .as_object() + .ok_or_else(|| ModelError::SerializationError("Expected an object".to_string()))? + .clone(); + Ok(fields) +} + +pub type INostrRelayCreate = INostrRelayFields; +pub type INostrRelayCreateResolve = IResult<String>; + +pub async fn lib_model_nostr_relay_create( + db: &DatabaseConnection, + opts: INostrRelayCreate, +) -> Result<INostrRelayCreateResolve, ModelError> { + let id: String = uuidv4(); + let created_at: String = time_created_on(); + let updated_at: String = created_at.clone(); + let fields = lib_table_nostr_relay_parse_fields(opts)?; + + let query = format!( + "INSERT INTO nostr_relay (id, created_at, updated_at, {}) VALUES (?, ?, ?, {});", + fields + .keys() + .map(|k| k.to_string()) + .collect::<Vec<String>>() + .join(","), + (0..fields.len()) + .map(|_| "?") + .collect::<Vec<&str>>() + .join(",") + ); + + let mut query_builder = sqlx::query(&query); + query_builder = query_builder.bind(&id); + query_builder = query_builder.bind(&created_at); + query_builder = query_builder.bind(&updated_at); + for (_, value) in fields.iter() { + match value { + serde_json::Value::Bool(b) => { + let bool_str = if *b { "1" } else { "0" }; + query_builder = query_builder.bind(bool_str); + } + serde_json::Value::Number(n) => { + if let Some(f) = n.as_f64() { + query_builder = query_builder.bind(f); + } else if let Some(i64) = n.as_i64() { + query_builder = query_builder.bind(i64); + } else if let Some(u64) = n.as_u64() { + if u64 <= u32::MAX as u64 { + query_builder = query_builder.bind(u64 as u32); + } + } + } + serde_json::Value::String(s) => { + query_builder = query_builder.bind(s); + } + _ => { + query_builder = query_builder.bind::<Option<serde_json::Value>>(None); + } + } + } + query_builder + .execute(db) + .await + .map_err(|e| ModelError::InvalidQuery(e.to_string()))?; + Ok(IResult { result: id }) +} + +#[derive(serde::Serialize, serde::Deserialize)] +pub struct INostrRelayQueryRead { + pub query: String, + pub bind_values: Vec<serde_json::Value>, +} + +pub type INostrRelayRead = INostrRelayQueryRead; +pub type INostrRelayReadResolve = IResultList<NostrRelay>; + +pub async fn lib_model_nostr_relay_read( + db: &DatabaseConnection, + opts: INostrRelayRead, +) -> Result<INostrRelayReadResolve, ModelError> { + let mut query_builder = sqlx::query_as::<_, NostrRelay>(&opts.query); + for value in opts.bind_values.iter() { + query_builder = query_builder.bind(parse_query_value(value)?); + } + let results = query_builder + .fetch(db) + .try_collect() + .await + .map_err(|e: sqlx::Error| ModelError::InvalidQuery(e.to_string()))?; + Ok(IResultList { results }) +} + +#[derive(serde::Serialize, serde::Deserialize)] +pub struct INostrRelayQueryReadList { + pub query: String, + pub bind_values: Vec<serde_json::Value>, +} + +pub type INostrRelayReadList = INostrRelayQueryReadList; +pub type INostrRelayReadListResolve = IResultList<NostrRelay>; + +pub async fn lib_model_nostr_relay_read_list( + db: &DatabaseConnection, + opts: INostrRelayReadList, +) -> Result<INostrRelayReadListResolve, ModelError> { + let mut query_builder = sqlx::query_as::<_, NostrRelay>(&opts.query); + for value in opts.bind_values.iter() { + query_builder = query_builder.bind(parse_query_value(value)?); + } + let results = query_builder + .fetch(db) + .try_collect() + .await + .map_err(|e: sqlx::Error| ModelError::InvalidQuery(e.to_string()))?; + Ok(IResultList { results }) +} + +pub type INostrRelayUpdate = NostrRelayQueryBindValues; +pub type INostrRelayUpdateResolve = (); + +pub async fn lib_model_nostr_relay_update( + _db: &sqlx::Pool<sqlx::Sqlite>, + _opts: INostrRelayUpdate, +) -> Result<INostrRelayUpdateResolve, ModelError> { + Ok(()) +} +pub type INostrRelayDelete = NostrRelayQueryBindValues; +pub type INostrRelayDeleteResolve = IResultPass; + +pub async fn lib_model_nostr_relay_delete( + db: &DatabaseConnection, + opts: INostrRelayDelete, +) -> Result<INostrRelayDeleteResolve, ModelError> { + let (bv_k, bv) = nostr_relay_query_bind_values(opts); + let query = format!("DELETE FROM nostr_relay WHERE {} = ?1;", bv_k); + let result = sqlx::query(&query) + .bind(bv) + .execute(db) + .await + .map_err(|e: sqlx::Error| ModelError::InvalidQuery(e.to_string()))?; + if result.rows_affected() > 0 { + Ok(IResultPass { pass: true }) + } else { + Err(ModelError::NotFound("model.nostr_relay.name".to_string())) + } +} diff --git a/crates/model/src/tables/trade_product.rs b/crates/model/src/tables/trade_product.rs @@ -0,0 +1,260 @@ +use futures::TryStreamExt; +use radroots_core::{ + types::{IModelsQueryBindValueTuple, IResult, IResultList, IResultPass}, + util::{time_created_on, uuidv4}, +}; + +use crate::{error::ModelError, types::DatabaseConnection, util::parse_query_value}; + +#[derive(Debug, serde::Serialize, serde::Deserialize, sqlx::FromRow)] +pub struct TradeProduct { + id: String, + created_at: String, + updated_at: String, + key: String, + category: String, + title: String, + summary: String, + process: String, + lot: String, + profile: String, + year: i64, + qty_amt: i64, + qty_unit: String, + qty_label: Option<String>, + qty_avail: Option<i64>, + price_amt: f64, + price_currency: String, + price_qty_amt: u32, + price_qty_unit: String, + notes: Option<String>, +} + +#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] +pub struct ITradeProductFields { + pub key: String, + pub category: String, + pub title: String, + pub summary: String, + pub process: String, + pub lot: String, + pub profile: String, + pub year: i64, + pub qty_amt: i64, + pub qty_unit: String, + pub qty_label: Option<String>, + pub qty_avail: Option<i64>, + pub price_amt: f64, + pub price_currency: String, + pub price_qty_amt: u32, + pub price_qty_unit: String, + pub notes: Option<String>, +} + +#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] +pub struct ITradeProductFieldsPartial { + pub key: Option<String>, + pub category: Option<String>, + pub title: Option<String>, + pub summary: Option<String>, + pub process: Option<String>, + pub lot: Option<String>, + pub profile: Option<String>, + pub year: Option<i64>, + pub qty_amt: Option<i64>, + pub qty_unit: Option<String>, + pub qty_label: Option<String>, + pub qty_avail: Option<i64>, + pub price_amt: Option<f64>, + pub price_currency: Option<String>, + pub price_qty_amt: Option<u32>, + pub price_qty_unit: Option<String>, + pub notes: Option<String>, +} + +#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] +pub struct ITradeProductFieldsFilter { + pub created_at: Option<String>, + pub updated_at: Option<String>, + pub key: Option<String>, + pub category: Option<String>, + pub title: Option<String>, + pub summary: Option<String>, + pub process: Option<String>, + pub lot: Option<String>, + pub profile: Option<String>, + pub year: Option<String>, + pub qty_amt: Option<String>, + pub qty_unit: Option<String>, + pub qty_label: Option<String>, + pub qty_avail: Option<String>, + pub price_amt: Option<String>, + pub price_currency: Option<String>, + pub price_qty_amt: Option<String>, + pub price_qty_unit: Option<String>, + pub notes: Option<String>, +} + +#[derive(Clone, serde::Serialize, serde::Deserialize)] +#[serde(untagged)] +pub enum TradeProductQueryBindValues { + Id { id: String }, +} + +pub fn trade_product_query_bind_values(opts: TradeProductQueryBindValues) -> IModelsQueryBindValueTuple { + match opts { + TradeProductQueryBindValues::Id { id } => ("id".to_string(), id), + } +} + +fn lib_table_trade_product_parse_fields( + opts: ITradeProductFields, +) -> Result<serde_json::Map<String, serde_json::Value>, ModelError> { + let fields = serde_json::to_value(opts) + .map_err(|err| ModelError::SerializationError(err.to_string()))? + .as_object() + .ok_or_else(|| ModelError::SerializationError("Expected an object".to_string()))? + .clone(); + Ok(fields) +} + +pub type ITradeProductCreate = ITradeProductFields; +pub type ITradeProductCreateResolve = IResult<String>; + +pub async fn lib_model_trade_product_create( + db: &DatabaseConnection, + opts: ITradeProductCreate, +) -> Result<ITradeProductCreateResolve, ModelError> { + let id: String = uuidv4(); + let created_at: String = time_created_on(); + let updated_at: String = created_at.clone(); + let fields = lib_table_trade_product_parse_fields(opts)?; + + let query = format!( + "INSERT INTO trade_product (id, created_at, updated_at, {}) VALUES (?, ?, ?, {});", + fields + .keys() + .map(|k| k.to_string()) + .collect::<Vec<String>>() + .join(","), + (0..fields.len()) + .map(|_| "?") + .collect::<Vec<&str>>() + .join(",") + ); + + let mut query_builder = sqlx::query(&query); + query_builder = query_builder.bind(&id); + query_builder = query_builder.bind(&created_at); + query_builder = query_builder.bind(&updated_at); + for (_, value) in fields.iter() { + match value { + serde_json::Value::Bool(b) => { + let bool_str = if *b { "1" } else { "0" }; + query_builder = query_builder.bind(bool_str); + } + serde_json::Value::Number(n) => { + if let Some(f) = n.as_f64() { + query_builder = query_builder.bind(f); + } else if let Some(i64) = n.as_i64() { + query_builder = query_builder.bind(i64); + } else if let Some(u64) = n.as_u64() { + if u64 <= u32::MAX as u64 { + query_builder = query_builder.bind(u64 as u32); + } + } + } + serde_json::Value::String(s) => { + query_builder = query_builder.bind(s); + } + _ => { + query_builder = query_builder.bind::<Option<serde_json::Value>>(None); + } + } + } + query_builder + .execute(db) + .await + .map_err(|e| ModelError::InvalidQuery(e.to_string()))?; + Ok(IResult { result: id }) +} + +#[derive(serde::Serialize, serde::Deserialize)] +pub struct ITradeProductQueryRead { + pub query: String, + pub bind_values: Vec<serde_json::Value>, +} + +pub type ITradeProductRead = ITradeProductQueryRead; +pub type ITradeProductReadResolve = IResultList<TradeProduct>; + +pub async fn lib_model_trade_product_read( + db: &DatabaseConnection, + opts: ITradeProductRead, +) -> Result<ITradeProductReadResolve, ModelError> { + let mut query_builder = sqlx::query_as::<_, TradeProduct>(&opts.query); + for value in opts.bind_values.iter() { + query_builder = query_builder.bind(parse_query_value(value)?); + } + let results = query_builder + .fetch(db) + .try_collect() + .await + .map_err(|e: sqlx::Error| ModelError::InvalidQuery(e.to_string()))?; + Ok(IResultList { results }) +} + +#[derive(serde::Serialize, serde::Deserialize)] +pub struct ITradeProductQueryReadList { + pub query: String, + pub bind_values: Vec<serde_json::Value>, +} + +pub type ITradeProductReadList = ITradeProductQueryReadList; +pub type ITradeProductReadListResolve = IResultList<TradeProduct>; + +pub async fn lib_model_trade_product_read_list( + db: &DatabaseConnection, + opts: ITradeProductReadList, +) -> Result<ITradeProductReadListResolve, ModelError> { + let mut query_builder = sqlx::query_as::<_, TradeProduct>(&opts.query); + for value in opts.bind_values.iter() { + query_builder = query_builder.bind(parse_query_value(value)?); + } + let results = query_builder + .fetch(db) + .try_collect() + .await + .map_err(|e: sqlx::Error| ModelError::InvalidQuery(e.to_string()))?; + Ok(IResultList { results }) +} + +pub type ITradeProductUpdate = TradeProductQueryBindValues; +pub type ITradeProductUpdateResolve = (); + +pub async fn lib_model_trade_product_update( + _db: &sqlx::Pool<sqlx::Sqlite>, + _opts: ITradeProductUpdate, +) -> Result<ITradeProductUpdateResolve, ModelError> { + Ok(()) +} +pub type ITradeProductDelete = TradeProductQueryBindValues; +pub type ITradeProductDeleteResolve = IResultPass; + +pub async fn lib_model_trade_product_delete( + db: &DatabaseConnection, + opts: ITradeProductDelete, +) -> Result<ITradeProductDeleteResolve, ModelError> { + let (bv_k, bv) = trade_product_query_bind_values(opts); + let query = format!("DELETE FROM trade_product WHERE {} = ?1;", bv_k); + let result = sqlx::query(&query) + .bind(bv) + .execute(db) + .await + .map_err(|e: sqlx::Error| ModelError::InvalidQuery(e.to_string()))?; + if result.rows_affected() > 0 { + Ok(IResultPass { pass: true }) + } else { + Err(ModelError::NotFound("model.trade_product.name".to_string())) + } +} diff --git a/crates/model/src/tables/trade_product_location.rs b/crates/model/src/tables/trade_product_location.rs @@ -0,0 +1,58 @@ +use super::{ + location_gcs::{location_gcs_query_bind_values, LocationGcsQueryBindValues}, + trade_product::{trade_product_query_bind_values, TradeProductQueryBindValues}, +}; +use crate::{error::ModelError, types::DatabaseConnection}; +use radroots_core::types::IResultPass; + +#[derive(serde::Serialize, serde::Deserialize, sqlx::FromRow)] +pub struct TradeProductLocation { + tb_tp: String, + tb_lg: String, +} + +#[derive(serde::Serialize, serde::Deserialize)] +pub struct ITradeProductLocationTables { + pub trade_product: TradeProductQueryBindValues, + pub location_gcs: LocationGcsQueryBindValues, +} + +pub type ITradeProductLocationRelation = ITradeProductLocationTables; +pub type ITradeProductLocationResolve = IResultPass; + +pub async fn lib_model_trade_product_location_set( + db: &DatabaseConnection, + opts: ITradeProductLocationRelation, +) -> Result<ITradeProductLocationResolve, ModelError> { + let (bv_tp_k, bv_tp) = trade_product_query_bind_values(opts.trade_product); + let (bv_lg_k, bv_lg) = location_gcs_query_bind_values(opts.location_gcs); + let query_vals = vec![bv_tp, bv_lg]; + let query = format!("INSERT INTO trade_product_location (tb_tp, tb_lg) VALUES ((SELECT id FROM trade_product WHERE {} = ?), (SELECT id FROM location_gcs WHERE {} = ?));", bv_tp_k, bv_lg_k); + let mut query_builder = sqlx::query(&query); + for value in query_vals.iter() { + query_builder = query_builder.bind(value); + } + query_builder + .execute(db) + .await + .map_err(|e| ModelError::InvalidQuery(e.to_string()))?; + Ok(IResultPass { pass: true }) +} +pub async fn lib_model_trade_product_location_unset( + db: &DatabaseConnection, + opts: ITradeProductLocationRelation, +) -> Result<ITradeProductLocationResolve, ModelError> { + let (bv_tp_k, bv_tp) = trade_product_query_bind_values(opts.trade_product); + let (bv_lg_k, bv_lg) = location_gcs_query_bind_values(opts.location_gcs); + let query_vals = vec![bv_tp, bv_lg]; + let query = format!("DELETE FROM trade_product_location WHERE tb_tp = (SELECT id FROM trade_product WHERE {} = ?) AND tb_lg = (SELECT id FROM location_gcs WHERE {} = ?);", bv_tp_k, bv_lg_k); + let mut query_builder = sqlx::query(&query); + for value in query_vals.iter() { + query_builder = query_builder.bind(value); + } + query_builder + .execute(db) + .await + .map_err(|e| ModelError::InvalidQuery(e.to_string()))?; + Ok(IResultPass { pass: true }) +} diff --git a/crates/model/src/tables/trade_product_media.rs b/crates/model/src/tables/trade_product_media.rs @@ -0,0 +1,58 @@ +use super::{ + media_image::{media_image_query_bind_values, MediaImageQueryBindValues}, + trade_product::{trade_product_query_bind_values, TradeProductQueryBindValues}, +}; +use crate::{error::ModelError, types::DatabaseConnection}; +use radroots_core::types::IResultPass; + +#[derive(serde::Serialize, serde::Deserialize, sqlx::FromRow)] +pub struct TradeProductMedia { + tb_tp: String, + tb_mu: String, +} + +#[derive(serde::Serialize, serde::Deserialize)] +pub struct ITradeProductMediaTables { + pub trade_product: TradeProductQueryBindValues, + pub media_image: MediaImageQueryBindValues, +} + +pub type ITradeProductMediaRelation = ITradeProductMediaTables; +pub type ITradeProductMediaResolve = IResultPass; + +pub async fn lib_model_trade_product_media_set( + db: &DatabaseConnection, + opts: ITradeProductMediaRelation, +) -> Result<ITradeProductMediaResolve, ModelError> { + let (bv_tp_k, bv_tp) = trade_product_query_bind_values(opts.trade_product); + let (bv_mu_k, bv_mu) = media_image_query_bind_values(opts.media_image); + let query_vals = vec![bv_tp, bv_mu]; + let query = format!("INSERT INTO trade_product_media (tb_tp, tb_mu) VALUES ((SELECT id FROM trade_product WHERE {} = ?), (SELECT id FROM media_image WHERE {} = ?));", bv_tp_k, bv_mu_k); + let mut query_builder = sqlx::query(&query); + for value in query_vals.iter() { + query_builder = query_builder.bind(value); + } + query_builder + .execute(db) + .await + .map_err(|e| ModelError::InvalidQuery(e.to_string()))?; + Ok(IResultPass { pass: true }) +} +pub async fn lib_model_trade_product_media_unset( + db: &DatabaseConnection, + opts: ITradeProductMediaRelation, +) -> Result<ITradeProductMediaResolve, ModelError> { + let (bv_tp_k, bv_tp) = trade_product_query_bind_values(opts.trade_product); + let (bv_mu_k, bv_mu) = media_image_query_bind_values(opts.media_image); + let query_vals = vec![bv_tp, bv_mu]; + let query = format!("DELETE FROM trade_product_media WHERE tb_tp = (SELECT id FROM trade_product WHERE {} = ?) AND tb_mu = (SELECT id FROM media_image WHERE {} = ?);", bv_tp_k, bv_mu_k); + let mut query_builder = sqlx::query(&query); + for value in query_vals.iter() { + query_builder = query_builder.bind(value); + } + query_builder + .execute(db) + .await + .map_err(|e| ModelError::InvalidQuery(e.to_string()))?; + Ok(IResultPass { pass: true }) +} diff --git a/crates/model/src/types.rs b/crates/model/src/types.rs @@ -0,0 +1,9 @@ +pub type DatabaseConnection = sqlx::Pool<sqlx::Sqlite>; + +#[derive(Debug)] +pub enum ModelsQueryBindValue { + String(String), + Number(f64), + Boolean(bool), + Null, +} diff --git a/crates/model/src/util.rs b/crates/model/src/util.rs @@ -0,0 +1,20 @@ +use crate::{error::ModelError, types::DatabaseConnection}; + +pub fn parse_query_value(value: &serde_json::Value) -> Result<String, ModelError> { + match value { + serde_json::Value::String(s) => Ok(s.to_string()), + serde_json::Value::Number(n) if n.is_i64() => Ok(n.to_string()), + serde_json::Value::Bool(b) => Ok((*b as u8).to_string()), + _ => Err(ModelError::InvalidArgument(format!( + "Unsupported value type: {:?}", + value + ))), + } +} + +pub async fn db_conn(db_path: std::path::PathBuf) -> DatabaseConnection { + sqlx::sqlite::SqlitePoolOptions::new() + .connect(db_path.to_str().unwrap()) + .await + .unwrap() +} diff --git a/crates/tauri/Cargo.toml b/crates/tauri/Cargo.toml @@ -14,24 +14,19 @@ crate-type = ["staticlib", "cdylib", "rlib"] tauri-build = { version = "2.0.0", features = [] } [dependencies] -env_logger = "0.11.5" -keyring = { version = "3.2.0", features = ["apple-native", "linux-native", "windows-native"] } -log = "0.4" +tauri = { version = "2.2.0", features = ["protocol-asset"] } +tauri-plugin-dialog = { version = "2.2.0" } +tauri-plugin-fs = { version = "2.2.0" } +tauri-plugin-geolocation = { path = "../../../../../../lib/plugins-workspace/plugins/geolocation/" } +tauri-plugin-http = { version = "2.2.0" } +tauri-plugin-notification = { version = "2.2.0" } +tauri-plugin-os = { version = "2.2.0" } +tauri-plugin-store = { version = "2.2.0" } +tauri-plugin-shell = { version = "2.2.0" } + radroots_core = { path = "../core" } -serde = { version = "1.0", features = ["derive"] } +radroots_model = { path = "../model" } + +serde = "1.0" serde_json = "1.0" sqlx = { version = "0.8.2", features = ["sqlite", "runtime-tokio"] } -tauri = { version = "2.0.0", features = ["protocol-asset"] } -tauri-plugin-dialog = "2.0.0" -tauri-plugin-fs = "2.0.0" -tauri-plugin-geolocation = "2.0.0" -tauri-plugin-http = "2.0.0" -tauri-plugin-log = "2.0.0" -tauri-plugin-map-display = { git = "https://github.com/inkibra/tauri-plugins", tag = "@inkibra/tauri-plugin-map-display@0.2.0", package="tauri-plugin-map-display" } -tauri-plugin-notification = "2.0.0" -tauri-plugin-os = "2.0.0" -tauri-plugin-store = "2.1.0" -tauri-plugin-shell = "2.0.0" - -[profile.dev] -incremental = true diff --git a/crates/tauri/Info.ios.plist b/crates/tauri/Info.ios.plist @@ -1,28 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> -<plist version="1.0"> - <dict> - <key>NSLocationWhenInUseUsageDescription</key> - <string>Uses device GPS to read current location.</string> - <key>NFCReaderUsageDescription</key> - <string>Uses device NFC to enable wireless communication.</string> - <key>NSLocationWhenInUseUsageDescription</key> - <string>Uses device mapping features to display and explore locations.</string> - <key>NSAppTransportSecurity</key> - <dict> - <key>NSAllowsArbitraryLoads</key> - <true/> - </dict> - <key>NSPrivacyAccessedAPITypes</key> - <array> - <dict> - <key>NSPrivacyAccessedAPIType</key> - <string>NSPrivacyAccessedAPICategoryFileTimestamp</string> - <key>NSPrivacyAccessedAPITypeReasons</key> - <array> - <string>C617.1</string> - </array> - </dict> - </array> - </dict> -</plist> -\ No newline at end of file diff --git a/crates/tauri/LICENSE b/crates/tauri/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + <program> Copyright (C) <year> <name of author> + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +<https://www.gnu.org/licenses/>. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +<https://www.gnu.org/licenses/why-not-lgpl.html>. +\ No newline at end of file diff --git a/crates/tauri/capabilities/default.json b/crates/tauri/capabilities/default.json @@ -1,7 +1,6 @@ { - "$schema": "../gen/schemas/desktop-schema.json", + "$schema": "../gen/schemas/capabilities.json", "identifier": "default", - "description": "enables the default permissions", "windows": [ "main" ], @@ -10,20 +9,17 @@ "dialog:default", "fs:default", "fs:allow-stat", - "geolocation:allow-check-permissions", - "geolocation:allow-get-current-position", - "geolocation:allow-request-permissions", - "geolocation:allow-watch-position", - "geolocation:allow-clear-permissions", - "geolocation:allow-clear-watch", { "identifier": "http:default", "allow": [ { - "url": "https://radroots.org" + "url": "https://radroots.market" }, { - "url": "https://*.radroots.org" + "url": "https://*.radroots.market" + }, + { + "url": "http://127.0.0.1:*" } ], "deny": [] @@ -31,7 +27,6 @@ "notification:default", "os:default", "store:default", - "shell:allow-open", - "map-display:default" + "shell:allow-open" ] } \ No newline at end of file diff --git a/crates/tauri/capabilities/desktop.json b/crates/tauri/capabilities/desktop.json @@ -0,0 +1,13 @@ +{ + "$schema": "../gen/schemas/capabilities.json", + "identifier": "desktop", + "windows": [ + "main" + ], + "platforms": [ + "linux", + "macOS", + "windows" + ], + "permissions": [] +} +\ No newline at end of file diff --git a/crates/tauri/capabilities/mobile.json b/crates/tauri/capabilities/mobile.json @@ -0,0 +1,19 @@ +{ + "$schema": "../gen/schemas/mobile-schema.json", + "identifier": "mobile", + "windows": [ + "main" + ], + "platforms": [ + "iOS", + "android" + ], + "permissions": [ + "geolocation:allow-check-permissions", + "geolocation:allow-get-current-position", + "geolocation:allow-request-permissions", + "geolocation:allow-watch-position", + "geolocation:allow-clear-permissions", + "geolocation:allow-clear-watch" + ] +} +\ No newline at end of file diff --git a/crates/tauri/icons/icon.icns b/crates/tauri/icons/icon.icns Binary files differ. diff --git a/crates/tauri/icons/ios/AppIcon-20x20@1x.png b/crates/tauri/icons/ios/AppIcon-20x20@1x.png Binary files differ. diff --git a/crates/tauri/icons/ios/AppIcon-20x20@2x-1.png b/crates/tauri/icons/ios/AppIcon-20x20@2x-1.png Binary files differ. diff --git a/crates/tauri/icons/ios/AppIcon-20x20@2x.png b/crates/tauri/icons/ios/AppIcon-20x20@2x.png Binary files differ. diff --git a/crates/tauri/icons/ios/AppIcon-20x20@3x.png b/crates/tauri/icons/ios/AppIcon-20x20@3x.png Binary files differ. diff --git a/crates/tauri/icons/ios/AppIcon-29x29@1x.png b/crates/tauri/icons/ios/AppIcon-29x29@1x.png Binary files differ. diff --git a/crates/tauri/icons/ios/AppIcon-29x29@2x-1.png b/crates/tauri/icons/ios/AppIcon-29x29@2x-1.png Binary files differ. diff --git a/crates/tauri/icons/ios/AppIcon-29x29@2x.png b/crates/tauri/icons/ios/AppIcon-29x29@2x.png Binary files differ. diff --git a/crates/tauri/icons/ios/AppIcon-29x29@3x.png b/crates/tauri/icons/ios/AppIcon-29x29@3x.png Binary files differ. diff --git a/crates/tauri/icons/ios/AppIcon-40x40@1x.png b/crates/tauri/icons/ios/AppIcon-40x40@1x.png Binary files differ. diff --git a/crates/tauri/icons/ios/AppIcon-40x40@2x-1.png b/crates/tauri/icons/ios/AppIcon-40x40@2x-1.png Binary files differ. diff --git a/crates/tauri/icons/ios/AppIcon-40x40@2x.png b/crates/tauri/icons/ios/AppIcon-40x40@2x.png Binary files differ. diff --git a/crates/tauri/icons/ios/AppIcon-40x40@3x.png b/crates/tauri/icons/ios/AppIcon-40x40@3x.png Binary files differ. diff --git a/crates/tauri/icons/ios/AppIcon-512@2x.png b/crates/tauri/icons/ios/AppIcon-512@2x.png Binary files differ. diff --git a/crates/tauri/icons/ios/AppIcon-60x60@2x.png b/crates/tauri/icons/ios/AppIcon-60x60@2x.png Binary files differ. diff --git a/crates/tauri/icons/ios/AppIcon-60x60@3x.png b/crates/tauri/icons/ios/AppIcon-60x60@3x.png Binary files differ. diff --git a/crates/tauri/icons/ios/AppIcon-76x76@1x.png b/crates/tauri/icons/ios/AppIcon-76x76@1x.png Binary files differ. diff --git a/crates/tauri/icons/ios/AppIcon-76x76@2x.png b/crates/tauri/icons/ios/AppIcon-76x76@2x.png Binary files differ. diff --git a/crates/tauri/icons/ios/AppIcon-83.5x83.5@2x.png b/crates/tauri/icons/ios/AppIcon-83.5x83.5@2x.png Binary files differ. diff --git a/crates/tauri/migrations/0001_location_gcs.sql b/crates/tauri/migrations/0001_location_gcs.sql @@ -1,20 +0,0 @@ -CREATE TABLE IF NOT EXISTS location_gcs ( - id CHAR(36) PRIMARY KEY NOT NULL UNIQUE CHECK(length(id) = 36), - created_at DATETIME NOT NULL CHECK(length(created_at) = 24), - updated_at DATETIME NOT NULL CHECK(length(updated_at) = 24), - lat REAL NOT NULL, - lng REAL NOT NULL, - geohash CHAR(12) UNIQUE CHECK(length(geohash) = 9), - kind TEXT NOT NULL, - label TEXT, - area REAL, - elevation INTEGER, - soil TEXT, - climate TEXT, - gc_id TEXT, - gc_name TEXT, - gc_admin1_id TEXT, - gc_admin1_name TEXT, - gc_country_id TEXT, - gc_country_name TEXT -); -\ No newline at end of file diff --git a/crates/tauri/migrations/0005_media_upload.sql b/crates/tauri/migrations/0005_media_upload.sql @@ -1,11 +0,0 @@ -CREATE TABLE IF NOT EXISTS media_upload ( - id CHAR(36) PRIMARY KEY NOT NULL UNIQUE CHECK(length(id) = 36), - created_at DATETIME NOT NULL CHECK(length(created_at) = 24), - updated_at DATETIME NOT NULL CHECK(length(updated_at) = 24), - file_path TEXT NOT NULL, - mime_type TEXT NOT NULL, - res_base TEXT NOT NULL, - res_path TEXT NOT NULL, - label TEXT, - description TEXT -); -\ No newline at end of file diff --git a/crates/tauri/migrations/0006_log_error.sql b/crates/tauri/migrations/0006_log_error.sql @@ -1,12 +0,0 @@ -CREATE TABLE IF NOT EXISTS log_error ( - id CHAR(36) PRIMARY KEY NOT NULL UNIQUE CHECK(length(id) = 36), - created_at DATETIME NOT NULL CHECK(length(created_at) = 24), - error TEXT NOT NULL, - message TEXT NOT NULL, - stack_trace TEXT, - cause TEXT, - app_system TEXT NOT NULL, - app_version TEXT NOT NULL, - nostr_pubkey TEXT NOT NULL, - data TEXT -); -\ No newline at end of file diff --git a/crates/tauri/migrations/0009_trade_product_media.sql b/crates/tauri/migrations/0009_trade_product_media.sql @@ -1,7 +0,0 @@ -CREATE TABLE IF NOT EXISTS trade_product_media ( - tb_tp CHAR(36), - tb_mu CHAR(36), - FOREIGN KEY (tb_tp) REFERENCES trade_product(id) ON DELETE CASCADE, - FOREIGN KEY (tb_mu) REFERENCES media_upload(id) ON DELETE CASCADE, - PRIMARY KEY (tb_tp, tb_mu) -); -\ No newline at end of file diff --git a/crates/tauri/migrations/down/0001_location_gcs.sql b/crates/tauri/migrations/down/0001_location_gcs.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS location_gcs; +\ No newline at end of file diff --git a/crates/tauri/migrations/down/0002_trade_product.sql b/crates/tauri/migrations/down/0002_trade_product.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS trade_product; +\ No newline at end of file diff --git a/crates/tauri/migrations/down/0003_nostr_profile.sql b/crates/tauri/migrations/down/0003_nostr_profile.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS nostr_profile; +\ No newline at end of file diff --git a/crates/tauri/migrations/down/0004_nostr_relay.sql b/crates/tauri/migrations/down/0004_nostr_relay.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS nostr_relay; +\ No newline at end of file diff --git a/crates/tauri/migrations/down/0005_media_image.sql b/crates/tauri/migrations/down/0005_media_image.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS media_image; +\ No newline at end of file diff --git a/crates/tauri/migrations/down/0006_log_error.sql b/crates/tauri/migrations/down/0006_log_error.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS log_error; +\ No newline at end of file diff --git a/crates/tauri/migrations/down/0007_nostr_profile_relay.sql b/crates/tauri/migrations/down/0007_nostr_profile_relay.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS nostr_profile_relay; +\ No newline at end of file diff --git a/crates/tauri/migrations/down/0008_trade_product_location.sql b/crates/tauri/migrations/down/0008_trade_product_location.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS trade_product_location; +\ No newline at end of file diff --git a/crates/tauri/migrations/down/0009_trade_product_media.sql b/crates/tauri/migrations/down/0009_trade_product_media.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS trade_product_media; +\ No newline at end of file diff --git a/crates/tauri/migrations/up/0001_location_gcs.sql b/crates/tauri/migrations/up/0001_location_gcs.sql @@ -0,0 +1,20 @@ +CREATE TABLE IF NOT EXISTS location_gcs ( + id CHAR(36) PRIMARY KEY NOT NULL UNIQUE CHECK(length(id) = 36), + created_at DATETIME NOT NULL CHECK(length(created_at) = 24), + updated_at DATETIME NOT NULL CHECK(length(updated_at) = 24), + lat REAL NOT NULL, + lng REAL NOT NULL, + geohash TEXT, + kind TEXT NOT NULL, + label TEXT, + area REAL, + elevation INTEGER, + soil TEXT, + climate TEXT, + gc_id TEXT, + gc_name TEXT, + gc_admin1_id TEXT, + gc_admin1_name TEXT, + gc_country_id TEXT, + gc_country_name TEXT +); +\ No newline at end of file diff --git a/crates/tauri/migrations/0002_trade_product.sql b/crates/tauri/migrations/up/0002_trade_product.sql diff --git a/crates/tauri/migrations/0003_nostr_profile.sql b/crates/tauri/migrations/up/0003_nostr_profile.sql diff --git a/crates/tauri/migrations/0004_nostr_relay.sql b/crates/tauri/migrations/up/0004_nostr_relay.sql diff --git a/crates/tauri/migrations/up/0005_media_image.sql b/crates/tauri/migrations/up/0005_media_image.sql @@ -0,0 +1,11 @@ +CREATE TABLE IF NOT EXISTS media_image ( + id CHAR(36) PRIMARY KEY NOT NULL UNIQUE CHECK(length(id) = 36), + created_at DATETIME NOT NULL CHECK(length(created_at) = 24), + updated_at DATETIME NOT NULL CHECK(length(updated_at) = 24), + file_path TEXT NOT NULL, + mime_type TEXT NOT NULL, + res_base TEXT NOT NULL, + res_path TEXT NOT NULL, + label TEXT, + description TEXT +); +\ No newline at end of file diff --git a/crates/tauri/migrations/up/0006_log_error.sql b/crates/tauri/migrations/up/0006_log_error.sql @@ -0,0 +1,13 @@ +CREATE TABLE IF NOT EXISTS log_error ( + id CHAR(36) PRIMARY KEY NOT NULL UNIQUE CHECK(length(id) = 36), + created_at DATETIME NOT NULL CHECK(length(created_at) = 24), + updated_at DATETIME NOT NULL CHECK(length(updated_at) = 24), + error TEXT NOT NULL, + message TEXT NOT NULL, + stack_trace TEXT, + cause TEXT, + app_system TEXT NOT NULL, + app_version TEXT NOT NULL, + nostr_pubkey TEXT NOT NULL, + data TEXT +); +\ No newline at end of file diff --git a/crates/tauri/migrations/0007_nostr_profile_relay.sql b/crates/tauri/migrations/up/0007_nostr_profile_relay.sql diff --git a/crates/tauri/migrations/0008_trade_product_location.sql b/crates/tauri/migrations/up/0008_trade_product_location.sql diff --git a/crates/tauri/migrations/up/0009_trade_product_media.sql b/crates/tauri/migrations/up/0009_trade_product_media.sql @@ -0,0 +1,7 @@ +CREATE TABLE IF NOT EXISTS trade_product_media ( + tb_tp CHAR(36), + tb_mu CHAR(36), + FOREIGN KEY (tb_tp) REFERENCES trade_product(id) ON DELETE CASCADE, + FOREIGN KEY (tb_mu) REFERENCES media_image(id) ON DELETE CASCADE, + PRIMARY KEY (tb_tp, tb_mu) +); +\ No newline at end of file diff --git a/crates/tauri/src/commands/keys.rs b/crates/tauri/src/commands/keys.rs @@ -0,0 +1,74 @@ +use radroots_core::{ + keystore, + nostr::keys::{ + lib_nostr_keys_gen, lib_nostr_keys_parse, lib_nostr_public_key_hex, + lib_nostr_secret_key_hex, + }, + types::{IResult, IResultList, IResultPass}, +}; + +use crate::radroots::Radroots; + +#[tauri::command(rename_all = "snake_case")] +pub async fn keys_nostr_gen(rr: tauri::State<'_, Radroots>) -> Result<IResult<String>, String> { + let keys = lib_nostr_keys_gen(); + keystore::key_add(&keys, &rr.data_dir) + .map_err(|e| format!("Failed to create private key: {}", e))?; + Ok(IResult { + result: keys.public_key.to_hex(), + }) +} + +#[tauri::command(rename_all = "snake_case")] +pub async fn keys_nostr_add( + secret_key: String, + rr: tauri::State<'_, Radroots>, +) -> Result<IResult<String>, String> { + let keys = lib_nostr_keys_parse(secret_key).map_err(|e| e.to_string())?; + keystore::key_add(&keys, &rr.data_dir) + .map_err(|e| format!("Failed to add private key: {}", e))?; + Ok(IResult { + result: lib_nostr_public_key_hex(keys), + }) +} + +#[tauri::command(rename_all = "snake_case")] +pub async fn keys_nostr_read( + public_key: String, + rr: tauri::State<'_, Radroots>, +) -> Result<IResult<String>, String> { + let keys = keystore::key_read(&public_key, &rr.data_dir) + .map_err(|e| format!("Failed to read private key: {}", e))?; + Ok(IResult { + result: lib_nostr_secret_key_hex(keys), + }) +} + +#[tauri::command(rename_all = "snake_case")] +pub async fn keys_nostr_read_all( + rr: tauri::State<'_, Radroots>, +) -> Result<IResultList<String>, String> { + let keystore_keys = keystore::keys_read_all(&rr.data_dir) + .map_err(|e| format!("Failed to read keystore: {}", e))?; + Ok(IResultList { + results: keystore_keys, + }) +} + +#[tauri::command(rename_all = "snake_case")] +pub async fn keys_nostr_delete( + public_key: String, + rr: tauri::State<'_, Radroots>, +) -> Result<IResultPass, String> { + keystore::key_delete(&public_key, &rr.data_dir) + .map_err(|e| format!("Failed to delete private key: {}", e))?; + Ok(IResultPass { pass: true }) +} + +#[tauri::command(rename_all = "snake_case")] +pub async fn keys_nostr_keystore_reset( + rr: tauri::State<'_, Radroots>, +) -> Result<IResultPass, String> { + keystore::reset(&rr.data_dir).map_err(|e| format!("Failed to reset keystore: {}", e))?; + Ok(IResultPass { pass: true }) +} diff --git a/crates/tauri/src/commands/mod.rs b/crates/tauri/src/commands/mod.rs @@ -0,0 +1,2 @@ +pub mod keys; +pub mod model; diff --git a/crates/tauri/src/commands/model/location_gcs.rs b/crates/tauri/src/commands/model/location_gcs.rs @@ -0,0 +1,60 @@ +use crate::radroots::Radroots; +use radroots_core::types::IError; +use radroots_model::{ + tables::location_gcs::{lib_model_location_gcs_create, ILocationGcsCreate, ILocationGcsCreateResolve, lib_model_location_gcs_read, ILocationGcsRead, ILocationGcsReadResolve, lib_model_location_gcs_read_list, ILocationGcsReadList, ILocationGcsReadListResolve, lib_model_location_gcs_delete, ILocationGcsDelete, ILocationGcsDeleteResolve, lib_model_location_gcs_update, ILocationGcsUpdate, ILocationGcsUpdateResolve}, +}; + +#[tauri::command(rename_all = "snake_case")] +pub async fn model_location_gcs_create( + state: tauri::State<'_, Radroots>, + args: ILocationGcsCreate, +) -> Result<ILocationGcsCreateResolve, IError> { + match lib_model_location_gcs_create(&state.db, args).await { + Ok(result) => Ok(result), + Err(e) => Err(IError { err: e.to_string() }), + } +} + +#[tauri::command(rename_all = "snake_case")] +pub async fn model_location_gcs_read( + state: tauri::State<'_, Radroots>, + args: ILocationGcsRead, +) -> Result<ILocationGcsReadResolve, IError> { + match lib_model_location_gcs_read(&state.db, args).await { + Ok(result) => Ok(result), + Err(e) => Err(IError { err: e.to_string() }), + } +} + +#[tauri::command(rename_all = "snake_case")] +pub async fn model_location_gcs_read_list( + state: tauri::State<'_, Radroots>, + args: ILocationGcsReadList, +) -> Result<ILocationGcsReadListResolve, IError> { + match lib_model_location_gcs_read_list(&state.db, args).await { + Ok(result) => Ok(result), + Err(e) => Err(IError { err: e.to_string() }), + } +} + +#[tauri::command(rename_all = "snake_case")] +pub async fn model_location_gcs_update( + state: tauri::State<'_, Radroots>, + args: ILocationGcsUpdate, +) -> Result<ILocationGcsUpdateResolve, IError> { + match lib_model_location_gcs_update(&state.db, args).await { + Ok(result) => Ok(result), + Err(e) => Err(IError { err: e.to_string() }), + } +} + +#[tauri::command(rename_all = "snake_case")] +pub async fn model_location_gcs_delete( + state: tauri::State<'_, Radroots>, + args: ILocationGcsDelete, +) -> Result<ILocationGcsDeleteResolve, IError> { + match lib_model_location_gcs_delete(&state.db, args).await { + Ok(result) => Ok(result), + Err(e) => Err(IError { err: e.to_string() }), + } +} +\ No newline at end of file diff --git a/crates/tauri/src/commands/model/log_error.rs b/crates/tauri/src/commands/model/log_error.rs @@ -0,0 +1,60 @@ +use crate::radroots::Radroots; +use radroots_core::types::IError; +use radroots_model::{ + tables::log_error::{lib_model_log_error_create, ILogErrorCreate, ILogErrorCreateResolve, lib_model_log_error_read, ILogErrorRead, ILogErrorReadResolve, lib_model_log_error_read_list, ILogErrorReadList, ILogErrorReadListResolve, lib_model_log_error_delete, ILogErrorDelete, ILogErrorDeleteResolve, lib_model_log_error_update, ILogErrorUpdate, ILogErrorUpdateResolve}, +}; + +#[tauri::command(rename_all = "snake_case")] +pub async fn model_log_error_create( + state: tauri::State<'_, Radroots>, + args: ILogErrorCreate, +) -> Result<ILogErrorCreateResolve, IError> { + match lib_model_log_error_create(&state.db, args).await { + Ok(result) => Ok(result), + Err(e) => Err(IError { err: e.to_string() }), + } +} + +#[tauri::command(rename_all = "snake_case")] +pub async fn model_log_error_read( + state: tauri::State<'_, Radroots>, + args: ILogErrorRead, +) -> Result<ILogErrorReadResolve, IError> { + match lib_model_log_error_read(&state.db, args).await { + Ok(result) => Ok(result), + Err(e) => Err(IError { err: e.to_string() }), + } +} + +#[tauri::command(rename_all = "snake_case")] +pub async fn model_log_error_read_list( + state: tauri::State<'_, Radroots>, + args: ILogErrorReadList, +) -> Result<ILogErrorReadListResolve, IError> { + match lib_model_log_error_read_list(&state.db, args).await { + Ok(result) => Ok(result), + Err(e) => Err(IError { err: e.to_string() }), + } +} + +#[tauri::command(rename_all = "snake_case")] +pub async fn model_log_error_update( + state: tauri::State<'_, Radroots>, + args: ILogErrorUpdate, +) -> Result<ILogErrorUpdateResolve, IError> { + match lib_model_log_error_update(&state.db, args).await { + Ok(result) => Ok(result), + Err(e) => Err(IError { err: e.to_string() }), + } +} + +#[tauri::command(rename_all = "snake_case")] +pub async fn model_log_error_delete( + state: tauri::State<'_, Radroots>, + args: ILogErrorDelete, +) -> Result<ILogErrorDeleteResolve, IError> { + match lib_model_log_error_delete(&state.db, args).await { + Ok(result) => Ok(result), + Err(e) => Err(IError { err: e.to_string() }), + } +} +\ No newline at end of file diff --git a/crates/tauri/src/commands/model/media_image.rs b/crates/tauri/src/commands/model/media_image.rs @@ -0,0 +1,60 @@ +use crate::radroots::Radroots; +use radroots_core::types::IError; +use radroots_model::{ + tables::media_image::{lib_model_media_image_create, IMediaImageCreate, IMediaImageCreateResolve, lib_model_media_image_read, IMediaImageRead, IMediaImageReadResolve, lib_model_media_image_read_list, IMediaImageReadList, IMediaImageReadListResolve, lib_model_media_image_delete, IMediaImageDelete, IMediaImageDeleteResolve, lib_model_media_image_update, IMediaImageUpdate, IMediaImageUpdateResolve}, +}; + +#[tauri::command(rename_all = "snake_case")] +pub async fn model_media_image_create( + state: tauri::State<'_, Radroots>, + args: IMediaImageCreate, +) -> Result<IMediaImageCreateResolve, IError> { + match lib_model_media_image_create(&state.db, args).await { + Ok(result) => Ok(result), + Err(e) => Err(IError { err: e.to_string() }), + } +} + +#[tauri::command(rename_all = "snake_case")] +pub async fn model_media_image_read( + state: tauri::State<'_, Radroots>, + args: IMediaImageRead, +) -> Result<IMediaImageReadResolve, IError> { + match lib_model_media_image_read(&state.db, args).await { + Ok(result) => Ok(result), + Err(e) => Err(IError { err: e.to_string() }), + } +} + +#[tauri::command(rename_all = "snake_case")] +pub async fn model_media_image_read_list( + state: tauri::State<'_, Radroots>, + args: IMediaImageReadList, +) -> Result<IMediaImageReadListResolve, IError> { + match lib_model_media_image_read_list(&state.db, args).await { + Ok(result) => Ok(result), + Err(e) => Err(IError { err: e.to_string() }), + } +} + +#[tauri::command(rename_all = "snake_case")] +pub async fn model_media_image_update( + state: tauri::State<'_, Radroots>, + args: IMediaImageUpdate, +) -> Result<IMediaImageUpdateResolve, IError> { + match lib_model_media_image_update(&state.db, args).await { + Ok(result) => Ok(result), + Err(e) => Err(IError { err: e.to_string() }), + } +} + +#[tauri::command(rename_all = "snake_case")] +pub async fn model_media_image_delete( + state: tauri::State<'_, Radroots>, + args: IMediaImageDelete, +) -> Result<IMediaImageDeleteResolve, IError> { + match lib_model_media_image_delete(&state.db, args).await { + Ok(result) => Ok(result), + Err(e) => Err(IError { err: e.to_string() }), + } +} +\ No newline at end of file diff --git a/crates/tauri/src/commands/model/mod.rs b/crates/tauri/src/commands/model/mod.rs @@ -0,0 +1,21 @@ +use crate::radroots::Radroots; +use radroots_core::types::{IError, IResultPass}; +use radroots_model::tables::lib_model_tables_reset; + +pub(crate) mod location_gcs; +pub(crate) mod log_error; +pub(crate) mod media_image; +pub(crate) mod nostr_profile; +pub(crate) mod nostr_profile_relay; +pub(crate) mod nostr_relay; +pub(crate) mod trade_product; +pub(crate) mod trade_product_location; +pub(crate) mod trade_product_media; + +#[tauri::command(rename_all = "snake_case")] +pub async fn model_tables_reset(state: tauri::State<'_, Radroots>) -> Result<IResultPass, IError> { + match lib_model_tables_reset(&state.db).await { + Ok(result) => Ok(result), + Err(e) => Err(IError { err: e.to_string() }), + } +} diff --git a/crates/tauri/src/commands/model/nostr_profile.rs b/crates/tauri/src/commands/model/nostr_profile.rs @@ -0,0 +1,60 @@ +use crate::radroots::Radroots; +use radroots_core::types::IError; +use radroots_model::{ + tables::nostr_profile::{lib_model_nostr_profile_create, INostrProfileCreate, INostrProfileCreateResolve, lib_model_nostr_profile_read, INostrProfileRead, INostrProfileReadResolve, lib_model_nostr_profile_read_list, INostrProfileReadList, INostrProfileReadListResolve, lib_model_nostr_profile_delete, INostrProfileDelete, INostrProfileDeleteResolve, lib_model_nostr_profile_update, INostrProfileUpdate, INostrProfileUpdateResolve}, +}; + +#[tauri::command(rename_all = "snake_case")] +pub async fn model_nostr_profile_create( + state: tauri::State<'_, Radroots>, + args: INostrProfileCreate, +) -> Result<INostrProfileCreateResolve, IError> { + match lib_model_nostr_profile_create(&state.db, args).await { + Ok(result) => Ok(result), + Err(e) => Err(IError { err: e.to_string() }), + } +} + +#[tauri::command(rename_all = "snake_case")] +pub async fn model_nostr_profile_read( + state: tauri::State<'_, Radroots>, + args: INostrProfileRead, +) -> Result<INostrProfileReadResolve, IError> { + match lib_model_nostr_profile_read(&state.db, args).await { + Ok(result) => Ok(result), + Err(e) => Err(IError { err: e.to_string() }), + } +} + +#[tauri::command(rename_all = "snake_case")] +pub async fn model_nostr_profile_read_list( + state: tauri::State<'_, Radroots>, + args: INostrProfileReadList, +) -> Result<INostrProfileReadListResolve, IError> { + match lib_model_nostr_profile_read_list(&state.db, args).await { + Ok(result) => Ok(result), + Err(e) => Err(IError { err: e.to_string() }), + } +} + +#[tauri::command(rename_all = "snake_case")] +pub async fn model_nostr_profile_update( + state: tauri::State<'_, Radroots>, + args: INostrProfileUpdate, +) -> Result<INostrProfileUpdateResolve, IError> { + match lib_model_nostr_profile_update(&state.db, args).await { + Ok(result) => Ok(result), + Err(e) => Err(IError { err: e.to_string() }), + } +} + +#[tauri::command(rename_all = "snake_case")] +pub async fn model_nostr_profile_delete( + state: tauri::State<'_, Radroots>, + args: INostrProfileDelete, +) -> Result<INostrProfileDeleteResolve, IError> { + match lib_model_nostr_profile_delete(&state.db, args).await { + Ok(result) => Ok(result), + Err(e) => Err(IError { err: e.to_string() }), + } +} +\ No newline at end of file diff --git a/crates/tauri/src/commands/model/nostr_profile_relay.rs b/crates/tauri/src/commands/model/nostr_profile_relay.rs @@ -0,0 +1,28 @@ +use crate::radroots::Radroots; +use radroots_core::types::IError; +use radroots_model::tables::nostr_profile_relay::{ + lib_model_nostr_profile_relay_set, lib_model_nostr_profile_relay_unset, + INostrProfileRelayRelation, INostrProfileRelayResolve, +}; + +#[tauri::command(rename_all = "snake_case")] +pub async fn model_nostr_profile_relay_set( + state: tauri::State<'_, Radroots>, + args: INostrProfileRelayRelation, +) -> Result<INostrProfileRelayResolve, IError> { + match lib_model_nostr_profile_relay_set(&state.db, args).await { + Ok(result) => Ok(result), + Err(e) => Err(IError { err: e.to_string() }), + } +} + +#[tauri::command(rename_all = "snake_case")] +pub async fn model_nostr_profile_relay_unset( + state: tauri::State<'_, Radroots>, + args: INostrProfileRelayRelation, +) -> Result<INostrProfileRelayResolve, IError> { + match lib_model_nostr_profile_relay_unset(&state.db, args).await { + Ok(result) => Ok(result), + Err(e) => Err(IError { err: e.to_string() }), + } +} +\ No newline at end of file diff --git a/crates/tauri/src/commands/model/nostr_relay.rs b/crates/tauri/src/commands/model/nostr_relay.rs @@ -0,0 +1,60 @@ +use crate::radroots::Radroots; +use radroots_core::types::IError; +use radroots_model::{ + tables::nostr_relay::{lib_model_nostr_relay_create, INostrRelayCreate, INostrRelayCreateResolve, lib_model_nostr_relay_read, INostrRelayRead, INostrRelayReadResolve, lib_model_nostr_relay_read_list, INostrRelayReadList, INostrRelayReadListResolve, lib_model_nostr_relay_delete, INostrRelayDelete, INostrRelayDeleteResolve, lib_model_nostr_relay_update, INostrRelayUpdate, INostrRelayUpdateResolve}, +}; + +#[tauri::command(rename_all = "snake_case")] +pub async fn model_nostr_relay_create( + state: tauri::State<'_, Radroots>, + args: INostrRelayCreate, +) -> Result<INostrRelayCreateResolve, IError> { + match lib_model_nostr_relay_create(&state.db, args).await { + Ok(result) => Ok(result), + Err(e) => Err(IError { err: e.to_string() }), + } +} + +#[tauri::command(rename_all = "snake_case")] +pub async fn model_nostr_relay_read( + state: tauri::State<'_, Radroots>, + args: INostrRelayRead, +) -> Result<INostrRelayReadResolve, IError> { + match lib_model_nostr_relay_read(&state.db, args).await { + Ok(result) => Ok(result), + Err(e) => Err(IError { err: e.to_string() }), + } +} + +#[tauri::command(rename_all = "snake_case")] +pub async fn model_nostr_relay_read_list( + state: tauri::State<'_, Radroots>, + args: INostrRelayReadList, +) -> Result<INostrRelayReadListResolve, IError> { + match lib_model_nostr_relay_read_list(&state.db, args).await { + Ok(result) => Ok(result), + Err(e) => Err(IError { err: e.to_string() }), + } +} + +#[tauri::command(rename_all = "snake_case")] +pub async fn model_nostr_relay_update( + state: tauri::State<'_, Radroots>, + args: INostrRelayUpdate, +) -> Result<INostrRelayUpdateResolve, IError> { + match lib_model_nostr_relay_update(&state.db, args).await { + Ok(result) => Ok(result), + Err(e) => Err(IError { err: e.to_string() }), + } +} + +#[tauri::command(rename_all = "snake_case")] +pub async fn model_nostr_relay_delete( + state: tauri::State<'_, Radroots>, + args: INostrRelayDelete, +) -> Result<INostrRelayDeleteResolve, IError> { + match lib_model_nostr_relay_delete(&state.db, args).await { + Ok(result) => Ok(result), + Err(e) => Err(IError { err: e.to_string() }), + } +} +\ No newline at end of file diff --git a/crates/tauri/src/commands/model/trade_product.rs b/crates/tauri/src/commands/model/trade_product.rs @@ -0,0 +1,60 @@ +use crate::radroots::Radroots; +use radroots_core::types::IError; +use radroots_model::{ + tables::trade_product::{lib_model_trade_product_create, ITradeProductCreate, ITradeProductCreateResolve, lib_model_trade_product_read, ITradeProductRead, ITradeProductReadResolve, lib_model_trade_product_read_list, ITradeProductReadList, ITradeProductReadListResolve, lib_model_trade_product_delete, ITradeProductDelete, ITradeProductDeleteResolve, lib_model_trade_product_update, ITradeProductUpdate, ITradeProductUpdateResolve}, +}; + +#[tauri::command(rename_all = "snake_case")] +pub async fn model_trade_product_create( + state: tauri::State<'_, Radroots>, + args: ITradeProductCreate, +) -> Result<ITradeProductCreateResolve, IError> { + match lib_model_trade_product_create(&state.db, args).await { + Ok(result) => Ok(result), + Err(e) => Err(IError { err: e.to_string() }), + } +} + +#[tauri::command(rename_all = "snake_case")] +pub async fn model_trade_product_read( + state: tauri::State<'_, Radroots>, + args: ITradeProductRead, +) -> Result<ITradeProductReadResolve, IError> { + match lib_model_trade_product_read(&state.db, args).await { + Ok(result) => Ok(result), + Err(e) => Err(IError { err: e.to_string() }), + } +} + +#[tauri::command(rename_all = "snake_case")] +pub async fn model_trade_product_read_list( + state: tauri::State<'_, Radroots>, + args: ITradeProductReadList, +) -> Result<ITradeProductReadListResolve, IError> { + match lib_model_trade_product_read_list(&state.db, args).await { + Ok(result) => Ok(result), + Err(e) => Err(IError { err: e.to_string() }), + } +} + +#[tauri::command(rename_all = "snake_case")] +pub async fn model_trade_product_update( + state: tauri::State<'_, Radroots>, + args: ITradeProductUpdate, +) -> Result<ITradeProductUpdateResolve, IError> { + match lib_model_trade_product_update(&state.db, args).await { + Ok(result) => Ok(result), + Err(e) => Err(IError { err: e.to_string() }), + } +} + +#[tauri::command(rename_all = "snake_case")] +pub async fn model_trade_product_delete( + state: tauri::State<'_, Radroots>, + args: ITradeProductDelete, +) -> Result<ITradeProductDeleteResolve, IError> { + match lib_model_trade_product_delete(&state.db, args).await { + Ok(result) => Ok(result), + Err(e) => Err(IError { err: e.to_string() }), + } +} +\ No newline at end of file diff --git a/crates/tauri/src/commands/model/trade_product_location.rs b/crates/tauri/src/commands/model/trade_product_location.rs @@ -0,0 +1,28 @@ +use crate::radroots::Radroots; +use radroots_core::types::IError; +use radroots_model::tables::trade_product_location::{ + lib_model_trade_product_location_set, lib_model_trade_product_location_unset, + ITradeProductLocationRelation, ITradeProductLocationResolve, +}; + +#[tauri::command(rename_all = "snake_case")] +pub async fn model_trade_product_location_set( + state: tauri::State<'_, Radroots>, + args: ITradeProductLocationRelation, +) -> Result<ITradeProductLocationResolve, IError> { + match lib_model_trade_product_location_set(&state.db, args).await { + Ok(result) => Ok(result), + Err(e) => Err(IError { err: e.to_string() }), + } +} + +#[tauri::command(rename_all = "snake_case")] +pub async fn model_trade_product_location_unset( + state: tauri::State<'_, Radroots>, + args: ITradeProductLocationRelation, +) -> Result<ITradeProductLocationResolve, IError> { + match lib_model_trade_product_location_unset(&state.db, args).await { + Ok(result) => Ok(result), + Err(e) => Err(IError { err: e.to_string() }), + } +} +\ No newline at end of file diff --git a/crates/tauri/src/commands/model/trade_product_media.rs b/crates/tauri/src/commands/model/trade_product_media.rs @@ -0,0 +1,28 @@ +use crate::radroots::Radroots; +use radroots_core::types::IError; +use radroots_model::tables::trade_product_media::{ + lib_model_trade_product_media_set, lib_model_trade_product_media_unset, + ITradeProductMediaRelation, ITradeProductMediaResolve, +}; + +#[tauri::command(rename_all = "snake_case")] +pub async fn model_trade_product_media_set( + state: tauri::State<'_, Radroots>, + args: ITradeProductMediaRelation, +) -> Result<ITradeProductMediaResolve, IError> { + match lib_model_trade_product_media_set(&state.db, args).await { + Ok(result) => Ok(result), + Err(e) => Err(IError { err: e.to_string() }), + } +} + +#[tauri::command(rename_all = "snake_case")] +pub async fn model_trade_product_media_unset( + state: tauri::State<'_, Radroots>, + args: ITradeProductMediaRelation, +) -> Result<ITradeProductMediaResolve, IError> { + match lib_model_trade_product_media_unset(&state.db, args).await { + Ok(result) => Ok(result), + Err(e) => Err(IError { err: e.to_string() }), + } +} +\ No newline at end of file diff --git a/crates/tauri/src/database.rs b/crates/tauri/src/database.rs @@ -1,30 +0,0 @@ -use sqlx::sqlite::SqlitePoolOptions; -use std::fs::OpenOptions; -use std::path::PathBuf; - -pub async fn setup_db(data_dir: &PathBuf) -> sqlx::Pool<sqlx::Sqlite> { - let mut path = data_dir.clone(); - match std::fs::create_dir_all(path.clone()) { - Ok(_) => {} - Err(err) => { - panic!("Error resolving databse directory {}", err); - } - }; - path.push("radroots_db.sqlite"); - let result = OpenOptions::new().create_new(true).write(true).open(&path); - match result { - Ok(_) => println!("Database file created"), - Err(err) => match err.kind() { - std::io::ErrorKind::AlreadyExists => println!("Database file exists"), - _ => { - panic!("Error creating databse file {}", err); - } - }, - } - let db = SqlitePoolOptions::new() - .connect(path.to_str().unwrap()) - .await - .unwrap(); - sqlx::migrate!("./migrations").run(&db).await.unwrap(); - db -} diff --git a/crates/tauri/src/lib.rs b/crates/tauri/src/lib.rs @@ -1,107 +1,133 @@ -mod database; -pub mod models; -mod radroots; +pub mod commands; +pub mod radroots; +pub mod util; -use database::setup_db; -use models::{ - location_gcs::{ - model_location_gcs_add, model_location_gcs_delete, model_location_gcs_get, - model_location_gcs_update, - }, - log_error::{model_log_error_add, model_log_error_delete, model_log_error_get}, - media_upload::{ - model_media_upload_add, model_media_upload_delete, model_media_upload_get, - model_media_upload_update, - }, - nostr_profile::{ - model_nostr_profile_add, model_nostr_profile_delete, model_nostr_profile_get, - model_nostr_profile_update, - }, - nostr_profile_relay::{ - model_nostr_profile_relay_get_all, model_nostr_profile_relay_set, - model_nostr_profile_relay_unset, - }, - nostr_relay::{ - model_nostr_relay_add, model_nostr_relay_delete, model_nostr_relay_get, - model_nostr_relay_update, - }, - trade_product::{ - model_trade_product_add, model_trade_product_delete, model_trade_product_get, - model_trade_product_update, - }, - trade_product_location::{ - model_trade_product_location_get_all, model_trade_product_location_set, - model_trade_product_location_unset, - }, - trade_product_media::{ - model_trade_product_media_get_all, model_trade_product_media_set, - model_trade_product_media_unset, - }, +use commands::keys::{ + keys_nostr_add, keys_nostr_delete, keys_nostr_gen, keys_nostr_keystore_reset, keys_nostr_read, + keys_nostr_read_all, }; +use commands::model::location_gcs::{ + model_location_gcs_create, model_location_gcs_delete, model_location_gcs_read, + model_location_gcs_read_list, model_location_gcs_update, + }; +use commands::model::log_error::{ + model_log_error_create, model_log_error_delete, model_log_error_read, + model_log_error_read_list, model_log_error_update, + }; +use commands::model::media_image::{ + model_media_image_create, model_media_image_delete, model_media_image_read, + model_media_image_read_list, model_media_image_update, + }; +use commands::model::model_tables_reset; +use commands::model::nostr_profile::{ + model_nostr_profile_create, model_nostr_profile_delete, model_nostr_profile_read, + model_nostr_profile_read_list, model_nostr_profile_update, + }; +use commands::model::nostr_profile_relay::{ + model_nostr_profile_relay_set, model_nostr_profile_relay_unset, + }; +use commands::model::nostr_relay::{ + model_nostr_relay_create, model_nostr_relay_delete, model_nostr_relay_read, + model_nostr_relay_read_list, model_nostr_relay_update, + }; +use commands::model::trade_product::{ + model_trade_product_create, model_trade_product_delete, model_trade_product_read, + model_trade_product_read_list, model_trade_product_update, + }; +use commands::model::trade_product_location::{ + model_trade_product_location_set, model_trade_product_location_unset, + }; +use commands::model::trade_product_media::{ + model_trade_product_media_set, model_trade_product_media_unset, + }; use radroots::Radroots; +use std::path::PathBuf; use tauri::Manager; #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { - env_logger::init(); - tauri::Builder::default() - .setup(|app| { - tauri::async_runtime::block_on(async move { - let data_dir = app - .handle() - .path() - .app_data_dir() - .expect("Failed to resolve app data dir"); - - let db = setup_db(&data_dir).await; - app.manage(Radroots { db }); - - Ok(()) - }) - }) + .plugin(tauri_plugin_shell::init()) .plugin(tauri_plugin_dialog::init()) .plugin(tauri_plugin_fs::init()) .plugin(tauri_plugin_geolocation::init()) .plugin(tauri_plugin_http::init()) - .plugin(tauri_plugin_map_display::init()) .plugin(tauri_plugin_notification::init()) .plugin(tauri_plugin_os::init()) - .plugin(tauri_plugin_shell::init()) .plugin(tauri_plugin_store::Builder::new().build()) + .setup(|app| { + let data_dir = app + .handle() + .path() + .app_data_dir() + .expect("Failed to resolve app data dir"); + + let logs_dir = app.handle().path().app_log_dir().unwrap(); + + let fmt_data_dir = if cfg!(dev) { + PathBuf::from(format!("{}/dev", data_dir.to_string_lossy())) + } else { + PathBuf::from(format!("{}/release", data_dir.to_string_lossy())) + }; + + let fmt_logs_dir = if cfg!(dev) { + PathBuf::from(format!("{}/dev", logs_dir.to_string_lossy())) + } else { + PathBuf::from(format!("{}/release", logs_dir.to_string_lossy())) + }; + + tauri::async_runtime::block_on(async move { + let radroots = Radroots::new(fmt_data_dir, fmt_logs_dir).await; + app.manage(radroots); + }); + Ok(()) + }) .invoke_handler(tauri::generate_handler![ - model_location_gcs_add, - model_location_gcs_get, + // %model% + model_location_gcs_create, model_location_gcs_delete, + model_location_gcs_read, + model_location_gcs_read_list, model_location_gcs_update, - model_trade_product_add, - model_trade_product_get, - model_trade_product_delete, - model_trade_product_update, - model_nostr_profile_add, - model_nostr_profile_get, + model_log_error_create, + model_log_error_delete, + model_log_error_read, + model_log_error_read_list, + model_log_error_update, + model_media_image_create, + model_media_image_delete, + model_media_image_read, + model_media_image_read_list, + model_media_image_update, + model_nostr_profile_create, model_nostr_profile_delete, + model_nostr_profile_read, + model_nostr_profile_read_list, + model_nostr_profile_relay_set, + model_nostr_profile_relay_unset, model_nostr_profile_update, - model_nostr_relay_add, - model_nostr_relay_get, + model_nostr_relay_create, model_nostr_relay_delete, + model_nostr_relay_read, + model_nostr_relay_read_list, model_nostr_relay_update, - model_nostr_profile_relay_set, - model_nostr_profile_relay_unset, - model_nostr_profile_relay_get_all, + model_tables_reset, + model_trade_product_create, + model_trade_product_delete, model_trade_product_location_set, model_trade_product_location_unset, - model_trade_product_location_get_all, - model_media_upload_add, - model_media_upload_get, - model_media_upload_delete, - model_media_upload_update, model_trade_product_media_set, model_trade_product_media_unset, - model_trade_product_media_get_all, - model_log_error_add, - model_log_error_get, - model_log_error_delete, + model_trade_product_read, + model_trade_product_read_list, + model_trade_product_update, + // %model% + keys_nostr_gen, + keys_nostr_add, + keys_nostr_read, + keys_nostr_read_all, + keys_nostr_delete, + keys_nostr_keystore_reset ]) .run(tauri::generate_context!()) .expect("error while running tauri application"); diff --git a/crates/tauri/src/models/location_gcs.rs b/crates/tauri/src/models/location_gcs.rs @@ -1,60 +0,0 @@ -use crate::radroots::Radroots; -use radroots_core::{ - models::location_gcs::{lib_model_location_gcs_add, ILocationGcsAdd, ILocationGcsAddResolve, lib_model_location_gcs_get, ILocationGcsGet, ILocationGcsGetResolve, lib_model_location_gcs_delete, ILocationGcsDelete, ILocationGcsDeleteResolve, lib_model_location_gcs_update, ILocationGcsUpdate, ILocationGcsUpdateResolve}, -}; - -#[tauri::command] -pub async fn model_location_gcs_add( - state: tauri::State<'_, Radroots>, - opts: ILocationGcsAdd, -) -> Result<ILocationGcsAddResolve, String> { - match lib_model_location_gcs_add(&state.db, opts).await { - Ok(result) => Ok(result), - Err(e) => { - println!("ERROR {}", e); - Err(e.to_string()) - } - } -} - -#[tauri::command] -pub async fn model_location_gcs_get( - state: tauri::State<'_, Radroots>, - opts: ILocationGcsGet, -) -> Result<ILocationGcsGetResolve, String> { - match lib_model_location_gcs_get(&state.db, opts).await { - Ok(result) => Ok(result), - Err(e) => { - println!("ERROR {}", e); - Err(e.to_string()) - } - } -} - -#[tauri::command] -pub async fn model_location_gcs_delete( - state: tauri::State<'_, Radroots>, - opts: ILocationGcsDelete, -) -> Result<ILocationGcsDeleteResolve, String> { - match lib_model_location_gcs_delete(&state.db, opts).await { - Ok(result) => Ok(result), - Err(e) => { - println!("ERROR {}", e); - Err(e.to_string()) - } - } -} - -#[tauri::command] -pub async fn model_location_gcs_update( - state: tauri::State<'_, Radroots>, - opts: ILocationGcsUpdate, -) -> Result<ILocationGcsUpdateResolve, String> { - match lib_model_location_gcs_update(&state.db, opts).await { - Ok(result) => Ok(result), - Err(e) => { - println!("ERROR {}", e); - Err(e.to_string()) - } - } -} diff --git a/crates/tauri/src/models/log_error.rs b/crates/tauri/src/models/log_error.rs @@ -1,46 +0,0 @@ -use crate::radroots::Radroots; -use radroots_core::{ - models::log_error::{lib_model_log_error_add, ILogErrorAdd, ILogErrorAddResolve, lib_model_log_error_get, ILogErrorGet, ILogErrorGetResolve, lib_model_log_error_delete, ILogErrorDelete, ILogErrorDeleteResolve}, -}; - -#[tauri::command] -pub async fn model_log_error_add( - state: tauri::State<'_, Radroots>, - opts: ILogErrorAdd, -) -> Result<ILogErrorAddResolve, String> { - match lib_model_log_error_add(&state.db, opts).await { - Ok(result) => Ok(result), - Err(e) => { - println!("ERROR {}", e); - Err(e.to_string()) - } - } -} - -#[tauri::command] -pub async fn model_log_error_get( - state: tauri::State<'_, Radroots>, - opts: ILogErrorGet, -) -> Result<ILogErrorGetResolve, String> { - match lib_model_log_error_get(&state.db, opts).await { - Ok(result) => Ok(result), - Err(e) => { - println!("ERROR {}", e); - Err(e.to_string()) - } - } -} - -#[tauri::command] -pub async fn model_log_error_delete( - state: tauri::State<'_, Radroots>, - opts: ILogErrorDelete, -) -> Result<ILogErrorDeleteResolve, String> { - match lib_model_log_error_delete(&state.db, opts).await { - Ok(result) => Ok(result), - Err(e) => { - println!("ERROR {}", e); - Err(e.to_string()) - } - } -} diff --git a/crates/tauri/src/models/media_upload.rs b/crates/tauri/src/models/media_upload.rs @@ -1,60 +0,0 @@ -use crate::radroots::Radroots; -use radroots_core::{ - models::media_upload::{lib_model_media_upload_add, IMediaUploadAdd, IMediaUploadAddResolve, lib_model_media_upload_get, IMediaUploadGet, IMediaUploadGetResolve, lib_model_media_upload_delete, IMediaUploadDelete, IMediaUploadDeleteResolve, lib_model_media_upload_update, IMediaUploadUpdate, IMediaUploadUpdateResolve}, -}; - -#[tauri::command] -pub async fn model_media_upload_add( - state: tauri::State<'_, Radroots>, - opts: IMediaUploadAdd, -) -> Result<IMediaUploadAddResolve, String> { - match lib_model_media_upload_add(&state.db, opts).await { - Ok(result) => Ok(result), - Err(e) => { - println!("ERROR {}", e); - Err(e.to_string()) - } - } -} - -#[tauri::command] -pub async fn model_media_upload_get( - state: tauri::State<'_, Radroots>, - opts: IMediaUploadGet, -) -> Result<IMediaUploadGetResolve, String> { - match lib_model_media_upload_get(&state.db, opts).await { - Ok(result) => Ok(result), - Err(e) => { - println!("ERROR {}", e); - Err(e.to_string()) - } - } -} - -#[tauri::command] -pub async fn model_media_upload_delete( - state: tauri::State<'_, Radroots>, - opts: IMediaUploadDelete, -) -> Result<IMediaUploadDeleteResolve, String> { - match lib_model_media_upload_delete(&state.db, opts).await { - Ok(result) => Ok(result), - Err(e) => { - println!("ERROR {}", e); - Err(e.to_string()) - } - } -} - -#[tauri::command] -pub async fn model_media_upload_update( - state: tauri::State<'_, Radroots>, - opts: IMediaUploadUpdate, -) -> Result<IMediaUploadUpdateResolve, String> { - match lib_model_media_upload_update(&state.db, opts).await { - Ok(result) => Ok(result), - Err(e) => { - println!("ERROR {}", e); - Err(e.to_string()) - } - } -} diff --git a/crates/tauri/src/models/mod.rs b/crates/tauri/src/models/mod.rs @@ -1,9 +0,0 @@ -pub(crate) mod location_gcs; -pub(crate) mod log_error; -pub(crate) mod media_upload; -pub(crate) mod nostr_profile; -pub(crate) mod nostr_profile_relay; -pub(crate) mod nostr_relay; -pub(crate) mod trade_product; -pub(crate) mod trade_product_location; -pub(crate) mod trade_product_media; diff --git a/crates/tauri/src/models/nostr_profile.rs b/crates/tauri/src/models/nostr_profile.rs @@ -1,60 +0,0 @@ -use crate::radroots::Radroots; -use radroots_core::{ - models::nostr_profile::{lib_model_nostr_profile_add, INostrProfileAdd, INostrProfileAddResolve, lib_model_nostr_profile_get, INostrProfileGet, INostrProfileGetResolve, lib_model_nostr_profile_delete, INostrProfileDelete, INostrProfileDeleteResolve, lib_model_nostr_profile_update, INostrProfileUpdate, INostrProfileUpdateResolve}, -}; - -#[tauri::command] -pub async fn model_nostr_profile_add( - state: tauri::State<'_, Radroots>, - opts: INostrProfileAdd, -) -> Result<INostrProfileAddResolve, String> { - match lib_model_nostr_profile_add(&state.db, opts).await { - Ok(result) => Ok(result), - Err(e) => { - println!("ERROR {}", e); - Err(e.to_string()) - } - } -} - -#[tauri::command] -pub async fn model_nostr_profile_get( - state: tauri::State<'_, Radroots>, - opts: INostrProfileGet, -) -> Result<INostrProfileGetResolve, String> { - match lib_model_nostr_profile_get(&state.db, opts).await { - Ok(result) => Ok(result), - Err(e) => { - println!("ERROR {}", e); - Err(e.to_string()) - } - } -} - -#[tauri::command] -pub async fn model_nostr_profile_delete( - state: tauri::State<'_, Radroots>, - opts: INostrProfileDelete, -) -> Result<INostrProfileDeleteResolve, String> { - match lib_model_nostr_profile_delete(&state.db, opts).await { - Ok(result) => Ok(result), - Err(e) => { - println!("ERROR {}", e); - Err(e.to_string()) - } - } -} - -#[tauri::command] -pub async fn model_nostr_profile_update( - state: tauri::State<'_, Radroots>, - opts: INostrProfileUpdate, -) -> Result<INostrProfileUpdateResolve, String> { - match lib_model_nostr_profile_update(&state.db, opts).await { - Ok(result) => Ok(result), - Err(e) => { - println!("ERROR {}", e); - Err(e.to_string()) - } - } -} diff --git a/crates/tauri/src/models/nostr_profile_relay.rs b/crates/tauri/src/models/nostr_profile_relay.rs @@ -1,45 +0,0 @@ -use crate::radroots::Radroots; -use radroots_core::{ - models::nostr_profile_relay::{lib_model_nostr_profile_relay_set, lib_model_nostr_profile_relay_unset, lib_model_nostr_profile_relay_get_all, INostrProfileRelayRelation, INostrProfileRelayRelationResolve, INostrProfileRelayRelationResolveGetAll}, -}; - -#[tauri::command] -pub async fn model_nostr_profile_relay_set( - state: tauri::State<'_, Radroots>, - opts: INostrProfileRelayRelation, -) -> Result<INostrProfileRelayRelationResolve, String> { - match lib_model_nostr_profile_relay_set(&state.db, opts).await { - Ok(result) => Ok(result), - Err(e) => { - println!("ERROR {}", e); - Err(e.to_string()) - } - } -} - -#[tauri::command] -pub async fn model_nostr_profile_relay_unset( - state: tauri::State<'_, Radroots>, - opts: INostrProfileRelayRelation, -) -> Result<INostrProfileRelayRelationResolve, String> { - match lib_model_nostr_profile_relay_unset(&state.db, opts).await { - Ok(result) => Ok(result), - Err(e) => { - println!("ERROR {}", e); - Err(e.to_string()) - } - } -} - -#[tauri::command] -pub async fn model_nostr_profile_relay_get_all( - state: tauri::State<'_, Radroots>, -) -> Result<INostrProfileRelayRelationResolveGetAll, String> { - match lib_model_nostr_profile_relay_get_all(&state.db).await { - Ok(result) => Ok(result), - Err(e) => { - println!("ERROR {}", e); - Err(e.to_string()) - } - } -} diff --git a/crates/tauri/src/models/nostr_relay.rs b/crates/tauri/src/models/nostr_relay.rs @@ -1,60 +0,0 @@ -use crate::radroots::Radroots; -use radroots_core::{ - models::nostr_relay::{lib_model_nostr_relay_add, INostrRelayAdd, INostrRelayAddResolve, lib_model_nostr_relay_get, INostrRelayGet, INostrRelayGetResolve, lib_model_nostr_relay_delete, INostrRelayDelete, INostrRelayDeleteResolve, lib_model_nostr_relay_update, INostrRelayUpdate, INostrRelayUpdateResolve}, -}; - -#[tauri::command] -pub async fn model_nostr_relay_add( - state: tauri::State<'_, Radroots>, - opts: INostrRelayAdd, -) -> Result<INostrRelayAddResolve, String> { - match lib_model_nostr_relay_add(&state.db, opts).await { - Ok(result) => Ok(result), - Err(e) => { - println!("ERROR {}", e); - Err(e.to_string()) - } - } -} - -#[tauri::command] -pub async fn model_nostr_relay_get( - state: tauri::State<'_, Radroots>, - opts: INostrRelayGet, -) -> Result<INostrRelayGetResolve, String> { - match lib_model_nostr_relay_get(&state.db, opts).await { - Ok(result) => Ok(result), - Err(e) => { - println!("ERROR {}", e); - Err(e.to_string()) - } - } -} - -#[tauri::command] -pub async fn model_nostr_relay_delete( - state: tauri::State<'_, Radroots>, - opts: INostrRelayDelete, -) -> Result<INostrRelayDeleteResolve, String> { - match lib_model_nostr_relay_delete(&state.db, opts).await { - Ok(result) => Ok(result), - Err(e) => { - println!("ERROR {}", e); - Err(e.to_string()) - } - } -} - -#[tauri::command] -pub async fn model_nostr_relay_update( - state: tauri::State<'_, Radroots>, - opts: INostrRelayUpdate, -) -> Result<INostrRelayUpdateResolve, String> { - match lib_model_nostr_relay_update(&state.db, opts).await { - Ok(result) => Ok(result), - Err(e) => { - println!("ERROR {}", e); - Err(e.to_string()) - } - } -} diff --git a/crates/tauri/src/models/trade_product.rs b/crates/tauri/src/models/trade_product.rs @@ -1,60 +0,0 @@ -use crate::radroots::Radroots; -use radroots_core::{ - models::trade_product::{lib_model_trade_product_add, ITradeProductAdd, ITradeProductAddResolve, lib_model_trade_product_get, ITradeProductGet, ITradeProductGetResolve, lib_model_trade_product_delete, ITradeProductDelete, ITradeProductDeleteResolve, lib_model_trade_product_update, ITradeProductUpdate, ITradeProductUpdateResolve}, -}; - -#[tauri::command] -pub async fn model_trade_product_add( - state: tauri::State<'_, Radroots>, - opts: ITradeProductAdd, -) -> Result<ITradeProductAddResolve, String> { - match lib_model_trade_product_add(&state.db, opts).await { - Ok(result) => Ok(result), - Err(e) => { - println!("ERROR {}", e); - Err(e.to_string()) - } - } -} - -#[tauri::command] -pub async fn model_trade_product_get( - state: tauri::State<'_, Radroots>, - opts: ITradeProductGet, -) -> Result<ITradeProductGetResolve, String> { - match lib_model_trade_product_get(&state.db, opts).await { - Ok(result) => Ok(result), - Err(e) => { - println!("ERROR {}", e); - Err(e.to_string()) - } - } -} - -#[tauri::command] -pub async fn model_trade_product_delete( - state: tauri::State<'_, Radroots>, - opts: ITradeProductDelete, -) -> Result<ITradeProductDeleteResolve, String> { - match lib_model_trade_product_delete(&state.db, opts).await { - Ok(result) => Ok(result), - Err(e) => { - println!("ERROR {}", e); - Err(e.to_string()) - } - } -} - -#[tauri::command] -pub async fn model_trade_product_update( - state: tauri::State<'_, Radroots>, - opts: ITradeProductUpdate, -) -> Result<ITradeProductUpdateResolve, String> { - match lib_model_trade_product_update(&state.db, opts).await { - Ok(result) => Ok(result), - Err(e) => { - println!("ERROR {}", e); - Err(e.to_string()) - } - } -} diff --git a/crates/tauri/src/models/trade_product_location.rs b/crates/tauri/src/models/trade_product_location.rs @@ -1,45 +0,0 @@ -use crate::radroots::Radroots; -use radroots_core::{ - models::trade_product_location::{lib_model_trade_product_location_set, lib_model_trade_product_location_unset, lib_model_trade_product_location_get_all, ITradeProductLocationRelation, ITradeProductLocationRelationResolve, ITradeProductLocationRelationResolveGetAll}, -}; - -#[tauri::command] -pub async fn model_trade_product_location_set( - state: tauri::State<'_, Radroots>, - opts: ITradeProductLocationRelation, -) -> Result<ITradeProductLocationRelationResolve, String> { - match lib_model_trade_product_location_set(&state.db, opts).await { - Ok(result) => Ok(result), - Err(e) => { - println!("ERROR {}", e); - Err(e.to_string()) - } - } -} - -#[tauri::command] -pub async fn model_trade_product_location_unset( - state: tauri::State<'_, Radroots>, - opts: ITradeProductLocationRelation, -) -> Result<ITradeProductLocationRelationResolve, String> { - match lib_model_trade_product_location_unset(&state.db, opts).await { - Ok(result) => Ok(result), - Err(e) => { - println!("ERROR {}", e); - Err(e.to_string()) - } - } -} - -#[tauri::command] -pub async fn model_trade_product_location_get_all( - state: tauri::State<'_, Radroots>, -) -> Result<ITradeProductLocationRelationResolveGetAll, String> { - match lib_model_trade_product_location_get_all(&state.db).await { - Ok(result) => Ok(result), - Err(e) => { - println!("ERROR {}", e); - Err(e.to_string()) - } - } -} diff --git a/crates/tauri/src/models/trade_product_media.rs b/crates/tauri/src/models/trade_product_media.rs @@ -1,45 +0,0 @@ -use crate::radroots::Radroots; -use radroots_core::{ - models::trade_product_media::{lib_model_trade_product_media_set, lib_model_trade_product_media_unset, lib_model_trade_product_media_get_all, ITradeProductMediaRelation, ITradeProductMediaRelationResolve, ITradeProductMediaRelationResolveGetAll}, -}; - -#[tauri::command] -pub async fn model_trade_product_media_set( - state: tauri::State<'_, Radroots>, - opts: ITradeProductMediaRelation, -) -> Result<ITradeProductMediaRelationResolve, String> { - match lib_model_trade_product_media_set(&state.db, opts).await { - Ok(result) => Ok(result), - Err(e) => { - println!("ERROR {}", e); - Err(e.to_string()) - } - } -} - -#[tauri::command] -pub async fn model_trade_product_media_unset( - state: tauri::State<'_, Radroots>, - opts: ITradeProductMediaRelation, -) -> Result<ITradeProductMediaRelationResolve, String> { - match lib_model_trade_product_media_unset(&state.db, opts).await { - Ok(result) => Ok(result), - Err(e) => { - println!("ERROR {}", e); - Err(e.to_string()) - } - } -} - -#[tauri::command] -pub async fn model_trade_product_media_get_all( - state: tauri::State<'_, Radroots>, -) -> Result<ITradeProductMediaRelationResolveGetAll, String> { - match lib_model_trade_product_media_get_all(&state.db).await { - Ok(result) => Ok(result), - Err(e) => { - println!("ERROR {}", e); - Err(e.to_string()) - } - } -} diff --git a/crates/tauri/src/radroots.rs b/crates/tauri/src/radroots.rs @@ -1,3 +1,24 @@ +use std::path::PathBuf; + +use radroots_model::types::DatabaseConnection; + +use crate::util; + pub struct Radroots { - pub db: sqlx::Pool<sqlx::Sqlite>, + pub db: DatabaseConnection, + pub data_dir: PathBuf, + pub logs_dir: PathBuf, +} + +impl Radroots { + pub async fn new(data_dir: PathBuf, logs_dir: PathBuf) -> Self { + util::init_keyring(&data_dir).await; + let db = util::init_db(&data_dir).await; + + Self { + db, + data_dir, + logs_dir, + } + } } diff --git a/crates/tauri/src/util.rs b/crates/tauri/src/util.rs @@ -0,0 +1,63 @@ +use std::fs::OpenOptions; +use std::path::PathBuf; + +use radroots_model::types::DatabaseConnection; +use radroots_model::util::db_conn; + +pub async fn init_db(data_dir: &PathBuf) -> DatabaseConnection { + let mut path = data_dir.clone(); + match std::fs::create_dir_all(path.clone()) { + Ok(_) => {} + Err(e) => { + panic!("Error resolving databse directory {}", e); + } + }; + path.push("radroots_db2.sqlite"); + let result = OpenOptions::new().create_new(true).write(true).open(&path); + match result { + Ok(_) => println!("Database file created"), + Err(e) => match e.kind() { + std::io::ErrorKind::AlreadyExists => println!("Database file exists"), + _ => { + panic!("Error creating databse file {}", e); + } + }, + } + let db = db_conn(path).await; + sqlx::migrate!("./migrations/up").run(&db).await.unwrap(); + db +} + +pub async fn init_keyring(data_dir: &PathBuf) -> String { + let mut path = data_dir.clone(); + match std::fs::create_dir_all(path.clone()) { + Ok(_) => {} + Err(err) => { + panic!("Error resolving databse directory {}", err); + } + }; + path.push("keyring_id.txt"); + if path.exists() { + let keyring_read = match std::fs::read_to_string(&path) { + Ok(res) => res, + Err(e) => { + panic!("Error reading keyring_id.txt {}", e); + } + }; + keyring_read.trim().to_string() + } else { + let keyring_new = "test-keyring-id".to_string(); + + match OpenOptions::new().create_new(true).write(true).open(&path) { + Ok(_) => match std::fs::write(&path, &keyring_new) { + Ok(_) => keyring_new, + Err(e) => { + panic!("Error writing keyring_id.txt {}", e); + } + }, + Err(e) => { + panic!("Unexpected error {}", e); + } + } + } +} diff --git a/crates/tauri/tauri.conf.json b/crates/tauri/tauri.conf.json @@ -1,39 +1,26 @@ { "$schema": "../../node_modules/@tauri-apps/cli/config.schema.json", - "productName": "Radroots", - "version": "0.0.1", "identifier": "mobile.radroots.app", + "productName": "radroots", + "mainBinaryName": "radroots", "build": { - "frontendDist": "../../build", + "frontendDist": "../../app/build", "devUrl": "http://localhost:21640", - "beforeDevCommand": "pnpm run dev:serve", - "beforeBuildCommand": "pnpm run build" + "beforeDevCommand": "cd app && pnpm run dev:hmr", + "beforeBuildCommand": "cd app && pnpm run build" }, "app": { "windows": [ { - "title": "Radroots", - "width": 800, - "height": 600, - "resizable": true, - "fullscreen": false, + "title": "radroots", + "resizable": false, + "fullscreen": true, "visible": true } - ], - "security": { - "csp": null, - "assetProtocol": { - "enable": true, - "scope": { - "allow": [ - "**" - ] - } - } - } + ] }, "bundle": { - "active": true, + "active": false, "targets": "all", "icon": [ "icons/32x32.png", diff --git a/package.json b/package.json @@ -1,53 +1,17 @@ { - "name": "app", - "version": "0.0.0", - "private": true, - "license": "GPLv3", - "scripts": { - "build:dev": "vite build --mode development", - "build": "vite build --mode production", - "dev:serve": "vite dev --mode development --debug hmr", - "prod:serve": "vite dev --mode production", - "dev": "", - "sql:wasm": "rsync -u node_modules/sql.js/dist/sql-wasm.wasm static", - "preview": "vite preview", - "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", - "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", - "tauri": "tauri" - }, - "devDependencies": { - "@sveltejs/adapter-static": "^3.0.0", - "@sveltejs/kit": "^2.0.0", - "@sveltejs/vite-plugin-svelte": "^3.0.0", - "@tailwindcss/aspect-ratio": "^0.4.2", - "@tauri-apps/cli": "2.0.4", - "@types/node": "^20.12.7", - "@types/sql.js": "^1.4.9", - "autoprefixer": "^10.4.19", - "daisyui": "^4.10.0", - "postcss": "^8.4.38", - "postcss-import": "^16.1.0", - "svelte": "^4.2.19", - "svelte-check": "^3.8.6", - "tailwindcss": "^3.4.3", - "tailwindcss-hero-patterns": "^0.1.2", - "tslib": "^2.4.1", - "typescript": "^5.3.3", - "vite": "^5.0.11" - }, - "type": "module", - "dependencies": { - "@nostr-dev-kit/ndk": "^2.10.6", - "@nostr-dev-kit/ndk-svelte": "^2.3.1", - "@radroots/client": "workspace:*", - "@radroots/geocoder": "workspace:*", - "@radroots/models": "workspace:*", - "@radroots/svelte-lib": "workspace:*", - "@radroots/svelte-maplibre": "workspace:*", - "@radroots/theme": "workspace:*", - "@radroots/utils": "workspace:*", - "chart.js": "^4.4.5", - "css-paint-polyfill": "^3.4.0", - "sql.js": "^1.11.0" - } + "name": "radroots", + "private": true, + "license": "GPLv3", + "scripts": { + "build": "turbo build --filter=app", + "dev": "turbo dev", + "dev:ios": "pnpm run tauri ios dev 'iPhone 16'", + "tauri": "tauri" + }, + "devDependencies": { + "@tauri-apps/cli": "2.0.4", + "turbo": "^2.3.3", + "typescript": "^5.7.3" + }, + "packageManager": "pnpm@9.12.1" } \ No newline at end of file diff --git a/packages/client b/packages/client @@ -0,0 +1 @@ +Subproject commit a07d7837124bac77079ae851dca1e617f19dec25 diff --git a/packages/eslint b/packages/eslint @@ -0,0 +1 @@ +Subproject commit 8b2309172ef25f6a68f4e66edd8392023aa8043d diff --git a/packages/geocoder b/packages/geocoder @@ -0,0 +1 @@ +Subproject commit 9d2ca40be90d2cbbab76eb6ea34359b690603665 diff --git a/packages/lib-app b/packages/lib-app @@ -0,0 +1 @@ +Subproject commit ed746e2058e386e2e86c7edf3daedcc2c9a0cff6 diff --git a/packages/models b/packages/models @@ -0,0 +1 @@ +Subproject commit 3a1039195100f2f512219c88e17a34e443ff4f49 diff --git a/packages/svelte-maplibre b/packages/svelte-maplibre @@ -0,0 +1 @@ +Subproject commit d177474e5dc309aabd897e2b553df7834de8b958 diff --git a/packages/theme b/packages/theme @@ -0,0 +1 @@ +Subproject commit 24cc559ab8c9f52b14c0b80b8645ee9f48f5acbd diff --git a/packages/tsconfig b/packages/tsconfig @@ -0,0 +1 @@ +Subproject commit c339551d9627b976a3f790b07067a16d743fca08 diff --git a/packages/util b/packages/util @@ -0,0 +1 @@ +Subproject commit 8febde7e6e115d8cad38d268bb55ec0bda49f0db diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml @@ -0,0 +1,7314 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + devDependencies: + '@tauri-apps/cli': + specifier: 2.0.4 + version: 2.0.4 + turbo: + specifier: ^2.3.3 + version: 2.4.0 + typescript: + specifier: ^5.7.3 + version: 5.7.3 + + app: + dependencies: + '@nostr-dev-kit/ndk': + specifier: ^2.10.6 + version: 2.11.0(typescript@5.7.3) + '@nostr-dev-kit/ndk-svelte': + specifier: ^2.3.1 + version: 2.4.0(svelte@5.19.9)(typescript@5.7.3) + '@radroots/client': + specifier: workspace:* + version: link:../packages/client + '@radroots/geocoder': + specifier: workspace:* + version: link:../packages/geocoder + '@radroots/lib-app': + specifier: workspace:* + version: link:../packages/lib-app + '@radroots/models': + specifier: workspace:* + version: link:../packages/models + '@radroots/svelte-maplibre': + specifier: workspace:* + version: link:../packages/svelte-maplibre + '@radroots/theme': + specifier: workspace:* + version: link:../packages/theme + '@radroots/util': + specifier: workspace:* + version: link:../packages/util + chart.js: + specifier: ^4.4.5 + version: 4.4.7 + css-paint-polyfill: + specifier: ^3.4.0 + version: 3.4.0 + devDependencies: + '@sveltejs/adapter-static': + specifier: ^3.0.0 + version: 3.0.8(@sveltejs/kit@2.17.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.19.9)(vite@6.1.0(@types/node@22.13.1)(jiti@1.21.7)(yaml@2.7.0)))(svelte@5.19.9)(vite@6.1.0(@types/node@22.13.1)(jiti@1.21.7)(yaml@2.7.0))) + '@sveltejs/kit': + specifier: ^2.16.0 + version: 2.17.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.19.9)(vite@6.1.0(@types/node@22.13.1)(jiti@1.21.7)(yaml@2.7.0)))(svelte@5.19.9)(vite@6.1.0(@types/node@22.13.1)(jiti@1.21.7)(yaml@2.7.0)) + '@sveltejs/vite-plugin-svelte': + specifier: ^5.0.0 + version: 5.0.3(svelte@5.19.9)(vite@6.1.0(@types/node@22.13.1)(jiti@1.21.7)(yaml@2.7.0)) + autoprefixer: + specifier: ^10.4.20 + version: 10.4.20(postcss@8.5.1) + daisyui: + specifier: ^4.10.0 + version: 4.12.23(postcss@8.5.1) + postcss: + specifier: ^8.5.1 + version: 8.5.1 + postcss-import: + specifier: ^16.1.0 + version: 16.1.0(postcss@8.5.1) + svelte: + specifier: ^5.0.0 + version: 5.19.9 + svelte-check: + specifier: ^4.0.0 + version: 4.1.4(picomatch@4.0.2)(svelte@5.19.9)(typescript@5.7.3) + tailwindcss: + specifier: ^3.4.3 + version: 3.4.17 + typescript: + specifier: ^5.0.0 + version: 5.7.3 + vite: + specifier: ^6.0.0 + version: 6.1.0(@types/node@22.13.1)(jiti@1.21.7)(yaml@2.7.0) + + packages/client: + dependencies: + '@noble/hashes': + specifier: ^1.4.0 + version: 1.7.1 + '@nostr-dev-kit/ndk': + specifier: ^2.10.7 + version: 2.11.0(typescript@5.7.3) + '@radroots/models': + specifier: workspace:* + version: link:../models + '@radroots/util': + specifier: workspace:* + version: link:../util + '@tauri-apps/api': + specifier: 2.0.3 + version: 2.0.3 + '@tauri-apps/plugin-dialog': + specifier: ^2.0.1 + version: 2.2.0 + '@tauri-apps/plugin-fs': + specifier: ^2.0.1 + version: 2.2.0 + '@tauri-apps/plugin-geolocation': + specifier: ^2.0.0 + version: 2.2.3 + '@tauri-apps/plugin-haptics': + specifier: ^2.0.0 + version: 2.2.3 + '@tauri-apps/plugin-http': + specifier: ^2.0.1 + version: 2.3.0 + '@tauri-apps/plugin-log': + specifier: ^2.0.0 + version: 2.2.1 + '@tauri-apps/plugin-notification': + specifier: ^2.0.0 + version: 2.2.1 + '@tauri-apps/plugin-os': + specifier: ^2.0.0 + version: 2.2.0 + '@tauri-apps/plugin-store': + specifier: ^2.1.0 + version: 2.2.0 + nostr-tools: + specifier: ^2.10.4 + version: 2.10.4(typescript@5.7.3) + devDependencies: + '@types/debug': + specifier: ^4.1.12 + version: 4.1.12 + typescript: + specifier: ^5.3.3 + version: 5.7.3 + + packages/eslint: + devDependencies: + '@typescript-eslint/eslint-plugin': + specifier: ^7.1.0 + version: 7.18.0(@typescript-eslint/parser@7.18.0(eslint@9.20.0(jiti@1.21.7))(typescript@5.7.3))(eslint@9.20.0(jiti@1.21.7))(typescript@5.7.3) + '@typescript-eslint/parser': + specifier: ^7.1.0 + version: 7.18.0(eslint@9.20.0(jiti@1.21.7))(typescript@5.7.3) + eslint: + specifier: ^9.3.0 + version: 9.20.0(jiti@1.21.7) + eslint-config-prettier: + specifier: ^9.1.0 + version: 9.1.0(eslint@9.20.0(jiti@1.21.7)) + eslint-config-turbo: + specifier: ^2.0.0 + version: 2.4.0(eslint@9.20.0(jiti@1.21.7))(turbo@2.4.0) + prettier: + specifier: ^3.2.5 + version: 3.5.0 + prettier-plugin-svelte: + specifier: ^3.2.3 + version: 3.3.3(prettier@3.5.0)(svelte@5.19.9) + typescript: + specifier: ^5.3.3 + version: 5.7.3 + + packages/geocoder: + dependencies: + '@radroots/util': + specifier: workspace:* + version: link:../util + sql.js: + specifier: 1.12.0 + version: 1.12.0 + devDependencies: + '@types/sql.js': + specifier: ^1.4.9 + version: 1.4.9 + tsup: + specifier: ^6.2.3 + version: 6.7.0(postcss@8.5.1)(typescript@5.7.3) + typescript: + specifier: ^5.3.3 + version: 5.7.3 + + packages/lib-app: + dependencies: + '@nostr-dev-kit/ndk': + specifier: ^2.11.0 + version: 2.11.0(typescript@5.7.3) + '@nostr-dev-kit/ndk-cache-dexie': + specifier: ^2.5.9 + version: 2.5.9(typescript@5.7.3) + '@nostr-dev-kit/ndk-svelte': + specifier: ^2.4.0 + version: 2.4.0(svelte@5.19.9)(typescript@5.7.3) + '@radroots/svelte-maplibre': + specifier: workspace:* + version: link:../svelte-maplibre + '@radroots/theme': + specifier: workspace:* + version: link:../theme + '@radroots/util': + specifier: workspace:* + version: link:../util + '@sveltekit-i18n/base': + specifier: ^1.3.7 + version: 1.3.7(svelte@5.19.9) + '@sveltekit-i18n/parser-icu': + specifier: ^1.0.8 + version: 1.0.8 + luxon: + specifier: ^3.5.0 + version: 3.5.0 + sveltekit-search-params: + specifier: ^3.0.0 + version: 3.0.0(@sveltejs/kit@2.17.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.19.9)(vite@6.1.0(@types/node@22.13.1)(jiti@1.21.7)(yaml@2.7.0)))(svelte@5.19.9)(vite@6.1.0(@types/node@22.13.1)(jiti@1.21.7)(yaml@2.7.0)))(svelte@5.19.9)(vite@6.1.0(@types/node@22.13.1)(jiti@1.21.7)(yaml@2.7.0)) + devDependencies: + '@sveltejs/adapter-auto': + specifier: ^4.0.0 + version: 4.0.0(@sveltejs/kit@2.17.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.19.9)(vite@6.1.0(@types/node@22.13.1)(jiti@1.21.7)(yaml@2.7.0)))(svelte@5.19.9)(vite@6.1.0(@types/node@22.13.1)(jiti@1.21.7)(yaml@2.7.0))) + '@sveltejs/kit': + specifier: ^2.16.0 + version: 2.17.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.19.9)(vite@6.1.0(@types/node@22.13.1)(jiti@1.21.7)(yaml@2.7.0)))(svelte@5.19.9)(vite@6.1.0(@types/node@22.13.1)(jiti@1.21.7)(yaml@2.7.0)) + '@sveltejs/package': + specifier: ^2.0.0 + version: 2.3.10(svelte@5.19.9)(typescript@5.7.3) + '@sveltejs/vite-plugin-svelte': + specifier: ^5.0.0 + version: 5.0.3(svelte@5.19.9)(vite@6.1.0(@types/node@22.13.1)(jiti@1.21.7)(yaml@2.7.0)) + '@types/node': + specifier: ^22.5.0 + version: 22.13.1 + publint: + specifier: ^0.3.2 + version: 0.3.4 + svelte: + specifier: ^5.0.0 + version: 5.19.9 + svelte-check: + specifier: ^4.0.0 + version: 4.1.4(picomatch@4.0.2)(svelte@5.19.9)(typescript@5.7.3) + typescript: + specifier: ^5.0.0 + version: 5.7.3 + vite: + specifier: ^6.0.0 + version: 6.1.0(@types/node@22.13.1)(jiti@1.21.7)(yaml@2.7.0) + + packages/models: + dependencies: + zod: + specifier: ^3.23.8 + version: 3.24.1 + devDependencies: + '@radroots/util': + specifier: workspace:* + version: link:../util + typescript: + specifier: ^5.3.3 + version: 5.7.3 + + packages/svelte-maplibre: + dependencies: + d3-geo: + specifier: ^3.1.0 + version: 3.1.1 + dequal: + specifier: ^2.0.3 + version: 2.0.3 + just-compare: + specifier: ^2.3.0 + version: 2.3.0 + maplibre-gl: + specifier: ^4.0.0 || ^5.0.1 + version: 5.1.0 + pmtiles: + specifier: ^3.0.3 + version: 3.2.1 + devDependencies: + '@changesets/changelog-github': + specifier: ^0.5.0 + version: 0.5.0 + '@changesets/cli': + specifier: ^2.27.9 + version: 2.27.12 + '@deck.gl/core': + specifier: ~9.1.0 + version: 9.1.0 + '@deck.gl/layers': + specifier: ~9.1.0 + version: 9.1.0(@deck.gl/core@9.1.0)(@loaders.gl/core@4.3.3)(@luma.gl/core@9.1.0)(@luma.gl/engine@9.1.0(@luma.gl/core@9.1.0)(@luma.gl/shadertools@9.1.0(@luma.gl/core@9.1.0))) + '@deck.gl/mapbox': + specifier: ~9.1.0 + version: 9.1.0(@deck.gl/core@9.1.0)(@luma.gl/core@9.1.0) + '@mapbox/mapbox-gl-draw': + specifier: ^1.4.3 + version: 1.5.0 + '@playwright/test': + specifier: ^1.48.2 + version: 1.50.1 + '@skeletonlabs/skeleton': + specifier: ^2.10.3 + version: 2.11.0(svelte@5.19.9) + '@skeletonlabs/tw-plugin': + specifier: ^0.4.0 + version: 0.4.1(tailwindcss@3.4.17) + '@sveltejs/adapter-vercel': + specifier: ^5.4.6 + version: 5.6.1(@sveltejs/kit@2.17.1(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.19.9)(vite@5.4.14(@types/node@22.13.1)))(svelte@5.19.9)(vite@5.4.14(@types/node@22.13.1)))(rollup@4.34.6) + '@sveltejs/kit': + specifier: ^2.8.1 + version: 2.17.1(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.19.9)(vite@5.4.14(@types/node@22.13.1)))(svelte@5.19.9)(vite@5.4.14(@types/node@22.13.1)) + '@sveltejs/package': + specifier: ^2.3.7 + version: 2.3.10(svelte@5.19.9)(typescript@5.7.3) + '@sveltejs/vite-plugin-svelte': + specifier: ^4.0.0 + version: 4.0.4(svelte@5.19.9)(vite@5.4.14(@types/node@22.13.1)) + '@types/d3-color': + specifier: ^3.1.3 + version: 3.1.3 + '@types/d3-geo': + specifier: ^3.1.0 + version: 3.1.0 + '@types/geojson': + specifier: ^7946.0.14 + version: 7946.0.16 + '@types/mapbox__mapbox-gl-draw': + specifier: ^1.4.8 + version: 1.4.8 + '@types/node': + specifier: ^22.9.0 + version: 22.13.1 + '@typescript-eslint/eslint-plugin': + specifier: ^8.14.0 + version: 8.23.0(@typescript-eslint/parser@8.23.0(eslint@9.20.0(jiti@1.21.7))(typescript@5.7.3))(eslint@9.20.0(jiti@1.21.7))(typescript@5.7.3) + '@typescript-eslint/parser': + specifier: ^8.14.0 + version: 8.23.0(eslint@9.20.0(jiti@1.21.7))(typescript@5.7.3) + autoprefixer: + specifier: ^10.4.20 + version: 10.4.20(postcss@8.5.1) + d3-color: + specifier: ^3.1.0 + version: 3.1.0 + dedent: + specifier: ^1.5.3 + version: 1.5.3 + eslint: + specifier: ^9.14.0 + version: 9.20.0(jiti@1.21.7) + eslint-config-prettier: + specifier: ^9.1.0 + version: 9.1.0(eslint@9.20.0(jiti@1.21.7)) + eslint-plugin-svelte: + specifier: ^2.46.0 + version: 2.46.1(eslint@9.20.0(jiti@1.21.7))(svelte@5.19.9) + globals: + specifier: ^15.12.0 + version: 15.14.0 + highlight.js: + specifier: ^11.10.0 + version: 11.11.1 + highlightjs-svelte: + specifier: ^1.0.6 + version: 1.0.6 + husky: + specifier: ^9.1.6 + version: 9.1.7 + just-clamp: + specifier: ^4.2.0 + version: 4.2.0 + lint-staged: + specifier: ^15.2.10 + version: 15.4.3 + postcss: + specifier: ^8.4.49 + version: 8.5.1 + prettier: + specifier: ^3.3.3 + version: 3.5.0 + prettier-plugin-svelte: + specifier: ^3.2.8 + version: 3.3.3(prettier@3.5.0)(svelte@5.19.9) + prettier-plugin-tailwindcss: + specifier: ^0.6.8 + version: 0.6.11(prettier-plugin-svelte@3.3.3(prettier@3.5.0)(svelte@5.19.9))(prettier@3.5.0) + publint: + specifier: ^0.2.12 + version: 0.2.12 + svelte: + specifier: ^5.2.0 + version: 5.19.9 + svelte-check: + specifier: ^4.0.7 + version: 4.1.4(picomatch@4.0.2)(svelte@5.19.9)(typescript@5.7.3) + tailwindcss: + specifier: ^3.4.15 + version: 3.4.17 + tslib: + specifier: ^2.8.1 + version: 2.8.1 + typescript: + specifier: ^5.6.3 + version: 5.7.3 + typescript-eslint: + specifier: ^8.14.0 + version: 8.23.0(eslint@9.20.0(jiti@1.21.7))(typescript@5.7.3) + vite: + specifier: ^5.4.11 + version: 5.4.14(@types/node@22.13.1) + vitest: + specifier: ^2.1.5 + version: 2.1.9(@types/node@22.13.1) + + packages/theme: + devDependencies: + tsup: + specifier: ^6.2.3 + version: 6.7.0(postcss@8.5.1)(typescript@5.7.3) + typescript: + specifier: ^5.3.3 + version: 5.7.3 + + packages/tsconfig: {} + + packages/util: + dependencies: + '@noble/curves': + specifier: ^1.6.0 + version: 1.8.1 + '@noble/hashes': + specifier: ^1.4.0 + version: 1.7.1 + '@nostr-dev-kit/ndk': + specifier: ^2.11.0 + version: 2.11.0(typescript@5.7.3) + '@sveltekit-i18n/base': + specifier: ^1.3.7 + version: 1.3.7(svelte@5.19.9) + '@sveltekit-i18n/parser-icu': + specifier: ^1.0.8 + version: 1.0.8 + convert: + specifier: ^5.5.1 + version: 5.7.0 + geohashing: + specifier: ^2.0.1 + version: 2.0.1 + nostr-geotags: + specifier: ^0.7.1 + version: 0.7.2 + nostr-tools: + specifier: ^2.10.4 + version: 2.10.4(typescript@5.7.3) + uuid: + specifier: ^10.0.0 + version: 10.0.0 + zod: + specifier: ^3.23.8 + version: 3.24.1 + devDependencies: + '@types/ngeohash': + specifier: 0.6.8 + version: 0.6.8 + '@types/node': + specifier: ^22.13.1 + version: 22.13.1 + '@types/uuid': + specifier: ^10.0.0 + version: 10.0.0 + tsup: + specifier: ^6.2.3 + version: 6.7.0(postcss@8.5.1)(typescript@5.7.3) + typescript: + specifier: ^5.3.3 + version: 5.7.3 + +packages: + + '@alloc/quick-lru@5.2.0': + resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} + engines: {node: '>=10'} + + '@ampproject/remapping@2.3.0': + resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} + engines: {node: '>=6.0.0'} + + '@babel/runtime@7.26.7': + resolution: {integrity: sha512-AOPI3D+a8dXnja+iwsUqGRjr1BbZIe771sXdapOtYI531gSqpi92vXivKcq2asu/DFpdl1ceFAKZyRzK2PCVcQ==} + engines: {node: '>=6.9.0'} + + '@changesets/apply-release-plan@7.0.8': + resolution: {integrity: sha512-qjMUj4DYQ1Z6qHawsn7S71SujrExJ+nceyKKyI9iB+M5p9lCL55afuEd6uLBPRpLGWQwkwvWegDHtwHJb1UjpA==} + + '@changesets/assemble-release-plan@6.0.5': + resolution: {integrity: sha512-IgvBWLNKZd6k4t72MBTBK3nkygi0j3t3zdC1zrfusYo0KpdsvnDjrMM9vPnTCLCMlfNs55jRL4gIMybxa64FCQ==} + + '@changesets/changelog-git@0.2.0': + resolution: {integrity: sha512-bHOx97iFI4OClIT35Lok3sJAwM31VbUM++gnMBV16fdbtBhgYu4dxsphBF/0AZZsyAHMrnM0yFcj5gZM1py6uQ==} + + '@changesets/changelog-github@0.5.0': + resolution: {integrity: sha512-zoeq2LJJVcPJcIotHRJEEA2qCqX0AQIeFE+L21L8sRLPVqDhSXY8ZWAt2sohtBpFZkBwu+LUwMSKRr2lMy3LJA==} + + '@changesets/cli@2.27.12': + resolution: {integrity: sha512-9o3fOfHYOvBnyEn0mcahB7wzaA3P4bGJf8PNqGit5PKaMEFdsRixik+txkrJWd2VX+O6wRFXpxQL8j/1ANKE9g==} + hasBin: true + + '@changesets/config@3.0.5': + resolution: {integrity: sha512-QyXLSSd10GquX7hY0Mt4yQFMEeqnO5z/XLpbIr4PAkNNoQNKwDyiSrx4yd749WddusH1v3OSiA0NRAYmH/APpQ==} + + '@changesets/errors@0.2.0': + resolution: {integrity: sha512-6BLOQUscTpZeGljvyQXlWOItQyU71kCdGz7Pi8H8zdw6BI0g3m43iL4xKUVPWtG+qrrL9DTjpdn8eYuCQSRpow==} + + '@changesets/get-dependents-graph@2.1.2': + resolution: {integrity: sha512-sgcHRkiBY9i4zWYBwlVyAjEM9sAzs4wYVwJUdnbDLnVG3QwAaia1Mk5P8M7kraTOZN+vBET7n8KyB0YXCbFRLQ==} + + '@changesets/get-github-info@0.6.0': + resolution: {integrity: sha512-v/TSnFVXI8vzX9/w3DU2Ol+UlTZcu3m0kXTjTT4KlAdwSvwutcByYwyYn9hwerPWfPkT2JfpoX0KgvCEi8Q/SA==} + + '@changesets/get-release-plan@4.0.6': + resolution: {integrity: sha512-FHRwBkY7Eili04Y5YMOZb0ezQzKikTka4wL753vfUA5COSebt7KThqiuCN9BewE4/qFGgF/5t3AuzXx1/UAY4w==} + + '@changesets/get-version-range-type@0.4.0': + resolution: {integrity: sha512-hwawtob9DryoGTpixy1D3ZXbGgJu1Rhr+ySH2PvTLHvkZuQ7sRT4oQwMh0hbqZH1weAooedEjRsbrWcGLCeyVQ==} + + '@changesets/git@3.0.2': + resolution: {integrity: sha512-r1/Kju9Y8OxRRdvna+nxpQIsMsRQn9dhhAZt94FLDeu0Hij2hnOozW8iqnHBgvu+KdnJppCveQwK4odwfw/aWQ==} + + '@changesets/logger@0.1.1': + resolution: {integrity: sha512-OQtR36ZlnuTxKqoW4Sv6x5YIhOmClRd5pWsjZsddYxpWs517R0HkyiefQPIytCVh4ZcC5x9XaG8KTdd5iRQUfg==} + + '@changesets/parse@0.4.0': + resolution: {integrity: sha512-TS/9KG2CdGXS27S+QxbZXgr8uPsP4yNJYb4BC2/NeFUj80Rni3TeD2qwWmabymxmrLo7JEsytXH1FbpKTbvivw==} + + '@changesets/pre@2.0.1': + resolution: {integrity: sha512-vvBJ/If4jKM4tPz9JdY2kGOgWmCowUYOi5Ycv8dyLnEE8FgpYYUo1mgJZxcdtGGP3aG8rAQulGLyyXGSLkIMTQ==} + + '@changesets/read@0.6.2': + resolution: {integrity: sha512-wjfQpJvryY3zD61p8jR87mJdyx2FIhEcdXhKUqkja87toMrP/3jtg/Yg29upN+N4Ckf525/uvV7a4tzBlpk6gg==} + + '@changesets/should-skip-package@0.1.1': + resolution: {integrity: sha512-H9LjLbF6mMHLtJIc/eHR9Na+MifJ3VxtgP/Y+XLn4BF7tDTEN1HNYtH6QMcjP1uxp9sjaFYmW8xqloaCi/ckTg==} + + '@changesets/types@4.1.0': + resolution: {integrity: sha512-LDQvVDv5Kb50ny2s25Fhm3d9QSZimsoUGBsUioj6MC3qbMUCuC8GPIvk/M6IvXx3lYhAs0lwWUQLb+VIEUCECw==} + + '@changesets/types@6.0.0': + resolution: {integrity: sha512-b1UkfNulgKoWfqyHtzKS5fOZYSJO+77adgL7DLRDr+/7jhChN+QcHnbjiQVOz/U+Ts3PGNySq7diAItzDgugfQ==} + + '@changesets/write@0.3.2': + resolution: {integrity: sha512-kDxDrPNpUgsjDbWBvUo27PzKX4gqeKOlhibaOXDJA6kuBisGqNHv/HwGJrAu8U/dSf8ZEFIeHIPtvSlZI1kULw==} + + '@deck.gl/core@9.1.0': + resolution: {integrity: sha512-leocNGky9jZ0HUk8xm+HH4f+EL86PKiG8O4REpw4l/K1UbMssekR/L2moYwt7Bx98jSIibcUbhDWOBatKAUaTA==} + + '@deck.gl/layers@9.1.0': + resolution: {integrity: sha512-0GWZyHk5G48avEuGRbzHk60E0aNpo5h/3lSpzl0BSyqZnVTqsGa1jLJvjS17eaEkR51KIhUBf6NSCyGK1Tp22g==} + peerDependencies: + '@deck.gl/core': ^9.1.0 + '@loaders.gl/core': ^4.2.0 + '@luma.gl/core': ^9.1.0 + '@luma.gl/engine': ^9.1.0 + + '@deck.gl/mapbox@9.1.0': + resolution: {integrity: sha512-4ARArlKJ26QLzmDVcdzLGUL2hZnI7DP7Yh/zfwE43oadWE2zupFHU0GZprE/WA33VLZUK0G79Vhx6BBbaC6ksQ==} + peerDependencies: + '@deck.gl/core': ^9.1.0 + '@luma.gl/core': ^9.1.0 + + '@esbuild/aix-ppc64@0.21.5': + resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [aix] + + '@esbuild/aix-ppc64@0.24.2': + resolution: {integrity: sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.17.19': + resolution: {integrity: sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm64@0.21.5': + resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm64@0.24.2': + resolution: {integrity: sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.17.19': + resolution: {integrity: sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + + '@esbuild/android-arm@0.21.5': + resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + + '@esbuild/android-arm@0.24.2': + resolution: {integrity: sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.17.19': + resolution: {integrity: sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + + '@esbuild/android-x64@0.21.5': + resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + + '@esbuild/android-x64@0.24.2': + resolution: {integrity: sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.17.19': + resolution: {integrity: sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-arm64@0.21.5': + resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-arm64@0.24.2': + resolution: {integrity: sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.17.19': + resolution: {integrity: sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + + '@esbuild/darwin-x64@0.21.5': + resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + + '@esbuild/darwin-x64@0.24.2': + resolution: {integrity: sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.17.19': + resolution: {integrity: sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-arm64@0.21.5': + resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-arm64@0.24.2': + resolution: {integrity: sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.17.19': + resolution: {integrity: sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.21.5': + resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.24.2': + resolution: {integrity: sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.17.19': + resolution: {integrity: sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm64@0.21.5': + resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm64@0.24.2': + resolution: {integrity: sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.17.19': + resolution: {integrity: sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-arm@0.21.5': + resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-arm@0.24.2': + resolution: {integrity: sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.17.19': + resolution: {integrity: sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-ia32@0.21.5': + resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-ia32@0.24.2': + resolution: {integrity: sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.17.19': + resolution: {integrity: sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-loong64@0.21.5': + resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-loong64@0.24.2': + resolution: {integrity: sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.17.19': + resolution: {integrity: sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-mips64el@0.21.5': + resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-mips64el@0.24.2': + resolution: {integrity: sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.17.19': + resolution: {integrity: sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-ppc64@0.21.5': + resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-ppc64@0.24.2': + resolution: {integrity: sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.17.19': + resolution: {integrity: sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-riscv64@0.21.5': + resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-riscv64@0.24.2': + resolution: {integrity: sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.17.19': + resolution: {integrity: sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-s390x@0.21.5': + resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-s390x@0.24.2': + resolution: {integrity: sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.17.19': + resolution: {integrity: sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + + '@esbuild/linux-x64@0.21.5': + resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + + '@esbuild/linux-x64@0.24.2': + resolution: {integrity: sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.24.2': + resolution: {integrity: sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.17.19': + resolution: {integrity: sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.21.5': + resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.24.2': + resolution: {integrity: sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.24.2': + resolution: {integrity: sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.17.19': + resolution: {integrity: sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.21.5': + resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.24.2': + resolution: {integrity: sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/sunos-x64@0.17.19': + resolution: {integrity: sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + + '@esbuild/sunos-x64@0.21.5': + resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + + '@esbuild/sunos-x64@0.24.2': + resolution: {integrity: sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.17.19': + resolution: {integrity: sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-arm64@0.21.5': + resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-arm64@0.24.2': + resolution: {integrity: sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.17.19': + resolution: {integrity: sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-ia32@0.21.5': + resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-ia32@0.24.2': + resolution: {integrity: sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.17.19': + resolution: {integrity: sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + + '@esbuild/win32-x64@0.21.5': + resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + + '@esbuild/win32-x64@0.24.2': + resolution: {integrity: sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@eslint-community/eslint-utils@4.4.1': + resolution: {integrity: sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.1': + resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/config-array@0.19.2': + resolution: {integrity: sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.10.0': + resolution: {integrity: sha512-gFHJ+xBOo4G3WRlR1e/3G8A6/KZAH6zcE/hkLRCZTi/B9avAG365QhFA8uOGzTMqgTghpn7/fSnscW++dpMSAw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.11.0': + resolution: {integrity: sha512-DWUB2pksgNEb6Bz2fggIy1wh6fGgZP4Xyy/Mt0QZPiloKKXerbqq9D3SBQTlCRYOrcRPu4vuz+CGjwdfqxnoWA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/eslintrc@3.2.0': + resolution: {integrity: sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/js@9.20.0': + resolution: {integrity: sha512-iZA07H9io9Wn836aVTytRaNqh00Sad+EamwOVJT12GTLw1VGMFV/4JaME+JjLtr9fiGaoWgYnS54wrfWsSs4oQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/object-schema@2.1.6': + resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/plugin-kit@0.2.5': + resolution: {integrity: sha512-lB05FkqEdUg2AA0xEbUz0SnkXT1LcCTa438W4IWTUh4hdOnVbQyOJ81OrDXsJk/LSiJHubgGEFoR5EHq1NsH1A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@formatjs/ecma402-abstract@2.3.3': + resolution: {integrity: sha512-pJT1OkhplSmvvr6i3CWTPvC/FGC06MbN5TNBfRO6Ox62AEz90eMq+dVvtX9Bl3jxCEkS0tATzDarRZuOLw7oFg==} + + '@formatjs/fast-memoize@2.2.6': + resolution: {integrity: sha512-luIXeE2LJbQnnzotY1f2U2m7xuQNj2DA8Vq4ce1BY9ebRZaoPB1+8eZ6nXpLzsxuW5spQxr7LdCg+CApZwkqkw==} + + '@formatjs/icu-messageformat-parser@2.11.1': + resolution: {integrity: sha512-o0AhSNaOfKoic0Sn1GkFCK4MxdRsw7mPJ5/rBpIqdvcC7MIuyUSW8WChUEvrK78HhNpYOgqCQbINxCTumJLzZA==} + + '@formatjs/icu-skeleton-parser@1.8.13': + resolution: {integrity: sha512-N/LIdTvVc1TpJmMt2jVg0Fr1F7Q1qJPdZSCs19unMskCmVQ/sa0H9L8PWt13vq+gLdLg1+pPsvBLydL1Apahjg==} + + '@formatjs/intl-localematcher@0.6.0': + resolution: {integrity: sha512-4rB4g+3hESy1bHSBG3tDFaMY2CH67iT7yne1e+0CLTsGLDcmoEWWpJjjpWVaYgYfYuohIRuo0E+N536gd2ZHZA==} + + '@humanfs/core@0.19.1': + resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.6': + resolution: {integrity: sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==} + engines: {node: '>=18.18.0'} + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/retry@0.3.1': + resolution: {integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==} + engines: {node: '>=18.18'} + + '@humanwhocodes/retry@0.4.1': + resolution: {integrity: sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==} + engines: {node: '>=18.18'} + + '@isaacs/cliui@8.0.2': + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + + '@isaacs/fs-minipass@4.0.1': + resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} + engines: {node: '>=18.0.0'} + + '@jridgewell/gen-mapping@0.3.8': + resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==} + engines: {node: '>=6.0.0'} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/set-array@1.2.1': + resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.0': + resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} + + '@jridgewell/trace-mapping@0.3.25': + resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + + '@kurkle/color@0.3.4': + resolution: {integrity: sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==} + + '@loaders.gl/core@4.3.3': + resolution: {integrity: sha512-RaQ3uNg4ZaVqDRgvJ2CjaOjeeHdKvbKuzFFgbGnflVB9is5bu+h3EKc3Jke7NGVvLBsZ6oIXzkwHijVsMfxv8g==} + + '@loaders.gl/images@4.3.3': + resolution: {integrity: sha512-s4InjIXqEu0T7anZLj4OBUuDBt2BNnAD0GLzSexSkBfQZfpXY0XJNl4mMf5nUKb5NDfXhIKIqv8y324US+I28A==} + peerDependencies: + '@loaders.gl/core': ^4.3.0 + + '@loaders.gl/loader-utils@4.3.3': + resolution: {integrity: sha512-8erUIwWLiIsZX36fFa/seZsfTsWlLk72Sibh/YZJrPAefuVucV4mGGzMBZ96LE2BUfJhadn250eio/59TUFbNw==} + peerDependencies: + '@loaders.gl/core': ^4.3.0 + + '@loaders.gl/schema@4.3.3': + resolution: {integrity: sha512-zacc9/8je+VbuC6N/QRfiTjRd+BuxsYlddLX1u5/X/cg9s36WZZBlU1oNKUgTYe8eO6+qLyYx77yi+9JbbEehw==} + peerDependencies: + '@loaders.gl/core': ^4.3.0 + + '@loaders.gl/worker-utils@4.3.3': + resolution: {integrity: sha512-eg45Ux6xqsAfqPUqJkhmbFZh9qfmYuPfA+34VcLtfeXIwAngeP6o4SrTmm9LWLGUKiSh47anCEV1p7borDgvGQ==} + peerDependencies: + '@loaders.gl/core': ^4.3.0 + + '@luma.gl/constants@9.1.0': + resolution: {integrity: sha512-BIkRHF36eE1FoghbEKzBjbs7+tX6RUH7gI7ZFKzVJEgXvT6xg12HM7uk+6L54fR/rUxEMjgL+uRzIxprCOGjOg==} + + '@luma.gl/core@9.1.0': + resolution: {integrity: sha512-HkcqDlxal6gOP7Y6KTRcEjnPuxSFMy+oJYfk623TGIxrEbN3x5uLqvbNgqLMXhV60WWq5Fj0LG1gHs1NyJHrLg==} + + '@luma.gl/engine@9.1.0': + resolution: {integrity: sha512-fKa4XqUqS/wmhAPlmkemjJ6YZM3QEzRWX1bZXtVCsydZOun8KCVZsSMpCj1W1+cpoAOBVIqvBqZFF8fZClj5XQ==} + peerDependencies: + '@luma.gl/core': ^9.1.0-beta.1 + '@luma.gl/shadertools': ^9.1.0-beta.1 + + '@luma.gl/shadertools@9.1.0': + resolution: {integrity: sha512-BRDKnf2g+Xq86f1OK00F2PA2QbmkcKiM8HJ/Iw8wZB3DvPu2jBKBaboHmEoo6gxq46P32vFGyvxso8umai5eJw==} + peerDependencies: + '@luma.gl/core': ^9.1.0-beta.1 + + '@luma.gl/webgl@9.1.0': + resolution: {integrity: sha512-dTftLUfOnW6F9vYOl1ZvO2I28OYFdiqHkN7BpPd+8GPzepFT8OtEZwbcb/JjF9TsVhaeLyl1oDckQg2ckre3sw==} + peerDependencies: + '@luma.gl/core': ^9.1.0-alpha.1 + + '@manypkg/find-root@1.1.0': + resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==} + + '@manypkg/get-packages@1.1.3': + resolution: {integrity: sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A==} + + '@mapbox/geojson-area@0.2.2': + resolution: {integrity: sha512-bBqqFn1kIbLBfn7Yq1PzzwVkPYQr9lVUeT8Dhd0NL5n76PBuXzOcuLV7GOSbEB1ia8qWxH4COCvFpziEu/yReA==} + + '@mapbox/geojson-normalize@0.0.1': + resolution: {integrity: sha512-82V7YHcle8lhgIGqEWwtXYN5cy0QM/OHq3ypGhQTbvHR57DF0vMHMjjVSQKFfVXBe/yWCBZTyOuzvK7DFFnx5Q==} + hasBin: true + + '@mapbox/geojson-rewind@0.5.2': + resolution: {integrity: sha512-tJaT+RbYGJYStt7wI3cq4Nl4SXxG8W7JDG5DMJu97V25RnbNg3QtQtf+KD+VLjNpWKYsRvXDNmNrBgEETr1ifA==} + hasBin: true + + '@mapbox/jsonlint-lines-primitives@2.0.2': + resolution: {integrity: sha512-rY0o9A5ECsTQRVhv7tL/OyDpGAoUB4tTvLiW1DSzQGq4bvTPhNw1VpSNjDJc5GFZ2XuyOtSWSVN05qOtcD71qQ==} + engines: {node: '>= 0.6'} + + '@mapbox/mapbox-gl-draw@1.5.0': + resolution: {integrity: sha512-uchQbTa8wiv6GWWTbxW1g5b8H6VySz4t91SmduNH6jjWinPze7cjcmsPUEzhySXsYpYr2/50gRJLZz3bx7O88A==} + engines: {node: ^18.0.0 || >=20.0.0} + + '@mapbox/mapbox-gl-supported@3.0.0': + resolution: {integrity: sha512-2XghOwu16ZwPJLOFVuIOaLbN0iKMn867evzXFyf0P22dqugezfJwLmdanAgU25ITvz1TvOfVP4jsDImlDJzcWg==} + + '@mapbox/node-pre-gyp@2.0.0': + resolution: {integrity: sha512-llMXd39jtP0HpQLVI37Bf1m2ADlEb35GYSh1SDSLsBhR+5iCxiNGlT31yqbNtVHygHAtMy6dWFERpU2JgufhPg==} + engines: {node: '>=18'} + hasBin: true + + '@mapbox/point-geometry@0.1.0': + resolution: {integrity: sha512-6j56HdLTwWGO0fJPlrZtdU/B13q8Uwmo18Ck2GnGgN9PCFyKTZ3UbXeEdRFh18i9XQ92eH2VdtpJHpBD3aripQ==} + + '@mapbox/point-geometry@1.1.0': + resolution: {integrity: sha512-YGcBz1cg4ATXDCM/71L9xveh4dynfGmcLDqufR+nQQy3fKwsAZsWd/x4621/6uJaeB9mwOHE6hPeDgXz9uViUQ==} + + '@mapbox/tiny-sdf@2.0.6': + resolution: {integrity: sha512-qMqa27TLw+ZQz5Jk+RcwZGH7BQf5G/TrutJhspsca/3SHwmgKQ1iq+d3Jxz5oysPVYTGP6aXxCo5Lk9Er6YBAA==} + + '@mapbox/unitbezier@0.0.1': + resolution: {integrity: sha512-nMkuDXFv60aBr9soUG5q+GvZYL+2KZHVvsqFCzqnkGEf46U2fvmytHaEVc1/YZbiLn8X+eR3QzX1+dwDO1lxlw==} + + '@mapbox/vector-tile@1.3.1': + resolution: {integrity: sha512-MCEddb8u44/xfQ3oD+Srl/tNcQoqTw3goGk2oLsrFxOTc3dUp+kAnby3PvAeeBYSMSjSPD1nd1AJA6W49WnoUw==} + + '@mapbox/whoots-js@3.1.0': + resolution: {integrity: sha512-Es6WcD0nO5l+2BOQS4uLfNPYQaNDfbot3X1XUoloz+x0mPDS3eeORZJl06HXjwBG1fOGwCRnzK88LMdxKRrd6Q==} + engines: {node: '>=6.0.0'} + + '@maplibre/maplibre-gl-style-spec@23.1.0': + resolution: {integrity: sha512-R6/ihEuC5KRexmKIYkWqUv84Gm+/QwsOUgHyt1yy2XqCdGdLvlBWVWIIeTZWN4NGdwmY6xDzdSGU2R9oBLNg2w==} + hasBin: true + + '@math.gl/core@4.1.0': + resolution: {integrity: sha512-FrdHBCVG3QdrworwrUSzXIaK+/9OCRLscxI2OUy6sLOHyHgBMyfnEGs99/m3KNvs+95BsnQLWklVfpKfQzfwKA==} + + '@math.gl/polygon@4.1.0': + resolution: {integrity: sha512-YA/9PzaCRHbIP5/0E9uTYrqe+jsYTQoqoDWhf6/b0Ixz8bPZBaGDEafLg3z7ffBomZLacUty9U3TlPjqMtzPjA==} + + '@math.gl/sun@4.1.0': + resolution: {integrity: sha512-i3q6OCBLSZ5wgZVhXg+X7gsjY/TUtuFW/2KBiq/U1ypLso3S4sEykoU/MGjxUv1xiiGtr+v8TeMbO1OBIh/HmA==} + + '@math.gl/types@4.1.0': + resolution: {integrity: sha512-clYZdHcmRvMzVK5fjeDkQlHUzXQSNdZ7s4xOqC3nJPgz4C/TZkUecTo9YS4PruZqtDda/ag4erndP0MIn40dGA==} + + '@math.gl/web-mercator@4.1.0': + resolution: {integrity: sha512-HZo3vO5GCMkXJThxRJ5/QYUYRr3XumfT8CzNNCwoJfinxy5NtKUd7dusNTXn7yJ40UoB8FMIwkVwNlqaiRZZAw==} + + '@noble/ciphers@0.5.3': + resolution: {integrity: sha512-B0+6IIHiqEs3BPMT0hcRmHvEj2QHOLu+uwt+tqDDeVd0oyVzh7BPrDcPjRnV1PV/5LaknXJJQvOuRGR0zQJz+w==} + + '@noble/curves@1.1.0': + resolution: {integrity: sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA==} + + '@noble/curves@1.2.0': + resolution: {integrity: sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==} + + '@noble/curves@1.8.1': + resolution: {integrity: sha512-warwspo+UYUPep0Q+vtdVB4Ugn8GGQj8iyB3gnRWsztmUHTI3S1nhdiWNsPUGL0vud7JlRRk1XEu7Lq1KGTnMQ==} + engines: {node: ^14.21.3 || >=16} + + '@noble/hashes@1.3.1': + resolution: {integrity: sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==} + engines: {node: '>= 16'} + + '@noble/hashes@1.3.2': + resolution: {integrity: sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==} + engines: {node: '>= 16'} + + '@noble/hashes@1.7.1': + resolution: {integrity: sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ==} + engines: {node: ^14.21.3 || >=16} + + '@noble/secp256k1@2.2.3': + resolution: {integrity: sha512-l7r5oEQym9Us7EAigzg30/PQAvynhMt2uoYtT3t26eGDVm9Yii5mZ5jWSWmZ/oSIR2Et0xfc6DXrG0bZ787V3w==} + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@nostr-dev-kit/ndk-cache-dexie@2.5.9': + resolution: {integrity: sha512-SZ5FjON0QPekiC7oW9Hy3JQxG0Oxxtud9LBa1q/A49JV/Qppv1x37nFHxi0XLxEbDgFTNYbaN27Zjfp2NPem2g==} + + '@nostr-dev-kit/ndk-svelte@2.4.0': + resolution: {integrity: sha512-4ucVF7/zCn8nUzJEbKu1dnLGEPyCpLtMepFb0MQdiap+Dzpromp4ZfSto6ukFayIofUWLqsv0x0qruCWEAYy2w==} + peerDependencies: + svelte: '*' + + '@nostr-dev-kit/ndk@2.11.0': + resolution: {integrity: sha512-FKIMtcVsVcquzrC+yir9lOXHCIHmQ3IKEVCMohqEB7N96HjP2qrI9s5utbjI3lkavFNF5tXg1Gp9ODEo7XCfLA==} + engines: {node: '>=16'} + + '@pkgjs/parseargs@0.11.0': + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + + '@playwright/test@1.50.1': + resolution: {integrity: sha512-Jii3aBg+CEDpgnuDxEp/h7BimHcUTDlpEtce89xEumlJ5ef2hqepZ+PWp1DDpYC/VO9fmWVI1IlEaoI5fK9FXQ==} + engines: {node: '>=18'} + hasBin: true + + '@polka/url@1.0.0-next.28': + resolution: {integrity: sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw==} + + '@probe.gl/env@4.1.0': + resolution: {integrity: sha512-5ac2Jm2K72VCs4eSMsM7ykVRrV47w32xOGMvcgqn8vQdEMF9PRXyBGYEV9YbqRKWNKpNKmQJVi4AHM/fkCxs9w==} + + '@probe.gl/log@4.1.0': + resolution: {integrity: sha512-r4gRReNY6f+OZEMgfWEXrAE2qJEt8rX0HsDJQXUBMoc+5H47bdB7f/5HBHAmapK8UydwPKL9wCDoS22rJ0yq7Q==} + + '@probe.gl/stats@4.1.0': + resolution: {integrity: sha512-EI413MkWKBDVNIfLdqbeNSJTs7ToBz/KVGkwi3D+dQrSIkRI2IYbWGAU3xX+D6+CI4ls8ehxMhNpUVMaZggDvQ==} + + '@publint/pack@0.1.1': + resolution: {integrity: sha512-TvCl79Y8v18ZhFGd5mjO1kYPovSBq3+4LVCi5Nfl1JI8fS8i8kXbgQFGwBJRXczim8GlW8c2LMBKTtExYXOy/A==} + engines: {node: '>=18'} + + '@rollup/pluginutils@5.1.4': + resolution: {integrity: sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/rollup-android-arm-eabi@4.34.6': + resolution: {integrity: sha512-+GcCXtOQoWuC7hhX1P00LqjjIiS/iOouHXhMdiDSnq/1DGTox4SpUvO52Xm+div6+106r+TcvOeo/cxvyEyTgg==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.34.6': + resolution: {integrity: sha512-E8+2qCIjciYUnCa1AiVF1BkRgqIGW9KzJeesQqVfyRITGQN+dFuoivO0hnro1DjT74wXLRZ7QF8MIbz+luGaJA==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.34.6': + resolution: {integrity: sha512-z9Ib+OzqN3DZEjX7PDQMHEhtF+t6Mi2z/ueChQPLS/qUMKY7Ybn5A2ggFoKRNRh1q1T03YTQfBTQCJZiepESAg==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.34.6': + resolution: {integrity: sha512-PShKVY4u0FDAR7jskyFIYVyHEPCPnIQY8s5OcXkdU8mz3Y7eXDJPdyM/ZWjkYdR2m0izD9HHWA8sGcXn+Qrsyg==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.34.6': + resolution: {integrity: sha512-YSwyOqlDAdKqs0iKuqvRHLN4SrD2TiswfoLfvYXseKbL47ht1grQpq46MSiQAx6rQEN8o8URtpXARCpqabqxGQ==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.34.6': + resolution: {integrity: sha512-HEP4CgPAY1RxXwwL5sPFv6BBM3tVeLnshF03HMhJYCNc6kvSqBgTMmsEjb72RkZBAWIqiPUyF1JpEBv5XT9wKQ==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.34.6': + resolution: {integrity: sha512-88fSzjC5xeH9S2Vg3rPgXJULkHcLYMkh8faix8DX4h4TIAL65ekwuQMA/g2CXq8W+NJC43V6fUpYZNjaX3+IIg==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.34.6': + resolution: {integrity: sha512-wM4ztnutBqYFyvNeR7Av+reWI/enK9tDOTKNF+6Kk2Q96k9bwhDDOlnCUNRPvromlVXo04riSliMBs/Z7RteEg==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.34.6': + resolution: {integrity: sha512-9RyprECbRa9zEjXLtvvshhw4CMrRa3K+0wcp3KME0zmBe1ILmvcVHnypZ/aIDXpRyfhSYSuN4EPdCCj5Du8FIA==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.34.6': + resolution: {integrity: sha512-qTmklhCTyaJSB05S+iSovfo++EwnIEZxHkzv5dep4qoszUMX5Ca4WM4zAVUMbfdviLgCSQOu5oU8YoGk1s6M9Q==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loongarch64-gnu@4.34.6': + resolution: {integrity: sha512-4Qmkaps9yqmpjY5pvpkfOerYgKNUGzQpFxV6rnS7c/JfYbDSU0y6WpbbredB5cCpLFGJEqYX40WUmxMkwhWCjw==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-powerpc64le-gnu@4.34.6': + resolution: {integrity: sha512-Zsrtux3PuaxuBTX/zHdLaFmcofWGzaWW1scwLU3ZbW/X+hSsFbz9wDIp6XvnT7pzYRl9MezWqEqKy7ssmDEnuQ==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.34.6': + resolution: {integrity: sha512-aK+Zp+CRM55iPrlyKiU3/zyhgzWBxLVrw2mwiQSYJRobCURb781+XstzvA8Gkjg/hbdQFuDw44aUOxVQFycrAg==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.34.6': + resolution: {integrity: sha512-WoKLVrY9ogmaYPXwTH326+ErlCIgMmsoRSx6bO+l68YgJnlOXhygDYSZe/qbUJCSiCiZAQ+tKm88NcWuUXqOzw==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.34.6': + resolution: {integrity: sha512-Sht4aFvmA4ToHd2vFzwMFaQCiYm2lDFho5rPcvPBT5pCdC+GwHG6CMch4GQfmWTQ1SwRKS0dhDYb54khSrjDWw==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.34.6': + resolution: {integrity: sha512-zmmpOQh8vXc2QITsnCiODCDGXFC8LMi64+/oPpPx5qz3pqv0s6x46ps4xoycfUiVZps5PFn1gksZzo4RGTKT+A==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-win32-arm64-msvc@4.34.6': + resolution: {integrity: sha512-3/q1qUsO/tLqGBaD4uXsB6coVGB3usxw3qyeVb59aArCgedSF66MPdgRStUd7vbZOsko/CgVaY5fo2vkvPLWiA==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.34.6': + resolution: {integrity: sha512-oLHxuyywc6efdKVTxvc0135zPrRdtYVjtVD5GUm55I3ODxhU/PwkQFD97z16Xzxa1Fz0AEe4W/2hzRtd+IfpOA==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.34.6': + resolution: {integrity: sha512-0PVwmgzZ8+TZ9oGBmdZoQVXflbvuwzN/HRclujpl4N/q3i+y0lqLw8n1bXA8ru3sApDjlmONaNAuYr38y1Kr9w==} + cpu: [x64] + os: [win32] + + '@scure/base@1.1.1': + resolution: {integrity: sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA==} + + '@scure/base@1.2.4': + resolution: {integrity: sha512-5Yy9czTO47mqz+/J8GM6GIId4umdCk1wc1q8rKERQulIoc8VP9pzDcghv10Tl2E7R96ZUx/PhND3ESYUQX8NuQ==} + + '@scure/bip32@1.3.1': + resolution: {integrity: sha512-osvveYtyzdEVbt3OfwwXFr4P2iVBL5u1Q3q4ONBfDY/UpOuXmOlbgwc1xECEboY8wIays8Yt6onaWMUdUbfl0A==} + + '@scure/bip39@1.2.1': + resolution: {integrity: sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg==} + + '@skeletonlabs/skeleton@2.11.0': + resolution: {integrity: sha512-ORMZYACsIlfKyBx2ZIHBy7zE877t99fxU7LzcY1dveVmn2//+OeqnbQb5RryNILsMR62Tuu1VLnCu01/ByHlbQ==} + peerDependencies: + svelte: ^3.56.0 || ^4.0.0 || ^5.0.0 + + '@skeletonlabs/tw-plugin@0.4.1': + resolution: {integrity: sha512-crrC8BGKis0GNTp7V2HF6mk1ECLUvAxgTTV26LMgt/rV3U6Xd7N7dL5qIL8fE4MTHvpKa1SBsdqsnMbEvATeEg==} + peerDependencies: + tailwindcss: '>=3.0.0' + + '@sveltejs/adapter-auto@4.0.0': + resolution: {integrity: sha512-kmuYSQdD2AwThymQF0haQhM8rE5rhutQXG4LNbnbShwhMO4qQGnKaaTy+88DuNSuoQDi58+thpq8XpHc1+oEKQ==} + peerDependencies: + '@sveltejs/kit': ^2.0.0 + + '@sveltejs/adapter-static@3.0.8': + resolution: {integrity: sha512-YaDrquRpZwfcXbnlDsSrBQNCChVOT9MGuSg+dMAyfsAa1SmiAhrA5jUYUiIMC59G92kIbY/AaQOWcBdq+lh+zg==} + peerDependencies: + '@sveltejs/kit': ^2.0.0 + + '@sveltejs/adapter-vercel@5.6.1': + resolution: {integrity: sha512-ljeLT7p4VQtO9ux4p7MqSkNv+yMw9LcD0K8hl0TK495DhBbaBwOCx9jcp3+/RqGPzsTvTRJSiPRafIfiCjQqjQ==} + peerDependencies: + '@sveltejs/kit': ^2.4.0 + + '@sveltejs/kit@2.17.1': + resolution: {integrity: sha512-CpoGSLqE2MCmcQwA2CWJvOsZ9vW+p/1H3itrFykdgajUNAEyQPbsaSn7fZb6PLHQwe+07njxje9ss0fjZoCAyw==} + engines: {node: '>=18.13'} + hasBin: true + peerDependencies: + '@sveltejs/vite-plugin-svelte': ^3.0.0 || ^4.0.0-next.1 || ^5.0.0 + svelte: ^4.0.0 || ^5.0.0-next.0 + vite: ^5.0.3 || ^6.0.0 + + '@sveltejs/package@2.3.10': + resolution: {integrity: sha512-A4fQacgjJ7C/7oSmxR61/TdB14u6ecyMZ8V9JCR5Lol0bLj/PdJPU4uFodFBsKzO3iFiJMpNTgZZ+zYsYZNpUg==} + engines: {node: ^16.14 || >=18} + hasBin: true + peerDependencies: + svelte: ^3.44.0 || ^4.0.0 || ^5.0.0-next.1 + + '@sveltejs/vite-plugin-svelte-inspector@2.1.0': + resolution: {integrity: sha512-9QX28IymvBlSCqsCll5t0kQVxipsfhFFL+L2t3nTWfXnddYwxBuAEtTtlaVQpRz9c37BhJjltSeY4AJSC03SSg==} + engines: {node: ^18.0.0 || >=20} + peerDependencies: + '@sveltejs/vite-plugin-svelte': ^3.0.0 + svelte: ^4.0.0 || ^5.0.0-next.0 + vite: ^5.0.0 + + '@sveltejs/vite-plugin-svelte-inspector@3.0.1': + resolution: {integrity: sha512-2CKypmj1sM4GE7HjllT7UKmo4Q6L5xFRd7VMGEWhYnZ+wc6AUVU01IBd7yUi6WnFndEwWoMNOd6e8UjoN0nbvQ==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22} + peerDependencies: + '@sveltejs/vite-plugin-svelte': ^4.0.0-next.0||^4.0.0 + svelte: ^5.0.0-next.96 || ^5.0.0 + vite: ^5.0.0 + + '@sveltejs/vite-plugin-svelte-inspector@4.0.1': + resolution: {integrity: sha512-J/Nmb2Q2y7mck2hyCX4ckVHcR5tu2J+MtBEQqpDrrgELZ2uvraQcK/ioCV61AqkdXFgriksOKIceDcQmqnGhVw==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22} + peerDependencies: + '@sveltejs/vite-plugin-svelte': ^5.0.0 + svelte: ^5.0.0 + vite: ^6.0.0 + + '@sveltejs/vite-plugin-svelte@3.1.2': + resolution: {integrity: sha512-Txsm1tJvtiYeLUVRNqxZGKR/mI+CzuIQuc2gn+YCs9rMTowpNZ2Nqt53JdL8KF9bLhAf2ruR/dr9eZCwdTriRA==} + engines: {node: ^18.0.0 || >=20} + peerDependencies: + svelte: ^4.0.0 || ^5.0.0-next.0 + vite: ^5.0.0 + + '@sveltejs/vite-plugin-svelte@4.0.4': + resolution: {integrity: sha512-0ba1RQ/PHen5FGpdSrW7Y3fAMQjrXantECALeOiOdBdzR5+5vPP6HVZRLmZaQL+W8m++o+haIAKq5qT+MiZ7VA==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22} + peerDependencies: + svelte: ^5.0.0-next.96 || ^5.0.0 + vite: ^5.0.0 + + '@sveltejs/vite-plugin-svelte@5.0.3': + resolution: {integrity: sha512-MCFS6CrQDu1yGwspm4qtli0e63vaPCehf6V7pIMP15AsWgMKrqDGCPFF/0kn4SP0ii4aySu4Pa62+fIRGFMjgw==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22} + peerDependencies: + svelte: ^5.0.0 + vite: ^6.0.0 + + '@sveltekit-i18n/base@1.3.7': + resolution: {integrity: sha512-kg1kql1/ro/lIudwFiWrv949Q07gmweln87tflUZR51MNdXXzK4fiJQv5Mw50K/CdQ5BOk/dJ0WOH2vOtBI6yw==} + peerDependencies: + svelte: '>=3.49.0' + + '@sveltekit-i18n/parser-icu@1.0.8': + resolution: {integrity: sha512-/LnvE1EJv+higIxB5cWIV+9neiOe+CfC7VKhpv9mnU35NcZO3yOhEZ8y6F8nHHkMYIABLcqr15yk4hSvmRGWDw==} + + '@tauri-apps/api@2.0.3': + resolution: {integrity: sha512-840qk6n8rbXBXMA5/aAgTYsg5JAubKO0nXw5wf7IzGnUuYKGbB4oFBIZtXOIWy+E0kNTDI3qhq5iqsoMJfwp8g==} + + '@tauri-apps/cli-darwin-arm64@2.0.4': + resolution: {integrity: sha512-siH7rOHobb16rPbc11k64p1mxIpiRCkWmzs2qmL5IX21Gx9K5onI3Tk67Oqpf2uNupbYzItrOttaDT4NHFC7tw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@tauri-apps/cli-darwin-x64@2.0.4': + resolution: {integrity: sha512-zIccfbCoZMfmUpnk6PFCV0keFyfVj1A9XV3Oiiitj/dkTZ9CQvzjhX3XC0XcK4rsTWegfr2PjSrK06aiPAROAw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@tauri-apps/cli-linux-arm-gnueabihf@2.0.4': + resolution: {integrity: sha512-fgQqJzefOGWCBNg4yrVA82Rg4s1XQr5K0dc2rCxBhJfa696e8dQ1LDrnWq/AiO5r+uHfVaoQTIUvxxpFicYRSA==} + engines: {node: '>= 10'} + cpu: [arm] + os: [linux] + + '@tauri-apps/cli-linux-arm64-gnu@2.0.4': + resolution: {integrity: sha512-u8wbt5tPA9pI6j+d7jGrfOz9UVCiTp+IYzKNiIqlrDsAjqAUFaNXYHKqOUboeFWEmI4zoCWj6LgpS2OJTQ5FKg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@tauri-apps/cli-linux-arm64-musl@2.0.4': + resolution: {integrity: sha512-hntF1V8e3V1hlrESm93PsghDhf3lA5pbvFrRfYxU1c+fVD/jRXGVw8BH3O1lW8MWwhEg1YdhKk01oAgsuHLuig==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@tauri-apps/cli-linux-x64-gnu@2.0.4': + resolution: {integrity: sha512-Iq1GGJb+oT1T0ZV8izrgf0cBtlzPCJaWcNueRbf1ZXquMf+FSTyQv+/Lo8rq5T6buOIJOH7cAOTuEWWqiCZteg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@tauri-apps/cli-linux-x64-musl@2.0.4': + resolution: {integrity: sha512-9NTk6Pf0bSwXqCBdAA+PDYts9HeHebZzIo8mbRzRyUbER6QngG5HZb9Ka36Z1QWtJjdRy6uxSb4zb/9NuTeHfA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@tauri-apps/cli-win32-arm64-msvc@2.0.4': + resolution: {integrity: sha512-OF2e9oxiBFR8A8wVMOhUx9QGN/I1ZkquWC7gVQBnA56nx9PabJlDT08QBy5UD8USqZFVznnfNr2ehlheQahb3g==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@tauri-apps/cli-win32-ia32-msvc@2.0.4': + resolution: {integrity: sha512-T+hCKB3rFP6q0saHHtR02hm6wr1ZPJ0Mkii3oRTxjPG6BBXoVzHNCYzvdgEGJPTA2sFuAQtJH764NRtNlDMifw==} + engines: {node: '>= 10'} + cpu: [ia32] + os: [win32] + + '@tauri-apps/cli-win32-x64-msvc@2.0.4': + resolution: {integrity: sha512-GVaiI3KWRFLomjJmApHqihhYlkJ+7FqhumhVfBO6Z2tWzZjQyVQgTdNp0kYEuW2WoAYEj0dKY6qd4YM33xYcUA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@tauri-apps/cli@2.0.4': + resolution: {integrity: sha512-Hl9eFXz+O366+6su9PfaSzu2EJdFe1p8K8ghkWmi40dz8VmSE7vsMTaOStD0I71ckSOkh2ICDX7FQTBgjlpjWw==} + engines: {node: '>= 10'} + hasBin: true + + '@tauri-apps/plugin-dialog@2.2.0': + resolution: {integrity: sha512-6bLkYK68zyK31418AK5fNccCdVuRnNpbxquCl8IqgFByOgWFivbiIlvb79wpSXi0O+8k8RCSsIpOquebusRVSg==} + + '@tauri-apps/plugin-fs@2.2.0': + resolution: {integrity: sha512-+08mApuONKI8/sCNEZ6AR8vf5vI9DXD4YfrQ9NQmhRxYKMLVhRW164vdW5BSLmMpuevftpQ2FVoL9EFkfG9Z+g==} + + '@tauri-apps/plugin-geolocation@2.2.3': + resolution: {integrity: sha512-UpeYb7wSGjbxGqTwlb+98pF07AivzrbRQ4y8mBMhDxaJxsUQBeZAPvGAx9ie0eGiTxMNOO4dMHfPfLDgGAX0wA==} + + '@tauri-apps/plugin-haptics@2.2.3': + resolution: {integrity: sha512-tHWAOR0TSOuWIdJ4Fh/4z+L8CvSfHdg5i7XfCqjErPu63PMf+2n856VIGcC6fjF29hf/vq+BiGApWt38n66Gvg==} + + '@tauri-apps/plugin-http@2.3.0': + resolution: {integrity: sha512-pigTvz+zzAqbIhCzRiR1GE98Jw7A03j2V+Eiexr9thBI8VfMiwFQMcbgON51xlwnVaI72LdbYKNajU84im8tlg==} + + '@tauri-apps/plugin-log@2.2.1': + resolution: {integrity: sha512-bOz9w0hhlXLGLc1ZR37GqkXvTqkykl4A3GEKLjRIs0dq3n0BzLyoRDMPcpt7PdUHqaq6WISME+zEX2bqjSbJ2A==} + + '@tauri-apps/plugin-notification@2.2.1': + resolution: {integrity: sha512-QF8Zod6XDhxD6xkD5nU/BjbOpJ6+3gxGCrVULOdLpvMuMSN2Z2IdObV/qgnrEJk1UamUCF1ClQUqNCbk4zTJNQ==} + + '@tauri-apps/plugin-os@2.2.0': + resolution: {integrity: sha512-HszbCdbisMlu5QhCNAN8YIWyz2v33abAWha6+uvV2CKX8P5VSct/y+kEe22JeyqrxCnWlQ3DRx7s49Byg7/0EA==} + + '@tauri-apps/plugin-store@2.2.0': + resolution: {integrity: sha512-hJTRtuJis4w5fW1dkcgftsYxKXK0+DbAqurZ3CURHG5WkAyyZgbxpeYctw12bbzF9ZbZREXZklPq8mocCC3Sgg==} + + '@types/cookie@0.6.0': + resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==} + + '@types/d3-color@3.1.3': + resolution: {integrity: sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==} + + '@types/d3-geo@3.1.0': + resolution: {integrity: sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==} + + '@types/debug@4.1.12': + resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} + + '@types/emscripten@1.40.0': + resolution: {integrity: sha512-MD2JJ25S4tnjnhjWyalMS6K6p0h+zQV6+Ylm+aGbiS8tSn/aHLSGNzBgduj6FB4zH0ax2GRMGYi/8G1uOxhXWA==} + + '@types/estree@1.0.6': + resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} + + '@types/geojson-vt@3.2.5': + resolution: {integrity: sha512-qDO7wqtprzlpe8FfQ//ClPV9xiuoh2nkIgiouIptON9w5jvD/fA4szvP9GBlDVdJ5dldAl0kX/sy3URbWwLx0g==} + + '@types/geojson@7946.0.16': + resolution: {integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/leaflet@1.9.16': + resolution: {integrity: sha512-wzZoyySUxkgMZ0ihJ7IaUIblG8Rdc8AbbZKLneyn+QjYsj5q1QU7TEKYqwTr10BGSzY5LI7tJk9Ifo+mEjdFRw==} + + '@types/mapbox__mapbox-gl-draw@1.4.8': + resolution: {integrity: sha512-700zPikQXfFMB2vtkJdXSROiqS5F19guf6QdYqvlgCdaMxGmdlLITRq6/zFpzfVQDrgpWex5M8vLtbwjZfup8g==} + + '@types/mapbox__point-geometry@0.1.4': + resolution: {integrity: sha512-mUWlSxAmYLfwnRBmgYV86tgYmMIICX4kza8YnE/eIlywGe2XoOxlpVnXWwir92xRLjwyarqwpu2EJKD2pk0IUA==} + + '@types/mapbox__vector-tile@1.3.4': + resolution: {integrity: sha512-bpd8dRn9pr6xKvuEBQup8pwQfD4VUyqO/2deGjfpe6AwC8YRlyEipvefyRJUSiCJTZuCb8Pl1ciVV5ekqJ96Bg==} + + '@types/ms@2.1.0': + resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} + + '@types/ngeohash@0.6.8': + resolution: {integrity: sha512-A90x3HMwE1yXbWCnd0ztHzv8rAQPjwTzX2diYI/6OrWm/3oairDaehw5WPWJFgZ+8+J/OuF99IbipmMa2le6tQ==} + + '@types/node@12.20.55': + resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} + + '@types/node@22.13.1': + resolution: {integrity: sha512-jK8uzQlrvXqEU91UxiK5J7pKHyzgnI1Qnl0QDHIgVGuolJhRb9EEl28Cj9b3rGR8B2lhFCtvIm5os8lFnO/1Ew==} + + '@types/offscreencanvas@2019.7.3': + resolution: {integrity: sha512-ieXiYmgSRXUDeOntE1InxjWyvEelZGP63M+cGuquuRLuIKKT1osnkXjxev9B7d1nXSug5vpunx+gNlbVxMlC9A==} + + '@types/pbf@3.0.5': + resolution: {integrity: sha512-j3pOPiEcWZ34R6a6mN07mUkM4o4Lwf6hPNt8eilOeZhTFbxFXmKhvXl9Y28jotFPaI1bpPDJsbCprUoNke6OrA==} + + '@types/sql.js@1.4.9': + resolution: {integrity: sha512-ep8b36RKHlgWPqjNG9ToUrPiwkhwh0AEzy883mO5Xnd+cL6VBH1EvSjBAAuxLUFF2Vn/moE3Me6v9E1Lo+48GQ==} + + '@types/supercluster@7.1.3': + resolution: {integrity: sha512-Z0pOY34GDFl3Q6hUFYf3HkTwKEE02e7QgtJppBt+beEAxnyOpJua+voGFvxINBHa06GwLFFym7gRPY2SiKIfIA==} + + '@types/uuid@10.0.0': + resolution: {integrity: sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==} + + '@typescript-eslint/eslint-plugin@7.18.0': + resolution: {integrity: sha512-94EQTWZ40mzBc42ATNIBimBEDltSJ9RQHCC8vc/PDbxi4k8dVwUAv4o98dk50M1zB+JGFxp43FP7f8+FP8R6Sw==} + engines: {node: ^18.18.0 || >=20.0.0} + peerDependencies: + '@typescript-eslint/parser': ^7.0.0 + eslint: ^8.56.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/eslint-plugin@8.23.0': + resolution: {integrity: sha512-vBz65tJgRrA1Q5gWlRfvoH+w943dq9K1p1yDBY2pc+a1nbBLZp7fB9+Hk8DaALUbzjqlMfgaqlVPT1REJdkt/w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0 + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.8.0' + + '@typescript-eslint/parser@7.18.0': + resolution: {integrity: sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg==} + engines: {node: ^18.18.0 || >=20.0.0} + peerDependencies: + eslint: ^8.56.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/parser@8.23.0': + resolution: {integrity: sha512-h2lUByouOXFAlMec2mILeELUbME5SZRN/7R9Cw2RD2lRQQY08MWMM+PmVVKKJNK1aIwqTo9t/0CvOxwPbRIE2Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.8.0' + + '@typescript-eslint/scope-manager@7.18.0': + resolution: {integrity: sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==} + engines: {node: ^18.18.0 || >=20.0.0} + + '@typescript-eslint/scope-manager@8.23.0': + resolution: {integrity: sha512-OGqo7+dXHqI7Hfm+WqkZjKjsiRtFUQHPdGMXzk5mYXhJUedO7e/Y7i8AK3MyLMgZR93TX4bIzYrfyVjLC+0VSw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/type-utils@7.18.0': + resolution: {integrity: sha512-XL0FJXuCLaDuX2sYqZUUSOJ2sG5/i1AAze+axqmLnSkNEVMVYLF+cbwlB2w8D1tinFuSikHmFta+P+HOofrLeA==} + engines: {node: ^18.18.0 || >=20.0.0} + peerDependencies: + eslint: ^8.56.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/type-utils@8.23.0': + resolution: {integrity: sha512-iIuLdYpQWZKbiH+RkCGc6iu+VwscP5rCtQ1lyQ7TYuKLrcZoeJVpcLiG8DliXVkUxirW/PWlmS+d6yD51L9jvA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.8.0' + + '@typescript-eslint/types@7.18.0': + resolution: {integrity: sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==} + engines: {node: ^18.18.0 || >=20.0.0} + + '@typescript-eslint/types@8.23.0': + resolution: {integrity: sha512-1sK4ILJbCmZOTt9k4vkoulT6/y5CHJ1qUYxqpF1K/DBAd8+ZUL4LlSCxOssuH5m4rUaaN0uS0HlVPvd45zjduQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@7.18.0': + resolution: {integrity: sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==} + engines: {node: ^18.18.0 || >=20.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/typescript-estree@8.23.0': + resolution: {integrity: sha512-LcqzfipsB8RTvH8FX24W4UUFk1bl+0yTOf9ZA08XngFwMg4Kj8A+9hwz8Cr/ZS4KwHrmo9PJiLZkOt49vPnuvQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <5.8.0' + + '@typescript-eslint/utils@7.18.0': + resolution: {integrity: sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==} + engines: {node: ^18.18.0 || >=20.0.0} + peerDependencies: + eslint: ^8.56.0 + + '@typescript-eslint/utils@8.23.0': + resolution: {integrity: sha512-uB/+PSo6Exu02b5ZEiVtmY6RVYO7YU5xqgzTIVZwTHvvK3HsL8tZZHFaTLFtRG3CsV4A5mhOv+NZx5BlhXPyIA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.8.0' + + '@typescript-eslint/visitor-keys@7.18.0': + resolution: {integrity: sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==} + engines: {node: ^18.18.0 || >=20.0.0} + + '@typescript-eslint/visitor-keys@8.23.0': + resolution: {integrity: sha512-oWWhcWDLwDfu++BGTZcmXWqpwtkwb5o7fxUIGksMQQDSdPW9prsSnfIOZMlsj4vBOSrcnjIUZMiIjODgGosFhQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@vercel/nft@0.29.1': + resolution: {integrity: sha512-6239JJM1V9b3OjvZIjbe+47/hGxMr44FEzlbTErrqzebCaoKAHK+yvahn3gdNacybDUbTxVF8Zuh0vqaeM8aKQ==} + engines: {node: '>=18'} + hasBin: true + + '@vitest/expect@2.1.9': + resolution: {integrity: sha512-UJCIkTBenHeKT1TTlKMJWy1laZewsRIzYighyYiJKZreqtdxSos/S1t+ktRMQWu2CKqaarrkeszJx1cgC5tGZw==} + + '@vitest/mocker@2.1.9': + resolution: {integrity: sha512-tVL6uJgoUdi6icpxmdrn5YNo3g3Dxv+IHJBr0GXHaEdTcw3F+cPKnsXFhli6nO+f/6SDKPHEK1UN+k+TQv0Ehg==} + peerDependencies: + msw: ^2.4.9 + vite: ^5.0.0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/pretty-format@2.1.9': + resolution: {integrity: sha512-KhRIdGV2U9HOUzxfiHmY8IFHTdqtOhIzCpd8WRdJiE7D/HUcZVD0EgQCVjm+Q9gkUXWgBvMmTtZgIG48wq7sOQ==} + + '@vitest/runner@2.1.9': + resolution: {integrity: sha512-ZXSSqTFIrzduD63btIfEyOmNcBmQvgOVsPNPe0jYtESiXkhd8u2erDLnMxmGrDCwHCCHE7hxwRDCT3pt0esT4g==} + + '@vitest/snapshot@2.1.9': + resolution: {integrity: sha512-oBO82rEjsxLNJincVhLhaxxZdEtV0EFHMK5Kmx5sJ6H9L183dHECjiefOAdnqpIgT5eZwT04PoggUnW88vOBNQ==} + + '@vitest/spy@2.1.9': + resolution: {integrity: sha512-E1B35FwzXXTs9FHNK6bDszs7mtydNi5MIfUWpceJ8Xbfb1gBMscAnwLbEu+B44ed6W3XjL9/ehLPHR1fkf1KLQ==} + + '@vitest/utils@2.1.9': + resolution: {integrity: sha512-v0psaMSkNJ3A2NMrUEHFRzJtDPFn+/VWZ5WxImB21T9fjucJRmS7xCS3ppEnARb9y11OAzaD+P2Ps+b+BGX5iQ==} + + abbrev@3.0.0: + resolution: {integrity: sha512-+/kfrslGQ7TNV2ecmQwMJj/B65g5KVq1/L3SGVZ3tCYGqlzFuFCGBZJtMP99wH3NpEUyAjn0zPdPUg0D+DwrOA==} + engines: {node: ^18.17.0 || >=20.5.0} + + acorn-import-attributes@1.9.5: + resolution: {integrity: sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==} + peerDependencies: + acorn: ^8 + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn-typescript@1.4.13: + resolution: {integrity: sha512-xsc9Xv0xlVfwp2o7sQ+GCQ1PgbkdcpWdTzrwXxO3xDMTAywVS3oXVOcOHuRjAPkS4P9b+yc/qNF15460v+jp4Q==} + peerDependencies: + acorn: '>=8.9.0' + + acorn@8.14.0: + resolution: {integrity: sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==} + engines: {node: '>=0.4.0'} + hasBin: true + + agent-base@7.1.3: + resolution: {integrity: sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==} + engines: {node: '>= 14'} + + ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + + ansi-colors@4.1.3: + resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} + engines: {node: '>=6'} + + ansi-escapes@7.0.0: + resolution: {integrity: sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==} + engines: {node: '>=18'} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-regex@6.1.0: + resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==} + engines: {node: '>=12'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansi-styles@6.2.1: + resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} + engines: {node: '>=12'} + + any-promise@1.3.0: + resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} + + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + + arg@5.0.2: + resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} + + argparse@1.0.10: + resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + aria-query@5.3.2: + resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} + engines: {node: '>= 0.4'} + + array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + + async-sema@3.1.1: + resolution: {integrity: sha512-tLRNUXati5MFePdAk8dw7Qt7DpxPB60ofAgn8WRhW6a2rcimZnYBP9oxHiv0OHy+Wz7kPMG+t4LGdt31+4EmGg==} + + autoprefixer@10.4.20: + resolution: {integrity: sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==} + engines: {node: ^10 || ^12 || >=14} + hasBin: true + peerDependencies: + postcss: ^8.1.0 + + axobject-query@4.1.0: + resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} + engines: {node: '>= 0.4'} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + better-path-resolve@1.0.0: + resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==} + engines: {node: '>=4'} + + binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} + engines: {node: '>=8'} + + bindings@1.5.0: + resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==} + + brace-expansion@1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + + brace-expansion@2.0.1: + resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + browserslist@4.24.4: + resolution: {integrity: sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + bufferutil@4.0.9: + resolution: {integrity: sha512-WDtdLmJvAuNNPzByAYpRo2rF1Mmradw6gvWsQKf63476DDXmomT9zUiGypLcG4ibIM67vhAj8jJRdbmEws2Aqw==} + engines: {node: '>=6.14.2'} + + bundle-require@4.2.1: + resolution: {integrity: sha512-7Q/6vkyYAwOmQNRw75x+4yRtZCZJXUDmHHlFdkiV0wgv/reNjtJwpu1jPJ0w2kbEpIM0uoKI3S4/f39dU7AjSA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + peerDependencies: + esbuild: '>=0.17' + + cac@6.7.14: + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} + engines: {node: '>=8'} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + camelcase-css@2.0.1: + resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} + engines: {node: '>= 6'} + + caniuse-lite@1.0.30001699: + resolution: {integrity: sha512-b+uH5BakXZ9Do9iK+CkDmctUSEqZl+SP056vc5usa0PL+ev5OHw003rZXcnjNDv3L8P5j6rwT6C0BPKSikW08w==} + + chai@5.1.2: + resolution: {integrity: sha512-aGtmf24DW6MLHHG5gCx4zaI3uBq3KRtxeVs0DjFH6Z0rDNbsvTxFASFvdj79pxjxZ8/5u3PIiN3IwEIQkiiuPw==} + engines: {node: '>=12'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + chalk@5.4.1: + resolution: {integrity: sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + + chardet@0.7.0: + resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} + + chart.js@4.4.7: + resolution: {integrity: sha512-pwkcKfdzTMAU/+jNosKhNL2bHtJc/sSmYgVbuGTEDhzkrhmyihmP7vUc/5ZK9WopidMDHNe3Wm7jOd/WhuHWuw==} + engines: {pnpm: '>=8'} + + cheap-ruler@4.0.0: + resolution: {integrity: sha512-0BJa8f4t141BYKQyn9NSQt1PguFQXMXwZiA5shfoaBYHAb2fFk2RAX+tiWMoQU+Agtzt3mdt0JtuyshAXqZ+Vw==} + + check-error@2.1.1: + resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} + engines: {node: '>= 16'} + + chokidar@3.6.0: + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} + engines: {node: '>= 8.10.0'} + + chokidar@4.0.3: + resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} + engines: {node: '>= 14.16.0'} + + chownr@3.0.0: + resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} + engines: {node: '>=18'} + + ci-info@3.9.0: + resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} + engines: {node: '>=8'} + + cli-cursor@5.0.0: + resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} + engines: {node: '>=18'} + + cli-truncate@4.0.0: + resolution: {integrity: sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==} + engines: {node: '>=18'} + + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + colorette@2.0.20: + resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} + + commander@13.1.0: + resolution: {integrity: sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==} + engines: {node: '>=18'} + + commander@4.1.1: + resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} + engines: {node: '>= 6'} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + consola@3.4.0: + resolution: {integrity: sha512-EiPU8G6dQG0GFHNR8ljnZFki/8a+cQwEQ+7wpxdChl02Q8HXlwEZWD5lqAF8vC2sEC3Tehr8hy7vErz88LHyUA==} + engines: {node: ^14.18.0 || >=16.10.0} + + convert@5.7.0: + resolution: {integrity: sha512-3j63MvYat3f42sv8vSBZhPn28YMifKlF6x7ym0PwpdYwY0Ej4iRmYzoHF7oaEclHTljFn5v+abQZlJVb8S+tdw==} + + cookie@0.6.0: + resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} + engines: {node: '>= 0.6'} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + css-paint-polyfill@3.4.0: + resolution: {integrity: sha512-B89ymLgMZie41d+QrD//5RWnwykvru5H9Yzd/MMJIgPdVsKB7t4lxuhIAyzy5FnoUbb0MztagNVRe9dI7kMbiA==} + + css-selector-tokenizer@0.8.0: + resolution: {integrity: sha512-Jd6Ig3/pe62/qe5SBPTN8h8LeUg/pT4lLgtavPf7updwwHpvFzxvOQBHYj2LZDMjUnBzgvIUSjRcf6oT5HzHFg==} + + csscolorparser@1.0.3: + resolution: {integrity: sha512-umPSgYwZkdFoUrH5hIq5kf0wPSXiro51nPw0j2K/c83KflkPSTBGMz6NJvMB+07VlL0y7VPo6QJcDjcgKTTm3w==} + + cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + + culori@3.3.0: + resolution: {integrity: sha512-pHJg+jbuFsCjz9iclQBqyL3B2HLCBF71BwVNujUYEvCeQMvV97R59MNK3R2+jgJ3a1fcZgI9B3vYgz8lzr/BFQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + d3-array@3.2.4: + resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==} + engines: {node: '>=12'} + + d3-color@3.1.0: + resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==} + engines: {node: '>=12'} + + d3-geo@3.1.1: + resolution: {integrity: sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==} + engines: {node: '>=12'} + + d@1.0.2: + resolution: {integrity: sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==} + engines: {node: '>=0.12'} + + daisyui@4.12.23: + resolution: {integrity: sha512-EM38duvxutJ5PD65lO/AFMpcw+9qEy6XAZrTpzp7WyaPeO/l+F/Qiq0ECHHmFNcFXh5aVoALY4MGrrxtCiaQCQ==} + engines: {node: '>=16.9.0'} + + dataloader@1.4.0: + resolution: {integrity: sha512-68s5jYdlvasItOJnCuI2Q9s4q98g0pCyL3HrcKJu8KNugUl8ahgmZYg38ysLTgQjjXX3H8CJLkAvWrclWfcalw==} + + debug@2.6.9: + resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@4.4.0: + resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + decimal.js@10.5.0: + resolution: {integrity: sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==} + + dedent-js@1.0.1: + resolution: {integrity: sha512-OUepMozQULMLUmhxS95Vudo0jb0UchLimi3+pQ2plj61Fcy8axbP9hbiD4Sz6DPqn6XG3kfmziVfQ1rSys5AJQ==} + + dedent@1.5.3: + resolution: {integrity: sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==} + peerDependencies: + babel-plugin-macros: ^3.1.0 + peerDependenciesMeta: + babel-plugin-macros: + optional: true + + deep-eql@5.0.2: + resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} + engines: {node: '>=6'} + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + deepmerge@4.3.1: + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} + engines: {node: '>=0.10.0'} + + dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + + detect-indent@6.1.0: + resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} + engines: {node: '>=8'} + + detect-libc@2.0.3: + resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==} + engines: {node: '>=8'} + + devalue@5.1.1: + resolution: {integrity: sha512-maua5KUiapvEwiEAe+XnlZ3Rh0GD+qI1J/nb9vrJc3muPXvcF/8gXYTWF76+5DAqHyDUtOIImEuo0YKE9mshVw==} + + dexie@4.0.11: + resolution: {integrity: sha512-SOKO002EqlvBYYKQSew3iymBoN2EQ4BDw/3yprjh7kAfFzjBYkaMNa/pZvcA7HSWlcKSQb9XhPe3wKyQ0x4A8A==} + + didyoumean@1.2.2: + resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} + + dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + + dlv@1.1.3: + resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} + + dotenv@16.0.3: + resolution: {integrity: sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==} + engines: {node: '>=12'} + + dotenv@8.6.0: + resolution: {integrity: sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==} + engines: {node: '>=10'} + + earcut@2.2.4: + resolution: {integrity: sha512-/pjZsA1b4RPHbeWZQn66SWS8nZZWLQQ23oE3Eam7aroEFGEvwKAsJfZ9ytiEMycfzXWpca4FA9QIOehf7PocBQ==} + + earcut@3.0.1: + resolution: {integrity: sha512-0l1/0gOjESMeQyYaK5IDiPNvFeu93Z/cO0TjZh9eZ1vyCtZnA7KMZ8rQggpsJHIbGSdrqYq9OhuveadOVHCshw==} + + eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + + electron-to-chromium@1.5.96: + resolution: {integrity: sha512-8AJUW6dh75Fm/ny8+kZKJzI1pgoE8bKLZlzDU2W1ENd+DXKJrx7I7l9hb8UWR4ojlnb5OlixMt00QWiYJoVw1w==} + + emoji-regex@10.4.0: + resolution: {integrity: sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + + enquirer@2.4.1: + resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==} + engines: {node: '>=8.6'} + + environment@1.1.0: + resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==} + engines: {node: '>=18'} + + es-module-lexer@1.6.0: + resolution: {integrity: sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==} + + es5-ext@0.10.64: + resolution: {integrity: sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==} + engines: {node: '>=0.10'} + + es6-iterator@2.0.3: + resolution: {integrity: sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==} + + es6-symbol@3.1.4: + resolution: {integrity: sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==} + engines: {node: '>=0.12'} + + esbuild@0.17.19: + resolution: {integrity: sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw==} + engines: {node: '>=12'} + hasBin: true + + esbuild@0.21.5: + resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} + engines: {node: '>=12'} + hasBin: true + + esbuild@0.24.2: + resolution: {integrity: sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==} + engines: {node: '>=18'} + hasBin: true + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + eslint-compat-utils@0.5.1: + resolution: {integrity: sha512-3z3vFexKIEnjHE3zCMRo6fn/e44U7T1khUjg+Hp0ZQMCigh28rALD0nPFBcGZuiLC5rLZa2ubQHDRln09JfU2Q==} + engines: {node: '>=12'} + peerDependencies: + eslint: '>=6.0.0' + + eslint-config-prettier@9.1.0: + resolution: {integrity: sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==} + hasBin: true + peerDependencies: + eslint: '>=7.0.0' + + eslint-config-turbo@2.4.0: + resolution: {integrity: sha512-AiRdy83iwyG4+iMSxXQGUbEClxkGxSlXYH8E2a+0972ao75OWnlDBiiuLMOzDpJubR+QVGC4zonn29AIFCSbFw==} + peerDependencies: + eslint: '>6.6.0' + turbo: '>2.0.0' + + eslint-plugin-svelte@2.46.1: + resolution: {integrity: sha512-7xYr2o4NID/f9OEYMqxsEQsCsj4KaMy4q5sANaKkAb6/QeCjYFxRmDm2S3YC3A3pl1kyPZ/syOx/i7LcWYSbIw==} + engines: {node: ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0-0 || ^9.0.0-0 + svelte: ^3.37.0 || ^4.0.0 || ^5.0.0 + peerDependenciesMeta: + svelte: + optional: true + + eslint-plugin-turbo@2.4.0: + resolution: {integrity: sha512-qCgoRi/OTc1VMxab7+sdKiV1xlkY4qjK9sM+kS7+WogrB1DxLguJSQXvk4HA13SD5VmJsq+8FYOw5q4EUk6Ixg==} + peerDependencies: + eslint: '>6.6.0' + turbo: '>2.0.0' + + eslint-scope@7.2.2: + resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-scope@8.2.0: + resolution: {integrity: sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@4.2.0: + resolution: {integrity: sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint@9.20.0: + resolution: {integrity: sha512-aL4F8167Hg4IvsW89ejnpTwx+B/UQRzJPGgbIOl+4XqffWsahVVsLEWoZvnrVuwpWmnRd7XeXmQI1zlKcFDteA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + esm-env@1.0.0: + resolution: {integrity: sha512-Cf6VksWPsTuW01vU9Mk/3vRue91Zevka5SjyNf3nEpokFRuqt/KjUQoGAwq9qMmhpLTHmXzSIrFRw8zxWzmFBA==} + + esm-env@1.2.2: + resolution: {integrity: sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==} + + esniff@2.0.1: + resolution: {integrity: sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==} + engines: {node: '>=0.10'} + + espree@10.3.0: + resolution: {integrity: sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + espree@9.6.1: + resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + + esquery@1.6.0: + resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} + engines: {node: '>=0.10'} + + esrap@1.4.3: + resolution: {integrity: sha512-Xddc1RsoFJ4z9nR7W7BFaEPIp4UXoeQ0+077UdWLxbafMQFyU79sQJMk7kxNgRwQ9/aVgaKacCHC2pUACGwmYw==} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + event-emitter@0.3.5: + resolution: {integrity: sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==} + + eventemitter3@5.0.1: + resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} + + execa@5.1.1: + resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} + engines: {node: '>=10'} + + execa@8.0.1: + resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} + engines: {node: '>=16.17'} + + expect-type@1.1.0: + resolution: {integrity: sha512-bFi65yM+xZgk+u/KRIpekdSYkTB5W1pEf0Lt8Q8Msh7b+eQ7LXVtIB1Bkm4fvclDEL1b2CZkMhv2mOeF8tMdkA==} + engines: {node: '>=12.0.0'} + + ext@1.7.0: + resolution: {integrity: sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==} + + extendable-error@0.1.7: + resolution: {integrity: sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==} + + external-editor@3.1.0: + resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==} + engines: {node: '>=4'} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-glob@3.3.3: + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} + engines: {node: '>=8.6.0'} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fastparse@1.1.2: + resolution: {integrity: sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==} + + fastq@1.19.0: + resolution: {integrity: sha512-7SFSRCNjBQIZH/xZR3iy5iQYR8aGBE0h3VG6/cwlbrpdciNYBMotQav8c1XI3HjHH+NikUpP53nPdlZSdWmFzA==} + + fdir@6.4.3: + resolution: {integrity: sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + fflate@0.8.2: + resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==} + + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} + + file-uri-to-path@1.0.0: + resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + find-up@4.1.0: + resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} + engines: {node: '>=8'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} + + flatted@3.3.2: + resolution: {integrity: sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==} + + foreground-child@3.3.0: + resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==} + engines: {node: '>=14'} + + fraction.js@4.3.7: + resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} + + fs-extra@7.0.1: + resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} + engines: {node: '>=6 <7 || >=8'} + + fs-extra@8.1.0: + resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} + engines: {node: '>=6 <7 || >=8'} + + fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + + fsevents@2.3.2: + resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + geohashing@2.0.1: + resolution: {integrity: sha512-u0H29yhqHEyBBgwv1GJrvqbD33AnQm2lbWOLNbXLZEWVrCKNoblyiWWnVGeYGaM4NNM7rBBTqvJZhZ/CDfrBVw==} + + geojson-vt@4.0.2: + resolution: {integrity: sha512-AV9ROqlNqoZEIJGfm1ncNjEXfkz2hdFlZf0qkVfmkwdKa8vj7H16YUOT81rJw1rdFhyEDlN2Tds91p/glzbl5A==} + + get-east-asian-width@1.3.0: + resolution: {integrity: sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==} + engines: {node: '>=18'} + + get-stream@6.0.1: + resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} + engines: {node: '>=10'} + + get-stream@8.0.1: + resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} + engines: {node: '>=16'} + + gl-matrix@3.4.3: + resolution: {integrity: sha512-wcCp8vu8FT22BnvKVPjXa/ICBWRq/zjFfdofZy1WSpQZpphblv12/bOQLBC1rMM7SGOFS9ltVmKOHil5+Ml7gA==} + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + glob@10.4.5: + resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} + hasBin: true + + glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + deprecated: Glob versions prior to v9 are no longer supported + + glob@8.1.0: + resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} + engines: {node: '>=12'} + deprecated: Glob versions prior to v9 are no longer supported + + global-prefix@4.0.0: + resolution: {integrity: sha512-w0Uf9Y9/nyHinEk5vMJKRie+wa4kR5hmDbEhGGds/kG1PwGLLHKRoNMeJOyCQjjBkANlnScqgzcFwGHgmgLkVA==} + engines: {node: '>=16'} + + globals@14.0.0: + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} + + globals@15.14.0: + resolution: {integrity: sha512-OkToC372DtlQeje9/zHIo5CT8lRP/FUgEOKBEhU4e0abL7J7CD24fD9ohiLN5hagG/kWCYj4K5oaxxtj2Z0Dig==} + engines: {node: '>=18'} + + globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + graphemer@1.4.0: + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + + grid-index@1.1.0: + resolution: {integrity: sha512-HZRwumpOGUrHyxO5bqKZL0B0GlUpwtCAzZ42sgxUPniu33R1LSFH5yrIcBCHjkctCAh3mtWKcKd9J4vDDdeVHA==} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + highlight.js@11.11.1: + resolution: {integrity: sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==} + engines: {node: '>=12.0.0'} + + highlightjs-svelte@1.0.6: + resolution: {integrity: sha512-aXuBPz8df3sOXg90q8rRcBLyxIR8ozPU39a6tJ2rpJUjjd9brRIr55aC0IccW4gsPhQxZ+B3rFugdXsg9/ckDw==} + + https-proxy-agent@7.0.6: + resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} + engines: {node: '>= 14'} + + human-id@1.0.2: + resolution: {integrity: sha512-UNopramDEhHJD+VR+ehk8rOslwSfByxPIZyJRfV739NDhN5LF1fa1MqnzKm2lGTQRjNrjK19Q5fhkgIfjlVUKw==} + + human-signals@2.1.0: + resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} + engines: {node: '>=10.17.0'} + + human-signals@5.0.0: + resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} + engines: {node: '>=16.17.0'} + + husky@9.1.7: + resolution: {integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==} + engines: {node: '>=18'} + hasBin: true + + iconv-lite@0.4.24: + resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} + engines: {node: '>=0.10.0'} + + ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + + ignore-walk@5.0.1: + resolution: {integrity: sha512-yemi4pMf51WKT7khInJqAvsIGzoqYXblnsz0ql8tM+yi1EKYTY1evX4NAbJrLL/Aanr2HyZeluqU+Oi7MGHokw==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + + import-meta-resolve@4.1.0: + resolution: {integrity: sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw==} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + ini@4.1.3: + resolution: {integrity: sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + internmap@2.0.3: + resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==} + engines: {node: '>=12'} + + intl-messageformat@10.7.15: + resolution: {integrity: sha512-LRyExsEsefQSBjU2p47oAheoKz+EOJxSLDdjOaEjdriajfHsMXOmV/EhMvYSg9bAgCUHasuAC+mcUBe/95PfIg==} + + is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + + is-core-module@2.16.1: + resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} + engines: {node: '>= 0.4'} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-fullwidth-code-point@4.0.0: + resolution: {integrity: sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==} + engines: {node: '>=12'} + + is-fullwidth-code-point@5.0.0: + resolution: {integrity: sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==} + engines: {node: '>=18'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-reference@3.0.3: + resolution: {integrity: sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==} + + is-stream@2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + + is-stream@3.0.0: + resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + is-subdir@1.2.0: + resolution: {integrity: sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw==} + engines: {node: '>=4'} + + is-typedarray@1.0.0: + resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==} + + is-windows@1.0.2: + resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} + engines: {node: '>=0.10.0'} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + isexe@3.1.1: + resolution: {integrity: sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==} + engines: {node: '>=16'} + + iso-3166@4.3.0: + resolution: {integrity: sha512-H4kM/sVbxTjSl9xnQCYOrNWdpN0R8Uz26j1BuXI9E6U+kw5wmd3HyPgr/v4+NCuvV/NcvwTfZxd5XZ4lPKvBNA==} + + jackspeak@3.4.3: + resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + + jiti@1.21.7: + resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==} + hasBin: true + + joycon@3.1.1: + resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} + engines: {node: '>=10'} + + js-yaml@3.14.1: + resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} + hasBin: true + + js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + + json-stringify-pretty-compact@4.0.0: + resolution: {integrity: sha512-3CNZ2DnrpByG9Nqj6Xo8vqbjT4F6N+tb4Gb28ESAZjYZ5yqvmc56J+/kuIwkaAMOyblTQhUW7PxMkUb8Q36N3Q==} + + jsonfile@4.0.0: + resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} + + just-clamp@4.2.0: + resolution: {integrity: sha512-ssHiAxuN7R+VP7jS1XyUP7ju5YC4bTQwQdzYtuZkASTrKe9KsI/X0t+5BJeD6Un6z/dNMDFVNQpQEgUphpXa0w==} + + just-compare@2.3.0: + resolution: {integrity: sha512-6shoR7HDT+fzfL3gBahx1jZG3hWLrhPAf+l7nCwahDdT9XDtosB9kIF0ZrzUp5QY8dJWfQVr5rnsPqsbvflDzg==} + + kdbush@4.0.2: + resolution: {integrity: sha512-WbCVYJ27Sz8zi9Q7Q0xHC+05iwkm3Znipc2XTlrnJbsHMYktW4hPhXUE8Ys1engBrvffoSCqbil1JQAa7clRpA==} + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + kind-of@6.0.3: + resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} + engines: {node: '>=0.10.0'} + + kleur@4.1.5: + resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} + engines: {node: '>=6'} + + known-css-properties@0.35.0: + resolution: {integrity: sha512-a/RAk2BfKk+WFGhhOCAYqSiFLc34k8Mt/6NWRI4joER0EYUzXIcFivjjnoD3+XU1DggLn/tZc3DOAgke7l8a4A==} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + light-bolt11-decoder@3.2.0: + resolution: {integrity: sha512-3QEofgiBOP4Ehs9BI+RkZdXZNtSys0nsJ6fyGeSiAGCBsMwHGUDS/JQlY/sTnWs91A2Nh0S9XXfA8Sy9g6QpuQ==} + + lilconfig@2.1.0: + resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} + engines: {node: '>=10'} + + lilconfig@3.1.3: + resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} + engines: {node: '>=14'} + + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + + lint-staged@15.4.3: + resolution: {integrity: sha512-FoH1vOeouNh1pw+90S+cnuoFwRfUD9ijY2GKy5h7HS3OR7JVir2N2xrsa0+Twc1B7cW72L+88geG5cW4wIhn7g==} + engines: {node: '>=18.12.0'} + hasBin: true + + listr2@8.2.5: + resolution: {integrity: sha512-iyAZCeyD+c1gPyE9qpFu8af0Y+MRtmKOncdGoA2S5EY8iFq99dmmvkNnHiWo+pj0s7yH7l3KPIgee77tKpXPWQ==} + engines: {node: '>=18.0.0'} + + load-tsconfig@0.2.5: + resolution: {integrity: sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + locate-character@3.0.0: + resolution: {integrity: sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==} + + locate-path@5.0.0: + resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} + engines: {node: '>=8'} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + + lodash.sortby@4.7.0: + resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==} + + lodash.startcase@4.4.0: + resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==} + + log-update@6.1.0: + resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==} + engines: {node: '>=18'} + + loupe@3.1.3: + resolution: {integrity: sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==} + + lower-case@2.0.2: + resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} + + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + + luxon@3.5.0: + resolution: {integrity: sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ==} + engines: {node: '>=12'} + + magic-string@0.30.17: + resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} + + mapbox-gl@3.9.4: + resolution: {integrity: sha512-IxfpdyNzjCMzkqj/q5OUamlF1QcS+IFEARteygEgao2B8l8+UF2ahpNRgHT2EpMSE8ma1bq4LKvr+EuJ6gqniw==} + + maplibre-gl@5.1.0: + resolution: {integrity: sha512-6lbf7qAnqAVm1T/vJBMmRtP+g8G/O/Z52IBtWX31SbFj7sEdlrk4YugxJen8IdV/pFjLFnDOw7HiHZl5nYdVjg==} + engines: {node: '>=16.14.0', npm: '>=8.1.0'} + + merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + + mimic-fn@2.1.0: + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} + + mimic-fn@4.0.0: + resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} + engines: {node: '>=12'} + + mimic-function@5.0.1: + resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} + engines: {node: '>=18'} + + minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + + minimatch@5.1.6: + resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} + engines: {node: '>=10'} + + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} + + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + minipass@7.1.2: + resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} + engines: {node: '>=16 || 14 >=14.17'} + + minizlib@3.0.1: + resolution: {integrity: sha512-umcy022ILvb5/3Djuu8LWeqUa8D68JaBzlttKeMWen48SjabqS3iY5w/vzeMzMUNhLDifyhbOwKDSznB1vvrwg==} + engines: {node: '>= 18'} + + mjolnir.js@3.0.0: + resolution: {integrity: sha512-siX3YCG7N2HnmN1xMH3cK4JkUZJhbkhRFJL+G5N1vH0mh1t5088rJknIoqDFWDIU6NPGvRRgLnYW3ZHjSMEBLA==} + + mkdirp@3.0.1: + resolution: {integrity: sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==} + engines: {node: '>=10'} + hasBin: true + + mri@1.2.0: + resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} + engines: {node: '>=4'} + + mrmime@2.0.0: + resolution: {integrity: sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==} + engines: {node: '>=10'} + + ms@2.0.0: + resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + murmurhash-js@1.0.0: + resolution: {integrity: sha512-TvmkNhkv8yct0SVBSy+o8wYzXjE4Zz3PCesbfs8HiCXXdcTuocApFv11UWlNFWKYsP2okqrhb7JNlSm9InBhIw==} + + mz@2.7.0: + resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} + + nanoid@3.3.8: + resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + nanoid@5.0.9: + resolution: {integrity: sha512-Aooyr6MXU6HpvvWXKoVoXwKMs/KyVakWwg7xQfv5/S/RIgJMy0Ifa45H9qqYy7pTCszrHzP21Uk4PZq2HpEM8Q==} + engines: {node: ^18 || >=20} + hasBin: true + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + next-tick@1.1.0: + resolution: {integrity: sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==} + + ngeohash@0.6.3: + resolution: {integrity: sha512-kltF0cOxgx1AbmVzKxYZaoB0aj7mOxZeHaerEtQV0YaqnkXNq26WWqMmJ6lTqShYxVRWZ/mwvvTrNeOwdslWiw==} + engines: {node: '>=v0.2.0'} + + no-case@3.0.4: + resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} + + node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + + node-gyp-build@4.8.4: + resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==} + hasBin: true + + node-releases@2.0.19: + resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} + + nopt@8.1.0: + resolution: {integrity: sha512-ieGu42u/Qsa4TFktmaKEwM6MQH0pOWnaB3htzh0JRtx84+Mebc0cbZYN5bC+6WTZ4+77xrL9Pn5m7CV6VIkV7A==} + engines: {node: ^18.17.0 || >=20.5.0} + hasBin: true + + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + normalize-range@0.1.2: + resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} + engines: {node: '>=0.10.0'} + + nostr-geotags@0.7.2: + resolution: {integrity: sha512-pP+FSpv9p2c6yUQDAYZJJ9cMMNt9u6TvoJbbtIAbVT8OyY98K3M1P8mJQNlqIY1S5mI6oPcHhWW1CRlZKZPFaA==} + + nostr-tools@2.10.4: + resolution: {integrity: sha512-biU7sk+jxHgVASfobg2T5ttxOGGSt69wEVBC51sHHOEaKAAdzHBLV/I2l9Rf61UzClhliZwNouYhqIso4a3HYg==} + peerDependencies: + typescript: '>=5.0.0' + peerDependenciesMeta: + typescript: + optional: true + + nostr-wasm@0.1.0: + resolution: {integrity: sha512-78BTryCLcLYv96ONU8Ws3Q1JzjlAt+43pWQhIl86xZmWeegYCNLPml7yQ+gG3vR6V5h4XGj+TxO+SS5dsThQIA==} + + npm-bundled@2.0.1: + resolution: {integrity: sha512-gZLxXdjEzE/+mOstGDqR6b0EkhJ+kM6fxM6vUuckuctuVPh80Q6pw/rSZj9s4Gex9GxWtIicO1pc8DB9KZWudw==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + + npm-normalize-package-bin@2.0.0: + resolution: {integrity: sha512-awzfKUO7v0FscrSpRoogyNm0sajikhBWpU0QMrW09AMi9n1PoKU6WaIqUzuJSQnpciZZmJ/jMZ2Egfmb/9LiWQ==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + + npm-packlist@5.1.3: + resolution: {integrity: sha512-263/0NGrn32YFYi4J533qzrQ/krmmrWwhKkzwTuM4f/07ug51odoaNjUexxO4vxlzURHcmYMH1QjvHjsNDKLVg==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + hasBin: true + + npm-run-path@4.0.1: + resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} + engines: {node: '>=8'} + + npm-run-path@5.3.0: + resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + object-hash@3.0.0: + resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} + engines: {node: '>= 6'} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + onetime@5.1.2: + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} + + onetime@6.0.0: + resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} + engines: {node: '>=12'} + + onetime@7.0.0: + resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} + engines: {node: '>=18'} + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + os-tmpdir@1.0.2: + resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} + engines: {node: '>=0.10.0'} + + outdent@0.5.0: + resolution: {integrity: sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==} + + p-filter@2.1.0: + resolution: {integrity: sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==} + engines: {node: '>=8'} + + p-limit@2.3.0: + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} + engines: {node: '>=6'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-locate@4.1.0: + resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} + engines: {node: '>=8'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + p-map@2.1.0: + resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==} + engines: {node: '>=6'} + + p-try@2.2.0: + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + engines: {node: '>=6'} + + package-json-from-dist@1.0.1: + resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + + package-manager-detector@0.2.9: + resolution: {integrity: sha512-+vYvA/Y31l8Zk8dwxHhL3JfTuHPm6tlxM2A3GeQyl7ovYnSp1+mzAxClxaOr0qO1TtPxbQxetI7v5XqKLJZk7Q==} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + pascal-case@3.1.2: + resolution: {integrity: sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-key@4.0.0: + resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} + engines: {node: '>=12'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + path-scurry@1.11.1: + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} + engines: {node: '>=16 || 14 >=14.18'} + + path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + + pathe@1.1.2: + resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} + + pathval@2.0.0: + resolution: {integrity: sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==} + engines: {node: '>= 14.16'} + + pbf@3.3.0: + resolution: {integrity: sha512-XDF38WCH3z5OV/OVa8GKUNtLAyneuzbCisx7QUCF8Q6Nutx0WnJrQe5O+kOtBlLfRNUws98Y58Lblp+NJG5T4Q==} + hasBin: true + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + picomatch@4.0.2: + resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} + engines: {node: '>=12'} + + pidtree@0.6.0: + resolution: {integrity: sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==} + engines: {node: '>=0.10'} + hasBin: true + + pify@2.3.0: + resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} + engines: {node: '>=0.10.0'} + + pify@4.0.1: + resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} + engines: {node: '>=6'} + + pirates@4.0.6: + resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} + engines: {node: '>= 6'} + + playwright-core@1.50.1: + resolution: {integrity: sha512-ra9fsNWayuYumt+NiM069M6OkcRb1FZSK8bgi66AtpFoWkg2+y0bJSNmkFrWhMbEBbVKC/EruAHH3g0zmtwGmQ==} + engines: {node: '>=18'} + hasBin: true + + playwright@1.50.1: + resolution: {integrity: sha512-G8rwsOQJ63XG6BbKj2w5rHeavFjy5zynBA9zsJMMtBoe/Uf757oG12NXz6e6OirF7RCrTVAKFXbLmn1RbL7Qaw==} + engines: {node: '>=18'} + hasBin: true + + pmtiles@3.2.1: + resolution: {integrity: sha512-3R4fBwwoli5mw7a6t1IGwOtfmcSAODq6Okz0zkXhS1zi9sz1ssjjIfslwPvcWw5TNhdjNBUg9fgfPLeqZlH6ng==} + + postcss-import@15.1.0: + resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==} + engines: {node: '>=14.0.0'} + peerDependencies: + postcss: ^8.0.0 + + postcss-import@16.1.0: + resolution: {integrity: sha512-7hsAZ4xGXl4MW+OKEWCnF6T5jqBw80/EE9aXg1r2yyn1RsVEU8EtKXbijEODa+rg7iih4bKf7vlvTGYR4CnPNg==} + engines: {node: '>=18.0.0'} + peerDependencies: + postcss: ^8.0.0 + + postcss-js@4.0.1: + resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==} + engines: {node: ^12 || ^14 || >= 16} + peerDependencies: + postcss: ^8.4.21 + + postcss-load-config@3.1.4: + resolution: {integrity: sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==} + engines: {node: '>= 10'} + peerDependencies: + postcss: '>=8.0.9' + ts-node: '>=9.0.0' + peerDependenciesMeta: + postcss: + optional: true + ts-node: + optional: true + + postcss-load-config@4.0.2: + resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==} + engines: {node: '>= 14'} + peerDependencies: + postcss: '>=8.0.9' + ts-node: '>=9.0.0' + peerDependenciesMeta: + postcss: + optional: true + ts-node: + optional: true + + postcss-nested@6.2.0: + resolution: {integrity: sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.2.14 + + postcss-safe-parser@6.0.0: + resolution: {integrity: sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.3.3 + + postcss-scss@4.0.9: + resolution: {integrity: sha512-AjKOeiwAitL/MXxQW2DliT28EKukvvbEWx3LBmJIRN8KfBGZbRTxNYW0kSqi1COiTZ57nZ9NW06S6ux//N1c9A==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.4.29 + + postcss-selector-parser@6.1.2: + resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} + engines: {node: '>=4'} + + postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + + postcss@8.5.1: + resolution: {integrity: sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==} + engines: {node: ^10 || ^12 || >=14} + + potpack@2.0.0: + resolution: {integrity: sha512-Q+/tYsFU9r7xoOJ+y/ZTtdVQwTWfzjbiXBDMM/JKUux3+QPP02iUuIoeBQ+Ot6oEDlC+/PGjB/5A3K7KKb7hcw==} + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + prettier-plugin-svelte@3.3.3: + resolution: {integrity: sha512-yViK9zqQ+H2qZD1w/bH7W8i+bVfKrD8GIFjkFe4Thl6kCT9SlAsXVNmt3jCvQOCsnOhcvYgsoVlRV/Eu6x5nNw==} + peerDependencies: + prettier: ^3.0.0 + svelte: ^3.2.0 || ^4.0.0-next.0 || ^5.0.0-next.0 + + prettier-plugin-tailwindcss@0.6.11: + resolution: {integrity: sha512-YxaYSIvZPAqhrrEpRtonnrXdghZg1irNg4qrjboCXrpybLWVs55cW2N3juhspVJiO0JBvYJT8SYsJpc8OQSnsA==} + engines: {node: '>=14.21.3'} + peerDependencies: + '@ianvs/prettier-plugin-sort-imports': '*' + '@prettier/plugin-pug': '*' + '@shopify/prettier-plugin-liquid': '*' + '@trivago/prettier-plugin-sort-imports': '*' + '@zackad/prettier-plugin-twig': '*' + prettier: ^3.0 + prettier-plugin-astro: '*' + prettier-plugin-css-order: '*' + prettier-plugin-import-sort: '*' + prettier-plugin-jsdoc: '*' + prettier-plugin-marko: '*' + prettier-plugin-multiline-arrays: '*' + prettier-plugin-organize-attributes: '*' + prettier-plugin-organize-imports: '*' + prettier-plugin-sort-imports: '*' + prettier-plugin-style-order: '*' + prettier-plugin-svelte: '*' + peerDependenciesMeta: + '@ianvs/prettier-plugin-sort-imports': + optional: true + '@prettier/plugin-pug': + optional: true + '@shopify/prettier-plugin-liquid': + optional: true + '@trivago/prettier-plugin-sort-imports': + optional: true + '@zackad/prettier-plugin-twig': + optional: true + prettier-plugin-astro: + optional: true + prettier-plugin-css-order: + optional: true + prettier-plugin-import-sort: + optional: true + prettier-plugin-jsdoc: + optional: true + prettier-plugin-marko: + optional: true + prettier-plugin-multiline-arrays: + optional: true + prettier-plugin-organize-attributes: + optional: true + prettier-plugin-organize-imports: + optional: true + prettier-plugin-sort-imports: + optional: true + prettier-plugin-style-order: + optional: true + prettier-plugin-svelte: + optional: true + + prettier@2.8.8: + resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} + engines: {node: '>=10.13.0'} + hasBin: true + + prettier@3.5.0: + resolution: {integrity: sha512-quyMrVt6svPS7CjQ9gKb3GLEX/rl3BCL2oa/QkNcXv4YNVBC9olt3s+H7ukto06q7B1Qz46PbrKLO34PR6vXcA==} + engines: {node: '>=14'} + hasBin: true + + protocol-buffers-schema@3.6.0: + resolution: {integrity: sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw==} + + publint@0.2.12: + resolution: {integrity: sha512-YNeUtCVeM4j9nDiTT2OPczmlyzOkIXNtdDZnSuajAxS/nZ6j3t7Vs9SUB4euQNddiltIwu7Tdd3s+hr08fAsMw==} + engines: {node: '>=16'} + hasBin: true + + publint@0.3.4: + resolution: {integrity: sha512-szQCgQXxLgrI2P+3Znv/KUiglp3YA+A2MeI+9Izjs01Ol5KXKCkdbmz69bM/ivPKMYKHhtKOKkib9VJ6JL3H/w==} + engines: {node: '>=18'} + hasBin: true + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + quickselect@3.0.0: + resolution: {integrity: sha512-XdjUArbK4Bm5fLLvlm5KpTFOiOThgfWWI4axAZDWg4E/0mKdZyI9tNEfds27qCi1ze/vwTR16kvmmGhRra3c2g==} + + read-cache@1.0.0: + resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} + + read-yaml-file@1.1.0: + resolution: {integrity: sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA==} + engines: {node: '>=6'} + + readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + + readdirp@4.1.1: + resolution: {integrity: sha512-h80JrZu/MHUZCyHu5ciuoI0+WxsCxzxJTILn6Fs8rxSnFPh+UVHYfeIxK1nVGugMqkfC4vJcBOYbkfkwYK0+gw==} + engines: {node: '>= 14.18.0'} + + regenerator-runtime@0.14.1: + resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + resolve-from@5.0.0: + resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} + engines: {node: '>=8'} + + resolve-protobuf-schema@2.1.0: + resolution: {integrity: sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ==} + + resolve@1.22.10: + resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==} + engines: {node: '>= 0.4'} + hasBin: true + + restore-cursor@5.1.0: + resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} + engines: {node: '>=18'} + + reusify@1.0.4: + resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + rfdc@1.4.1: + resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} + + rimraf@5.0.10: + resolution: {integrity: sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==} + hasBin: true + + rollup@3.29.5: + resolution: {integrity: sha512-GVsDdsbJzzy4S/v3dqWPJ7EfvZJfCHiDqe80IyrF59LYuP+e6U1LJoUqeuqRbwAWoMNoXivMNeNAOf5E22VA1w==} + engines: {node: '>=14.18.0', npm: '>=8.0.0'} + hasBin: true + + rollup@4.34.6: + resolution: {integrity: sha512-wc2cBWqJgkU3Iz5oztRkQbfVkbxoz5EhnCGOrnJvnLnQ7O0WhQUYyv18qQI79O8L7DdHrrlJNeCHd4VGpnaXKQ==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + rw@1.3.3: + resolution: {integrity: sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==} + + sade@1.8.1: + resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==} + engines: {node: '>=6'} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + semver@7.7.1: + resolution: {integrity: sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==} + engines: {node: '>=10'} + hasBin: true + + serialize-to-js@3.1.2: + resolution: {integrity: sha512-owllqNuDDEimQat7EPG0tH7JjO090xKNzUtYz6X+Sk2BXDnOCilDdNLwjWeFywG9xkJul1ULvtUQa9O4pUaY0w==} + engines: {node: '>=4.0.0'} + + set-cookie-parser@2.7.1: + resolution: {integrity: sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + + signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + sirv@3.0.0: + resolution: {integrity: sha512-BPwJGUeDaDCHihkORDchNyyTvWFhcusy1XMmhEVTQTwGeybFbp8YEmB+njbPnth1FibULBSBVwCQni25XlCUDg==} + engines: {node: '>=18'} + + slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + + slice-ansi@5.0.0: + resolution: {integrity: sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==} + engines: {node: '>=12'} + + slice-ansi@7.1.0: + resolution: {integrity: sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==} + engines: {node: '>=18'} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + source-map@0.8.0-beta.0: + resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==} + engines: {node: '>= 8'} + + spawndamnit@3.0.1: + resolution: {integrity: sha512-MmnduQUuHCoFckZoWnXsTg7JaiLBJrKFj9UI2MbRPGaJeVpsLcVBu6P/IGZovziM/YBsellCmsprgNA+w0CzVg==} + + sprintf-js@1.0.3: + resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + + sql.js@1.12.0: + resolution: {integrity: sha512-Bi+43yMx/tUFZVYD4AUscmdL6NHn3gYQ+CM+YheFWLftOmrEC/Mz6Yh7E96Y2WDHYz3COSqT+LP6Z79zgrwJlA==} + + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + + std-env@3.8.0: + resolution: {integrity: sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w==} + + string-argv@0.3.2: + resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} + engines: {node: '>=0.6.19'} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + + string-width@7.2.0: + resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} + engines: {node: '>=18'} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-ansi@7.1.0: + resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + engines: {node: '>=12'} + + strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + + strip-final-newline@2.0.0: + resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} + engines: {node: '>=6'} + + strip-final-newline@3.0.0: + resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} + engines: {node: '>=12'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + sucrase@3.35.0: + resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==} + engines: {node: '>=16 || 14 >=14.17'} + hasBin: true + + supercluster@8.0.1: + resolution: {integrity: sha512-IiOea5kJ9iqzD2t7QJq/cREyLHTtSmUT6gQsweojg9WH2sYJqZK9SswTu6jrscO6D1G5v5vYZ9ru/eq85lXeZQ==} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + svelte-check@4.1.4: + resolution: {integrity: sha512-v0j7yLbT29MezzaQJPEDwksybTE2Ups9rUxEXy92T06TiA0cbqcO8wAOwNUVkFW6B0hsYHA+oAX3BS8b/2oHtw==} + engines: {node: '>= 18.0.0'} + hasBin: true + peerDependencies: + svelte: ^4.0.0 || ^5.0.0-next.0 + typescript: '>=5.0.0' + + svelte-eslint-parser@0.43.0: + resolution: {integrity: sha512-GpU52uPKKcVnh8tKN5P4UZpJ/fUDndmq7wfsvoVXsyP+aY0anol7Yqo01fyrlaWGMFfm4av5DyrjlaXdLRJvGA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + svelte: ^3.37.0 || ^4.0.0 || ^5.0.0 + peerDependenciesMeta: + svelte: + optional: true + + svelte-hmr@0.16.0: + resolution: {integrity: sha512-Gyc7cOS3VJzLlfj7wKS0ZnzDVdv3Pn2IuVeJPk9m2skfhcu5bq3wtIZyQGggr7/Iim5rH5cncyQft/kRLupcnA==} + engines: {node: ^12.20 || ^14.13.1 || >= 16} + peerDependencies: + svelte: ^3.19.0 || ^4.0.0 + + svelte2tsx@0.7.34: + resolution: {integrity: sha512-WTMhpNhFf8/h3SMtR5dkdSy2qfveomkhYei/QW9gSPccb0/b82tjHvLop6vT303ZkGswU/da1s6XvrLgthQPCw==} + peerDependencies: + svelte: ^3.55 || ^4.0.0-next.0 || ^4.0 || ^5.0.0-next.0 + typescript: ^4.9.4 || ^5.0.0 + + svelte@5.19.9: + resolution: {integrity: sha512-860s752/ZZxHIsii31ELkdKBOCeAuDsfb/AGUXJyQyzUVLRSt4oqEw/BV5+2+mNg8mbqmD3OK+vMvwWMPM6f8A==} + engines: {node: '>=18'} + + sveltekit-search-params@3.0.0: + resolution: {integrity: sha512-wq1Yo5zITev8ty9CWGmHgvAh+Xb3mCUewyUmvCdv6MJWi+/aZ4o79Y6SjuduDL0Cfd/KYHkqt4f/wQ4FtokSdw==} + peerDependencies: + '@sveltejs/kit': ^1.0.0 || ^2.0.0 + svelte: ^3.55.0 || ^4.0.0 || ^5.0.0 + + tailwindcss@3.4.17: + resolution: {integrity: sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==} + engines: {node: '>=14.0.0'} + hasBin: true + + tar@7.4.3: + resolution: {integrity: sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==} + engines: {node: '>=18'} + + term-size@2.2.1: + resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==} + engines: {node: '>=8'} + + thenify-all@1.6.0: + resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} + engines: {node: '>=0.8'} + + thenify@3.3.1: + resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + + tinyexec@0.3.2: + resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} + + tinypool@1.0.2: + resolution: {integrity: sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==} + engines: {node: ^18.0.0 || >=20.0.0} + + tinyqueue@3.0.0: + resolution: {integrity: sha512-gRa9gwYU3ECmQYv3lslts5hxuIa90veaEcxDYuu3QGOIAEM2mOZkVHp48ANJuu1CURtRdHKUBY5Lm1tHV+sD4g==} + + tinyrainbow@1.2.0: + resolution: {integrity: sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==} + engines: {node: '>=14.0.0'} + + tinyspy@3.0.2: + resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==} + engines: {node: '>=14.0.0'} + + tmp@0.0.33: + resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} + engines: {node: '>=0.6.0'} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + totalist@3.0.1: + resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} + engines: {node: '>=6'} + + tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + + tr46@1.0.1: + resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==} + + tree-kill@1.2.2: + resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} + hasBin: true + + ts-api-utils@1.4.3: + resolution: {integrity: sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==} + engines: {node: '>=16'} + peerDependencies: + typescript: '>=4.2.0' + + ts-api-utils@2.0.1: + resolution: {integrity: sha512-dnlgjFSVetynI8nzgJ+qF62efpglpWRk8isUEWZGWlJYySCTD6aKvbUDu+zbPeDakk3bg5H4XpitHukgfL1m9w==} + engines: {node: '>=18.12'} + peerDependencies: + typescript: '>=4.8.4' + + ts-interface-checker@0.1.13: + resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} + + tseep@1.3.1: + resolution: {integrity: sha512-ZPtfk1tQnZVyr7BPtbJ93qaAh2lZuIOpTMjhrYa4XctT8xe7t4SAW9LIxrySDuYMsfNNayE51E/WNGrNVgVicQ==} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + tstl@2.5.16: + resolution: {integrity: sha512-+O2ybLVLKcBwKm4HymCEwZIT0PpwS3gCYnxfSDEjJEKADvIFruaQjd3m7CAKNU1c7N3X3WjVz87re7TA2A5FUw==} + + tsup@6.7.0: + resolution: {integrity: sha512-L3o8hGkaHnu5TdJns+mCqFsDBo83bJ44rlK7e6VdanIvpea4ArPcU3swWGsLVbXak1PqQx/V+SSmFPujBK+zEQ==} + engines: {node: '>=14.18'} + hasBin: true + peerDependencies: + '@swc/core': ^1 + postcss: ^8.4.12 + typescript: '>=4.1.0' + peerDependenciesMeta: + '@swc/core': + optional: true + postcss: + optional: true + typescript: + optional: true + + turbo-darwin-64@2.4.0: + resolution: {integrity: sha512-kVMScnPUa3R4n7woNmkR15kOY0aUwCLJcUyH5UC59ggKqr5HIHwweKYK8N1pwBQso0LQF4I9i93hIzfJguCcwQ==} + cpu: [x64] + os: [darwin] + + turbo-darwin-arm64@2.4.0: + resolution: {integrity: sha512-8JObIpfun1guA7UlFR5jC/SOVm49lRscxMxfg5jZ5ABft79rhFC+ygN9AwAhGKv6W2DUhIh2xENkSgu4EDmUyg==} + cpu: [arm64] + os: [darwin] + + turbo-linux-64@2.4.0: + resolution: {integrity: sha512-xWDGGcRlBuGV7HXWAVuTY6vsQi4aZxGMAnuiuNDg8Ij1aHGohOM0RUsWMXjxz4vuJmjk9+/D6NQqHH3AJEXezg==} + cpu: [x64] + os: [linux] + + turbo-linux-arm64@2.4.0: + resolution: {integrity: sha512-c3En99xMguc/Pdtk/rZP53LnDdw0W6lgUc04he8r8F+UHYSNvgzHh0WGXXmCC6lGbBH72kPhhGx4bAwyvi7dug==} + cpu: [arm64] + os: [linux] + + turbo-windows-64@2.4.0: + resolution: {integrity: sha512-/gOORuOlyA8JDPzyA16CD3wvyRcuBFePa1URAnFUof9hXQmKxK0VvSDO79cYZFsJSchCKNJpckUS0gYxGsWwoA==} + cpu: [x64] + os: [win32] + + turbo-windows-arm64@2.4.0: + resolution: {integrity: sha512-/DJIdTFijEMM5LSiEpSfarDOMOlYqJV+EzmppqWtHqDsOLF4hbbIBH9sJR6OOp5dURAu5eURBYdmvBRz9Lo6TA==} + cpu: [arm64] + os: [win32] + + turbo@2.4.0: + resolution: {integrity: sha512-ah/yQp2oMif1X0u7fBJ4MLMygnkbKnW5O8SG6pJvloPCpHfFoZctkSVQiJ3VnvNTq71V2JJIdwmOeu1i34OQyg==} + hasBin: true + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + type@2.7.3: + resolution: {integrity: sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==} + + typedarray-to-buffer@3.1.5: + resolution: {integrity: sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==} + + typescript-eslint@8.23.0: + resolution: {integrity: sha512-/LBRo3HrXr5LxmrdYSOCvoAMm7p2jNizNfbIpCgvG4HMsnoprRUOce/+8VJ9BDYWW68rqIENE/haVLWPeFZBVQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.8.0' + + typescript-lru-cache@2.0.0: + resolution: {integrity: sha512-Jp57Qyy8wXeMkdNuZiglE6v2Cypg13eDA1chHwDG6kq51X7gk4K7P7HaDdzZKCxkegXkVHNcPD0n5aW6OZH3aA==} + + typescript@5.7.3: + resolution: {integrity: sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==} + engines: {node: '>=14.17'} + hasBin: true + + undici-types@6.20.0: + resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==} + + universalify@0.1.2: + resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} + engines: {node: '>= 4.0.0'} + + update-browserslist-db@1.1.2: + resolution: {integrity: sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + utf-8-validate@5.0.10: + resolution: {integrity: sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==} + engines: {node: '>=6.14.2'} + + utf8-buffer@1.0.0: + resolution: {integrity: sha512-ueuhzvWnp5JU5CiGSY4WdKbiN/PO2AZ/lpeLiz2l38qwdLy/cW40XobgyuIWucNyum0B33bVB0owjFCeGBSLqg==} + engines: {node: '>=8'} + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + uuid@10.0.0: + resolution: {integrity: sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==} + hasBin: true + + vite-node@2.1.9: + resolution: {integrity: sha512-AM9aQ/IPrW/6ENLQg3AGY4K1N2TGZdR5e4gu/MmmR2xR3Ll1+dib+nook92g4TV3PXVyeyxdWwtaCAiUL0hMxA==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + + vite@5.4.14: + resolution: {integrity: sha512-EK5cY7Q1D8JNhSaPKVK4pwBFvaTmZxEnoKXLG/U9gmdDcihQGNzFlgIvaxezFR4glP1LsuiedwMBqCXH3wZccA==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || >=20.0.0 + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + + vite@6.1.0: + resolution: {integrity: sha512-RjjMipCKVoR4hVfPY6GQTgveinjNuyLw+qruksLDvA5ktI1150VmcMBKmQaEWJhg/j6Uaf6dNCNA0AfdzUb/hQ==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + jiti: '>=1.21.0' + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vitefu@0.2.5: + resolution: {integrity: sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q==} + peerDependencies: + vite: ^3.0.0 || ^4.0.0 || ^5.0.0 + peerDependenciesMeta: + vite: + optional: true + + vitefu@1.0.5: + resolution: {integrity: sha512-h4Vflt9gxODPFNGPwp4zAMZRpZR7eslzwH2c5hn5kNZ5rhnKyRJ50U+yGCdc2IRaBs8O4haIgLNGrV5CrpMsCA==} + peerDependencies: + vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 + peerDependenciesMeta: + vite: + optional: true + + vitest@2.1.9: + resolution: {integrity: sha512-MSmPM9REYqDGBI8439mA4mWhV5sKmDlBKWIYbA3lRb2PTHACE0mgKwA8yQ2xq9vxDTuk4iPrECBAEW2aoFXY0Q==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@types/node': ^18.0.0 || >=20.0.0 + '@vitest/browser': 2.1.9 + '@vitest/ui': 2.1.9 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@types/node': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + + vt-pbf@3.1.3: + resolution: {integrity: sha512-2LzDFzt0mZKZ9IpVF2r69G9bXaP2Q2sArJCmcCgvfTdCCZzSyz4aCLoQyUilu37Ll56tCblIZrXFIjNUpGIlmA==} + + webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + + webidl-conversions@4.0.2: + resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==} + + websocket-polyfill@0.0.3: + resolution: {integrity: sha512-pF3kR8Uaoau78MpUmFfzbIRxXj9PeQrCuPepGE6JIsfsJ/o/iXr07Q2iQNzKSSblQJ0FiGWlS64N4pVSm+O3Dg==} + + websocket@1.0.35: + resolution: {integrity: sha512-/REy6amwPZl44DDzvRCkaI1q1bIiQB0mEFQLUrhz3z2EK91cp3n72rAjUlrTP0zV22HJIUOVHQGPxhFRjxjt+Q==} + engines: {node: '>=4.0.0'} + + wgs84@0.0.0: + resolution: {integrity: sha512-ANHlY4Rb5kHw40D0NJ6moaVfOCMrp9Gpd1R/AIQYg2ko4/jzcJ+TVXYYF6kXJqQwITvEZP4yEthjM7U6rYlljQ==} + + wgsl_reflect@1.0.17: + resolution: {integrity: sha512-TzeBJSkFJRb8VEAOiUxZbcHhtcs8w4rJi9zcQTLv4OZYgPOpIGvd74ukV58CNU3FB+sIKTO0CpHeOAFsTjoc1g==} + + whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + + whatwg-url@7.1.0: + resolution: {integrity: sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + which@4.0.0: + resolution: {integrity: sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==} + engines: {node: ^16.13.0 || >=18.0.0} + hasBin: true + + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + + wrap-ansi@9.0.0: + resolution: {integrity: sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==} + engines: {node: '>=18'} + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + yaeti@0.0.6: + resolution: {integrity: sha512-MvQa//+KcZCUkBTIC9blM+CU9J2GzuTytsOUwf2lidtvkx/6gnEp1QvJv34t9vdjhFmha/mUiNDbN0D0mJWdug==} + engines: {node: '>=0.10.32'} + + yallist@5.0.0: + resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} + engines: {node: '>=18'} + + yaml@1.10.2: + resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} + engines: {node: '>= 6'} + + yaml@2.7.0: + resolution: {integrity: sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==} + engines: {node: '>= 14'} + hasBin: true + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + + zimmerframe@1.1.2: + resolution: {integrity: sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w==} + + zod@3.24.1: + resolution: {integrity: sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==} + +snapshots: + + '@alloc/quick-lru@5.2.0': {} + + '@ampproject/remapping@2.3.0': + dependencies: + '@jridgewell/gen-mapping': 0.3.8 + '@jridgewell/trace-mapping': 0.3.25 + + '@babel/runtime@7.26.7': + dependencies: + regenerator-runtime: 0.14.1 + + '@changesets/apply-release-plan@7.0.8': + dependencies: + '@changesets/config': 3.0.5 + '@changesets/get-version-range-type': 0.4.0 + '@changesets/git': 3.0.2 + '@changesets/should-skip-package': 0.1.1 + '@changesets/types': 6.0.0 + '@manypkg/get-packages': 1.1.3 + detect-indent: 6.1.0 + fs-extra: 7.0.1 + lodash.startcase: 4.4.0 + outdent: 0.5.0 + prettier: 2.8.8 + resolve-from: 5.0.0 + semver: 7.7.1 + + '@changesets/assemble-release-plan@6.0.5': + dependencies: + '@changesets/errors': 0.2.0 + '@changesets/get-dependents-graph': 2.1.2 + '@changesets/should-skip-package': 0.1.1 + '@changesets/types': 6.0.0 + '@manypkg/get-packages': 1.1.3 + semver: 7.7.1 + + '@changesets/changelog-git@0.2.0': + dependencies: + '@changesets/types': 6.0.0 + + '@changesets/changelog-github@0.5.0': + dependencies: + '@changesets/get-github-info': 0.6.0 + '@changesets/types': 6.0.0 + dotenv: 8.6.0 + transitivePeerDependencies: + - encoding + + '@changesets/cli@2.27.12': + dependencies: + '@changesets/apply-release-plan': 7.0.8 + '@changesets/assemble-release-plan': 6.0.5 + '@changesets/changelog-git': 0.2.0 + '@changesets/config': 3.0.5 + '@changesets/errors': 0.2.0 + '@changesets/get-dependents-graph': 2.1.2 + '@changesets/get-release-plan': 4.0.6 + '@changesets/git': 3.0.2 + '@changesets/logger': 0.1.1 + '@changesets/pre': 2.0.1 + '@changesets/read': 0.6.2 + '@changesets/should-skip-package': 0.1.1 + '@changesets/types': 6.0.0 + '@changesets/write': 0.3.2 + '@manypkg/get-packages': 1.1.3 + ansi-colors: 4.1.3 + ci-info: 3.9.0 + enquirer: 2.4.1 + external-editor: 3.1.0 + fs-extra: 7.0.1 + mri: 1.2.0 + p-limit: 2.3.0 + package-manager-detector: 0.2.9 + picocolors: 1.1.1 + resolve-from: 5.0.0 + semver: 7.7.1 + spawndamnit: 3.0.1 + term-size: 2.2.1 + + '@changesets/config@3.0.5': + dependencies: + '@changesets/errors': 0.2.0 + '@changesets/get-dependents-graph': 2.1.2 + '@changesets/logger': 0.1.1 + '@changesets/types': 6.0.0 + '@manypkg/get-packages': 1.1.3 + fs-extra: 7.0.1 + micromatch: 4.0.8 + + '@changesets/errors@0.2.0': + dependencies: + extendable-error: 0.1.7 + + '@changesets/get-dependents-graph@2.1.2': + dependencies: + '@changesets/types': 6.0.0 + '@manypkg/get-packages': 1.1.3 + picocolors: 1.1.1 + semver: 7.7.1 + + '@changesets/get-github-info@0.6.0': + dependencies: + dataloader: 1.4.0 + node-fetch: 2.7.0 + transitivePeerDependencies: + - encoding + + '@changesets/get-release-plan@4.0.6': + dependencies: + '@changesets/assemble-release-plan': 6.0.5 + '@changesets/config': 3.0.5 + '@changesets/pre': 2.0.1 + '@changesets/read': 0.6.2 + '@changesets/types': 6.0.0 + '@manypkg/get-packages': 1.1.3 + + '@changesets/get-version-range-type@0.4.0': {} + + '@changesets/git@3.0.2': + dependencies: + '@changesets/errors': 0.2.0 + '@manypkg/get-packages': 1.1.3 + is-subdir: 1.2.0 + micromatch: 4.0.8 + spawndamnit: 3.0.1 + + '@changesets/logger@0.1.1': + dependencies: + picocolors: 1.1.1 + + '@changesets/parse@0.4.0': + dependencies: + '@changesets/types': 6.0.0 + js-yaml: 3.14.1 + + '@changesets/pre@2.0.1': + dependencies: + '@changesets/errors': 0.2.0 + '@changesets/types': 6.0.0 + '@manypkg/get-packages': 1.1.3 + fs-extra: 7.0.1 + + '@changesets/read@0.6.2': + dependencies: + '@changesets/git': 3.0.2 + '@changesets/logger': 0.1.1 + '@changesets/parse': 0.4.0 + '@changesets/types': 6.0.0 + fs-extra: 7.0.1 + p-filter: 2.1.0 + picocolors: 1.1.1 + + '@changesets/should-skip-package@0.1.1': + dependencies: + '@changesets/types': 6.0.0 + '@manypkg/get-packages': 1.1.3 + + '@changesets/types@4.1.0': {} + + '@changesets/types@6.0.0': {} + + '@changesets/write@0.3.2': + dependencies: + '@changesets/types': 6.0.0 + fs-extra: 7.0.1 + human-id: 1.0.2 + prettier: 2.8.8 + + '@deck.gl/core@9.1.0': + dependencies: + '@loaders.gl/core': 4.3.3 + '@loaders.gl/images': 4.3.3(@loaders.gl/core@4.3.3) + '@luma.gl/constants': 9.1.0 + '@luma.gl/core': 9.1.0 + '@luma.gl/engine': 9.1.0(@luma.gl/core@9.1.0)(@luma.gl/shadertools@9.1.0(@luma.gl/core@9.1.0)) + '@luma.gl/shadertools': 9.1.0(@luma.gl/core@9.1.0) + '@luma.gl/webgl': 9.1.0(@luma.gl/core@9.1.0) + '@math.gl/core': 4.1.0 + '@math.gl/sun': 4.1.0 + '@math.gl/types': 4.1.0 + '@math.gl/web-mercator': 4.1.0 + '@probe.gl/env': 4.1.0 + '@probe.gl/log': 4.1.0 + '@probe.gl/stats': 4.1.0 + '@types/offscreencanvas': 2019.7.3 + gl-matrix: 3.4.3 + mjolnir.js: 3.0.0 + + '@deck.gl/layers@9.1.0(@deck.gl/core@9.1.0)(@loaders.gl/core@4.3.3)(@luma.gl/core@9.1.0)(@luma.gl/engine@9.1.0(@luma.gl/core@9.1.0)(@luma.gl/shadertools@9.1.0(@luma.gl/core@9.1.0)))': + dependencies: + '@deck.gl/core': 9.1.0 + '@loaders.gl/core': 4.3.3 + '@loaders.gl/images': 4.3.3(@loaders.gl/core@4.3.3) + '@loaders.gl/schema': 4.3.3(@loaders.gl/core@4.3.3) + '@luma.gl/core': 9.1.0 + '@luma.gl/engine': 9.1.0(@luma.gl/core@9.1.0)(@luma.gl/shadertools@9.1.0(@luma.gl/core@9.1.0)) + '@mapbox/tiny-sdf': 2.0.6 + '@math.gl/core': 4.1.0 + '@math.gl/polygon': 4.1.0 + '@math.gl/web-mercator': 4.1.0 + earcut: 2.2.4 + + '@deck.gl/mapbox@9.1.0(@deck.gl/core@9.1.0)(@luma.gl/core@9.1.0)': + dependencies: + '@deck.gl/core': 9.1.0 + '@luma.gl/constants': 9.1.0 + '@luma.gl/core': 9.1.0 + '@math.gl/web-mercator': 4.1.0 + + '@esbuild/aix-ppc64@0.21.5': + optional: true + + '@esbuild/aix-ppc64@0.24.2': + optional: true + + '@esbuild/android-arm64@0.17.19': + optional: true + + '@esbuild/android-arm64@0.21.5': + optional: true + + '@esbuild/android-arm64@0.24.2': + optional: true + + '@esbuild/android-arm@0.17.19': + optional: true + + '@esbuild/android-arm@0.21.5': + optional: true + + '@esbuild/android-arm@0.24.2': + optional: true + + '@esbuild/android-x64@0.17.19': + optional: true + + '@esbuild/android-x64@0.21.5': + optional: true + + '@esbuild/android-x64@0.24.2': + optional: true + + '@esbuild/darwin-arm64@0.17.19': + optional: true + + '@esbuild/darwin-arm64@0.21.5': + optional: true + + '@esbuild/darwin-arm64@0.24.2': + optional: true + + '@esbuild/darwin-x64@0.17.19': + optional: true + + '@esbuild/darwin-x64@0.21.5': + optional: true + + '@esbuild/darwin-x64@0.24.2': + optional: true + + '@esbuild/freebsd-arm64@0.17.19': + optional: true + + '@esbuild/freebsd-arm64@0.21.5': + optional: true + + '@esbuild/freebsd-arm64@0.24.2': + optional: true + + '@esbuild/freebsd-x64@0.17.19': + optional: true + + '@esbuild/freebsd-x64@0.21.5': + optional: true + + '@esbuild/freebsd-x64@0.24.2': + optional: true + + '@esbuild/linux-arm64@0.17.19': + optional: true + + '@esbuild/linux-arm64@0.21.5': + optional: true + + '@esbuild/linux-arm64@0.24.2': + optional: true + + '@esbuild/linux-arm@0.17.19': + optional: true + + '@esbuild/linux-arm@0.21.5': + optional: true + + '@esbuild/linux-arm@0.24.2': + optional: true + + '@esbuild/linux-ia32@0.17.19': + optional: true + + '@esbuild/linux-ia32@0.21.5': + optional: true + + '@esbuild/linux-ia32@0.24.2': + optional: true + + '@esbuild/linux-loong64@0.17.19': + optional: true + + '@esbuild/linux-loong64@0.21.5': + optional: true + + '@esbuild/linux-loong64@0.24.2': + optional: true + + '@esbuild/linux-mips64el@0.17.19': + optional: true + + '@esbuild/linux-mips64el@0.21.5': + optional: true + + '@esbuild/linux-mips64el@0.24.2': + optional: true + + '@esbuild/linux-ppc64@0.17.19': + optional: true + + '@esbuild/linux-ppc64@0.21.5': + optional: true + + '@esbuild/linux-ppc64@0.24.2': + optional: true + + '@esbuild/linux-riscv64@0.17.19': + optional: true + + '@esbuild/linux-riscv64@0.21.5': + optional: true + + '@esbuild/linux-riscv64@0.24.2': + optional: true + + '@esbuild/linux-s390x@0.17.19': + optional: true + + '@esbuild/linux-s390x@0.21.5': + optional: true + + '@esbuild/linux-s390x@0.24.2': + optional: true + + '@esbuild/linux-x64@0.17.19': + optional: true + + '@esbuild/linux-x64@0.21.5': + optional: true + + '@esbuild/linux-x64@0.24.2': + optional: true + + '@esbuild/netbsd-arm64@0.24.2': + optional: true + + '@esbuild/netbsd-x64@0.17.19': + optional: true + + '@esbuild/netbsd-x64@0.21.5': + optional: true + + '@esbuild/netbsd-x64@0.24.2': + optional: true + + '@esbuild/openbsd-arm64@0.24.2': + optional: true + + '@esbuild/openbsd-x64@0.17.19': + optional: true + + '@esbuild/openbsd-x64@0.21.5': + optional: true + + '@esbuild/openbsd-x64@0.24.2': + optional: true + + '@esbuild/sunos-x64@0.17.19': + optional: true + + '@esbuild/sunos-x64@0.21.5': + optional: true + + '@esbuild/sunos-x64@0.24.2': + optional: true + + '@esbuild/win32-arm64@0.17.19': + optional: true + + '@esbuild/win32-arm64@0.21.5': + optional: true + + '@esbuild/win32-arm64@0.24.2': + optional: true + + '@esbuild/win32-ia32@0.17.19': + optional: true + + '@esbuild/win32-ia32@0.21.5': + optional: true + + '@esbuild/win32-ia32@0.24.2': + optional: true + + '@esbuild/win32-x64@0.17.19': + optional: true + + '@esbuild/win32-x64@0.21.5': + optional: true + + '@esbuild/win32-x64@0.24.2': + optional: true + + '@eslint-community/eslint-utils@4.4.1(eslint@9.20.0(jiti@1.21.7))': + dependencies: + eslint: 9.20.0(jiti@1.21.7) + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.1': {} + + '@eslint/config-array@0.19.2': + dependencies: + '@eslint/object-schema': 2.1.6 + debug: 4.4.0 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + + '@eslint/core@0.10.0': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/core@0.11.0': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/eslintrc@3.2.0': + dependencies: + ajv: 6.12.6 + debug: 4.4.0 + espree: 10.3.0 + globals: 14.0.0 + ignore: 5.3.2 + import-fresh: 3.3.1 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@9.20.0': {} + + '@eslint/object-schema@2.1.6': {} + + '@eslint/plugin-kit@0.2.5': + dependencies: + '@eslint/core': 0.10.0 + levn: 0.4.1 + + '@formatjs/ecma402-abstract@2.3.3': + dependencies: + '@formatjs/fast-memoize': 2.2.6 + '@formatjs/intl-localematcher': 0.6.0 + decimal.js: 10.5.0 + tslib: 2.8.1 + + '@formatjs/fast-memoize@2.2.6': + dependencies: + tslib: 2.8.1 + + '@formatjs/icu-messageformat-parser@2.11.1': + dependencies: + '@formatjs/ecma402-abstract': 2.3.3 + '@formatjs/icu-skeleton-parser': 1.8.13 + tslib: 2.8.1 + + '@formatjs/icu-skeleton-parser@1.8.13': + dependencies: + '@formatjs/ecma402-abstract': 2.3.3 + tslib: 2.8.1 + + '@formatjs/intl-localematcher@0.6.0': + dependencies: + tslib: 2.8.1 + + '@humanfs/core@0.19.1': {} + + '@humanfs/node@0.16.6': + dependencies: + '@humanfs/core': 0.19.1 + '@humanwhocodes/retry': 0.3.1 + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/retry@0.3.1': {} + + '@humanwhocodes/retry@0.4.1': {} + + '@isaacs/cliui@8.0.2': + dependencies: + string-width: 5.1.2 + string-width-cjs: string-width@4.2.3 + strip-ansi: 7.1.0 + strip-ansi-cjs: strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: wrap-ansi@7.0.0 + + '@isaacs/fs-minipass@4.0.1': + dependencies: + minipass: 7.1.2 + + '@jridgewell/gen-mapping@0.3.8': + dependencies: + '@jridgewell/set-array': 1.2.1 + '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/trace-mapping': 0.3.25 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/set-array@1.2.1': {} + + '@jridgewell/sourcemap-codec@1.5.0': {} + + '@jridgewell/trace-mapping@0.3.25': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.0 + + '@kurkle/color@0.3.4': {} + + '@loaders.gl/core@4.3.3': + dependencies: + '@loaders.gl/loader-utils': 4.3.3(@loaders.gl/core@4.3.3) + '@loaders.gl/schema': 4.3.3(@loaders.gl/core@4.3.3) + '@loaders.gl/worker-utils': 4.3.3(@loaders.gl/core@4.3.3) + '@probe.gl/log': 4.1.0 + + '@loaders.gl/images@4.3.3(@loaders.gl/core@4.3.3)': + dependencies: + '@loaders.gl/core': 4.3.3 + '@loaders.gl/loader-utils': 4.3.3(@loaders.gl/core@4.3.3) + + '@loaders.gl/loader-utils@4.3.3(@loaders.gl/core@4.3.3)': + dependencies: + '@loaders.gl/core': 4.3.3 + '@loaders.gl/schema': 4.3.3(@loaders.gl/core@4.3.3) + '@loaders.gl/worker-utils': 4.3.3(@loaders.gl/core@4.3.3) + '@probe.gl/log': 4.1.0 + '@probe.gl/stats': 4.1.0 + + '@loaders.gl/schema@4.3.3(@loaders.gl/core@4.3.3)': + dependencies: + '@loaders.gl/core': 4.3.3 + '@types/geojson': 7946.0.16 + + '@loaders.gl/worker-utils@4.3.3(@loaders.gl/core@4.3.3)': + dependencies: + '@loaders.gl/core': 4.3.3 + + '@luma.gl/constants@9.1.0': {} + + '@luma.gl/core@9.1.0': + dependencies: + '@math.gl/types': 4.1.0 + '@probe.gl/env': 4.1.0 + '@probe.gl/log': 4.1.0 + '@probe.gl/stats': 4.1.0 + '@types/offscreencanvas': 2019.7.3 + + '@luma.gl/engine@9.1.0(@luma.gl/core@9.1.0)(@luma.gl/shadertools@9.1.0(@luma.gl/core@9.1.0))': + dependencies: + '@luma.gl/core': 9.1.0 + '@luma.gl/shadertools': 9.1.0(@luma.gl/core@9.1.0) + '@math.gl/core': 4.1.0 + '@math.gl/types': 4.1.0 + '@probe.gl/log': 4.1.0 + '@probe.gl/stats': 4.1.0 + + '@luma.gl/shadertools@9.1.0(@luma.gl/core@9.1.0)': + dependencies: + '@luma.gl/core': 9.1.0 + '@math.gl/core': 4.1.0 + '@math.gl/types': 4.1.0 + wgsl_reflect: 1.0.17 + + '@luma.gl/webgl@9.1.0(@luma.gl/core@9.1.0)': + dependencies: + '@luma.gl/constants': 9.1.0 + '@luma.gl/core': 9.1.0 + '@math.gl/types': 4.1.0 + '@probe.gl/env': 4.1.0 + + '@manypkg/find-root@1.1.0': + dependencies: + '@babel/runtime': 7.26.7 + '@types/node': 12.20.55 + find-up: 4.1.0 + fs-extra: 8.1.0 + + '@manypkg/get-packages@1.1.3': + dependencies: + '@babel/runtime': 7.26.7 + '@changesets/types': 4.1.0 + '@manypkg/find-root': 1.1.0 + fs-extra: 8.1.0 + globby: 11.1.0 + read-yaml-file: 1.1.0 + + '@mapbox/geojson-area@0.2.2': + dependencies: + wgs84: 0.0.0 + + '@mapbox/geojson-normalize@0.0.1': {} + + '@mapbox/geojson-rewind@0.5.2': + dependencies: + get-stream: 6.0.1 + minimist: 1.2.8 + + '@mapbox/jsonlint-lines-primitives@2.0.2': {} + + '@mapbox/mapbox-gl-draw@1.5.0': + dependencies: + '@mapbox/geojson-area': 0.2.2 + '@mapbox/geojson-normalize': 0.0.1 + '@mapbox/point-geometry': 1.1.0 + fast-deep-equal: 3.1.3 + nanoid: 5.0.9 + + '@mapbox/mapbox-gl-supported@3.0.0': {} + + '@mapbox/node-pre-gyp@2.0.0': + dependencies: + consola: 3.4.0 + detect-libc: 2.0.3 + https-proxy-agent: 7.0.6 + node-fetch: 2.7.0 + nopt: 8.1.0 + semver: 7.7.1 + tar: 7.4.3 + transitivePeerDependencies: + - encoding + - supports-color + + '@mapbox/point-geometry@0.1.0': {} + + '@mapbox/point-geometry@1.1.0': {} + + '@mapbox/tiny-sdf@2.0.6': {} + + '@mapbox/unitbezier@0.0.1': {} + + '@mapbox/vector-tile@1.3.1': + dependencies: + '@mapbox/point-geometry': 0.1.0 + + '@mapbox/whoots-js@3.1.0': {} + + '@maplibre/maplibre-gl-style-spec@23.1.0': + dependencies: + '@mapbox/jsonlint-lines-primitives': 2.0.2 + '@mapbox/unitbezier': 0.0.1 + json-stringify-pretty-compact: 4.0.0 + minimist: 1.2.8 + quickselect: 3.0.0 + rw: 1.3.3 + tinyqueue: 3.0.0 + + '@math.gl/core@4.1.0': + dependencies: + '@math.gl/types': 4.1.0 + + '@math.gl/polygon@4.1.0': + dependencies: + '@math.gl/core': 4.1.0 + + '@math.gl/sun@4.1.0': {} + + '@math.gl/types@4.1.0': {} + + '@math.gl/web-mercator@4.1.0': + dependencies: + '@math.gl/core': 4.1.0 + + '@noble/ciphers@0.5.3': {} + + '@noble/curves@1.1.0': + dependencies: + '@noble/hashes': 1.3.1 + + '@noble/curves@1.2.0': + dependencies: + '@noble/hashes': 1.3.2 + + '@noble/curves@1.8.1': + dependencies: + '@noble/hashes': 1.7.1 + + '@noble/hashes@1.3.1': {} + + '@noble/hashes@1.3.2': {} + + '@noble/hashes@1.7.1': {} + + '@noble/secp256k1@2.2.3': {} + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.19.0 + + '@nostr-dev-kit/ndk-cache-dexie@2.5.9(typescript@5.7.3)': + dependencies: + '@nostr-dev-kit/ndk': 2.11.0(typescript@5.7.3) + debug: 4.4.0 + dexie: 4.0.11 + nostr-tools: 2.10.4(typescript@5.7.3) + typescript-lru-cache: 2.0.0 + transitivePeerDependencies: + - supports-color + - typescript + + '@nostr-dev-kit/ndk-svelte@2.4.0(svelte@5.19.9)(typescript@5.7.3)': + dependencies: + '@nostr-dev-kit/ndk': 2.11.0(typescript@5.7.3) + svelte: 5.19.9 + transitivePeerDependencies: + - supports-color + - typescript + + '@nostr-dev-kit/ndk@2.11.0(typescript@5.7.3)': + dependencies: + '@noble/curves': 1.8.1 + '@noble/hashes': 1.7.1 + '@noble/secp256k1': 2.2.3 + '@scure/base': 1.2.4 + debug: 4.4.0 + light-bolt11-decoder: 3.2.0 + nostr-tools: 2.10.4(typescript@5.7.3) + tseep: 1.3.1 + typescript-lru-cache: 2.0.0 + utf8-buffer: 1.0.0 + websocket-polyfill: 0.0.3 + transitivePeerDependencies: + - supports-color + - typescript + + '@pkgjs/parseargs@0.11.0': + optional: true + + '@playwright/test@1.50.1': + dependencies: + playwright: 1.50.1 + + '@polka/url@1.0.0-next.28': {} + + '@probe.gl/env@4.1.0': {} + + '@probe.gl/log@4.1.0': + dependencies: + '@probe.gl/env': 4.1.0 + + '@probe.gl/stats@4.1.0': {} + + '@publint/pack@0.1.1': {} + + '@rollup/pluginutils@5.1.4(rollup@4.34.6)': + dependencies: + '@types/estree': 1.0.6 + estree-walker: 2.0.2 + picomatch: 4.0.2 + optionalDependencies: + rollup: 4.34.6 + + '@rollup/rollup-android-arm-eabi@4.34.6': + optional: true + + '@rollup/rollup-android-arm64@4.34.6': + optional: true + + '@rollup/rollup-darwin-arm64@4.34.6': + optional: true + + '@rollup/rollup-darwin-x64@4.34.6': + optional: true + + '@rollup/rollup-freebsd-arm64@4.34.6': + optional: true + + '@rollup/rollup-freebsd-x64@4.34.6': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.34.6': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.34.6': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.34.6': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.34.6': + optional: true + + '@rollup/rollup-linux-loongarch64-gnu@4.34.6': + optional: true + + '@rollup/rollup-linux-powerpc64le-gnu@4.34.6': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.34.6': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.34.6': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.34.6': + optional: true + + '@rollup/rollup-linux-x64-musl@4.34.6': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.34.6': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.34.6': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.34.6': + optional: true + + '@scure/base@1.1.1': {} + + '@scure/base@1.2.4': {} + + '@scure/bip32@1.3.1': + dependencies: + '@noble/curves': 1.1.0 + '@noble/hashes': 1.3.1 + '@scure/base': 1.1.1 + + '@scure/bip39@1.2.1': + dependencies: + '@noble/hashes': 1.3.1 + '@scure/base': 1.1.1 + + '@skeletonlabs/skeleton@2.11.0(svelte@5.19.9)': + dependencies: + esm-env: 1.0.0 + svelte: 5.19.9 + + '@skeletonlabs/tw-plugin@0.4.1(tailwindcss@3.4.17)': + dependencies: + tailwindcss: 3.4.17 + + '@sveltejs/adapter-auto@4.0.0(@sveltejs/kit@2.17.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.19.9)(vite@6.1.0(@types/node@22.13.1)(jiti@1.21.7)(yaml@2.7.0)))(svelte@5.19.9)(vite@6.1.0(@types/node@22.13.1)(jiti@1.21.7)(yaml@2.7.0)))': + dependencies: + '@sveltejs/kit': 2.17.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.19.9)(vite@6.1.0(@types/node@22.13.1)(jiti@1.21.7)(yaml@2.7.0)))(svelte@5.19.9)(vite@6.1.0(@types/node@22.13.1)(jiti@1.21.7)(yaml@2.7.0)) + import-meta-resolve: 4.1.0 + + '@sveltejs/adapter-static@3.0.8(@sveltejs/kit@2.17.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.19.9)(vite@6.1.0(@types/node@22.13.1)(jiti@1.21.7)(yaml@2.7.0)))(svelte@5.19.9)(vite@6.1.0(@types/node@22.13.1)(jiti@1.21.7)(yaml@2.7.0)))': + dependencies: + '@sveltejs/kit': 2.17.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.19.9)(vite@6.1.0(@types/node@22.13.1)(jiti@1.21.7)(yaml@2.7.0)))(svelte@5.19.9)(vite@6.1.0(@types/node@22.13.1)(jiti@1.21.7)(yaml@2.7.0)) + + '@sveltejs/adapter-vercel@5.6.1(@sveltejs/kit@2.17.1(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.19.9)(vite@5.4.14(@types/node@22.13.1)))(svelte@5.19.9)(vite@5.4.14(@types/node@22.13.1)))(rollup@4.34.6)': + dependencies: + '@sveltejs/kit': 2.17.1(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.19.9)(vite@5.4.14(@types/node@22.13.1)))(svelte@5.19.9)(vite@5.4.14(@types/node@22.13.1)) + '@vercel/nft': 0.29.1(rollup@4.34.6) + esbuild: 0.24.2 + transitivePeerDependencies: + - encoding + - rollup + - supports-color + + '@sveltejs/kit@2.17.1(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.19.9)(vite@5.4.14(@types/node@22.13.1)))(svelte@5.19.9)(vite@5.4.14(@types/node@22.13.1))': + dependencies: + '@sveltejs/vite-plugin-svelte': 4.0.4(svelte@5.19.9)(vite@5.4.14(@types/node@22.13.1)) + '@types/cookie': 0.6.0 + cookie: 0.6.0 + devalue: 5.1.1 + esm-env: 1.2.2 + import-meta-resolve: 4.1.0 + kleur: 4.1.5 + magic-string: 0.30.17 + mrmime: 2.0.0 + sade: 1.8.1 + set-cookie-parser: 2.7.1 + sirv: 3.0.0 + svelte: 5.19.9 + vite: 5.4.14(@types/node@22.13.1) + + '@sveltejs/kit@2.17.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.19.9)(vite@6.1.0(@types/node@22.13.1)(jiti@1.21.7)(yaml@2.7.0)))(svelte@5.19.9)(vite@6.1.0(@types/node@22.13.1)(jiti@1.21.7)(yaml@2.7.0))': + dependencies: + '@sveltejs/vite-plugin-svelte': 5.0.3(svelte@5.19.9)(vite@6.1.0(@types/node@22.13.1)(jiti@1.21.7)(yaml@2.7.0)) + '@types/cookie': 0.6.0 + cookie: 0.6.0 + devalue: 5.1.1 + esm-env: 1.2.2 + import-meta-resolve: 4.1.0 + kleur: 4.1.5 + magic-string: 0.30.17 + mrmime: 2.0.0 + sade: 1.8.1 + set-cookie-parser: 2.7.1 + sirv: 3.0.0 + svelte: 5.19.9 + vite: 6.1.0(@types/node@22.13.1)(jiti@1.21.7)(yaml@2.7.0) + + '@sveltejs/package@2.3.10(svelte@5.19.9)(typescript@5.7.3)': + dependencies: + chokidar: 4.0.3 + kleur: 4.1.5 + sade: 1.8.1 + semver: 7.7.1 + svelte: 5.19.9 + svelte2tsx: 0.7.34(svelte@5.19.9)(typescript@5.7.3) + transitivePeerDependencies: + - typescript + + '@sveltejs/vite-plugin-svelte-inspector@2.1.0(@sveltejs/vite-plugin-svelte@3.1.2(svelte@5.19.9)(vite@6.1.0(@types/node@22.13.1)(jiti@1.21.7)(yaml@2.7.0)))(svelte@5.19.9)(vite@6.1.0(@types/node@22.13.1)(jiti@1.21.7)(yaml@2.7.0))': + dependencies: + '@sveltejs/vite-plugin-svelte': 3.1.2(svelte@5.19.9)(vite@6.1.0(@types/node@22.13.1)(jiti@1.21.7)(yaml@2.7.0)) + debug: 4.4.0 + svelte: 5.19.9 + vite: 6.1.0(@types/node@22.13.1)(jiti@1.21.7)(yaml@2.7.0) + transitivePeerDependencies: + - supports-color + + '@sveltejs/vite-plugin-svelte-inspector@3.0.1(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.19.9)(vite@5.4.14(@types/node@22.13.1)))(svelte@5.19.9)(vite@5.4.14(@types/node@22.13.1))': + dependencies: + '@sveltejs/vite-plugin-svelte': 4.0.4(svelte@5.19.9)(vite@5.4.14(@types/node@22.13.1)) + debug: 4.4.0 + svelte: 5.19.9 + vite: 5.4.14(@types/node@22.13.1) + transitivePeerDependencies: + - supports-color + + '@sveltejs/vite-plugin-svelte-inspector@4.0.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.19.9)(vite@6.1.0(@types/node@22.13.1)(jiti@1.21.7)(yaml@2.7.0)))(svelte@5.19.9)(vite@6.1.0(@types/node@22.13.1)(jiti@1.21.7)(yaml@2.7.0))': + dependencies: + '@sveltejs/vite-plugin-svelte': 5.0.3(svelte@5.19.9)(vite@6.1.0(@types/node@22.13.1)(jiti@1.21.7)(yaml@2.7.0)) + debug: 4.4.0 + svelte: 5.19.9 + vite: 6.1.0(@types/node@22.13.1)(jiti@1.21.7)(yaml@2.7.0) + transitivePeerDependencies: + - supports-color + + '@sveltejs/vite-plugin-svelte@3.1.2(svelte@5.19.9)(vite@6.1.0(@types/node@22.13.1)(jiti@1.21.7)(yaml@2.7.0))': + dependencies: + '@sveltejs/vite-plugin-svelte-inspector': 2.1.0(@sveltejs/vite-plugin-svelte@3.1.2(svelte@5.19.9)(vite@6.1.0(@types/node@22.13.1)(jiti@1.21.7)(yaml@2.7.0)))(svelte@5.19.9)(vite@6.1.0(@types/node@22.13.1)(jiti@1.21.7)(yaml@2.7.0)) + debug: 4.4.0 + deepmerge: 4.3.1 + kleur: 4.1.5 + magic-string: 0.30.17 + svelte: 5.19.9 + svelte-hmr: 0.16.0(svelte@5.19.9) + vite: 6.1.0(@types/node@22.13.1)(jiti@1.21.7)(yaml@2.7.0) + vitefu: 0.2.5(vite@6.1.0(@types/node@22.13.1)(jiti@1.21.7)(yaml@2.7.0)) + transitivePeerDependencies: + - supports-color + + '@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.19.9)(vite@5.4.14(@types/node@22.13.1))': + dependencies: + '@sveltejs/vite-plugin-svelte-inspector': 3.0.1(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.19.9)(vite@5.4.14(@types/node@22.13.1)))(svelte@5.19.9)(vite@5.4.14(@types/node@22.13.1)) + debug: 4.4.0 + deepmerge: 4.3.1 + kleur: 4.1.5 + magic-string: 0.30.17 + svelte: 5.19.9 + vite: 5.4.14(@types/node@22.13.1) + vitefu: 1.0.5(vite@5.4.14(@types/node@22.13.1)) + transitivePeerDependencies: + - supports-color + + '@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.19.9)(vite@6.1.0(@types/node@22.13.1)(jiti@1.21.7)(yaml@2.7.0))': + dependencies: + '@sveltejs/vite-plugin-svelte-inspector': 4.0.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.19.9)(vite@6.1.0(@types/node@22.13.1)(jiti@1.21.7)(yaml@2.7.0)))(svelte@5.19.9)(vite@6.1.0(@types/node@22.13.1)(jiti@1.21.7)(yaml@2.7.0)) + debug: 4.4.0 + deepmerge: 4.3.1 + kleur: 4.1.5 + magic-string: 0.30.17 + svelte: 5.19.9 + vite: 6.1.0(@types/node@22.13.1)(jiti@1.21.7)(yaml@2.7.0) + vitefu: 1.0.5(vite@6.1.0(@types/node@22.13.1)(jiti@1.21.7)(yaml@2.7.0)) + transitivePeerDependencies: + - supports-color + + '@sveltekit-i18n/base@1.3.7(svelte@5.19.9)': + dependencies: + svelte: 5.19.9 + + '@sveltekit-i18n/parser-icu@1.0.8': + dependencies: + intl-messageformat: 10.7.15 + + '@tauri-apps/api@2.0.3': {} + + '@tauri-apps/cli-darwin-arm64@2.0.4': + optional: true + + '@tauri-apps/cli-darwin-x64@2.0.4': + optional: true + + '@tauri-apps/cli-linux-arm-gnueabihf@2.0.4': + optional: true + + '@tauri-apps/cli-linux-arm64-gnu@2.0.4': + optional: true + + '@tauri-apps/cli-linux-arm64-musl@2.0.4': + optional: true + + '@tauri-apps/cli-linux-x64-gnu@2.0.4': + optional: true + + '@tauri-apps/cli-linux-x64-musl@2.0.4': + optional: true + + '@tauri-apps/cli-win32-arm64-msvc@2.0.4': + optional: true + + '@tauri-apps/cli-win32-ia32-msvc@2.0.4': + optional: true + + '@tauri-apps/cli-win32-x64-msvc@2.0.4': + optional: true + + '@tauri-apps/cli@2.0.4': + optionalDependencies: + '@tauri-apps/cli-darwin-arm64': 2.0.4 + '@tauri-apps/cli-darwin-x64': 2.0.4 + '@tauri-apps/cli-linux-arm-gnueabihf': 2.0.4 + '@tauri-apps/cli-linux-arm64-gnu': 2.0.4 + '@tauri-apps/cli-linux-arm64-musl': 2.0.4 + '@tauri-apps/cli-linux-x64-gnu': 2.0.4 + '@tauri-apps/cli-linux-x64-musl': 2.0.4 + '@tauri-apps/cli-win32-arm64-msvc': 2.0.4 + '@tauri-apps/cli-win32-ia32-msvc': 2.0.4 + '@tauri-apps/cli-win32-x64-msvc': 2.0.4 + + '@tauri-apps/plugin-dialog@2.2.0': + dependencies: + '@tauri-apps/api': 2.0.3 + + '@tauri-apps/plugin-fs@2.2.0': + dependencies: + '@tauri-apps/api': 2.0.3 + + '@tauri-apps/plugin-geolocation@2.2.3': + dependencies: + '@tauri-apps/api': 2.0.3 + + '@tauri-apps/plugin-haptics@2.2.3': + dependencies: + '@tauri-apps/api': 2.0.3 + + '@tauri-apps/plugin-http@2.3.0': + dependencies: + '@tauri-apps/api': 2.0.3 + + '@tauri-apps/plugin-log@2.2.1': + dependencies: + '@tauri-apps/api': 2.0.3 + + '@tauri-apps/plugin-notification@2.2.1': + dependencies: + '@tauri-apps/api': 2.0.3 + + '@tauri-apps/plugin-os@2.2.0': + dependencies: + '@tauri-apps/api': 2.0.3 + + '@tauri-apps/plugin-store@2.2.0': + dependencies: + '@tauri-apps/api': 2.0.3 + + '@types/cookie@0.6.0': {} + + '@types/d3-color@3.1.3': {} + + '@types/d3-geo@3.1.0': + dependencies: + '@types/geojson': 7946.0.16 + + '@types/debug@4.1.12': + dependencies: + '@types/ms': 2.1.0 + + '@types/emscripten@1.40.0': {} + + '@types/estree@1.0.6': {} + + '@types/geojson-vt@3.2.5': + dependencies: + '@types/geojson': 7946.0.16 + + '@types/geojson@7946.0.16': {} + + '@types/json-schema@7.0.15': {} + + '@types/leaflet@1.9.16': + dependencies: + '@types/geojson': 7946.0.16 + + '@types/mapbox__mapbox-gl-draw@1.4.8': + dependencies: + '@types/geojson': 7946.0.16 + mapbox-gl: 3.9.4 + + '@types/mapbox__point-geometry@0.1.4': {} + + '@types/mapbox__vector-tile@1.3.4': + dependencies: + '@types/geojson': 7946.0.16 + '@types/mapbox__point-geometry': 0.1.4 + '@types/pbf': 3.0.5 + + '@types/ms@2.1.0': {} + + '@types/ngeohash@0.6.8': {} + + '@types/node@12.20.55': {} + + '@types/node@22.13.1': + dependencies: + undici-types: 6.20.0 + + '@types/offscreencanvas@2019.7.3': {} + + '@types/pbf@3.0.5': {} + + '@types/sql.js@1.4.9': + dependencies: + '@types/emscripten': 1.40.0 + '@types/node': 22.13.1 + + '@types/supercluster@7.1.3': + dependencies: + '@types/geojson': 7946.0.16 + + '@types/uuid@10.0.0': {} + + '@typescript-eslint/eslint-plugin@7.18.0(@typescript-eslint/parser@7.18.0(eslint@9.20.0(jiti@1.21.7))(typescript@5.7.3))(eslint@9.20.0(jiti@1.21.7))(typescript@5.7.3)': + dependencies: + '@eslint-community/regexpp': 4.12.1 + '@typescript-eslint/parser': 7.18.0(eslint@9.20.0(jiti@1.21.7))(typescript@5.7.3) + '@typescript-eslint/scope-manager': 7.18.0 + '@typescript-eslint/type-utils': 7.18.0(eslint@9.20.0(jiti@1.21.7))(typescript@5.7.3) + '@typescript-eslint/utils': 7.18.0(eslint@9.20.0(jiti@1.21.7))(typescript@5.7.3) + '@typescript-eslint/visitor-keys': 7.18.0 + eslint: 9.20.0(jiti@1.21.7) + graphemer: 1.4.0 + ignore: 5.3.2 + natural-compare: 1.4.0 + ts-api-utils: 1.4.3(typescript@5.7.3) + optionalDependencies: + typescript: 5.7.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/eslint-plugin@8.23.0(@typescript-eslint/parser@8.23.0(eslint@9.20.0(jiti@1.21.7))(typescript@5.7.3))(eslint@9.20.0(jiti@1.21.7))(typescript@5.7.3)': + dependencies: + '@eslint-community/regexpp': 4.12.1 + '@typescript-eslint/parser': 8.23.0(eslint@9.20.0(jiti@1.21.7))(typescript@5.7.3) + '@typescript-eslint/scope-manager': 8.23.0 + '@typescript-eslint/type-utils': 8.23.0(eslint@9.20.0(jiti@1.21.7))(typescript@5.7.3) + '@typescript-eslint/utils': 8.23.0(eslint@9.20.0(jiti@1.21.7))(typescript@5.7.3) + '@typescript-eslint/visitor-keys': 8.23.0 + eslint: 9.20.0(jiti@1.21.7) + graphemer: 1.4.0 + ignore: 5.3.2 + natural-compare: 1.4.0 + ts-api-utils: 2.0.1(typescript@5.7.3) + typescript: 5.7.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@7.18.0(eslint@9.20.0(jiti@1.21.7))(typescript@5.7.3)': + dependencies: + '@typescript-eslint/scope-manager': 7.18.0 + '@typescript-eslint/types': 7.18.0 + '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.7.3) + '@typescript-eslint/visitor-keys': 7.18.0 + debug: 4.4.0 + eslint: 9.20.0(jiti@1.21.7) + optionalDependencies: + typescript: 5.7.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@8.23.0(eslint@9.20.0(jiti@1.21.7))(typescript@5.7.3)': + dependencies: + '@typescript-eslint/scope-manager': 8.23.0 + '@typescript-eslint/types': 8.23.0 + '@typescript-eslint/typescript-estree': 8.23.0(typescript@5.7.3) + '@typescript-eslint/visitor-keys': 8.23.0 + debug: 4.4.0 + eslint: 9.20.0(jiti@1.21.7) + typescript: 5.7.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@7.18.0': + dependencies: + '@typescript-eslint/types': 7.18.0 + '@typescript-eslint/visitor-keys': 7.18.0 + + '@typescript-eslint/scope-manager@8.23.0': + dependencies: + '@typescript-eslint/types': 8.23.0 + '@typescript-eslint/visitor-keys': 8.23.0 + + '@typescript-eslint/type-utils@7.18.0(eslint@9.20.0(jiti@1.21.7))(typescript@5.7.3)': + dependencies: + '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.7.3) + '@typescript-eslint/utils': 7.18.0(eslint@9.20.0(jiti@1.21.7))(typescript@5.7.3) + debug: 4.4.0 + eslint: 9.20.0(jiti@1.21.7) + ts-api-utils: 1.4.3(typescript@5.7.3) + optionalDependencies: + typescript: 5.7.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/type-utils@8.23.0(eslint@9.20.0(jiti@1.21.7))(typescript@5.7.3)': + dependencies: + '@typescript-eslint/typescript-estree': 8.23.0(typescript@5.7.3) + '@typescript-eslint/utils': 8.23.0(eslint@9.20.0(jiti@1.21.7))(typescript@5.7.3) + debug: 4.4.0 + eslint: 9.20.0(jiti@1.21.7) + ts-api-utils: 2.0.1(typescript@5.7.3) + typescript: 5.7.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@7.18.0': {} + + '@typescript-eslint/types@8.23.0': {} + + '@typescript-eslint/typescript-estree@7.18.0(typescript@5.7.3)': + dependencies: + '@typescript-eslint/types': 7.18.0 + '@typescript-eslint/visitor-keys': 7.18.0 + debug: 4.4.0 + globby: 11.1.0 + is-glob: 4.0.3 + minimatch: 9.0.5 + semver: 7.7.1 + ts-api-utils: 1.4.3(typescript@5.7.3) + optionalDependencies: + typescript: 5.7.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/typescript-estree@8.23.0(typescript@5.7.3)': + dependencies: + '@typescript-eslint/types': 8.23.0 + '@typescript-eslint/visitor-keys': 8.23.0 + debug: 4.4.0 + fast-glob: 3.3.3 + is-glob: 4.0.3 + minimatch: 9.0.5 + semver: 7.7.1 + ts-api-utils: 2.0.1(typescript@5.7.3) + typescript: 5.7.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@7.18.0(eslint@9.20.0(jiti@1.21.7))(typescript@5.7.3)': + dependencies: + '@eslint-community/eslint-utils': 4.4.1(eslint@9.20.0(jiti@1.21.7)) + '@typescript-eslint/scope-manager': 7.18.0 + '@typescript-eslint/types': 7.18.0 + '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.7.3) + eslint: 9.20.0(jiti@1.21.7) + transitivePeerDependencies: + - supports-color + - typescript + + '@typescript-eslint/utils@8.23.0(eslint@9.20.0(jiti@1.21.7))(typescript@5.7.3)': + dependencies: + '@eslint-community/eslint-utils': 4.4.1(eslint@9.20.0(jiti@1.21.7)) + '@typescript-eslint/scope-manager': 8.23.0 + '@typescript-eslint/types': 8.23.0 + '@typescript-eslint/typescript-estree': 8.23.0(typescript@5.7.3) + eslint: 9.20.0(jiti@1.21.7) + typescript: 5.7.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/visitor-keys@7.18.0': + dependencies: + '@typescript-eslint/types': 7.18.0 + eslint-visitor-keys: 3.4.3 + + '@typescript-eslint/visitor-keys@8.23.0': + dependencies: + '@typescript-eslint/types': 8.23.0 + eslint-visitor-keys: 4.2.0 + + '@vercel/nft@0.29.1(rollup@4.34.6)': + dependencies: + '@mapbox/node-pre-gyp': 2.0.0 + '@rollup/pluginutils': 5.1.4(rollup@4.34.6) + acorn: 8.14.0 + acorn-import-attributes: 1.9.5(acorn@8.14.0) + async-sema: 3.1.1 + bindings: 1.5.0 + estree-walker: 2.0.2 + glob: 7.2.3 + graceful-fs: 4.2.11 + node-gyp-build: 4.8.4 + picomatch: 4.0.2 + resolve-from: 5.0.0 + transitivePeerDependencies: + - encoding + - rollup + - supports-color + + '@vitest/expect@2.1.9': + dependencies: + '@vitest/spy': 2.1.9 + '@vitest/utils': 2.1.9 + chai: 5.1.2 + tinyrainbow: 1.2.0 + + '@vitest/mocker@2.1.9(vite@5.4.14(@types/node@22.13.1))': + dependencies: + '@vitest/spy': 2.1.9 + estree-walker: 3.0.3 + magic-string: 0.30.17 + optionalDependencies: + vite: 5.4.14(@types/node@22.13.1) + + '@vitest/pretty-format@2.1.9': + dependencies: + tinyrainbow: 1.2.0 + + '@vitest/runner@2.1.9': + dependencies: + '@vitest/utils': 2.1.9 + pathe: 1.1.2 + + '@vitest/snapshot@2.1.9': + dependencies: + '@vitest/pretty-format': 2.1.9 + magic-string: 0.30.17 + pathe: 1.1.2 + + '@vitest/spy@2.1.9': + dependencies: + tinyspy: 3.0.2 + + '@vitest/utils@2.1.9': + dependencies: + '@vitest/pretty-format': 2.1.9 + loupe: 3.1.3 + tinyrainbow: 1.2.0 + + abbrev@3.0.0: {} + + acorn-import-attributes@1.9.5(acorn@8.14.0): + dependencies: + acorn: 8.14.0 + + acorn-jsx@5.3.2(acorn@8.14.0): + dependencies: + acorn: 8.14.0 + + acorn-typescript@1.4.13(acorn@8.14.0): + dependencies: + acorn: 8.14.0 + + acorn@8.14.0: {} + + agent-base@7.1.3: {} + + ajv@6.12.6: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ansi-colors@4.1.3: {} + + ansi-escapes@7.0.0: + dependencies: + environment: 1.1.0 + + ansi-regex@5.0.1: {} + + ansi-regex@6.1.0: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansi-styles@6.2.1: {} + + any-promise@1.3.0: {} + + anymatch@3.1.3: + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + + arg@5.0.2: {} + + argparse@1.0.10: + dependencies: + sprintf-js: 1.0.3 + + argparse@2.0.1: {} + + aria-query@5.3.2: {} + + array-union@2.1.0: {} + + assertion-error@2.0.1: {} + + async-sema@3.1.1: {} + + autoprefixer@10.4.20(postcss@8.5.1): + dependencies: + browserslist: 4.24.4 + caniuse-lite: 1.0.30001699 + fraction.js: 4.3.7 + normalize-range: 0.1.2 + picocolors: 1.1.1 + postcss: 8.5.1 + postcss-value-parser: 4.2.0 + + axobject-query@4.1.0: {} + + balanced-match@1.0.2: {} + + better-path-resolve@1.0.0: + dependencies: + is-windows: 1.0.2 + + binary-extensions@2.3.0: {} + + bindings@1.5.0: + dependencies: + file-uri-to-path: 1.0.0 + + brace-expansion@1.1.11: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@2.0.1: + dependencies: + balanced-match: 1.0.2 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + browserslist@4.24.4: + dependencies: + caniuse-lite: 1.0.30001699 + electron-to-chromium: 1.5.96 + node-releases: 2.0.19 + update-browserslist-db: 1.1.2(browserslist@4.24.4) + + bufferutil@4.0.9: + dependencies: + node-gyp-build: 4.8.4 + + bundle-require@4.2.1(esbuild@0.17.19): + dependencies: + esbuild: 0.17.19 + load-tsconfig: 0.2.5 + + cac@6.7.14: {} + + callsites@3.1.0: {} + + camelcase-css@2.0.1: {} + + caniuse-lite@1.0.30001699: {} + + chai@5.1.2: + dependencies: + assertion-error: 2.0.1 + check-error: 2.1.1 + deep-eql: 5.0.2 + loupe: 3.1.3 + pathval: 2.0.0 + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + chalk@5.4.1: {} + + chardet@0.7.0: {} + + chart.js@4.4.7: + dependencies: + '@kurkle/color': 0.3.4 + + cheap-ruler@4.0.0: {} + + check-error@2.1.1: {} + + chokidar@3.6.0: + dependencies: + anymatch: 3.1.3 + braces: 3.0.3 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + + chokidar@4.0.3: + dependencies: + readdirp: 4.1.1 + + chownr@3.0.0: {} + + ci-info@3.9.0: {} + + cli-cursor@5.0.0: + dependencies: + restore-cursor: 5.1.0 + + cli-truncate@4.0.0: + dependencies: + slice-ansi: 5.0.0 + string-width: 7.2.0 + + clsx@2.1.1: {} + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + colorette@2.0.20: {} + + commander@13.1.0: {} + + commander@4.1.1: {} + + concat-map@0.0.1: {} + + consola@3.4.0: {} + + convert@5.7.0: {} + + cookie@0.6.0: {} + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + css-paint-polyfill@3.4.0: {} + + css-selector-tokenizer@0.8.0: + dependencies: + cssesc: 3.0.0 + fastparse: 1.1.2 + + csscolorparser@1.0.3: {} + + cssesc@3.0.0: {} + + culori@3.3.0: {} + + d3-array@3.2.4: + dependencies: + internmap: 2.0.3 + + d3-color@3.1.0: {} + + d3-geo@3.1.1: + dependencies: + d3-array: 3.2.4 + + d@1.0.2: + dependencies: + es5-ext: 0.10.64 + type: 2.7.3 + + daisyui@4.12.23(postcss@8.5.1): + dependencies: + css-selector-tokenizer: 0.8.0 + culori: 3.3.0 + picocolors: 1.1.1 + postcss-js: 4.0.1(postcss@8.5.1) + transitivePeerDependencies: + - postcss + + dataloader@1.4.0: {} + + debug@2.6.9: + dependencies: + ms: 2.0.0 + + debug@4.4.0: + dependencies: + ms: 2.1.3 + + decimal.js@10.5.0: {} + + dedent-js@1.0.1: {} + + dedent@1.5.3: {} + + deep-eql@5.0.2: {} + + deep-is@0.1.4: {} + + deepmerge@4.3.1: {} + + dequal@2.0.3: {} + + detect-indent@6.1.0: {} + + detect-libc@2.0.3: {} + + devalue@5.1.1: {} + + dexie@4.0.11: {} + + didyoumean@1.2.2: {} + + dir-glob@3.0.1: + dependencies: + path-type: 4.0.0 + + dlv@1.1.3: {} + + dotenv@16.0.3: {} + + dotenv@8.6.0: {} + + earcut@2.2.4: {} + + earcut@3.0.1: {} + + eastasianwidth@0.2.0: {} + + electron-to-chromium@1.5.96: {} + + emoji-regex@10.4.0: {} + + emoji-regex@8.0.0: {} + + emoji-regex@9.2.2: {} + + enquirer@2.4.1: + dependencies: + ansi-colors: 4.1.3 + strip-ansi: 6.0.1 + + environment@1.1.0: {} + + es-module-lexer@1.6.0: {} + + es5-ext@0.10.64: + dependencies: + es6-iterator: 2.0.3 + es6-symbol: 3.1.4 + esniff: 2.0.1 + next-tick: 1.1.0 + + es6-iterator@2.0.3: + dependencies: + d: 1.0.2 + es5-ext: 0.10.64 + es6-symbol: 3.1.4 + + es6-symbol@3.1.4: + dependencies: + d: 1.0.2 + ext: 1.7.0 + + esbuild@0.17.19: + optionalDependencies: + '@esbuild/android-arm': 0.17.19 + '@esbuild/android-arm64': 0.17.19 + '@esbuild/android-x64': 0.17.19 + '@esbuild/darwin-arm64': 0.17.19 + '@esbuild/darwin-x64': 0.17.19 + '@esbuild/freebsd-arm64': 0.17.19 + '@esbuild/freebsd-x64': 0.17.19 + '@esbuild/linux-arm': 0.17.19 + '@esbuild/linux-arm64': 0.17.19 + '@esbuild/linux-ia32': 0.17.19 + '@esbuild/linux-loong64': 0.17.19 + '@esbuild/linux-mips64el': 0.17.19 + '@esbuild/linux-ppc64': 0.17.19 + '@esbuild/linux-riscv64': 0.17.19 + '@esbuild/linux-s390x': 0.17.19 + '@esbuild/linux-x64': 0.17.19 + '@esbuild/netbsd-x64': 0.17.19 + '@esbuild/openbsd-x64': 0.17.19 + '@esbuild/sunos-x64': 0.17.19 + '@esbuild/win32-arm64': 0.17.19 + '@esbuild/win32-ia32': 0.17.19 + '@esbuild/win32-x64': 0.17.19 + + esbuild@0.21.5: + optionalDependencies: + '@esbuild/aix-ppc64': 0.21.5 + '@esbuild/android-arm': 0.21.5 + '@esbuild/android-arm64': 0.21.5 + '@esbuild/android-x64': 0.21.5 + '@esbuild/darwin-arm64': 0.21.5 + '@esbuild/darwin-x64': 0.21.5 + '@esbuild/freebsd-arm64': 0.21.5 + '@esbuild/freebsd-x64': 0.21.5 + '@esbuild/linux-arm': 0.21.5 + '@esbuild/linux-arm64': 0.21.5 + '@esbuild/linux-ia32': 0.21.5 + '@esbuild/linux-loong64': 0.21.5 + '@esbuild/linux-mips64el': 0.21.5 + '@esbuild/linux-ppc64': 0.21.5 + '@esbuild/linux-riscv64': 0.21.5 + '@esbuild/linux-s390x': 0.21.5 + '@esbuild/linux-x64': 0.21.5 + '@esbuild/netbsd-x64': 0.21.5 + '@esbuild/openbsd-x64': 0.21.5 + '@esbuild/sunos-x64': 0.21.5 + '@esbuild/win32-arm64': 0.21.5 + '@esbuild/win32-ia32': 0.21.5 + '@esbuild/win32-x64': 0.21.5 + + esbuild@0.24.2: + optionalDependencies: + '@esbuild/aix-ppc64': 0.24.2 + '@esbuild/android-arm': 0.24.2 + '@esbuild/android-arm64': 0.24.2 + '@esbuild/android-x64': 0.24.2 + '@esbuild/darwin-arm64': 0.24.2 + '@esbuild/darwin-x64': 0.24.2 + '@esbuild/freebsd-arm64': 0.24.2 + '@esbuild/freebsd-x64': 0.24.2 + '@esbuild/linux-arm': 0.24.2 + '@esbuild/linux-arm64': 0.24.2 + '@esbuild/linux-ia32': 0.24.2 + '@esbuild/linux-loong64': 0.24.2 + '@esbuild/linux-mips64el': 0.24.2 + '@esbuild/linux-ppc64': 0.24.2 + '@esbuild/linux-riscv64': 0.24.2 + '@esbuild/linux-s390x': 0.24.2 + '@esbuild/linux-x64': 0.24.2 + '@esbuild/netbsd-arm64': 0.24.2 + '@esbuild/netbsd-x64': 0.24.2 + '@esbuild/openbsd-arm64': 0.24.2 + '@esbuild/openbsd-x64': 0.24.2 + '@esbuild/sunos-x64': 0.24.2 + '@esbuild/win32-arm64': 0.24.2 + '@esbuild/win32-ia32': 0.24.2 + '@esbuild/win32-x64': 0.24.2 + + escalade@3.2.0: {} + + escape-string-regexp@4.0.0: {} + + eslint-compat-utils@0.5.1(eslint@9.20.0(jiti@1.21.7)): + dependencies: + eslint: 9.20.0(jiti@1.21.7) + semver: 7.7.1 + + eslint-config-prettier@9.1.0(eslint@9.20.0(jiti@1.21.7)): + dependencies: + eslint: 9.20.0(jiti@1.21.7) + + eslint-config-turbo@2.4.0(eslint@9.20.0(jiti@1.21.7))(turbo@2.4.0): + dependencies: + eslint: 9.20.0(jiti@1.21.7) + eslint-plugin-turbo: 2.4.0(eslint@9.20.0(jiti@1.21.7))(turbo@2.4.0) + turbo: 2.4.0 + + eslint-plugin-svelte@2.46.1(eslint@9.20.0(jiti@1.21.7))(svelte@5.19.9): + dependencies: + '@eslint-community/eslint-utils': 4.4.1(eslint@9.20.0(jiti@1.21.7)) + '@jridgewell/sourcemap-codec': 1.5.0 + eslint: 9.20.0(jiti@1.21.7) + eslint-compat-utils: 0.5.1(eslint@9.20.0(jiti@1.21.7)) + esutils: 2.0.3 + known-css-properties: 0.35.0 + postcss: 8.5.1 + postcss-load-config: 3.1.4(postcss@8.5.1) + postcss-safe-parser: 6.0.0(postcss@8.5.1) + postcss-selector-parser: 6.1.2 + semver: 7.7.1 + svelte-eslint-parser: 0.43.0(svelte@5.19.9) + optionalDependencies: + svelte: 5.19.9 + transitivePeerDependencies: + - ts-node + + eslint-plugin-turbo@2.4.0(eslint@9.20.0(jiti@1.21.7))(turbo@2.4.0): + dependencies: + dotenv: 16.0.3 + eslint: 9.20.0(jiti@1.21.7) + turbo: 2.4.0 + + eslint-scope@7.2.2: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-scope@8.2.0: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@4.2.0: {} + + eslint@9.20.0(jiti@1.21.7): + dependencies: + '@eslint-community/eslint-utils': 4.4.1(eslint@9.20.0(jiti@1.21.7)) + '@eslint-community/regexpp': 4.12.1 + '@eslint/config-array': 0.19.2 + '@eslint/core': 0.11.0 + '@eslint/eslintrc': 3.2.0 + '@eslint/js': 9.20.0 + '@eslint/plugin-kit': 0.2.5 + '@humanfs/node': 0.16.6 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.1 + '@types/estree': 1.0.6 + '@types/json-schema': 7.0.15 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.0 + escape-string-regexp: 4.0.0 + eslint-scope: 8.2.0 + eslint-visitor-keys: 4.2.0 + espree: 10.3.0 + esquery: 1.6.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.4 + optionalDependencies: + jiti: 1.21.7 + transitivePeerDependencies: + - supports-color + + esm-env@1.0.0: {} + + esm-env@1.2.2: {} + + esniff@2.0.1: + dependencies: + d: 1.0.2 + es5-ext: 0.10.64 + event-emitter: 0.3.5 + type: 2.7.3 + + espree@10.3.0: + dependencies: + acorn: 8.14.0 + acorn-jsx: 5.3.2(acorn@8.14.0) + eslint-visitor-keys: 4.2.0 + + espree@9.6.1: + dependencies: + acorn: 8.14.0 + acorn-jsx: 5.3.2(acorn@8.14.0) + eslint-visitor-keys: 3.4.3 + + esprima@4.0.1: {} + + esquery@1.6.0: + dependencies: + estraverse: 5.3.0 + + esrap@1.4.3: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + + estree-walker@2.0.2: {} + + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.6 + + esutils@2.0.3: {} + + event-emitter@0.3.5: + dependencies: + d: 1.0.2 + es5-ext: 0.10.64 + + eventemitter3@5.0.1: {} + + execa@5.1.1: + dependencies: + cross-spawn: 7.0.6 + get-stream: 6.0.1 + human-signals: 2.1.0 + is-stream: 2.0.1 + merge-stream: 2.0.0 + npm-run-path: 4.0.1 + onetime: 5.1.2 + signal-exit: 3.0.7 + strip-final-newline: 2.0.0 + + execa@8.0.1: + dependencies: + cross-spawn: 7.0.6 + get-stream: 8.0.1 + human-signals: 5.0.0 + is-stream: 3.0.0 + merge-stream: 2.0.0 + npm-run-path: 5.3.0 + onetime: 6.0.0 + signal-exit: 4.1.0 + strip-final-newline: 3.0.0 + + expect-type@1.1.0: {} + + ext@1.7.0: + dependencies: + type: 2.7.3 + + extendable-error@0.1.7: {} + + external-editor@3.1.0: + dependencies: + chardet: 0.7.0 + iconv-lite: 0.4.24 + tmp: 0.0.33 + + fast-deep-equal@3.1.3: {} + + fast-glob@3.3.3: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fastparse@1.1.2: {} + + fastq@1.19.0: + dependencies: + reusify: 1.0.4 + + fdir@6.4.3(picomatch@4.0.2): + optionalDependencies: + picomatch: 4.0.2 + + fflate@0.8.2: {} + + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + + file-uri-to-path@1.0.0: {} + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + find-up@4.1.0: + dependencies: + locate-path: 5.0.0 + path-exists: 4.0.0 + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + flat-cache@4.0.1: + dependencies: + flatted: 3.3.2 + keyv: 4.5.4 + + flatted@3.3.2: {} + + foreground-child@3.3.0: + dependencies: + cross-spawn: 7.0.6 + signal-exit: 4.1.0 + + fraction.js@4.3.7: {} + + fs-extra@7.0.1: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 4.0.0 + universalify: 0.1.2 + + fs-extra@8.1.0: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 4.0.0 + universalify: 0.1.2 + + fs.realpath@1.0.0: {} + + fsevents@2.3.2: + optional: true + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + geohashing@2.0.1: {} + + geojson-vt@4.0.2: {} + + get-east-asian-width@1.3.0: {} + + get-stream@6.0.1: {} + + get-stream@8.0.1: {} + + gl-matrix@3.4.3: {} + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + glob@10.4.5: + dependencies: + foreground-child: 3.3.0 + jackspeak: 3.4.3 + minimatch: 9.0.5 + minipass: 7.1.2 + package-json-from-dist: 1.0.1 + path-scurry: 1.11.1 + + glob@7.2.3: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + + glob@8.1.0: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 5.1.6 + once: 1.4.0 + + global-prefix@4.0.0: + dependencies: + ini: 4.1.3 + kind-of: 6.0.3 + which: 4.0.0 + + globals@14.0.0: {} + + globals@15.14.0: {} + + globby@11.1.0: + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.3.3 + ignore: 5.3.2 + merge2: 1.4.1 + slash: 3.0.0 + + graceful-fs@4.2.11: {} + + graphemer@1.4.0: {} + + grid-index@1.1.0: {} + + has-flag@4.0.0: {} + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + highlight.js@11.11.1: {} + + highlightjs-svelte@1.0.6: {} + + https-proxy-agent@7.0.6: + dependencies: + agent-base: 7.1.3 + debug: 4.4.0 + transitivePeerDependencies: + - supports-color + + human-id@1.0.2: {} + + human-signals@2.1.0: {} + + human-signals@5.0.0: {} + + husky@9.1.7: {} + + iconv-lite@0.4.24: + dependencies: + safer-buffer: 2.1.2 + + ieee754@1.2.1: {} + + ignore-walk@5.0.1: + dependencies: + minimatch: 5.1.6 + + ignore@5.3.2: {} + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + import-meta-resolve@4.1.0: {} + + imurmurhash@0.1.4: {} + + inflight@1.0.6: + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + + inherits@2.0.4: {} + + ini@4.1.3: {} + + internmap@2.0.3: {} + + intl-messageformat@10.7.15: + dependencies: + '@formatjs/ecma402-abstract': 2.3.3 + '@formatjs/fast-memoize': 2.2.6 + '@formatjs/icu-messageformat-parser': 2.11.1 + tslib: 2.8.1 + + is-binary-path@2.1.0: + dependencies: + binary-extensions: 2.3.0 + + is-core-module@2.16.1: + dependencies: + hasown: 2.0.2 + + is-extglob@2.1.1: {} + + is-fullwidth-code-point@3.0.0: {} + + is-fullwidth-code-point@4.0.0: {} + + is-fullwidth-code-point@5.0.0: + dependencies: + get-east-asian-width: 1.3.0 + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-number@7.0.0: {} + + is-reference@3.0.3: + dependencies: + '@types/estree': 1.0.6 + + is-stream@2.0.1: {} + + is-stream@3.0.0: {} + + is-subdir@1.2.0: + dependencies: + better-path-resolve: 1.0.0 + + is-typedarray@1.0.0: {} + + is-windows@1.0.2: {} + + isexe@2.0.0: {} + + isexe@3.1.1: {} + + iso-3166@4.3.0: {} + + jackspeak@3.4.3: + dependencies: + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + + jiti@1.21.7: {} + + joycon@3.1.1: {} + + js-yaml@3.14.1: + dependencies: + argparse: 1.0.10 + esprima: 4.0.1 + + js-yaml@4.1.0: + dependencies: + argparse: 2.0.1 + + json-buffer@3.0.1: {} + + json-schema-traverse@0.4.1: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + json-stringify-pretty-compact@4.0.0: {} + + jsonfile@4.0.0: + optionalDependencies: + graceful-fs: 4.2.11 + + just-clamp@4.2.0: {} + + just-compare@2.3.0: {} + + kdbush@4.0.2: {} + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + kind-of@6.0.3: {} + + kleur@4.1.5: {} + + known-css-properties@0.35.0: {} + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + light-bolt11-decoder@3.2.0: + dependencies: + '@scure/base': 1.1.1 + + lilconfig@2.1.0: {} + + lilconfig@3.1.3: {} + + lines-and-columns@1.2.4: {} + + lint-staged@15.4.3: + dependencies: + chalk: 5.4.1 + commander: 13.1.0 + debug: 4.4.0 + execa: 8.0.1 + lilconfig: 3.1.3 + listr2: 8.2.5 + micromatch: 4.0.8 + pidtree: 0.6.0 + string-argv: 0.3.2 + yaml: 2.7.0 + transitivePeerDependencies: + - supports-color + + listr2@8.2.5: + dependencies: + cli-truncate: 4.0.0 + colorette: 2.0.20 + eventemitter3: 5.0.1 + log-update: 6.1.0 + rfdc: 1.4.1 + wrap-ansi: 9.0.0 + + load-tsconfig@0.2.5: {} + + locate-character@3.0.0: {} + + locate-path@5.0.0: + dependencies: + p-locate: 4.1.0 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + lodash.merge@4.6.2: {} + + lodash.sortby@4.7.0: {} + + lodash.startcase@4.4.0: {} + + log-update@6.1.0: + dependencies: + ansi-escapes: 7.0.0 + cli-cursor: 5.0.0 + slice-ansi: 7.1.0 + strip-ansi: 7.1.0 + wrap-ansi: 9.0.0 + + loupe@3.1.3: {} + + lower-case@2.0.2: + dependencies: + tslib: 2.8.1 + + lru-cache@10.4.3: {} + + luxon@3.5.0: {} + + magic-string@0.30.17: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.0 + + mapbox-gl@3.9.4: + dependencies: + '@mapbox/jsonlint-lines-primitives': 2.0.2 + '@mapbox/mapbox-gl-supported': 3.0.0 + '@mapbox/point-geometry': 0.1.0 + '@mapbox/tiny-sdf': 2.0.6 + '@mapbox/unitbezier': 0.0.1 + '@mapbox/vector-tile': 1.3.1 + '@mapbox/whoots-js': 3.1.0 + '@types/geojson': 7946.0.16 + '@types/geojson-vt': 3.2.5 + '@types/mapbox__point-geometry': 0.1.4 + '@types/mapbox__vector-tile': 1.3.4 + '@types/pbf': 3.0.5 + '@types/supercluster': 7.1.3 + cheap-ruler: 4.0.0 + csscolorparser: 1.0.3 + earcut: 3.0.1 + geojson-vt: 4.0.2 + gl-matrix: 3.4.3 + grid-index: 1.1.0 + kdbush: 4.0.2 + murmurhash-js: 1.0.0 + pbf: 3.3.0 + potpack: 2.0.0 + quickselect: 3.0.0 + serialize-to-js: 3.1.2 + supercluster: 8.0.1 + tinyqueue: 3.0.0 + vt-pbf: 3.1.3 + + maplibre-gl@5.1.0: + dependencies: + '@mapbox/geojson-rewind': 0.5.2 + '@mapbox/jsonlint-lines-primitives': 2.0.2 + '@mapbox/point-geometry': 0.1.0 + '@mapbox/tiny-sdf': 2.0.6 + '@mapbox/unitbezier': 0.0.1 + '@mapbox/vector-tile': 1.3.1 + '@mapbox/whoots-js': 3.1.0 + '@maplibre/maplibre-gl-style-spec': 23.1.0 + '@types/geojson': 7946.0.16 + '@types/geojson-vt': 3.2.5 + '@types/mapbox__point-geometry': 0.1.4 + '@types/mapbox__vector-tile': 1.3.4 + '@types/pbf': 3.0.5 + '@types/supercluster': 7.1.3 + earcut: 3.0.1 + geojson-vt: 4.0.2 + gl-matrix: 3.4.3 + global-prefix: 4.0.0 + kdbush: 4.0.2 + murmurhash-js: 1.0.0 + pbf: 3.3.0 + potpack: 2.0.0 + quickselect: 3.0.0 + supercluster: 8.0.1 + tinyqueue: 3.0.0 + vt-pbf: 3.1.3 + + merge-stream@2.0.0: {} + + merge2@1.4.1: {} + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + + mimic-fn@2.1.0: {} + + mimic-fn@4.0.0: {} + + mimic-function@5.0.1: {} + + minimatch@3.1.2: + dependencies: + brace-expansion: 1.1.11 + + minimatch@5.1.6: + dependencies: + brace-expansion: 2.0.1 + + minimatch@9.0.5: + dependencies: + brace-expansion: 2.0.1 + + minimist@1.2.8: {} + + minipass@7.1.2: {} + + minizlib@3.0.1: + dependencies: + minipass: 7.1.2 + rimraf: 5.0.10 + + mjolnir.js@3.0.0: {} + + mkdirp@3.0.1: {} + + mri@1.2.0: {} + + mrmime@2.0.0: {} + + ms@2.0.0: {} + + ms@2.1.3: {} + + murmurhash-js@1.0.0: {} + + mz@2.7.0: + dependencies: + any-promise: 1.3.0 + object-assign: 4.1.1 + thenify-all: 1.6.0 + + nanoid@3.3.8: {} + + nanoid@5.0.9: {} + + natural-compare@1.4.0: {} + + next-tick@1.1.0: {} + + ngeohash@0.6.3: {} + + no-case@3.0.4: + dependencies: + lower-case: 2.0.2 + tslib: 2.8.1 + + node-fetch@2.7.0: + dependencies: + whatwg-url: 5.0.0 + + node-gyp-build@4.8.4: {} + + node-releases@2.0.19: {} + + nopt@8.1.0: + dependencies: + abbrev: 3.0.0 + + normalize-path@3.0.0: {} + + normalize-range@0.1.2: {} + + nostr-geotags@0.7.2: + dependencies: + iso-3166: 4.3.0 + ngeohash: 0.6.3 + + nostr-tools@2.10.4(typescript@5.7.3): + dependencies: + '@noble/ciphers': 0.5.3 + '@noble/curves': 1.2.0 + '@noble/hashes': 1.3.1 + '@scure/base': 1.1.1 + '@scure/bip32': 1.3.1 + '@scure/bip39': 1.2.1 + optionalDependencies: + nostr-wasm: 0.1.0 + typescript: 5.7.3 + + nostr-wasm@0.1.0: + optional: true + + npm-bundled@2.0.1: + dependencies: + npm-normalize-package-bin: 2.0.0 + + npm-normalize-package-bin@2.0.0: {} + + npm-packlist@5.1.3: + dependencies: + glob: 8.1.0 + ignore-walk: 5.0.1 + npm-bundled: 2.0.1 + npm-normalize-package-bin: 2.0.0 + + npm-run-path@4.0.1: + dependencies: + path-key: 3.1.1 + + npm-run-path@5.3.0: + dependencies: + path-key: 4.0.0 + + object-assign@4.1.1: {} + + object-hash@3.0.0: {} + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + onetime@5.1.2: + dependencies: + mimic-fn: 2.1.0 + + onetime@6.0.0: + dependencies: + mimic-fn: 4.0.0 + + onetime@7.0.0: + dependencies: + mimic-function: 5.0.1 + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + os-tmpdir@1.0.2: {} + + outdent@0.5.0: {} + + p-filter@2.1.0: + dependencies: + p-map: 2.1.0 + + p-limit@2.3.0: + dependencies: + p-try: 2.2.0 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@4.1.0: + dependencies: + p-limit: 2.3.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + p-map@2.1.0: {} + + p-try@2.2.0: {} + + package-json-from-dist@1.0.1: {} + + package-manager-detector@0.2.9: {} + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + pascal-case@3.1.2: + dependencies: + no-case: 3.0.4 + tslib: 2.8.1 + + path-exists@4.0.0: {} + + path-is-absolute@1.0.1: {} + + path-key@3.1.1: {} + + path-key@4.0.0: {} + + path-parse@1.0.7: {} + + path-scurry@1.11.1: + dependencies: + lru-cache: 10.4.3 + minipass: 7.1.2 + + path-type@4.0.0: {} + + pathe@1.1.2: {} + + pathval@2.0.0: {} + + pbf@3.3.0: + dependencies: + ieee754: 1.2.1 + resolve-protobuf-schema: 2.1.0 + + picocolors@1.1.1: {} + + picomatch@2.3.1: {} + + picomatch@4.0.2: {} + + pidtree@0.6.0: {} + + pify@2.3.0: {} + + pify@4.0.1: {} + + pirates@4.0.6: {} + + playwright-core@1.50.1: {} + + playwright@1.50.1: + dependencies: + playwright-core: 1.50.1 + optionalDependencies: + fsevents: 2.3.2 + + pmtiles@3.2.1: + dependencies: + '@types/leaflet': 1.9.16 + fflate: 0.8.2 + + postcss-import@15.1.0(postcss@8.5.1): + dependencies: + postcss: 8.5.1 + postcss-value-parser: 4.2.0 + read-cache: 1.0.0 + resolve: 1.22.10 + + postcss-import@16.1.0(postcss@8.5.1): + dependencies: + postcss: 8.5.1 + postcss-value-parser: 4.2.0 + read-cache: 1.0.0 + resolve: 1.22.10 + + postcss-js@4.0.1(postcss@8.5.1): + dependencies: + camelcase-css: 2.0.1 + postcss: 8.5.1 + + postcss-load-config@3.1.4(postcss@8.5.1): + dependencies: + lilconfig: 2.1.0 + yaml: 1.10.2 + optionalDependencies: + postcss: 8.5.1 + + postcss-load-config@4.0.2(postcss@8.5.1): + dependencies: + lilconfig: 3.1.3 + yaml: 2.7.0 + optionalDependencies: + postcss: 8.5.1 + + postcss-nested@6.2.0(postcss@8.5.1): + dependencies: + postcss: 8.5.1 + postcss-selector-parser: 6.1.2 + + postcss-safe-parser@6.0.0(postcss@8.5.1): + dependencies: + postcss: 8.5.1 + + postcss-scss@4.0.9(postcss@8.5.1): + dependencies: + postcss: 8.5.1 + + postcss-selector-parser@6.1.2: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + + postcss-value-parser@4.2.0: {} + + postcss@8.5.1: + dependencies: + nanoid: 3.3.8 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + potpack@2.0.0: {} + + prelude-ls@1.2.1: {} + + prettier-plugin-svelte@3.3.3(prettier@3.5.0)(svelte@5.19.9): + dependencies: + prettier: 3.5.0 + svelte: 5.19.9 + + prettier-plugin-tailwindcss@0.6.11(prettier-plugin-svelte@3.3.3(prettier@3.5.0)(svelte@5.19.9))(prettier@3.5.0): + dependencies: + prettier: 3.5.0 + optionalDependencies: + prettier-plugin-svelte: 3.3.3(prettier@3.5.0)(svelte@5.19.9) + + prettier@2.8.8: {} + + prettier@3.5.0: {} + + protocol-buffers-schema@3.6.0: {} + + publint@0.2.12: + dependencies: + npm-packlist: 5.1.3 + picocolors: 1.1.1 + sade: 1.8.1 + + publint@0.3.4: + dependencies: + '@publint/pack': 0.1.1 + package-manager-detector: 0.2.9 + picocolors: 1.1.1 + sade: 1.8.1 + + punycode@2.3.1: {} + + queue-microtask@1.2.3: {} + + quickselect@3.0.0: {} + + read-cache@1.0.0: + dependencies: + pify: 2.3.0 + + read-yaml-file@1.1.0: + dependencies: + graceful-fs: 4.2.11 + js-yaml: 3.14.1 + pify: 4.0.1 + strip-bom: 3.0.0 + + readdirp@3.6.0: + dependencies: + picomatch: 2.3.1 + + readdirp@4.1.1: {} + + regenerator-runtime@0.14.1: {} + + resolve-from@4.0.0: {} + + resolve-from@5.0.0: {} + + resolve-protobuf-schema@2.1.0: + dependencies: + protocol-buffers-schema: 3.6.0 + + resolve@1.22.10: + dependencies: + is-core-module: 2.16.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + restore-cursor@5.1.0: + dependencies: + onetime: 7.0.0 + signal-exit: 4.1.0 + + reusify@1.0.4: {} + + rfdc@1.4.1: {} + + rimraf@5.0.10: + dependencies: + glob: 10.4.5 + + rollup@3.29.5: + optionalDependencies: + fsevents: 2.3.3 + + rollup@4.34.6: + dependencies: + '@types/estree': 1.0.6 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.34.6 + '@rollup/rollup-android-arm64': 4.34.6 + '@rollup/rollup-darwin-arm64': 4.34.6 + '@rollup/rollup-darwin-x64': 4.34.6 + '@rollup/rollup-freebsd-arm64': 4.34.6 + '@rollup/rollup-freebsd-x64': 4.34.6 + '@rollup/rollup-linux-arm-gnueabihf': 4.34.6 + '@rollup/rollup-linux-arm-musleabihf': 4.34.6 + '@rollup/rollup-linux-arm64-gnu': 4.34.6 + '@rollup/rollup-linux-arm64-musl': 4.34.6 + '@rollup/rollup-linux-loongarch64-gnu': 4.34.6 + '@rollup/rollup-linux-powerpc64le-gnu': 4.34.6 + '@rollup/rollup-linux-riscv64-gnu': 4.34.6 + '@rollup/rollup-linux-s390x-gnu': 4.34.6 + '@rollup/rollup-linux-x64-gnu': 4.34.6 + '@rollup/rollup-linux-x64-musl': 4.34.6 + '@rollup/rollup-win32-arm64-msvc': 4.34.6 + '@rollup/rollup-win32-ia32-msvc': 4.34.6 + '@rollup/rollup-win32-x64-msvc': 4.34.6 + fsevents: 2.3.3 + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + rw@1.3.3: {} + + sade@1.8.1: + dependencies: + mri: 1.2.0 + + safer-buffer@2.1.2: {} + + semver@7.7.1: {} + + serialize-to-js@3.1.2: {} + + set-cookie-parser@2.7.1: {} + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + siginfo@2.0.0: {} + + signal-exit@3.0.7: {} + + signal-exit@4.1.0: {} + + sirv@3.0.0: + dependencies: + '@polka/url': 1.0.0-next.28 + mrmime: 2.0.0 + totalist: 3.0.1 + + slash@3.0.0: {} + + slice-ansi@5.0.0: + dependencies: + ansi-styles: 6.2.1 + is-fullwidth-code-point: 4.0.0 + + slice-ansi@7.1.0: + dependencies: + ansi-styles: 6.2.1 + is-fullwidth-code-point: 5.0.0 + + source-map-js@1.2.1: {} + + source-map@0.8.0-beta.0: + dependencies: + whatwg-url: 7.1.0 + + spawndamnit@3.0.1: + dependencies: + cross-spawn: 7.0.6 + signal-exit: 4.1.0 + + sprintf-js@1.0.3: {} + + sql.js@1.12.0: {} + + stackback@0.0.2: {} + + std-env@3.8.0: {} + + string-argv@0.3.2: {} + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string-width@5.1.2: + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.1.0 + + string-width@7.2.0: + dependencies: + emoji-regex: 10.4.0 + get-east-asian-width: 1.3.0 + strip-ansi: 7.1.0 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-ansi@7.1.0: + dependencies: + ansi-regex: 6.1.0 + + strip-bom@3.0.0: {} + + strip-final-newline@2.0.0: {} + + strip-final-newline@3.0.0: {} + + strip-json-comments@3.1.1: {} + + sucrase@3.35.0: + dependencies: + '@jridgewell/gen-mapping': 0.3.8 + commander: 4.1.1 + glob: 10.4.5 + lines-and-columns: 1.2.4 + mz: 2.7.0 + pirates: 4.0.6 + ts-interface-checker: 0.1.13 + + supercluster@8.0.1: + dependencies: + kdbush: 4.0.2 + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-preserve-symlinks-flag@1.0.0: {} + + svelte-check@4.1.4(picomatch@4.0.2)(svelte@5.19.9)(typescript@5.7.3): + dependencies: + '@jridgewell/trace-mapping': 0.3.25 + chokidar: 4.0.3 + fdir: 6.4.3(picomatch@4.0.2) + picocolors: 1.1.1 + sade: 1.8.1 + svelte: 5.19.9 + typescript: 5.7.3 + transitivePeerDependencies: + - picomatch + + svelte-eslint-parser@0.43.0(svelte@5.19.9): + dependencies: + eslint-scope: 7.2.2 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + postcss: 8.5.1 + postcss-scss: 4.0.9(postcss@8.5.1) + optionalDependencies: + svelte: 5.19.9 + + svelte-hmr@0.16.0(svelte@5.19.9): + dependencies: + svelte: 5.19.9 + + svelte2tsx@0.7.34(svelte@5.19.9)(typescript@5.7.3): + dependencies: + dedent-js: 1.0.1 + pascal-case: 3.1.2 + svelte: 5.19.9 + typescript: 5.7.3 + + svelte@5.19.9: + dependencies: + '@ampproject/remapping': 2.3.0 + '@jridgewell/sourcemap-codec': 1.5.0 + '@types/estree': 1.0.6 + acorn: 8.14.0 + acorn-typescript: 1.4.13(acorn@8.14.0) + aria-query: 5.3.2 + axobject-query: 4.1.0 + clsx: 2.1.1 + esm-env: 1.2.2 + esrap: 1.4.3 + is-reference: 3.0.3 + locate-character: 3.0.0 + magic-string: 0.30.17 + zimmerframe: 1.1.2 + + sveltekit-search-params@3.0.0(@sveltejs/kit@2.17.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.19.9)(vite@6.1.0(@types/node@22.13.1)(jiti@1.21.7)(yaml@2.7.0)))(svelte@5.19.9)(vite@6.1.0(@types/node@22.13.1)(jiti@1.21.7)(yaml@2.7.0)))(svelte@5.19.9)(vite@6.1.0(@types/node@22.13.1)(jiti@1.21.7)(yaml@2.7.0)): + dependencies: + '@sveltejs/kit': 2.17.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.19.9)(vite@6.1.0(@types/node@22.13.1)(jiti@1.21.7)(yaml@2.7.0)))(svelte@5.19.9)(vite@6.1.0(@types/node@22.13.1)(jiti@1.21.7)(yaml@2.7.0)) + '@sveltejs/vite-plugin-svelte': 3.1.2(svelte@5.19.9)(vite@6.1.0(@types/node@22.13.1)(jiti@1.21.7)(yaml@2.7.0)) + svelte: 5.19.9 + transitivePeerDependencies: + - supports-color + - vite + + tailwindcss@3.4.17: + dependencies: + '@alloc/quick-lru': 5.2.0 + arg: 5.0.2 + chokidar: 3.6.0 + didyoumean: 1.2.2 + dlv: 1.1.3 + fast-glob: 3.3.3 + glob-parent: 6.0.2 + is-glob: 4.0.3 + jiti: 1.21.7 + lilconfig: 3.1.3 + micromatch: 4.0.8 + normalize-path: 3.0.0 + object-hash: 3.0.0 + picocolors: 1.1.1 + postcss: 8.5.1 + postcss-import: 15.1.0(postcss@8.5.1) + postcss-js: 4.0.1(postcss@8.5.1) + postcss-load-config: 4.0.2(postcss@8.5.1) + postcss-nested: 6.2.0(postcss@8.5.1) + postcss-selector-parser: 6.1.2 + resolve: 1.22.10 + sucrase: 3.35.0 + transitivePeerDependencies: + - ts-node + + tar@7.4.3: + dependencies: + '@isaacs/fs-minipass': 4.0.1 + chownr: 3.0.0 + minipass: 7.1.2 + minizlib: 3.0.1 + mkdirp: 3.0.1 + yallist: 5.0.0 + + term-size@2.2.1: {} + + thenify-all@1.6.0: + dependencies: + thenify: 3.3.1 + + thenify@3.3.1: + dependencies: + any-promise: 1.3.0 + + tinybench@2.9.0: {} + + tinyexec@0.3.2: {} + + tinypool@1.0.2: {} + + tinyqueue@3.0.0: {} + + tinyrainbow@1.2.0: {} + + tinyspy@3.0.2: {} + + tmp@0.0.33: + dependencies: + os-tmpdir: 1.0.2 + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + totalist@3.0.1: {} + + tr46@0.0.3: {} + + tr46@1.0.1: + dependencies: + punycode: 2.3.1 + + tree-kill@1.2.2: {} + + ts-api-utils@1.4.3(typescript@5.7.3): + dependencies: + typescript: 5.7.3 + + ts-api-utils@2.0.1(typescript@5.7.3): + dependencies: + typescript: 5.7.3 + + ts-interface-checker@0.1.13: {} + + tseep@1.3.1: {} + + tslib@2.8.1: {} + + tstl@2.5.16: {} + + tsup@6.7.0(postcss@8.5.1)(typescript@5.7.3): + dependencies: + bundle-require: 4.2.1(esbuild@0.17.19) + cac: 6.7.14 + chokidar: 3.6.0 + debug: 4.4.0 + esbuild: 0.17.19 + execa: 5.1.1 + globby: 11.1.0 + joycon: 3.1.1 + postcss-load-config: 3.1.4(postcss@8.5.1) + resolve-from: 5.0.0 + rollup: 3.29.5 + source-map: 0.8.0-beta.0 + sucrase: 3.35.0 + tree-kill: 1.2.2 + optionalDependencies: + postcss: 8.5.1 + typescript: 5.7.3 + transitivePeerDependencies: + - supports-color + - ts-node + + turbo-darwin-64@2.4.0: + optional: true + + turbo-darwin-arm64@2.4.0: + optional: true + + turbo-linux-64@2.4.0: + optional: true + + turbo-linux-arm64@2.4.0: + optional: true + + turbo-windows-64@2.4.0: + optional: true + + turbo-windows-arm64@2.4.0: + optional: true + + turbo@2.4.0: + optionalDependencies: + turbo-darwin-64: 2.4.0 + turbo-darwin-arm64: 2.4.0 + turbo-linux-64: 2.4.0 + turbo-linux-arm64: 2.4.0 + turbo-windows-64: 2.4.0 + turbo-windows-arm64: 2.4.0 + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + type@2.7.3: {} + + typedarray-to-buffer@3.1.5: + dependencies: + is-typedarray: 1.0.0 + + typescript-eslint@8.23.0(eslint@9.20.0(jiti@1.21.7))(typescript@5.7.3): + dependencies: + '@typescript-eslint/eslint-plugin': 8.23.0(@typescript-eslint/parser@8.23.0(eslint@9.20.0(jiti@1.21.7))(typescript@5.7.3))(eslint@9.20.0(jiti@1.21.7))(typescript@5.7.3) + '@typescript-eslint/parser': 8.23.0(eslint@9.20.0(jiti@1.21.7))(typescript@5.7.3) + '@typescript-eslint/utils': 8.23.0(eslint@9.20.0(jiti@1.21.7))(typescript@5.7.3) + eslint: 9.20.0(jiti@1.21.7) + typescript: 5.7.3 + transitivePeerDependencies: + - supports-color + + typescript-lru-cache@2.0.0: {} + + typescript@5.7.3: {} + + undici-types@6.20.0: {} + + universalify@0.1.2: {} + + update-browserslist-db@1.1.2(browserslist@4.24.4): + dependencies: + browserslist: 4.24.4 + escalade: 3.2.0 + picocolors: 1.1.1 + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + utf-8-validate@5.0.10: + dependencies: + node-gyp-build: 4.8.4 + + utf8-buffer@1.0.0: {} + + util-deprecate@1.0.2: {} + + uuid@10.0.0: {} + + vite-node@2.1.9(@types/node@22.13.1): + dependencies: + cac: 6.7.14 + debug: 4.4.0 + es-module-lexer: 1.6.0 + pathe: 1.1.2 + vite: 5.4.14(@types/node@22.13.1) + transitivePeerDependencies: + - '@types/node' + - less + - lightningcss + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + + vite@5.4.14(@types/node@22.13.1): + dependencies: + esbuild: 0.21.5 + postcss: 8.5.1 + rollup: 4.34.6 + optionalDependencies: + '@types/node': 22.13.1 + fsevents: 2.3.3 + + vite@6.1.0(@types/node@22.13.1)(jiti@1.21.7)(yaml@2.7.0): + dependencies: + esbuild: 0.24.2 + postcss: 8.5.1 + rollup: 4.34.6 + optionalDependencies: + '@types/node': 22.13.1 + fsevents: 2.3.3 + jiti: 1.21.7 + yaml: 2.7.0 + + vitefu@0.2.5(vite@6.1.0(@types/node@22.13.1)(jiti@1.21.7)(yaml@2.7.0)): + optionalDependencies: + vite: 6.1.0(@types/node@22.13.1)(jiti@1.21.7)(yaml@2.7.0) + + vitefu@1.0.5(vite@5.4.14(@types/node@22.13.1)): + optionalDependencies: + vite: 5.4.14(@types/node@22.13.1) + + vitefu@1.0.5(vite@6.1.0(@types/node@22.13.1)(jiti@1.21.7)(yaml@2.7.0)): + optionalDependencies: + vite: 6.1.0(@types/node@22.13.1)(jiti@1.21.7)(yaml@2.7.0) + + vitest@2.1.9(@types/node@22.13.1): + dependencies: + '@vitest/expect': 2.1.9 + '@vitest/mocker': 2.1.9(vite@5.4.14(@types/node@22.13.1)) + '@vitest/pretty-format': 2.1.9 + '@vitest/runner': 2.1.9 + '@vitest/snapshot': 2.1.9 + '@vitest/spy': 2.1.9 + '@vitest/utils': 2.1.9 + chai: 5.1.2 + debug: 4.4.0 + expect-type: 1.1.0 + magic-string: 0.30.17 + pathe: 1.1.2 + std-env: 3.8.0 + tinybench: 2.9.0 + tinyexec: 0.3.2 + tinypool: 1.0.2 + tinyrainbow: 1.2.0 + vite: 5.4.14(@types/node@22.13.1) + vite-node: 2.1.9(@types/node@22.13.1) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 22.13.1 + transitivePeerDependencies: + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + + vt-pbf@3.1.3: + dependencies: + '@mapbox/point-geometry': 0.1.0 + '@mapbox/vector-tile': 1.3.1 + pbf: 3.3.0 + + webidl-conversions@3.0.1: {} + + webidl-conversions@4.0.2: {} + + websocket-polyfill@0.0.3: + dependencies: + tstl: 2.5.16 + websocket: 1.0.35 + transitivePeerDependencies: + - supports-color + + websocket@1.0.35: + dependencies: + bufferutil: 4.0.9 + debug: 2.6.9 + es5-ext: 0.10.64 + typedarray-to-buffer: 3.1.5 + utf-8-validate: 5.0.10 + yaeti: 0.0.6 + transitivePeerDependencies: + - supports-color + + wgs84@0.0.0: {} + + wgsl_reflect@1.0.17: {} + + whatwg-url@5.0.0: + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + + whatwg-url@7.1.0: + dependencies: + lodash.sortby: 4.7.0 + tr46: 1.0.1 + webidl-conversions: 4.0.2 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + which@4.0.0: + dependencies: + isexe: 3.1.1 + + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + + word-wrap@1.2.5: {} + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@8.1.0: + dependencies: + ansi-styles: 6.2.1 + string-width: 5.1.2 + strip-ansi: 7.1.0 + + wrap-ansi@9.0.0: + dependencies: + ansi-styles: 6.2.1 + string-width: 7.2.0 + strip-ansi: 7.1.0 + + wrappy@1.0.2: {} + + yaeti@0.0.6: {} + + yallist@5.0.0: {} + + yaml@1.10.2: {} + + yaml@2.7.0: {} + + yocto-queue@0.1.0: {} + + zimmerframe@1.1.2: {} + + zod@3.24.1: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml @@ -0,0 +1,3 @@ +packages: + - 'app' + - 'packages/*' diff --git a/postcss.config.js b/postcss.config.js @@ -1,7 +0,0 @@ -export default { - plugins: { - 'postcss-import': {}, - tailwindcss: {}, - autoprefixer: {}, - }, -} diff --git a/src/app.css b/src/app.css @@ -1,26 +0,0 @@ -@import "/static/stylesheets/tailwindcss-app-carousel.css"; -@import "/static/stylesheets/tailwindcss-app-form.css"; -@import "/static/stylesheets/tailwindcss-app-styles.css"; -@import "/static/stylesheets/tailwindcss-app.css"; -@import "/static/stylesheets/tailwindcss-spinner.css"; - -@import "/static/stylesheets/tailwindcss-app-layer-1.css"; - -@import "/static/webfonts/apercu-mono-pro/styles.css"; -@import "/static/webfonts/lust/styles.css"; -@import "/static/webfonts/magda-text/styles.css"; -@import "/static/webfonts/sf-pro-display/styles.css"; -@import "/static/webfonts/sf-pro-rounded/styles.css"; -@import "/static/webfonts/circular/styles.css"; -@import "/static/webfonts/archivo/styles.css"; -@import "/static/webfonts/space-grotesk/styles.css"; -@import "/static/webfonts/spartan/styles.css"; -@import "/static/webfonts/outfit/styles.css"; - -@tailwind base; -@tailwind components; -@tailwind utilities; - -@layer base {} - -@layer components {} -\ No newline at end of file diff --git a/src/app.d.ts b/src/app.d.ts @@ -1,138 +0,0 @@ -declare global { - namespace App { } - - declare class Keyva { - /** - * An IDBKeyRange that has no upper or lower bounding. - */ - static readonly unbound: IDBKeyRange; - /** - * Returns an IDBKeyRange that matches all keys that start - * with the specified string prefix. - */ - static prefix(prefix: string): IDBKeyRange; - /** - * @returns An array of strings that contain the names of all - * Keyva-created IndexedDB databases. - */ - static each(): Promise<string[]>; - /** - * Deletes Keyva-created IndexedDB databases with the - * specified names. - * - * @param names The names of the databases to delete. - * If no names are provided, all Keyva IndexedDB databases - * are deleted. - */ - static delete(...names: string[]): Promise<void>; - /** Stores the prefix that is added to every IndexedDB database created by Keyva. */ - private static readonly kvPrefix; - /** - * Creates a new IndexedDB-backed database - */ - constructor(options?: Keyva.IConstructorOptions); - private readonly indexes; - private readonly name; - /** - * Get a value by its key. - * @param key The key of the value to get. - */ - get<T = any>(key: Keyva.Key): Promise<T>; - /** - * Get a series of values from the keys specified. - * @param keys The key of the value to get. - */ - get<T = any>(keys: Keyva.Key[]): Promise<T[]>; - /** - * Gets all keys and values from the Keyva database. - * @param key The key of the value to get. - */ - each<T = any>(): Promise<[Keyva.Key, T][]>; - /** - * Gets a series of keys and values that match the specified - * set of options. - */ - each<T = any>(options: Keyva.IQuery): Promise<[Keyva.Key, T][]>; - /** - * Gets a series of keys only that match the specified set of options. - */ - each(options: Keyva.IQuery, only: "keys"): Promise<Keyva.Key[]>; - /** - * Gets a series of values only that match the specified set of options. - */ - each<T = any>(options: Keyva.IQuery, only: "values"): Promise<T[]>; - /** - * Set a value with a key. - */ - set(key: Keyva.Key, value: any): Promise<void>; - /** - * Set multiple values at once. This is faster than calling set() multiple times. - * It's also atomic – if one of the pairs can't be added, none will be added. - * @param entries Array of entries, where each entry is an array of `[key, value]`. - */ - set(entries: [Keyva.Key, any][]): Promise<void>; - /** - * Deletes all objects from this Keyva database - * (but keeps the Keyva database itself is kept). - */ - delete(): Promise<void>; - /** - * Delete a single object from the store with the specified key. - */ - delete(range: IDBKeyRange): Promise<void>; - /** - * Delete a single object from the store with the specified key. - */ - delete(key: Keyva.Key): Promise<void>; - /** - * Delete a series of objects from the store at once, with the specified keys. - */ - delete(keys: Keyva.Key[]): Promise<void>; - /** */ - private getStore; - /** */ - private getDatabase; - private database; - /** - * Works around a Safari 14 bug. - * - * Safari has a bug where IDB requests can hang while the browser is - * starting up. https://bugs.webkit.org/show_bug.cgi?id=226547 - * The only solution is to keep nudging it until it's awake. - */ - private maybeFixSafari; - /** */ - private static asPromise; - } - declare namespace Keyva { - /** */ - interface IConstructorOptions { - /** - * Defines the name of the IndexedDB database as it is stored in the browser. - * Note that the name is prefixed with the Keyva database prefix constant. - */ - name?: string | number; - /** - * Defines the name or names of the index or indexes to define on the database. - */ - indexes?: string | string[]; - } - /** */ - interface IQuery { - /** - * A standard IDBKeyRange to use for the query. Worth noting that the methods - * in the static Keyva.* namespace contain utility functions to ease the creation - * of IDBKeyRange objects. - */ - range?: IDBKeyRange; - /** The name of the index to use for the query. */ - index?: string; - /** A number which indicates the maximum number of objects to return from a query. */ - limit?: number; - } - /** */ - type Key = string | number | Date | BufferSource; - } -} - -export { }; diff --git a/src/app.html b/src/app.html @@ -1,26 +0,0 @@ -<!doctype html> -<html lang="en" data-theme=""> - -<head> - <meta charset="utf-8" /> - - <link rel="stylesheet" type="text/css" href="/phosphor-icons/bold.css" /> - <link rel="stylesheet" type="text/css" href="/phosphor-icons/fill.css" /> - <link rel="stylesheet" type="text/css" href="/phosphor-icons/light.css" /> - <link rel="stylesheet" type="text/css" href="/phosphor-icons/regular.css" /> - <link rel="stylesheet" type="text/css" href="/stylesheets/styles-maplibre-gl.css" /> - <link rel="stylesheet" type="text/css" href="/stylesheets/styles-superellipse.css" /> - - <script src="/keyva.min.js"></script> - <script src="/squircle.min.js"></script> - - <meta name="viewport" - content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no viewport-fit=cover" /> - %sveltekit.head% -</head> - -<body data-sveltekit-preload-data="hover"> - <div style="display: contents">%sveltekit.body%</div> -</body> - -</html> -\ No newline at end of file diff --git a/src/lib/client.ts b/src/lib/client.ts @@ -1,18 +0,0 @@ -import { ClientNostr, TauriClientDatabase, TauriClientDevice, TauriClientDialog, TauriClientFs, TauriClientGeolocation, TauriClientHaptics, TauriClientHttp, TauriClientKeying, TauriClientKeystore, TauriClientLogger, TauriClientNotification, TauriClientOs, TauriClientWindow } from "@radroots/client"; -import { Geocoder } from "@radroots/geocoder"; - -export const geoc = new Geocoder(`/geonames/geonames.db`); -export const db = new TauriClientDatabase(); -export const device = new TauriClientDevice(); -export const dialog = new TauriClientDialog(); -export const fs = new TauriClientFs(); -export const geol = new TauriClientGeolocation(); -export const haptics = new TauriClientHaptics(); -export const os = new TauriClientOs(); -export const http = new TauriClientHttp(); -export const keystore = new TauriClientKeystore(); -export const keyring = new TauriClientKeying(); -export const nostr = new ClientNostr(); -export const notification = new TauriClientNotification(); -export const win = new TauriClientWindow(); -export const logger = new TauriClientLogger(); -\ No newline at end of file diff --git a/src/lib/components/image_upload_add_photo.svelte b/src/lib/components/image_upload_add_photo.svelte @@ -1,42 +0,0 @@ -<script lang="ts"> - import { dialog } from "$lib/client"; - import { catch_err, Glyph, ls } from "@radroots/svelte-lib"; - - export let photo_path: string; - - const handle_photo_add = async (): Promise<void> => { - try { - const photo_paths_open = await dialog.open_photos(); - if (!photo_paths_open) return; - photo_path = photo_paths_open.results[0]; - } catch (e) { - await catch_err(e, `handle_photo_add`); - } - }; -</script> - -<div class={`relative flex flex-row w-full justify-center items-center`}> - <button - class={`flex flex-row h-[5rem] w-[5rem] justify-center items-center bg-layer-1-surface/60 rounded-full`} - on:click={async () => { - await handle_photo_add(); - }} - > - <Glyph - basis={{ - classes: `text-[40px] text-layer-2-glyph`, - dim: `sm`, - key: `camera`, - }} - /> - <div - class={`absolute -bottom-[1.8rem] flex flex-row justify-start items-center`} - > - <p - class={`font-arch font-[600] text-sm text-layer-0-glyph capitalize`} - > - {`${$ls(`icu.add_*`, { value: `${$ls(`common.photo`)}` })}`} - </p> - </div> - </button> -</div> diff --git a/src/lib/components/image_upload_control.svelte b/src/lib/components/image_upload_control.svelte @@ -1,269 +0,0 @@ -<script lang="ts"> - import { dialog, fs } from "$lib/client"; - import { - app_layout, - catch_err, - Glyph, - ImageBlob, - ls, - } from "@radroots/svelte-lib"; - import { list_assign } from "@radroots/utils"; - import { fade } from "svelte/transition"; - - export let photo_paths: string[]; - export let photo_edit: { index: number; file_path: string } | undefined; - - export let basis: { - id: string; - }; - - let photo_file_path_0: string; - let photo_file_path_1: string; - let photo_file_path_2: string; - let photo_file_path_3: string; - let photo_file_path_4: string; - let photo_file_path_5: string; - let photo_file_path_6: string; - - $: if (photo_paths.length > 0) { - if (photo_paths[0]) photo_file_path_0 = photo_paths[0]; - if (photo_paths[1]) photo_file_path_1 = photo_paths[1]; - if (photo_paths[2]) photo_file_path_2 = photo_paths[2]; - if (photo_paths[3]) photo_file_path_3 = photo_paths[3]; - if (photo_paths[4]) photo_file_path_4 = photo_paths[4]; - if (photo_paths[5]) photo_file_path_5 = photo_paths[5]; - if (photo_paths[6]) photo_file_path_6 = photo_paths[6]; - } - - const handle_photo_add = async (): Promise<void> => { - try { - const photo_paths_select = await dialog.open_photos(); - if (!photo_paths_select) return; - photo_paths = list_assign(photo_paths, photo_paths_select.results); - } catch (e) { - await catch_err(e, `handle_photo_add`); - } - }; - - const handle_photo_envelope_edit = async ( - opts_photo_index: number, - ): Promise<void> => { - try { - if (!photo_paths[opts_photo_index]) photo_edit = undefined; - else - photo_edit = { - index: opts_photo_index, - file_path: photo_paths[opts_photo_index], - }; - } catch (e) { - await catch_err(e, `handle_photo_envelope_edit`); - } - }; -</script> - -<div class={`flex flex-col w-full px-4 justify-start items-center`}> - <button - id={basis.id} - class={`flex flex-row h-[11rem] w-[22rem] justify-center items-center bg-layer-1-surface rounded-[2rem] overflow-hidden`} - > - <button - on:click|stopPropagation={async () => { - if (photo_file_path_0) await handle_photo_envelope_edit(0); - else await handle_photo_add(); - }} - class={`group relative flex flex-col h-[11rem] w-[11rem] justify-center items-center border-r-line border-layer-0-glyph_d rounded-tl-3xl rounded-bl-3xl overflow-hidden el-re`} - > - {#if photo_file_path_0} - {#await fs.read_bin(photo_file_path_0) then file_data} - <ImageBlob - basis={{ - data: file_data, - }} - /> - {/await} - {:else} - <div - in:fade={{ duration: 200 }} - out:fade={{ - delay: 0, - duration: 50, - }} - class={`flex flex-row justify-start items-center`} - > - <Glyph - basis={{ - classes: `text-layer-0-glyph group-active:text-layer-0-glyph/60 delay-100 duration-300 ease-in-out transition-all`, - dim: `lg`, - key: `camera`, - }} - /> - </div> - {/if} - </button> - <div - class={`flex flex-row flex-wrap h-full w-[11rem] justify-start items-start`} - > - <button - class={`flex flex-row h-[5.5rem] w-[calc(11rem/3)] justify-center items-center border-b-line border-r-line border-layer-0-glyph_d overflow-hidden`} - on:click|stopPropagation={async () => { - if (photo_file_path_1) await handle_photo_envelope_edit(1); - else await handle_photo_add(); - }} - > - {#if photo_file_path_1} - {#await fs.read_bin(photo_file_path_1) then file_data} - <ImageBlob - basis={{ - data: file_data, - }} - /> - {/await} - {:else} - <Glyph - basis={{ - classes: `text-layer-0-glyph text-layer-0-glyph/80 group-active:text-layer-0-glyph/60 el-re`, - dim: `sm`, - key: `plus`, - }} - /> - {/if} - </button> - <button - class={`flex flex-row h-[5.5rem] w-[calc(11rem/3)] justify-center items-center border-b-line border-r-line border-layer-0-glyph_d overflow-hidden`} - on:click|stopPropagation={async () => { - if (photo_file_path_2) await handle_photo_envelope_edit(2); - else await handle_photo_add(); - }} - > - {#if photo_file_path_2} - {#await fs.read_bin(photo_file_path_2) then file_data} - <ImageBlob - basis={{ - data: file_data, - }} - /> - {/await} - {:else} - <Glyph - basis={{ - classes: `text-layer-0-glyph text-layer-0-glyph/80 group-active:text-layer-0-glyph/60 el-re`, - dim: `sm`, - key: `plus`, - }} - /> - {/if} - </button> - <button - class={`flex flex-row h-[5.5rem] w-[calc(11rem/3)] justify-center items-center border-b-line border-layer-0-glyph_d overflow-hidden`} - on:click|stopPropagation={async () => { - if (photo_file_path_3) await handle_photo_envelope_edit(3); - else await handle_photo_add(); - }} - > - {#if photo_file_path_3} - {#await fs.read_bin(photo_file_path_3) then file_data} - <ImageBlob - basis={{ - data: file_data, - }} - /> - {/await} - {:else} - <Glyph - basis={{ - classes: `text-layer-0-glyph text-layer-0-glyph/80 group-active:text-layer-0-glyph/60 el-re`, - dim: `sm`, - key: `plus`, - }} - /> - {/if} - </button> - <button - class={`flex flex-row h-[5.5rem] w-[calc(11rem/3)] justify-center items-center border-r-line border-layer-0-glyph_d overflow-hidden`} - on:click|stopPropagation={async () => { - if (photo_file_path_4) await handle_photo_envelope_edit(4); - else await handle_photo_add(); - }} - > - {#if photo_file_path_4} - {#await fs.read_bin(photo_file_path_4) then file_data} - <ImageBlob - basis={{ - data: file_data, - }} - /> - {/await} - {:else} - <Glyph - basis={{ - classes: `text-layer-0-glyph text-layer-0-glyph/80 group-active:text-layer-0-glyph/60 el-re`, - dim: `sm`, - key: `plus`, - }} - /> - {/if} - </button> - <button - class={`flex flex-row h-[5.5rem] w-[calc(11rem/3)] justify-center items-center border-r-line border-layer-0-glyph_d overflow-hidden`} - on:click|stopPropagation={async () => { - if (photo_file_path_5) await handle_photo_envelope_edit(5); - else await handle_photo_add(); - }} - > - {#if photo_file_path_5} - {#await fs.read_bin(photo_file_path_5) then file_data} - <ImageBlob - basis={{ - data: file_data, - }} - /> - {/await} - {:else} - <Glyph - basis={{ - classes: `text-layer-0-glyph text-layer-0-glyph/80 group-active:text-layer-0-glyph/60 el-re`, - dim: `sm`, - key: `plus`, - }} - /> - {/if} - </button> - <button - class={`flex flex-row h-[5.5rem] w-[calc(11rem/3)] justify-center items-center border-layer-0-glyph_d overflow-hidden`} - on:click|stopPropagation={async () => { - if (photo_file_path_6) await handle_photo_envelope_edit(6); - else await handle_photo_add(); - }} - > - {#if photo_file_path_6} - {#await fs.read_bin(photo_file_path_6) then file_data} - <ImageBlob - basis={{ - data: file_data, - }} - /> - {/await} - {:else} - <Glyph - basis={{ - classes: `text-layer-0-glyph text-layer-0-glyph/80 group-active:text-layer-0-glyph/60 el-re`, - dim: `sm`, - key: `plus`, - }} - /> - {/if} - </button> - </div> - </button> - <div - class={`flex flex-row h-8 w-${$app_layout} justify-start items-center`} - > - <div class={`flex flex-row w-[11rem] justify-center items-center`}> - <p - class={`font-sans font-[500] text-[0.9rem] text-layer-0-glyph/80 scale-y-[94%]`} - > - {`${$ls(`icu.primary_*`, { value: `${$ls(`common.photo`)}` })}`} - </p> - </div> - </div> -</div> diff --git a/src/lib/components/image_upload_edit_envelope.svelte b/src/lib/components/image_upload_edit_envelope.svelte @@ -1,160 +0,0 @@ -<script lang="ts"> - import { fs } from "$lib/client"; - import { - catch_err, - envelope_tilt, - envelope_visible, - EnvelopeLower, - ImageBlob, - ls, - time_iso, - } from "@radroots/svelte-lib"; - import { - format_file_bytes, - list_move_index, - parse_file_name, - } from "@radroots/utils"; - import { onDestroy, onMount } from "svelte"; - - export let photo_paths: string[]; - export let photo_edit: { index: number; file_path: string } | undefined; - - $: envelope_visible.set(!!photo_edit); - - onMount(async () => { - try { - envelope_tilt.set(false); - } catch (e) { - } finally { - } - }); - - onDestroy(async () => { - try { - envelope_tilt.set(true); - } catch (e) { - } finally { - } - }); - - const handle_photo_edit_move = async ( - opts_photo_index: number, - ): Promise<void> => { - try { - photo_paths = list_move_index(photo_paths, opts_photo_index, 0); - } catch (e) { - await catch_err(e, `handle_photo_edit_move`); - } - }; -</script> - -<EnvelopeLower - basis={{ - close: async () => { - photo_edit = undefined; - }, - }} -> - {#if photo_edit} - <div - class={`flex flex-col w-full px-4 gap-4 justify-start items-center`} - > - <div - class={`flex flex-row w-full justify-center items-center round-44 overflow-hidden`} - > - {#await fs.read_bin(photo_edit.file_path) then file_data} - <ImageBlob - basis={{ - data: file_data, - }} - /> - {/await} - </div> - <div - class={`flex flex-col w-full pb-16 gap-4 justify-center items-center`} - > - <button - class={`flex flex-row h-touch_guide w-full justify-center items-center rounded-2xl bg-layer-1-glyph-hl`} - on:click={async () => { - if (photo_edit?.index === 0) return; - else if (photo_edit) - await handle_photo_edit_move(photo_edit.index); - photo_edit = undefined; - }} - > - <p class={`font-sans font-[600] text-lg text-white`}> - {#if photo_edit.index === 0} - {`${$ls(`icu.primary_*`, { value: `${$ls(`common.photo`)}`.toLowerCase() })}`} - {:else} - {`${$ls(`common.make_primary`)}`} - {/if} - </p> - </button> - {#await fs.info(photo_edit.file_path) then fs_info} - {#if fs_info} - <div - class={`flex flex-col w-full px-4 gap-3 justify-start items-start`} - > - <div - class={`flex flex-col gap-1 justify-start items-start`} - > - <p - class={`col-span-8 font-sans font-[400] text-[1.05rem] text-layer-0-glyph`} - > - {`${$ls(`common.file_name`)}:`} - </p> - <p - class={`col-span-8 font-sans font-[400] text-[1.05rem] text-layer-0-glyph`} - > - {`${parse_file_name(photo_edit.file_path)}`} - </p> - </div> - <div - class={`flex flex-col gap-1 justify-start items-start`} - > - <p - class={`col-span-8 font-sans font-[400] text-[1.05rem] text-layer-0-glyph`} - > - {`${$ls(`common.file_size`)}:`} - </p> - <p - class={`col-span-8 font-sans font-[400] text-[1.05rem] text-layer-0-glyph`} - > - {`${format_file_bytes(fs_info.size, `mb`)}`} - </p> - </div> - <div - class={`flex flex-col gap-1 justify-start items-start`} - > - <p - class={`col-span-8 font-sans font-[400] text-[1.05rem] text-layer-0-glyph`} - > - {`${$ls(`common.date_created`)}:`} - </p> - <p - class={`col-span-8 font-sans font-[400] text-[1.05rem] text-layer-0-glyph`} - > - {`${time_iso(fs_info.birthtime?.toISOString(), `file_info`).replaceAll(`,`, ` ${`${$ls(`common.at`)}`.toLowerCase()}`)}`} - </p> - </div> - <div - class={`flex flex-col gap-1 justify-start items-start`} - > - <p - class={`col-span-8 font-sans font-[400] text-[1.05rem] text-layer-0-glyph`} - > - {`${$ls(`common.date_modified`)}:`} - </p> - <p - class={`col-span-8 font-sans font-[400] text-[1.05rem] text-layer-0-glyph`} - > - {`${time_iso(fs_info.mtime?.toISOString(), `file_info`).replaceAll(`,`, ` ${`${$ls(`common.at`)}`.toLowerCase()}`)}`} - </p> - </div> - </div> - {/if} - {/await} - </div> - </div> - {/if} -</EnvelopeLower> diff --git a/src/lib/components/line_entries_between.svelte b/src/lib/components/line_entries_between.svelte @@ -1,23 +0,0 @@ -<script lang="ts"> - import { fmt_cl, type IClOpt } from "@radroots/svelte-lib"; - import LineEntryData from "./line_entry_data.svelte"; - import LineEntryLabel from "./line_entry_label.svelte"; - - export let basis: IClOpt & { - label: IClOpt & { - value: string; - value_f?: string; - }; - data: IClOpt & { - value: string; - value_f?: string; - }; - }; -</script> - -<div - class={`${fmt_cl(basis.classes)} flex flex-row h-6 w-full justify-between items-center`} -> - <LineEntryLabel basis={basis.label} /> - <LineEntryData basis={basis.data} /> -</div> diff --git a/src/lib/components/line_entry_data.svelte b/src/lib/components/line_entry_data.svelte @@ -1,17 +0,0 @@ -<script lang="ts"> - import { fmt_cl, type IClOpt, type IClWrapOpt } from "@radroots/svelte-lib"; - - export let basis: IClOpt & - IClWrapOpt & { - value: string; - value_f?: string; - }; -</script> - -<div class={`${fmt_cl(basis.classes_wrap)} flex flex-row h-6`}> - <p - class={`${fmt_cl(basis.classes)} font-sans font-[500] text-[1.05rem] text-justify truncate text-layer-1-glyph-shade`} - > - {basis.value || basis.value_f || ``} - </p> -</div> diff --git a/src/lib/components/line_entry_label.svelte b/src/lib/components/line_entry_label.svelte @@ -1,17 +0,0 @@ -<script lang="ts"> - import { fmt_cl, type IClOpt, type IClWrapOpt } from "@radroots/svelte-lib"; - - export let basis: IClOpt & - IClWrapOpt & { - value: string; - value_f?: string; - }; -</script> - -<div class={`${fmt_cl(basis.classes_wrap)} flex flex-row h-6`}> - <p - class={`${fmt_cl(basis.classes)} font-sans font-[400] text-layer-1-glyph_d text-[1.05rem] capitalize`} - > - {basis.value || basis.value_f || ``} - </p> -</div> diff --git a/src/lib/components/map_marker_dot.svelte b/src/lib/components/map_marker_dot.svelte @@ -1,15 +0,0 @@ -<script lang="ts"> - import { Fill } from "@radroots/svelte-lib"; -</script> - -<div class="flex flex-row p-1"> - <div - class={`z-20 flex flex-row h-map_circle w-map_circle justify-center items-center bg-white rounded-full shadow-lg`} - > - <div - class={`z-10 flex flex-row h-map_circle_inner w-map_circle_inner justify-center items-center bg-blue-400 rounded-full`} - > - <Fill /> - </div> - </div> -</div> diff --git a/src/lib/components/map_point_display.svelte b/src/lib/components/map_point_display.svelte @@ -1,24 +0,0 @@ -<script lang="ts"> - import { cfg } from "$lib/conf"; - import { app_thc } from "@radroots/svelte-lib"; - import { MapLibre, Marker } from "@radroots/svelte-maplibre"; - import type { GeolocationCoordinatesPoint } from "@radroots/utils"; - import MapMarkerDot from "./map_marker_dot.svelte"; - - export let basis: { - point: GeolocationCoordinatesPoint; - zoom?: number; - }; -</script> - -<MapLibre - center={basis.point} - zoom={basis.zoom || 4} - class={`h-full w-full`} - style={cfg.map.styles.base[$app_thc]} - attributionControl={false} -> - <Marker bind:lngLat={basis.point} draggable={false}> - <MapMarkerDot /> - </Marker> -</MapLibre> diff --git a/src/lib/components/map_point_select.svelte b/src/lib/components/map_point_select.svelte @@ -1,80 +0,0 @@ -<script lang="ts"> - import { geoc } from "$lib/client"; - import { cfg } from "$lib/conf"; - import type { GeocoderReverseResult } from "@radroots/geocoder"; - import { app_thc, catch_err } from "@radroots/svelte-lib"; - import { MapLibre, Marker } from "@radroots/svelte-maplibre"; - import type { GeolocationCoordinatesPoint } from "@radroots/utils"; - import MapMarkerDot from "./map_marker_dot.svelte"; - import MapPopupPointGeolocation from "./map_popup_point_geolocation.svelte"; - - export let map_point: GeolocationCoordinatesPoint; - export let map_geoc: GeocoderReverseResult | undefined = undefined; - export let map_center: GeolocationCoordinatesPoint = cfg.map.coords.default; - - $: if (map_point && map_center.lat === 0 && map_center.lng === 0) { - map_center = { - lat: map_point.lat, - lng: map_point.lng - 0.065, - }; - } - - $: { - if ( - map_point && - map_center && - map_center.lat !== 0 && - map_center.lng !== 0 - ) { - (async () => { - try { - const geoc_res = await geoc.reverse({ - point: map_point, - }); - if (`results` in geoc_res && geoc_res.results.length > 0) - map_geoc = geoc_res.results[0]; - else map_geoc = undefined; - } catch (e) { - await catch_err(e, ``); - } - })(); - } - } -</script> - -{#if map_point} - <MapLibre - center={map_center} - zoom={10} - class={`h-full w-full`} - style={cfg.map.styles.base[$app_thc]} - attributionControl={false} - > - <Marker - bind:lngLat={map_point} - draggable - on:dragend={async () => { - if (!map_point) return; - const geoc_res = await geoc.reverse({ - point: map_point, - limit: 1, - }); - if (`results` in geoc_res && geoc_res.results.length > 0) - map_geoc = geoc_res.results[0]; - }} - > - <button - class={`flex flex-row justify-center items-center transform -translate-x-[42%]`} - on:click={async () => {}} - > - <MapPopupPointGeolocation - basis={{ - point: map_point, - geoc: map_geoc, - }} - /> - </button> - <MapMarkerDot /> - </Marker> - </MapLibre> -{/if} diff --git a/src/lib/components/map_point_select_envelope.svelte b/src/lib/components/map_point_select_envelope.svelte @@ -1,153 +0,0 @@ -<script lang="ts"> - import { geoc } from "$lib/client"; - import { cfg } from "$lib/conf"; - import type { GeocoderReverseResult } from "@radroots/geocoder"; - import { - app_thc, - catch_err, - envelope_tilt, - envelope_visible, - EnvelopeLower, - Glyph, - ls, - type CallbackPromise, - } from "@radroots/svelte-lib"; - import { MapLibre, Marker } from "@radroots/svelte-maplibre"; - import type { GeolocationCoordinatesPoint } from "@radroots/utils"; - import { onDestroy, onMount } from "svelte"; - import MapMarkerDot from "./map_marker_dot.svelte"; - import MapPopupPointGeolocation from "./map_popup_point_geolocation.svelte"; - - export let map_point_select: GeolocationCoordinatesPoint | undefined; - export let map_point_select_geoc: GeocoderReverseResult | undefined = - undefined; - - export let basis: { - visible: boolean; - close: CallbackPromise; - }; - $: basis = basis; - - let map_point_center: GeolocationCoordinatesPoint = cfg.map.coords.default; - - onMount(async () => { - try { - envelope_tilt.set(false); - } catch (e) { - } finally { - } - }); - - onDestroy(async () => { - try { - envelope_tilt.set(true); - } catch (e) { - } finally { - } - }); - - $: envelope_visible.set(!!basis.visible); - - $: if ( - map_point_select && - map_point_center.lat === 0 && - map_point_center.lng === 0 - ) { - map_point_center = { - lat: map_point_select.lat, - lng: map_point_select.lng - 0.065, - }; - } - - $: { - if ( - map_point_select && - map_point_center && - map_point_center.lat !== 0 && - map_point_center.lng !== 0 - ) { - (async () => { - try { - const geoc_res = await geoc.reverse({ - point: map_point_select, - }); - if (`results` in geoc_res && geoc_res.results.length > 0) - map_point_select_geoc = geoc_res.results[0]; - else map_point_select_geoc = undefined; - } catch (e) { - await catch_err(e, ``); - } - })(); - } - } -</script> - -<EnvelopeLower - basis={{ - full_cover: true, - close: async () => { - await basis.close(); - }, - }} -> - {#if basis.visible && map_point_select} - <MapLibre - center={map_point_center} - zoom={10} - class={`h-full w-full`} - style={cfg.map.styles.base[$app_thc]} - attributionControl={false} - > - <Marker - bind:lngLat={map_point_select} - draggable - on:dragend={async () => { - if (!map_point_select) return; - const geoc_res = await geoc.reverse({ - point: map_point_select, - limit: 1, - }); - if (`results` in geoc_res && geoc_res.results.length > 0) - map_point_select_geoc = geoc_res.results[0]; - }} - > - <button - class={`flex flex-row justify-center items-center transform -translate-x-[42%]`} - on:click={async () => {}} - > - <MapPopupPointGeolocation - basis={{ - point: map_point_select, - geoc: map_point_select_geoc, - }} - /> - </button> - <MapMarkerDot /> - </Marker> - </MapLibre> - <div - class={`absolute top-16 left-0 flex flex-col w-full pt-1 justify-center items-center`} - > - <button - class={`group flex flex-row h-8 px-4 gap-2 justify-center items-center bg-layer-1-surface active:bg-layer-1-surface_a rounded-2xl shadow-md el-re`} - on:click={async () => { - await basis.close(); - }} - > - <Glyph - basis={{ - classes: `text-layer-0-glyph`, - dim: `xs`, - weight: `bold`, - key: `arrow-up`, - }} - /> - <p - class={`font-sans font-[400] text-layer-0-glyph text-[1rem] capitalize`} - > - {`${$ls(`common.back`)}`} - </p> - </button> - </div> - {/if} -</EnvelopeLower> diff --git a/src/lib/components/map_popup_point_geolocation.svelte b/src/lib/components/map_popup_point_geolocation.svelte @@ -1,87 +0,0 @@ -<script lang="ts"> - import type { GeocoderReverseResult } from "@radroots/geocoder"; - import { - fmt_geol_latitude, - fmt_geol_longitude, - Glyph, - } from "@radroots/svelte-lib"; - import type { GeolocationCoordinatesPoint } from "@radroots/utils"; - - export let basis: { - point: GeolocationCoordinatesPoint; - geoc?: GeocoderReverseResult; - }; - $: basis = basis; -</script> - -<button - class={`flex flex-row justify-center items-center el-re`} - on:click={async () => {}} -> - <div - class={`flex flex-col w-fit px-5 py-[0.7rem] gap-2 justify-start items-start bg-layer-1-surface round-20 shadow-lg`} - > - {#if basis.geoc} - <div class={`flex flex-col w-full gap-2 justify-start items-start`}> - <div - class={`flex flex-col w-full gap-1 justify-start items-start`} - > - <div - class={`flex flex-row gap-1 justify-start items-center`} - > - <p - class={`font-sans font-[500] text-[1.05rem] text-layer-2-glyph`} - > - {basis.geoc.name} - </p> - <Glyph - basis={{ - classes: `text-layer-2-glyph -translate-y-[2px]`, - dim: `xs`, - weight: `bold`, - key: `map-pin-simple`, - }} - /> - </div> - <div - class={`flex flex-row w-full justify-start items-center`} - > - <p - class={`font-sans font-[500] text-[1rem] tracking-tight text-layer-2-glyph`} - > - {`${basis.geoc.admin1_name}, ${basis.geoc.country_name}`} - </p> - </div> - </div> - <div class={`flex flex-col w-full justify-start items-start`}> - <p - class={`font-sans font-[400] text-[0.9rem] text-layer-0-glyph`} - > - {`${fmt_geol_latitude(basis.point.lat, `dms`)}`} - </p> - <p - class={`font-sans font-[400] text-[0.9rem] text-layer-0-glyph`} - > - {`${fmt_geol_longitude(basis.point.lng, `dms`)}`} - </p> - </div> - </div> - {:else} - <div class={`flex flex-row w-full justify-start items-center`}> - <p class={`font-mono font-[400] text-layer-2-glyph`}> - {`Marker location:`} - </p> - </div> - <div - class={`flex flex-row w-full gap-2 justify-start items-center`} - > - <p class={`font-mono font-[400] text-layer-2-glyph`}> - {`${basis.point.lat.toFixed(4)}`} - </p> - <p class={`font-mono font-[400] text-layer-2-glyph`}> - {`${basis.point.lng.toFixed(4)}`} - </p> - </div> - {/if} - </div> -</button> diff --git a/src/lib/components/search_result_container.svelte b/src/lib/components/search_result_container.svelte @@ -1,16 +0,0 @@ -<script lang="ts"> - import type { CallbackPromise } from "@radroots/svelte-lib"; - - export let basis: { - callback: CallbackPromise; - }; -</script> - -<button - on:click={async () => { - await basis.callback(); - }} - class={`group flex flex-row h-[4rem] w-full px-4 justify-between items-center bg-layer-1-surface round-24 layer-1-active-surface el-re`} -> - <slot /> -</button> diff --git a/src/lib/components/search_result_display.svelte b/src/lib/components/search_result_display.svelte @@ -1,204 +0,0 @@ -<script lang="ts"> - import { ascii, Glyph, ls, route } from "@radroots/svelte-lib"; - import type { SearchServiceResult } from "@radroots/utils"; - import SearchResultContainer from "./search_result_container.svelte"; - - export let basis: SearchServiceResult; -</script> - -{#if `location_gcs` in basis && basis.location_gcs.id} - <SearchResultContainer - basis={{ - callback: async () => { - if (basis.location_gcs.kind === `farm_land`) - await route(`/farm/land/view`, [ - [`id`, basis.location_gcs.id], - ]); - }, - }} - > - <div class={`flex flex-row gap-4 justify-start items-center`}> - <div - class={`flex flex-row h-[1.5rem] w-[1.5rem] justify-center items-center bg-stone-500 round-24`} - > - <Glyph - basis={{ - classes: `text-white`, - dim: `xs`, - weight: `bold`, - key: `compass`, - }} - /> - </div> - <div class={`flex flex-row gap-1 justify-start items-center`}> - <div - class={`flex flex-row h-[1.2rem] px-2 justify-center items-center bg-stone-600 rounded-md`} - > - <p - class={`font-sans font-[900] text-[0.7rem] text-white uppercase`} - > - {`${$ls(`common.location`)}`} - </p> - </div> - <p - class={`font-sans font-[700] text-layer-0-glyph/30 -translate-y-[1px]`} - > - {ascii.bullet} - </p> - - {#if basis.location_gcs.kind === `farm_land`} - <div - class={`flex flex-row h-[1.2rem] px-2 justify-center items-center bg-lime-500 rounded-md`} - > - <p - class={`font-sans font-[900] text-[0.7rem] text-white uppercase`} - > - {`${$ls(`common.farm_land`)}`} - </p> - </div> - {/if} - </div> - </div> - <div - class={`flex flex-row flex-grow pl-4 pr-2 justify-end items-center overflow-hidden`} - > - {#if basis.location_gcs.label} - <p - class={`font-sand font-[500] text-[0.9rem] text-layer-0-glyph`} - > - {`${basis.location_gcs.label}`} - </p> - {:else} - <p - class={`font-sand font-[500] text-[0.9rem] text-layer-0-glyph truncate`} - > - {`${basis.location_gcs.gc_name}, ${basis.location_gcs.gc_admin1_id}`} - </p> - {/if} - </div> - </SearchResultContainer> -{:else if `nostr_profile` in basis && basis.nostr_profile.id} - <SearchResultContainer - basis={{ - callback: async () => { - await route(`/settings/profile`); - }, - }} - > - <div class={`flex flex-row gap-4 justify-start items-center`}> - <div - class={`flex flex-row h-[1.5rem] w-[1.5rem] justify-center items-center bg-blue-400 round-24`} - > - <Glyph - basis={{ - classes: `text-white`, - dim: `xs`, - weight: `bold`, - key: `user`, - }} - /> - </div> - <div class={`flex flex-row gap-1 justify-start items-center`}> - <div - class={`flex flex-row h-[1.2rem] px-2 justify-center items-center bg-stone-600 rounded-md`} - > - <p - class={`font-sans font-[900] text-[0.7rem] text-white uppercase`} - > - {`${$ls(`common.profile`)}`} - </p> - </div> - <p - class={`font-sans font-[700] text-layer-0-glyph/30 -translate-y-[1px]`} - > - {ascii.bullet} - </p> - <div - class={`flex flex-row h-[1.2rem] px-2 justify-center items-center bg-purple-400 rounded-md`} - > - <p - class={`font-sans font-[900] text-[0.7rem] text-white uppercase`} - > - {#if basis.result_k === `name`} - {`${$ls(`common.name`)}`} - {:else} - {`@todo`} - {/if} - </p> - </div> - </div> - </div> - <div - class={`flex flex-row flex-grow pl-4 pr-2 justify-end items-center overflow-hidden`} - > - {#if basis.nostr_profile.name} - <p - class={`font-sand font-[500] text-[0.9rem] text-layer-0-glyph tracking-wide truncate`} - > - {#if basis.result_k === `name`} - {`${basis.nostr_profile.name}`} - {:else if basis.result_v} - {`${basis.result_v}`} - {:else} - {`@todo`} - {/if} - </p> - {/if} - </div> - </SearchResultContainer> -{:else if `nostr_relay` in basis && basis.nostr_relay.id} - <SearchResultContainer - basis={{ - callback: async () => {}, - }} - > - <div class={`flex flex-row gap-4 justify-start items-center`}> - <div - class={`flex flex-row h-[1.5rem] w-[1.5rem] justify-center items-center bg-blue-400 round-24`} - > - <Glyph - basis={{ - classes: `text-white`, - dim: `xs`, - weight: `bold`, - key: `user`, - }} - /> - </div> - <div class={`flex flex-row gap-1 justify-start items-center`}> - <div - class={`flex flex-row h-[1.2rem] px-2 justify-center items-center bg-stone-600 rounded-md`} - > - <p - class={`font-sans font-[900] text-[0.7rem] text-white uppercase`} - > - {`${$ls(`common.relay`)}`} - </p> - </div> - <p - class={`font-sans font-[700] text-layer-0-glyph/30 -translate-y-[1px]`} - > - {ascii.bullet} - </p> - <div - class={`flex flex-row h-[1.2rem] px-2 justify-center items-center bg-yellow-400 rounded-md`} - > - <p - class={`font-sans font-[900] text-[0.7rem] text-white uppercase`} - > - {`${$ls(`common.url`)}`} - </p> - </div> - </div> - </div> - <div - class={`flex flex-row flex-grow pr-2 justify-end items-center overflow-hidden`} - > - <p - class={`font-sand font-[500] text-[0.9rem] text-layer-0-glyph tracking-wide truncate`} - > - {`${basis.nostr_relay.url}`} - </p> - </div> - </SearchResultContainer> -{/if} diff --git a/src/lib/components/trade_field_display_el.svelte b/src/lib/components/trade_field_display_el.svelte @@ -1,59 +0,0 @@ -<script lang="ts"> - import { fmt_cl, Glyph, type IClOpt } from "@radroots/svelte-lib"; - - export let basis: { - visible: boolean; - label: string; - display: IClOpt & { - undef?: string; - value: string; - nostyle?: boolean; - hide?: boolean; - }; - arrow_visible?: boolean; - slot_before?: boolean; - }; - $: basis = basis; - - $: classes_undef = - basis.visible && `value` in basis.display && !basis.display.value - ? `opacity-60` - : ``; -</script> - -<div class={`flex flex-row h-6 w-full justify-between items-center`}> - <p - class={`font-sans font-[400] text-layer-1-glyph_d text-[1.05rem] capitalize`} - > - {basis.label} - </p> - {#if basis.visible} - <button - class={`flex flex-row max-w-[220px] gap-1 justify-center items-center`} - on:click={async () => {}} - > - {#if basis.slot_before} - <slot /> - {/if} - {#if !basis.display.hide} - <p - class={`${fmt_cl(basis.display.classes)} font-sans font-[400] text-[1.05rem] text-justify truncate text-layer-1-glyph_d ${classes_undef} ${basis.display.nostyle ? `` : `capitalize`}`} - > - {basis.display.value || basis.display.undef || ``} - </p> - {/if} - {#if !basis.slot_before} - <slot /> - {/if} - {#if basis.arrow_visible} - <Glyph - basis={{ - classes: `text-layer-0-glyph ${classes_undef} pt-1`, - dim: `xs`, - key: `caret-right`, - }} - /> - {/if} - </button> - {/if} -</div> diff --git a/src/lib/components/trade_field_display_kv.svelte b/src/lib/components/trade_field_display_kv.svelte @@ -1,84 +0,0 @@ -<script lang="ts"> - import { el_focus } from "$lib/util/client"; - import { - fmt_cl, - fmt_id, - Glyph, - kv, - type CallbackPromise, - type IClOpt, - } from "@radroots/svelte-lib"; - - export let basis: IClOpt & { - visible: boolean; - label: string; - display: IClOpt & { - undef?: string; - nostyle?: boolean; - } & ( - | { - kv: string; - } - | { - value: string; - } - ); - - kv_wrap?: string; - handle_back: CallbackPromise; - }; - $: basis = basis; - - $: classes_undef = - basis.visible && - ((`kv` in basis.display && !kv.get(fmt_id(basis.display.kv))) || - (`value` in basis.display && !basis.display.value)) - ? `opacity-60` - : ``; -</script> - -<div - class={`${fmt_cl(basis.classes)} flex flex-row h-6 w-full justify-between items-start`} -> - <p - class={`font-sans font-[400] text-layer-1-glyph_d text-[1.05rem] capitalize`} - > - {basis.label} - </p> - {#if basis.visible} - <button - class={`flex flex-row max-w-[210px] gap-1 justify-end items-start`} - on:click={async () => { - await el_focus( - fmt_id( - basis.kv_wrap - ? basis.kv_wrap - : `kv` in basis.display - ? `${basis.display.kv}_wrap` - : ``, - ), - async () => await basis.handle_back(), - ); - }} - > - <p - class={`${fmt_cl(basis.display.classes)} font-sans font-[400] text-[1.05rem] text-justify truncate text-layer-1-glyph_d ${classes_undef} ${basis.display.nostyle ? `` : `capitalize`}`} - > - {#if `kv` in basis.display} - {#await kv.get(fmt_id(basis.display.kv)) then kv_val} - {kv_val || basis.display.undef || ``} - {/await} - {:else} - {basis.display.value || basis.display.undef || ``} - {/if} - </p> - <Glyph - basis={{ - classes: `text-layer-0-glyph ${classes_undef} pt-1`, - dim: `xs`, - key: `caret-right`, - }} - /> - </button> - {/if} -</div> diff --git a/src/lib/components/trade_product_list_card.svelte b/src/lib/components/trade_product_list_card.svelte @@ -1,350 +0,0 @@ -<script lang="ts"> - import { scroll_args } from "$lib/conf"; - import type { TradeProductBundle } from "$lib/types"; - import { fmt_location_gcs } from "@radroots/models"; - import { - app_layout, - catch_err, - el_id, - els_id_pref_index, - fmt_geol_latitude, - fmt_geol_longitude, - Glyph, - ImagePath, - locale, - ls, - route, - } from "@radroots/svelte-lib"; - import { - fmt_currency_price, - fmt_plural_agreement, - mass_tf_str, - num_min, - parse_currency_price, - } from "@radroots/utils"; - import LineEntriesBetween from "./line_entries_between.svelte"; - import LineEntryData from "./line_entry_data.svelte"; - import LineEntryLabel from "./line_entry_label.svelte"; - - const id_pref = `trade-product-list-card`; - - let el_c: HTMLDivElement; - - export let basis: { - index: number; - result: TradeProductBundle; - }; - $: ({ - result: { trade_product, location_gcs, media_uploads }, - } = basis); - - $: tradeproduct_qty_sold = 0; - $: tradeproduct_qty_avail = - num_min(trade_product.qty_avail, 1) - tradeproduct_qty_sold; - - const handle_display_focus = async ( - el: EventTarget & HTMLButtonElement, - ): Promise<void> => { - try { - el.focus(); - el_c.scrollTo(); - el_id( - `${id_pref}-control-${basis.index}-${trade_product.id}`, - )?.classList.remove(`hidden`); - els_id_pref_index( - `${id_pref}-control`, - basis.index, - `not`, - )?.forEach((el) => el.classList.add(`hidden`)); - els_id_pref_index(`${id_pref}-display`, basis.index)?.forEach( - (el) => el.classList.add(`translate-y-12`), - ); - } catch (e) { - await catch_err(e, `handle_display_focus`); - } - }; - - const handle_display_blur = async (): Promise<void> => { - try { - els_id_pref_index( - `${id_pref}-control`, - basis.index, - `not`, - )?.forEach((el) => el.classList.add(`hidden`)); - els_id_pref_index(`${id_pref}-display`, basis.index)?.forEach( - (el) => el.classList.remove(`translate-y-12`), - ); - } catch (e) { - await catch_err(e, `handle_display_blur`); - } - }; -</script> - -<div - bind:this={el_c} - class={`relative flex flex-col w-full justify-center items-center`} -> - <div - id={`${id_pref}-control-${basis.index}-${trade_product.id}`} - class={`hidden absolute top-0 left-0 flex flex-row h-12 w-full justify-center items-start el-re`} - > - <button - class={`flex flex-row px-5 py-1 justify-center items-center bg-layer-1-surface layer-1-active-surface rounded-full`} - on:click|stopPropagation={async () => { - await route(`/models/trade-product/view`, [ - [`id`, trade_product.id], - ]); - }} - > - <p class={`font-sans font-[500] text-layer-0-glyph`}> - {`${$ls(`common.view`)}`} - </p> - </button> - </div> - <button - id={`${id_pref}-display-${basis.index}-${trade_product.id}`} - class={`flex flex-col min-h-[22rem] w-${$app_layout} justify-start items-start bg-layer-1-surface layer-1-focus-surface layer-1-focus-raise-less round-44 focus:translate-y-12`} - on:click|stopPropagation={async ({ currentTarget: el }) => { - handle_display_focus(el); - }} - on:blur={async () => { - await handle_display_blur(); - }} - > - <div - class={`flex flex-row min-h-[10rem] w-full justify-center items-center border-b-line border-b-layer-1-surface-edge`} - > - {#if media_uploads && media_uploads.length} - <div - class={`relative flex flex-row h-full w-full justify-center items-center`} - > - <div - class={`flex flex-row justify-start items-center overflow-x-auto scroll-hide`} - > - {#each media_uploads as li, li_i (li.id)} - <ImagePath - basis={{ - id: `${li.id}-index${li_i}`, - path: `${li.res_base}/${li.res_path}.${li.mime_type}`, - callback: async ({ currentTarget: el }) => { - const el_id_next = el.id.replace( - `-index${li_i}`, - `-index${li_i + 1}`, - ); - el_id(el_id_next)?.scrollIntoView( - scroll_args.into_view, - ); - }, - }} - /> - {/each} - </div> - </div> - {:else} - <button - class={`group flex flex-row w-20 justify-center items-center`} - on:click|stopPropagation={async () => {}} - > - <div - class={`relative flex flex-col w-full justify-start items-center`} - > - <div - class={`relative flex flex-row py-2 px-[0.8rem] justify-center items-center`} - > - <Glyph - basis={{ - classes: `text-layer-0-glyph group-active:text-layer-0-glyph_a el-re`, - dim: `xl`, - weight: `bold`, - key: `camera`, - }} - /> - <div - class={`absolute top-0 right-0 flex flex-row justify-center items-center`} - > - <Glyph - basis={{ - classes: `text-layer-0-glyph group-active:text-layer-0-glyph_a el-re`, - dim: `xs`, - weight: `bold`, - key: `plus`, - }} - /> - </div> - </div> - <div - class={`absolute -bottom-4 left-0 flex flex-row w-full justify-center items-center`} - > - <p - class={`font-sans font-[500] text-[1rem] text-layer-0-glyph group-active:text-layer-0-glyph_a el-re`} - > - {`${$ls(`icu.no_*`, { value: `${$ls(`common.photos`)}`.toLowerCase() })}`} - </p> - </div> - </div> - </button> - {/if} - </div> - {#if location_gcs} - <div - class={`flex flex-col min-h-[11rem] w-full pt-8 pb-12 justify-start items-start`} - > - <div - class={`flex flex-col w-full px-5 gap-6 justify-center items-center`} - > - <div class={`grid grid-cols-12 w-full`}> - <LineEntryLabel - basis={{ - classes_wrap: `col-span-7`, - value: `"${trade_product.title}"`, - }} - /> - <LineEntryData - basis={{ - classes_wrap: `col-span-5 justify-end capitalize`, - value: `${trade_product.key}`, - }} - /> - </div> - <div class={`grid grid-cols-12 w-full gap-y-[2px]`}> - <LineEntriesBetween - basis={{ - classes: `col-span-12`, - label: { - value: `${$ls(`common.origin`)}`, - }, - data: { - value: `${fmt_geol_latitude( - location_gcs.lat, - `d`, - 4, - )}, ${fmt_geol_longitude( - location_gcs.lng, - `d`, - 4, - )}`, - }, - }} - /> - <LineEntryData - basis={{ - classes_wrap: `col-span-12 justify-end capitalize`, - value: `${fmt_location_gcs(location_gcs, `city`)}`, - }} - /> - </div> - <div class={`grid grid-cols-12 w-full gap-y-[2px]`}> - {#await parse_currency_price($locale, trade_product.price_currency, trade_product.price_amt) then price} - <LineEntriesBetween - basis={{ - classes: `col-span-12`, - label: { - value: `${$ls(`common.price`)}`, - }, - data: { - value: `${price ? fmt_currency_price(price) : ``} / ${`${$ls(`measurement.mass.unit.${trade_product.price_qty_unit}_ab`)}`}`, - }, - }} - /> - {/await} - <LineEntriesBetween - basis={{ - classes: `col-span-12`, - label: { - value: `${$ls(`icu.*_order`, { value: `${$ls(`common.quantity`)}` })}`, - }, - data: { - value: `${trade_product.qty_amt} / ${`${$ls(`measurement.mass.unit.${trade_product.qty_unit}_ab`)}`}`, - }, - }} - /> - <LineEntriesBetween - basis={{ - classes: `col-span-12`, - label: { - value: `${$ls(`icu.*_available`, { value: `${$ls(`common.quantity`)}` })}`, - }, - data: { - value: `${tradeproduct_qty_avail} ${trade_product.qty_label || fmt_plural_agreement(tradeproduct_qty_avail, `${$ls(`common.bag`)}`, `${$ls(`common.bags`)}`)}`, - }, - }} - /> - <LineEntriesBetween - basis={{ - classes: `col-span-12`, - label: { - value: `${$ls(`icu.*_sold`, { value: `${$ls(`common.quantity`)}` })}`, - }, - data: { - value: `${tradeproduct_qty_sold} ${trade_product.qty_label || fmt_plural_agreement(tradeproduct_qty_sold, `${$ls(`common.bag`)}`, `${$ls(`common.bags`)}`)}`, - }, - }} - /> - <LineEntriesBetween - basis={{ - classes: `col-span-12`, - label: { - value: `${$ls(`common.lot`)}`, - }, - data: { - classes: `capitalize`, - value: `${trade_product.lot}`, - }, - }} - /> - <LineEntriesBetween - basis={{ - classes: `col-span-12`, - label: { - value: `${$ls(`common.process`)}`, - }, - data: { - classes: `capitalize`, - value: `${trade_product.process.replaceAll(`_`, ` `)}`, - }, - }} - /> - <LineEntriesBetween - basis={{ - classes: `col-span-12`, - label: { - value: `${$ls(`common.profile`)}`, - }, - data: { - classes: `capitalize`, - value: `${trade_product.profile.replaceAll(`_`, ` `)}`, - }, - }} - /> - <LineEntriesBetween - basis={{ - classes: `col-span-12`, - label: { - value: `${$ls(`common.year`)}`, - }, - data: { - value: `${trade_product.year}`, - }, - }} - /> - </div> - <div class={`grid grid-cols-12 w-full gap-y-[2px]`}> - {#await parse_currency_price($locale, trade_product.price_currency, trade_product.price_amt * Math.floor(Math.max(trade_product.price_qty_amt, 1)) * mass_tf_str(trade_product.price_qty_unit, trade_product.qty_unit, trade_product.qty_amt)) then price} - <LineEntriesBetween - basis={{ - classes: `col-span-12`, - label: { - value: `${$ls(`icu.total_*`, { value: `${$ls(`common.value`)}` })}`, - }, - data: { - value: `${price ? fmt_currency_price(price) : ``} / ${`${$ls(`measurement.mass.unit.${trade_product.price_qty_unit}_ab`)}`}`, - }, - }} - /> - {/await} - </div> - </div> - </div> - {/if} - </button> -</div> diff --git a/src/lib/conf.ts b/src/lib/conf.ts @@ -1,87 +0,0 @@ -import { PUBLIC_RADROOTS_NOSTR_PUBKEY } from "$env/static/public"; -import type { NostrTagClient, NumberTuple } from "@radroots/utils"; - -export const ks = { - cfg_init: { - nostr_secretkey: `cfg:init:nostr:secretkey`, - nostr_profilename: `cfg:init:nostr:profilename`, - radroots_tok: `cfg:init:radroots:tok` - }, - pref: { - cfg_type: `pref:cfg_type`, - }, - keys: { - nostr_secretkey: (public_key: string) => `nostr:key:${public_key}`, - nostr_publickey: `keys:nostr:`, - } -}; - -export const root_symbol = "»--`--,---"; - -export const err = { - nostr: { - no_relays: `error.nostr.no_relays_connected` - } -} - -export const cfg = { - app: { - title: `Radroots`, - description: `Creating networks between farmers, communities and small businesses that give customers greater access to natural foods and grow circular economies where profits are more fairly distributed. Radroots is built on the Nostr protocol and released under a copyleft open source license to provide transparency and give users the option to offer feedback and add or request new features.`, - version: `0.0.1` - }, - nostr: { - relay_url: `wss://radroots.org`, - relay_pubkey: PUBLIC_RADROOTS_NOSTR_PUBKEY, - relay_polling_count_max: 10, - }, - delay: { - load: 321, - notify: 123, - mount_el: 500, - nostr_relay_poll_document: 3000, - entry_focus: 2000, - load_notify: 3000, - }, - debounce: { - search: 200 - }, - cmd: { - layout_route: `*-route` - }, - map: { - styles: { - base: { - light: `https://basemaps.cartocdn.com/gl/voyager-gl-style/style.json`, - dark: `https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json` - } - }, - popup: { - dot: { - offset: [0, -10] as NumberTuple - } - }, - coords: { - default: { - lat: 0, - lng: 0, - } - } - } -}; - -export const nostr_client: NostrTagClient = { - name: root_symbol, - pubkey: cfg.nostr.relay_pubkey, - relay: cfg.nostr.relay_url -}; - -export const scroll_args: { - into_view: ScrollIntoViewOptions -} = { - into_view: { - behavior: `smooth`, - block: `nearest`, - inline: `start`, - } -} -\ No newline at end of file diff --git a/src/lib/types.ts b/src/lib/types.ts @@ -1,19 +0,0 @@ -import type { NDKEvent } from "@nostr-dev-kit/ndk"; -import type { ExtendedBaseType, NDKEventStore } from "@nostr-dev-kit/ndk-svelte"; -import type { IClientDialogAlertOpts, IClientDialogConfirmOpts } from "@radroots/client"; -import type { LocationGcs, MediaUpload, TradeProduct } from "@radroots/models"; - -export type IDialogConfirm = { - confirm: IClientDialogConfirmOpts; -} - -export type IDialogAlert = { - alert: IClientDialogAlertOpts -} -export type TradeProductBundle = { - trade_product: TradeProduct; - location_gcs: LocationGcs; - media_uploads?: MediaUpload[]; -}; - -export type NostrEventPageStore = NDKEventStore<ExtendedBaseType<NDKEvent>>; -\ No newline at end of file diff --git a/src/lib/util/client.ts b/src/lib/util/client.ts @@ -1,88 +0,0 @@ -import { db, dialog, keystore } from "$lib/client"; -import { app_notify, catch_err, el_id, route, sleep, type CallbackPromise, type NavigationRoute } from "@radroots/svelte-lib"; -import type { ThemeLayer } from "@radroots/theme"; -import type { ErrorMessage } from "@radroots/utils"; - -export const keystore_reset = async (): Promise<ErrorMessage<string> | undefined> => { - try { - const ks_keys = await keystore.keys(); - if (`err` in ks_keys) return ks_keys; - for (const ks_key of ks_keys.results) await keystore.remove(ks_key); - } catch (e) { - await catch_err(e, `keystore_reset`); - } -}; - -export const callback_alert = async (message: string, callback: CallbackPromise): Promise<void> => { - try { - dialog.alert(message); - await callback(); - } catch (e) { - await catch_err(e, `cb_alert`); - } -}; - -export const page_reload = async (message?: string): Promise<void> => { - try { - if (message) dialog.alert(message); - location.reload(); - } catch (e) { - await catch_err(e, `page_reload`); - } -}; - -export const restart = async (opts?: { - notify_message?: string; - route?: NavigationRoute; -}): Promise<void> => { - try { - if (opts?.notify_message) app_notify.set(opts.notify_message); - if (opts?.route) await route(opts.route); - else location.reload(); - } catch (e) { - await catch_err(e, `restart`); - } -}; - -export const reset_device = async (): Promise<ErrorMessage<string> | undefined> => { - try { - // delete keystore keys - const ks_keys = await keystore.keys(); - if (`err` in ks_keys) return ks_keys; - for (const ks_key of ks_keys.results) await keystore.remove(ks_key); - // delete database tables - const location_gcss = await db.location_gcs_get({ list: [`all`] }); - if (`err` in location_gcss) return location_gcss; - for (const { id } of location_gcss.results) await db.location_gcs_delete({ id }); - const trade_products = await db.trade_product_get({ list: [`all`] }); - if (`err` in trade_products) return trade_products; - for (const { id } of trade_products.results) await db.trade_product_delete({ id }); - const nostr_profiles = await db.nostr_profile_get({ list: [`all`] }); - if (`err` in nostr_profiles) return nostr_profiles; - for (const { id } of nostr_profiles.results) await db.nostr_profile_delete({ id }); - const nostr_relays = await db.nostr_relay_get({ list: [`all`] }); - if (`err` in nostr_relays) return nostr_relays; - for (const { id } of nostr_relays.results) await db.nostr_relay_delete({ id }); - await route(`/`); - } catch (e) { - await catch_err(e, `reset_device`); - } -}; - -export const el_focus = async (id: string, callback: () => Promise<void>, layer: ThemeLayer = 1): Promise<void> => { - try { - const el = el_id(id); - el?.classList.add(`entry-layer-${layer}-highlight`); - el?.focus(); - await sleep(1200); - await callback(); - el?.classList.remove(`entry-layer-${layer}-highlight`); - } catch (e) { - await catch_err(e, `el_focus`); - } -}; - -export const throw_err = (param: string | ErrorMessage<string>): undefined => { - if (typeof param === `string`) throw new Error(param); - else throw new Error(param.err); -}; -\ No newline at end of file diff --git a/src/lib/util/fetch-radroots-profile.ts b/src/lib/util/fetch-radroots-profile.ts @@ -1,128 +0,0 @@ -import { PUBLIC_RADROOTS_URL } from "$env/static/public"; -import { http, nostr } from "$lib/client"; -import { cfg } from "$lib/conf"; -import { catch_err, get_store, ls, } from "@radroots/svelte-lib"; -import { err_msg, type ErrorMessage } from "@radroots/utils"; -import { throw_err } from "./client"; - -export const fetch_radroots_profile_validate = async (opts: { - profile_name: string; -}): Promise<{ profile_name: string } | ErrorMessage<string>> => { - const $ls = get_store(ls); - try { - const res = await http.fetch({ - url: `${PUBLIC_RADROOTS_URL}/public/accounts/list`, - method: `post`, - }); - console.log(JSON.stringify(res, null, 4), `res`); - if (`err` in res) throw_err(res) - else if (res.error) throw_err(res.error.message) - else if (res.data && Array.isArray(res.data.results)) { - const existing_profile = res.data.results.find( - (i: any) => - `nip_05` in i && - String(i.nip_05).toLowerCase() === - opts.profile_name.toLowerCase(), - ); - if (existing_profile) - return err_msg( - `${`${$ls(`icu.the_*_is_registered`, { value: `${$ls(`common.profile_name`)}`.toLowerCase() })} `}`, - ); - return { profile_name: opts.profile_name }; - } - return err_msg(`${$ls(`error.client.request_failure`)}`); - } catch (e) { - await catch_err(e, "fetch_radroots_profile_validate"); - return err_msg(`${$ls(`error.client.network_failure`)}`); - } -}; - -export const fetch_radroots_profile_init = async (opts: { - profile_name: string; - secret_key: string; - nostr_relays?: string[]; -}): Promise<{ tok: string } | ErrorMessage<string>> => { - const $ls = get_store(ls); - try { - const res = await http.fetch({ - url: `${PUBLIC_RADROOTS_URL}/public/accounts/add/init`, - method: `post`, - data: { - nip_05: opts.profile_name, - public_key: nostr.lib.public_key(opts.secret_key), - nostr_relays: opts.nostr_relays?.length - ? Array.from( - new Set([ - ...opts.nostr_relays, - cfg.nostr.relay_url, - ]), - ).join(`,`) - : [cfg.nostr.relay_url].join(`,`), - }, - }); - if (`err` in res) return res; - else if (res.data && `tok` in res.data) { - return { tok: res.data.tok }; - } else if (res.data && `message` in res.data) - return err_msg( - `${$ls(`radroots-org.error.${res.data.message}`)}`, - ); - return err_msg(`${$ls(`error.client.request_failure`)}`); - } catch (e) { - await catch_err(e, "fetch_radroots_profile_init"); - return err_msg(`${$ls(`error.client.network_failure`)}`); - } -}; - -export const fetch_radroots_profile_confirm = async ( - authorization: string, -): Promise<{ pass: true } | ErrorMessage<string>> => { - const $ls = get_store(ls); - try { - const res = await http.fetch({ - url: `${PUBLIC_RADROOTS_URL}/public/accounts/add/conf`, - method: `post`, - authorization, - }); - if (`err` in res) return res; - return { pass: true }; - } catch (e) { - await catch_err(e, "fetch_radroots_profile_confirm"); - return err_msg(`${$ls(`error.client.network_failure`)}`); - } -}; - -export const fetch_radroots_profile_status = async ( - authorization: string, -): Promise< - | { active: { public_key: string; nip_05?: string } } - | ErrorMessage<string> -> => { - const $ls = get_store(ls); - try { - const res = await http.fetch({ - url: `${PUBLIC_RADROOTS_URL}/public/accounts/add/status`, - method: `post`, - authorization, - }); - if (`err` in res) return res; - else if ( - `public_key` in res.data && - typeof res.data.public_key === `string` - ) - return { - active: { - public_key: res.data.public_key, - nip_05: - `nip_05` in res.data && - typeof res.data.nip_05 === `string` - ? res.data.nip_05 - : undefined, - }, - }; - return err_msg(`${$ls(`error.client.network_failure`)}`); - } catch (e) { - await catch_err(e, "fetch_radroots_profile_status"); - return err_msg(`${$ls(`error.client.network_failure`)}`); - } -}; -\ No newline at end of file diff --git a/src/lib/util/fetch-radroots-upload.ts b/src/lib/util/fetch-radroots-upload.ts @@ -1,52 +0,0 @@ -import { fs, http, keystore } from "$lib/client"; -import { ks } from "$lib/conf"; -import type { IClientHttpResponseError } from "@radroots/client"; -import { app_nostr_key, catch_err, get_store } from "@radroots/svelte-lib"; -import { err_msg, err_res, nostr_event_sign_attest, type ErrorMessage, type ErrorResponse, type FilePath } from "@radroots/utils"; - -export const fetch_radroots_upload = async (opts: { - url: string; - file_path: FilePath; -}): Promise<{ - res_base: string; - res_path: string; -} | ErrorResponse<IClientHttpResponseError> | ErrorMessage<string>> => { - try { - const nostr_public_key = get_store(app_nostr_key); - const secret_key = await keystore.get( - ks.keys.nostr_secretkey(nostr_public_key), - ); - if (`err` in secret_key) return err_msg(`error.client.keystore_nostr_secretkey`); - const { url, file_path } = opts; - const file_data = await fs.read_bin(file_path.file_path); - if (!file_data) return err_msg(`error.client.file_path_read_bin_undefined`);; - const res = await http.fetch({ - url, - method: `put`, - headers: { - "Content-Type": file_path.mime_type, - "X-Nostr-Event": JSON.stringify(nostr_event_sign_attest(secret_key.result)), - }, - authorization: nostr_public_key, - data_bin: file_data, - }); - if (`err` in res) err_msg(`error.client.request_failure`); - else if (res.error) return err_res(res.error); - else if ( - res.status === 200 && - res.data && - `pass` in res.data && - `res_base` in res.data && - typeof res.data.res_base === `string` && - `res_path` in res.data && - typeof res.data.res_path === `string` - ) return { - res_base: res.data.res_base, - res_path: res.data.res_path, - }; - return err_msg(`error.client.request_unhandled`); - } catch (e) { - await catch_err(e, `fetch_radroots_upload`); - return err_msg(`error.client.network_failure`); - } -}; diff --git a/src/lib/util/fetch.ts b/src/lib/util/fetch.ts @@ -1,73 +0,0 @@ -import { db, http } from "$lib/client"; -import { cfg } from "$lib/conf"; -import { parse_nostr_relay_form_keys, type NostrRelayFormFields } from "@radroots/models"; -import { app_nostr_key, catch_err, get_store, nostr_relays_connected, nostr_relays_poll_documents, nostr_relays_poll_documents_count } from "@radroots/svelte-lib"; -import { parse_nostr_relay_information_document_fields } from "@radroots/utils"; -import { throw_err } from "./client"; - -export const fetch_relay_documents = async (): Promise<void> => { - try { - const $nostr_relays_poll_documents_count = get_store(nostr_relays_poll_documents_count); - const $app_nostr_key = get_store(app_nostr_key); - const $nostr_relays_connected = get_store(nostr_relays_connected); - if ( - $nostr_relays_poll_documents_count >= - cfg.nostr.relay_polling_count_max - ) { - nostr_relays_poll_documents.set(false); - return; - } - nostr_relays_poll_documents_count.set( - $nostr_relays_poll_documents_count + 1, - ); - const nostr_relays = await db.nostr_relay_get({ - list: [`on_profile`, { public_key: $app_nostr_key }], - }); - if (`err` in nostr_relays) return throw_err(nostr_relays.err); - const unconnected_relays = nostr_relays.results.filter( - (i) => !$nostr_relays_connected.includes(i.id), - ); - if (unconnected_relays.length === 0) return void nostr_relays_poll_documents.set(false); - for (const nostr_relay of unconnected_relays) { - const res = await http.fetch({ - url: nostr_relay.url.replace(`ws://`, `http://`), - headers: { - Accept: "application/nostr+json", - }, - }); - if (`err` in res) continue; - else if (res.status === 200 && res.data) { - const doc = parse_nostr_relay_information_document_fields( - res.data, - ); - if (!doc) continue; - const fields: Partial<NostrRelayFormFields> = {}; - for (const [k, v] of Object.entries(doc)) { - const field_k = parse_nostr_relay_form_keys(k); - if (field_k) fields[field_k] = v; - } - if (Object.keys(fields).length < 1) continue; - await db.nostr_relay_update({ - on: { - url: nostr_relay.url, - }, - fields, - }); - nostr_relays_connected.set( - Array.from( - new Set([ - ...$nostr_relays_connected, - nostr_relay.id, - ]), - ), - ); - } - } - setTimeout( - fetch_relay_documents, - cfg.delay.nostr_relay_poll_document, - ); - } catch (e) { - await catch_err(e, `fetch_relay_documents`); - } -}; diff --git a/src/lib/util/geocode.ts b/src/lib/util/geocode.ts @@ -1,51 +0,0 @@ -import { geoc } from "$lib/client"; -import type { GeocoderReverseResult } from "@radroots/geocoder"; -import type { LocationGcs } from "@radroots/models"; -import { catch_err } from "@radroots/svelte-lib"; -import type { GeolocationCoordinatesPoint } from "@radroots/utils"; - -export const geoc_rev = async (point: GeolocationCoordinatesPoint): Promise<GeocoderReverseResult | undefined> => { - try { - const geoc_res = await geoc.reverse({ point }); - if (`results` in geoc_res && geoc_res.results.length > 0) - return geoc_res.results[0]; - } catch (e) { - await catch_err(e, `geoc_rev`); - } -}; - - -export const location_gcs_to_geoc = (opts: LocationGcs): GeocoderReverseResult | undefined => { - const { - gc_id: id, - gc_name: name, - gc_admin1_id: admin1_id, - gc_admin1_name: admin1_name, - gc_country_id: country_id, - gc_country_name: country_name, - lat: latitude, - lng: longitude, - } = opts; - if ( - (typeof id === `string` && id) && - (typeof name === `string` && name) && - (typeof admin1_id === `string` && admin1_id) && - (typeof admin1_name === `string` && admin1_name) && - (typeof country_id === `string` && country_id) && - (typeof country_name === `string` && country_name) && - (typeof latitude === `number`) && - (typeof longitude === `number`) - ) { - return { - id: Number(id), - name, - admin1_id, - admin1_name, - country_id, - country_name, - latitude, - longitude - } - } - return undefined; -}; -\ No newline at end of file diff --git a/src/lib/util/kv.ts b/src/lib/util/kv.ts @@ -1,32 +0,0 @@ -import { catch_err, fmt_id, kv } from "@radroots/svelte-lib"; - -export const kv_init_app = async (): Promise<void> => { - try { - const range = Keyva.prefix(`*`); - const kv_list = await kv.each({ range }, `keys`); - await Promise.all(kv_list.map((i) => kv.delete(i))); - } catch (e) { - await catch_err(e, `kv_init_app`); - } -}; - -export const kv_init_page = async (): Promise<void> => { - try { - const kv_pref = fmt_id(); - const range = Keyva.prefix(kv_pref); - const kv_list = await kv.each({ range }, `keys`); - await Promise.all(kv_list.map((i) => kv.delete(i))); - } catch (e) { - await catch_err(e, `kv_init_page`); - } -}; - -export const kv_sync = async (list: [string, string][]): Promise<void> => { - try { - for (const [key, val] of list) await kv.set(key, val); - - } catch (e) { - await catch_err(e, `kv_sync`); - } -}; - diff --git a/src/lib/util/models-location-gcs.ts b/src/lib/util/models-location-gcs.ts @@ -1,72 +0,0 @@ -import { db, geoc } from "$lib/client"; -import type { IClientGeolocationPosition } from "@radroots/client"; -import type { GeocoderReverseResult } from "@radroots/geocoder"; -import type { ILocationGcsAddResolve, LocationGcsFormFields } from "@radroots/models"; -import { catch_err } from "@radroots/svelte-lib"; -import { err_msg, location_geohash, type GeolocationCoordinatesPoint } from "@radroots/utils"; - -export const model_location_gcs_add_position = async (opts: { - label?: string; - kind: string; - geo_pos: IClientGeolocationPosition; -}): Promise<ILocationGcsAddResolve<string>> => { - try { - const { label, geo_pos } = opts; - const fields: LocationGcsFormFields = { - lat: geo_pos.lat.toString(), - lng: geo_pos.lng.toString(), - geohash: location_geohash(geo_pos), - kind: opts.kind, - } - if (label) fields.label = label; - const geoc_rev = await geoc.reverse({ - point: { - lat: geo_pos.lat, - lng: geo_pos.lng - } - }); - if (`results` in geoc_rev && geoc_rev.results.length > 0) { - const geoc_res = geoc_rev.results[0]; - fields.gc_id = geoc_res.id.toString(); - fields.gc_name = geoc_res.name; - fields.gc_admin1_id = geoc_res.admin1_id.toString(); - fields.gc_admin1_name = geoc_res.admin1_name; - fields.gc_country_id = geoc_res.country_id; - fields.gc_country_name = geoc_res.country_name; - }; - const res = await db.location_gcs_add(fields); - return res; - } catch (e) { - await catch_err(e, `model_location_gcs_add_position`); - return err_msg(`*`) - } -}; - -export const model_location_gcs_add_geocode = async (opts: { - label?: string; - kind: string; - geo_code: GeocoderReverseResult; - point: GeolocationCoordinatesPoint; -}): Promise<ILocationGcsAddResolve<string>> => { - try { - const { label, geo_code, point } = opts; - const fields: LocationGcsFormFields = { - lat: point.lat.toString(), - lng: point.lng.toString(), - geohash: location_geohash(point), - kind: opts.kind, - gc_id: geo_code.id.toString(), - gc_name: geo_code.name, - gc_admin1_id: geo_code.admin1_id.toString(), - gc_admin1_name: geo_code.admin1_name, - gc_country_id: geo_code.country_id, - gc_country_name: geo_code.country_name, - }; - if (label) fields.label = label; - const res = await db.location_gcs_add(fields); - return res; - } catch (e) { - await catch_err(e, `model_location_gcs_add_geocode`); - return err_msg(`*`) - } -}; diff --git a/src/lib/util/models-media-upload.ts b/src/lib/util/models-media-upload.ts @@ -1,116 +0,0 @@ -import { PUBLIC_RADROOTS_URL } from "$env/static/public"; -import { db } from "$lib/client"; -import type { IDialogAlert, IDialogConfirm } from "$lib/types"; -import type { IClientHttpResponseError } from "@radroots/client"; -import { catch_err, ls } from "@radroots/svelte-lib"; -import { parse_file_path, type FilePath, type ResultsList } from "@radroots/utils"; -import { get } from "svelte/store"; -import { fetch_radroots_upload } from "./fetch-radroots-upload"; - -export const model_media_upload_add_list = async (opts: { - photo_paths: string[]; -}): Promise<IDialogAlert | IDialogConfirm | ResultsList<string>> => { - try { - const $ls = get(ls); - if (!opts.photo_paths.length) { - return { - alert: `No photos provided` - } - } - const photo_path_uploads: { - file_path: FilePath; - res_base: string; - res_path: string; - }[] = []; - const photo_path_uploads_err: { - file_path: FilePath; - err_msg: string; - }[] = []; - const photo_path_uploads_error: IClientHttpResponseError[] = []; - - for (const photo_path of opts.photo_paths) { - const file_path = parse_file_path(photo_path); - console.log(JSON.stringify(file_path, null, 4), `file_path`) - if (!file_path) continue; - const url = `${PUBLIC_RADROOTS_URL}/public/upload/image`; //@todo - const radroots_upload = await fetch_radroots_upload({ - url, - file_path, - }); - console.log(JSON.stringify(radroots_upload, null, 4), `radroots_upload`) - if (`err` in radroots_upload) { - photo_path_uploads_err.push({ - file_path, - err_msg: radroots_upload.err, - }); - continue; - } else if (`error` in radroots_upload) { - photo_path_uploads_error.push(radroots_upload.error); - continue; - } - photo_path_uploads.push({ - file_path, - res_base: radroots_upload.res_base, - res_path: radroots_upload.res_path, - }); - } - - if (photo_path_uploads_error.length) { - return { - confirm: { - message: `${$ls(photo_path_uploads_error[0].message)}`, //@todo - ok_label: photo_path_uploads_error[0].label_ok - ? `${$ls(photo_path_uploads_error[0].label_ok)}` || - undefined - : undefined, - cancel_label: photo_path_uploads_error[0].label_cancel - ? `${$ls(photo_path_uploads_error[0].label_cancel)}` || - undefined - : undefined, - } - }; - - } - if (photo_path_uploads_err.length) { - return { - alert: `${$ls(`icu.there_was_a_failure_while_*`, { - value: `${$ls(`icu.uploading_*_photos`, { - value: - photo_path_uploads_err.length === - opts.photo_paths.length - ? `${$ls(`common.all`)}` - : `${photo_path_uploads_err.length}`, - })}`.toLowerCase(), - })}` - }; - } - - const results: string[] = []; - if (photo_path_uploads.length) { - for (const photo_path_upload of photo_path_uploads) { - const media_upload_add = await db.media_upload_add({ - file_path: photo_path_upload.file_path.file_path, - mime_type: photo_path_upload.file_path.mime_type, - res_base: photo_path_upload.res_base, - res_path: photo_path_upload.res_path, - }); - if ( - `err` in media_upload_add || - `err_s` in media_upload_add - ) - continue; //@todo - results.push(media_upload_add.id); - } - } - - return { - results - }; - } catch (e) { - await catch_err(e, `model_media_upload_add_list`); - return { - alert: `Failed to upload photos` //@todo - } - } -}; - diff --git a/src/lib/util/models-trade-product.ts b/src/lib/util/models-trade-product.ts @@ -1,123 +0,0 @@ -import { parse_trade_product_form_keys, trade_product_form_fields, trade_product_form_vals, type IModelsForm, type TradeProductFormFields } from "@radroots/models"; -import { catch_err, fmt_id, kv } from "@radroots/svelte-lib"; -import { err_msg, obj_en, type ErrorMessage, type ResultPass } from "@radroots/utils"; - -const trade_products_field_validate = (field_basis: IModelsForm, field_val: string): boolean => { - if ( - (!field_basis.optional && !field_basis.validation.test(field_val)) || - (field_basis.optional && - field_val && - !field_basis.validation.test(field_val)) - ) return false; - return true; -}; - -export const trade_product_fields_assign = async (opts?: { - kv_pref?: string; - field_defaults?: [keyof typeof trade_product_form_vals, string][]; - field_pass?: string[] | true; -}): Promise<TradeProductFormFields | ErrorMessage<string>> => { - try { - const fields = { - ...trade_product_form_vals - }; - for (const [field_k, _] of obj_en(trade_product_form_fields, parse_trade_product_form_keys)) { - if (!field_k) continue; - const field_val = await kv.get(`${opts?.kv_pref || fmt_id()}-${field_k}`); - if (field_val) fields[field_k] = field_val; - } - if (opts?.field_defaults && opts?.field_defaults?.length > 0) for (const [field_k, field_v] of opts?.field_defaults) if (!fields[field_k]) fields[field_k] = field_v; - return fields; - } catch (e) { - await catch_err(e, `trade_product_fields_assign`); - return err_msg(String(e)) - } -}; - -export const trade_product_fields_validate = async (opts: { - kv_pref?: string; - field_defaults?: [keyof typeof trade_product_form_vals, string][]; - fields_pass?: string[]; -}): Promise<TradeProductFormFields | ErrorMessage<string>> => { - try { - const fields = { - ...trade_product_form_vals - }; - for (const [field_k, _] of obj_en(trade_product_form_fields, parse_trade_product_form_keys)) { - if (!field_k) continue; - const field_val = await kv.get(`${opts?.kv_pref || fmt_id()}-${field_k}`); - if (field_val) fields[field_k] = field_val; - } - if (opts?.field_defaults && opts?.field_defaults?.length > 0) for (const [field_k, field_v] of opts?.field_defaults) if (!fields[field_k]) fields[field_k] = field_v; - for (const [field_k, field] of obj_en(fields, parse_trade_product_form_keys)) { - if (!field_k) continue; - if (!trade_products_field_validate(trade_product_form_fields[field_k], field)) { - if (opts.fields_pass?.includes(field_k)) continue; - return err_msg(field_k); - } - } - return fields; - } catch (e) { - await catch_err(e, `trade_product_fields_validate`); - return err_msg(String(e)) - } -}; - - -export const tradeproduct_validate_kv = async (opts?: { - kv_pref?: string; - fields_pass?: string[] | true; -}): Promise<TradeProductFormFields | ErrorMessage<string>> => { - try { - const vals = { - ...trade_product_form_vals - }; - for (const [field_k, field] of obj_en(trade_product_form_fields, parse_trade_product_form_keys)) { - if (!field_k) continue; - const field_id = `${opts?.kv_pref || fmt_id()}-${field_k}`; - const field_val = await kv.get(field_id); - if (field_val) vals[field_k] = field_val; - if (opts?.fields_pass === true) continue; - else if (!trade_products_field_validate(field, field_val)) { - if (opts?.fields_pass?.includes(field_k)) continue; - else return err_msg(field_k); - } - } - return vals; - } catch (e) { - await catch_err(e, `tradeproduct_validate_kv`); - return err_msg(String(e)) - - } -}; - -export const tradeproduct_init_kv = async (kv_pref: string): Promise<void> => { - try { - for (const [field_k, _] of obj_en(trade_product_form_fields, parse_trade_product_form_keys)) { - if (!field_k) continue; - const field_id = `${kv_pref}-${field_k}` - await kv.delete(field_id); - } - } catch (e) { - await catch_err(e, `tradeproduct_init_kv`); - } -}; - - -export const tradeproduct_validate_fields = async (opts: { - kv_pref: string; - fields: string[]; -}): Promise<ResultPass | ErrorMessage<string>> => { - try { - for (const field_k of opts.fields.map(parse_trade_product_form_keys)) { - if (!field_k) return err_msg(field_k); - const field_id = `${opts.kv_pref}-${field_k}`; - const field_val = await kv.get(field_id); - if (!trade_product_form_fields[field_k].validation.test(field_val)) return err_msg(field_k); - } - return { pass: true }; - } catch (e) { - await catch_err(e, `tradeproduct_validate_fields`); - return err_msg(String(e)) - } -}; diff --git a/src/lib/util/nostr-sync.ts b/src/lib/util/nostr-sync.ts @@ -1,147 +0,0 @@ -import { db, device, dialog } from "$lib/client"; -import { err, nostr_client, root_symbol } from "$lib/conf"; -import { NDKKind } from "@nostr-dev-kit/ndk"; -import type { NostrRelay } from "@radroots/models"; -import { - app_nostr_key, catch_err, ls, ndk, ndk_user, nostr_sync_prevent -} from "@radroots/svelte-lib"; -import { fmt_tags_basis_nip99, ndk_event, ndk_event_metadata, nevent_encode, num_str } from "@radroots/utils"; -import { get as get_store } from "svelte/store"; -import { throw_err } from "./client"; - -export const nostr_sync_metadata = async (): Promise<void> => { - try { - const $ndk = get_store(ndk); - const $ndk_user = get_store(ndk_user); - const $app_nostr_key = get_store(app_nostr_key); - const nostr_profile = await db.nostr_profile_get_one({ - public_key: $app_nostr_key - }); - if (`err` in nostr_profile) return throw_err(nostr_profile); - const ev_metadata = await ndk_event_metadata({ - $ndk, - $ndk_user, - metadata: nostr_profile.result - }); - if (ev_metadata) await ev_metadata.publish(); - } catch (e) { - await catch_err(e, `nostr_sync_metadata`); - } -}; - -export const nostr_sync_classified = async (nostr_relays: NostrRelay[]): Promise<void> => { - try { - const $ndk = get_store(ndk); - const $ndk_user = get_store(ndk_user); - const trade_products_all = await db.trade_product_get({ - list: [`all`], - }); - if (`err` in trade_products_all) return throw_err(trade_products_all); - for (const trade_product of trade_products_all.results) { - console.log(`sync trade_product.id `, trade_product.id) - const trade_product_location_res = await db.location_gcs_get({ - list: [`on_trade_product`, { id: trade_product.id }], - }); - if (`err` in trade_product_location_res) return throw_err(trade_product_location_res); - const trade_product_location = trade_product_location_res.results[0]; - - const media_upload_res = await db.media_upload_get({ - list: [`on_trade_product`, { id: trade_product.id }], - }); - if (`err` in media_upload_res) return throw_err(media_upload_res); - const ev = await ndk_event({ - $ndk, - $ndk_user, - basis: { - kind: NDKKind.Classified, - content: ``, - tags: fmt_tags_basis_nip99({ - d_tag: trade_product.id, - client: nostr_client, - listing: { - key: trade_product.key, - category: trade_product.category, - title: trade_product.title, - summary: trade_product.summary, - process: trade_product.process, - lot: trade_product.lot, - profile: trade_product.profile, - year: num_str(trade_product.year), - }, - quantity: { - amt: num_str(trade_product.qty_amt), - unit: trade_product.qty_unit, - label: trade_product.qty_label - }, - price: { - amt: num_str(trade_product.price_amt), - currency: trade_product.price_currency, - qty_amt: num_str(trade_product.price_qty_amt), - qty_unit: trade_product.price_qty_unit, - }, - location: { - city: trade_product_location.gc_name, - region: trade_product_location.gc_admin1_name, - region_code: trade_product_location.gc_admin1_id, - country: trade_product_location.gc_country_name, - country_code: trade_product_location.gc_country_id, - lat: trade_product_location.lat, - lng: trade_product_location.lng, - geohash: trade_product_location.geohash, - }, - images: media_upload_res.results.length ? media_upload_res.results.map(i => ({ url: `${i.res_base}/${i.res_path}.${i.mime_type}` })) : undefined - }), - }, - }); - if (ev) { - ev.content = `radroots:[nostr:${nevent_encode({ - id: ev.id, - author: ev.pubkey, - relays: nostr_relays.map(i => i.url), - kind: NDKKind.Classified, - })}]` - await ev.publish(); - } - } - } catch (e) { - await catch_err(e, `nostr_sync_classified`); - } -}; - -export const nostr_sync = async (): Promise<void> => { - try { - const $nostr_sync_prevent = get_store(nostr_sync_prevent); - const $ls = get_store(ls); - const $app_nostr_key = get_store(app_nostr_key); - if ($nostr_sync_prevent) { - const confirm = await dialog.confirm({ - message: `${$ls(`error.client.nostr_sync_disabled`)}`, - }); - if (confirm) { - nostr_sync_prevent.set(false); - await nostr_sync(); - } - return; - } - console.log(`nostr_sync start`) - const nostr_relays = await db.nostr_relay_get({ - list: [`on_profile`, { public_key: $app_nostr_key }], - }); - if (`err` in nostr_relays) return throw_err(nostr_relays); - if (!nostr_relays.results.length) return throw_err(err.nostr.no_relays); - // - // sync - await nostr_sync_metadata(); - await nostr_sync_classified(nostr_relays.results); - console.log(`nostr_sync done`) - } catch (e) { - await catch_err(e, `nostr_sync`); - } -}; - - -export const nostr_tags_basis = (): string[][] => { - const tags: string[][] = []; - for (const tag of [`app${device.metadata?.version ? `/${device.metadata.version}` : ``}`]) tags.push([root_symbol, tag]) - return tags; -}; diff --git a/src/routes/(app)/+layout.svelte b/src/routes/(app)/+layout.svelte @@ -1,108 +0,0 @@ -<script lang="ts"> - import { db, geoc, keystore, notification } from "$lib/client"; - import { cfg, ks } from "$lib/conf"; - import { fetch_relay_documents } from "$lib/util/fetch"; - import { nostr_sync } from "$lib/util/nostr-sync"; - import { - app_cfg_type, - app_geoc, - app_init, - app_nostr_key, - app_splash, - catch_err, - ndk, - ndk_user, - nostr_ndk_configured, - nostr_relays_poll_documents, - sleep, - } from "@radroots/svelte-lib"; - import { ndk_init } from "@radroots/utils"; - import { onMount } from "svelte"; - - onMount(async () => { - try { - const geoc_connected = await geoc.connect(); - app_geoc.set(!!geoc_connected); - } catch (e) { - console.log(`e (app) onMount`, e); - } finally { - app_splash.set(false); - app_init.set(true); - } - }); - - app_cfg_type.subscribe(async (_app_cfg_type) => { - //@todo - }); - - app_splash.subscribe(async (_app_splash) => { - //@todo - }); - - app_init.subscribe(async (_app_init) => { - try { - if (!app_init) return; - await sleep(cfg.delay.load_notify); - await notification.init(); - } catch (e) { - await catch_err(e, `app_init-subscribe`); - } - }); - - app_nostr_key.subscribe(async (_app_nostr_key) => { - try { - console.log(`_app_nostr_key `, _app_nostr_key); - if (!_app_nostr_key) return; - const ks_nostr_secretkey = await keystore.get( - ks.keys.nostr_secretkey($app_nostr_key), - ); - if (`err` in ks_nostr_secretkey) { - return; //@todo; - } - const nostr_relays = await db.nostr_relay_get({ - list: [`on_profile`, { public_key: $app_nostr_key }], - }); - if (`err` in nostr_relays) throw new Error(nostr_relays.err); - for (const { url } of nostr_relays.results) - $ndk.addExplicitRelay(url); - await $ndk.connect(); - const ndk_user = await ndk_init({ - $ndk, - secret_key: ks_nostr_secretkey.result, - }); - if (!ndk_user) { - nostr_ndk_configured.set(false); - return; - } - $ndk_user = ndk_user; - $ndk_user.ndk = $ndk; - nostr_ndk_configured.set(true); - } catch (e) { - await catch_err(e, `app_nostr_key-subscribe`); - } - }); - - nostr_ndk_configured.subscribe(async (_nostr_ndk_configured) => { - try { - if (!_nostr_ndk_configured) return; - console.log(`(nostr_ndk_configured) success`); - nostr_relays_poll_documents.set(true); - await nostr_sync(); - } catch (e) { - await catch_err(e, `nostr_ndk_configured-subscribe`); - } - }); - - nostr_relays_poll_documents.subscribe( - async (_nostr_relays_poll_documents) => { - try { - if (!_nostr_relays_poll_documents) return; - await fetch_relay_documents(); - } catch (e) { - await catch_err(e, `nostr_relays_poll_documents-subscribe`); - } - }, - ); -</script> - -<slot /> diff --git a/src/routes/(app)/+layout.ts b/src/routes/(app)/+layout.ts @@ -1,36 +0,0 @@ -import { keystore } from '$lib/client'; -import { ks } from '$lib/conf'; -import { app_cfg_type, app_nostr_key, app_splash, catch_err, parse_cfg_type, route } from '@radroots/svelte-lib'; -import type { LayoutLoad, LayoutLoadEvent } from './$types'; - -export const load: LayoutLoad = async (_: LayoutLoadEvent) => { - try { - const ks_nostr_publickey = await keystore.get( - ks.keys.nostr_publickey, - ); - if (`err` in ks_nostr_publickey) { - await route(`/cfg/init`); - return; - } - const ks_nostr_secretkey = await keystore.get( - ks.keys.nostr_secretkey(ks_nostr_publickey.result), - ); - if (`err` in ks_nostr_secretkey) { - await route(`/cfg/error`); - return; - } - const ks_pref_cfg_type = await keystore.get( - ks.pref.cfg_type - ); - //@todo handle err - if (`result` in ks_pref_cfg_type) { - app_cfg_type.set(parse_cfg_type(ks_pref_cfg_type.result)) - } - app_splash.set(false); - app_nostr_key.set(ks_nostr_publickey.result); - } catch (e) { - await catch_err(e, `(app)load`) - } finally { - return {}; - }; -}; diff --git a/src/routes/(app)/+page.svelte b/src/routes/(app)/+page.svelte @@ -1,67 +0,0 @@ -<script lang="ts"> - import { db, dialog } from "$lib/client"; - import { - app_nostr_key, - envelope_visible, - EnvelopeLower, - LayoutView, - ls, - nav_prev, - NavToolbar, - PageHeader, - route, - TabsFloat, - } from "@radroots/svelte-lib"; - import { onMount } from "svelte"; - - onMount(async () => { - try { - nav_prev.set([]); - - const nostr_profile = await db.nostr_profile_get({ - public_key: $app_nostr_key, - }); - if (`err` in nostr_profile) { - await dialog.alert(`@todo Nostr profile configuration failure`); - return; - } - } catch (e) { - } finally { - } - }); -</script> - -<LayoutView> - <NavToolbar /> - <PageHeader basis={{ label: `${$ls(`common.general`)}` }} /> - <div class={`flex flex-col w-full px-4 gap-4 justify-center items-center`}> - <div class={`flex flex-col w-full gap-5 justify-center items-center`}> - <button - class={`group flex flex-row h-[3.5rem] w-full justify-center items-center rounded-touch bg-layer-1-surface layer-1-active-surface layer-1-active-ring`} - on:click={async () => { - await route(`/farm/land`); - }} - > - <p - class={`font-sans font-[700] text-xl text-layer-0-glyph capitalize tracking-wider opacity-active`} - > - {`${$ls(`common.farm_land`)}`} - </p> - </button> - </div> - </div> -</LayoutView> -<TabsFloat /> -<EnvelopeLower - basis={{ - close: async () => { - envelope_visible.set(false); - }, - }} -> - <div class={`flex flex-col h-full w-full justify-center items-center px-2`}> - <p class={`font-apercu font-[400] text-layer-2-glyph break-all`}> - {`Your nostr key is ${$app_nostr_key}`} - </p> - </div> -</EnvelopeLower> diff --git a/src/routes/(app)/farm/land/+page.svelte b/src/routes/(app)/farm/land/+page.svelte @@ -1,136 +0,0 @@ -<script lang="ts"> - import { db } from "$lib/client"; - import MapPointDisplay from "$lib/components/map_point_display.svelte"; - import type { LocationGcs } from "@radroots/models"; - import { - ButtonGlyphSimple, - Fade, - LayoutView, - NavToolbar, - PageHeader, - TabsFloat, - app_notify, - catch_err, - fmt_geol_latitude, - fmt_geol_longitude, - ls, - route, - } from "@radroots/svelte-lib"; - import { onMount } from "svelte"; - - type LoadData = { - location_gcss: LocationGcs[]; - }; - let ld: LoadData | undefined = undefined; - - onMount(async () => { - try { - ld = await load_data(); - } catch (e) { - } finally { - } - }); - - const load_data = async (): Promise<LoadData | undefined> => { - try { - const location_gcss = await db.location_gcs_get({ - list: [`all`], - }); - if (`err` in location_gcss) - return void app_notify.set(`${$ls(`error.client.page.load`)}`); - return { - location_gcss: location_gcss.results, - } satisfies LoadData; - } catch (e) { - await catch_err(e, `load_data`); - } - }; -</script> - -<LayoutView> - <NavToolbar /> - <PageHeader basis={{ label: `${$ls(`common.farm_land`)}` }}> - <div slot="option" class={`flex flex-row justify-start items-center`}> - {#if ld && ld.location_gcss.length} - <Fade> - <ButtonGlyphSimple - basis={{ - label: `${$ls(`common.add`)}`, - callback: async () => { - await route(`/farm/land/add`); - }, - }} - /> - </Fade> - {/if} - </div> - </PageHeader> - <div - class={`flex flex-col w-full pt-2 px-4 gap-5 justify-start items-center`} - > - {#if ld && ld.location_gcss.length} - {#each ld.location_gcss.filter((i) => i.kind === `farm_land`) as li} - <button - class={`group flex flex-row h-[5rem] w-full px-8 gap-8 justify-start items-center bg-layer-1-surface layer-1-active-surface round-36 el-re`} - on:click={async () => { - await route(`/farm/land/view`, [[`id`, li.id]]); - }} - > - <div - class={`flex flex-col h-[4rem] w-[4rem] justify-start items-center bg-layer-2-surface round-24`} - > - <MapPointDisplay - basis={{ - point: { - lat: li.lat, - lng: li.lng, - }, - }} - /> - </div> - <div - class={`flex flex-col flex-grow h-[3.25rem] justify-between items-start`} - > - <div - class={`flex flex-row w-full justify-start items-center`} - > - <p - class={`font-sans font-[500] text-layer-0-glyph`} - > - {`${ - li.label || - `${fmt_geol_latitude( - li.lat, - `d`, - 4, - )}, ${fmt_geol_longitude(li.lng, `d`, 4)}` - }`} - </p> - </div> - <div - class={`flex flex-row w-full gap-2 justify-start items-center`} - > - {#if li.kind === `farm_land`} - <div - class={`flex flex-row h-5 px-2 justify-center items-center bg-layer-2-surface rounded-md`} - > - <p - class={`font-sans font-[700] text-[0.8rem] text-white`} - > - {`${$ls(`common.farm`)}`} - </p> - </div> - {/if} - <p - class={`font-sansd font-[500] text-layer-0-glyph`} - > - {`${li.gc_name}, ${li.gc_admin1_id}, ${li.gc_country_id}`} - </p> - </div> - </div> - </button> - {/each} - {/if} - </div> -</LayoutView> -<TabsFloat /> diff --git a/src/routes/(app)/farm/land/add/+page.svelte b/src/routes/(app)/farm/land/add/+page.svelte @@ -1,462 +0,0 @@ -<script lang="ts"> - import { dialog, geol } from "$lib/client"; - import MapPointSelect from "$lib/components/map_point_select.svelte"; - import { callback_alert } from "$lib/util/client"; - import { kv_init_page } from "$lib/util/kv"; - import { model_location_gcs_add_geocode } from "$lib/util/models-location-gcs"; - import type { IClientGeolocationPosition } from "@radroots/client"; - import type { GeocoderReverseResult } from "@radroots/geocoder"; - import { location_gcs_form_fields } from "@radroots/models"; - import { - ButtonGlyphSimple, - carousel_dec, - carousel_inc, - carousel_index, - carousel_init, - catch_err, - Fade, - fmt_geol_latitude, - fmt_geol_longitude, - fmt_id, - Glyph, - GlyphTitleSelectLabel, - InputElement, - LayoutView, - ls, - NavToolbar, - PageHeader, - route, - SelectMenu, - } from "@radroots/svelte-lib"; - import { regex } from "@radroots/utils"; - import { onMount } from "svelte"; - - let view_init: View = `c_1`; - type View = `c_1`; - let view: View = view_init; - - let geol_pos: IClientGeolocationPosition | undefined = undefined; - let geol_c: GeocoderReverseResult | undefined = undefined; - - let lgcs_label = ``; - let lgcs_area = ``; - let lgcs_area_unit = `ha`; - let lgcs_elevation = ``; - let lgcs_elevation_unit = `m`; - let lgcs_climate = ``; - - onMount(async () => { - try { - await init_page(); - } catch (e) { - } finally { - } - }); - - const init_page = async (): Promise<void> => { - try { - await kv_init_page(); - await carousel_init(view, 1); - const geolc = await geol.current(); - if (`err` in geolc) { - await dialog.alert( - `${$ls(`icu.failure_*`, { value: `${$ls(`icu.reading_*`, { value: `${$ls(`common.geocode`)}`.toLowerCase() })}` })}`, - ); - return; - } - geol_pos = geolc; - } catch (e) { - await catch_err(e, `init_page`); - } - }; - - const handle_inc = async (): Promise<void> => { - try { - await carousel_inc(view); - } catch (e) { - await catch_err(e, `handle_inc`); - } - }; - - const handle_dec = async (): Promise<void> => { - try { - await carousel_dec(view); - } catch (e) { - await catch_err(e, `handle_dec`); - } - }; - - const submit = async (): Promise<void> => { - try { - if (!geol_pos || !geol_c) - return await callback_alert( - `${$ls(`error.geolocation.result_missing`)}`, - async () => await init_page(), - ); - - const location_gcs = await model_location_gcs_add_geocode({ - geo_code: geol_c, - point: geol_pos, - kind: `farm_land`, - }); - if (`err` in location_gcs) { - return void (await dialog.alert( - `${$ls(`error.client.operation_failure`)}`, - )); - } - await route(`/farm/land`); - } catch (e) { - await catch_err(e, `submit`); - } - }; -</script> - -<LayoutView> - <NavToolbar /> - <PageHeader - basis={{ - label: [ - `${$ls(`icu.add_*`, { value: `${$ls(`common.farm_land`)}` })}`, - { - route: `/farm/land`, - }, - ], - }} - > - <div slot="option" class={`flex flex-row justify-start items-center`}> - {#if $carousel_index > 0} - <Fade> - <ButtonGlyphSimple - basis={{ - label: `${$ls(`common.back`)}`, - callback: async () => { - await handle_dec(); - }, - }} - /> - </Fade> - {/if} - </div> - </PageHeader> - <div - data-view={`c_1`} - class={`flex flex-col h-full w-full justify-start items-center`} - > - <div - data-carousel-container={`c_1`} - class={`carousel-container flex h-full w-full`} - > - <div - data-carousel-item={`c_1`} - class={`carousel-item flex flex-col w-full justify-start items-center`} - > - <div - class={`flex flex-col w-full px-4 gap-4 justify-start items-center`} - > - {#if geol_pos} - <div - class={`flex flex-row h-[24rem] w-full justify-center items-center bg-layer-2-surface round-44 overflow-hidden`} - > - <MapPointSelect - bind:map_point={geol_pos} - bind:map_geoc={geol_c} - /> - </div> - <div - class={`flex flex-col w-full pt-2 justify-center items-center`} - > - <ButtonGlyphSimple - basis={{ - label: `${$ls(`icu.add_*`, { value: `${$ls(`common.location`)}` })}`, - callback: async () => { - if (geol_c) await handle_inc(); - }, - }} - /> - </div> - {:else} - <div - class={`flex flex-row h-[24rem] w-full justify-center items-center bg-layer-2-surface round-44`} - > - <Glyph - basis={{ - classes: `text-layer-0-glyph`, - dim: `md`, - weight: `bold`, - key: `compass`, - }} - /> - </div> - {/if} - </div> - </div> - <div - data-carousel-item={`c_1`} - class={`carousel-item flex flex-col w-full justify-start items-center`} - > - <div - class={`flex flex-col w-full px-4 gap-4 justify-start items-center`} - > - <div - class={`flex flex-col h-[24rem] w-full px-2 gap-4 justify-start items-center`} - > - {#if geol_c && geol_pos} - <div - class={`flex flex-col w-full gap-1 justify-start items-start`} - > - <div - class={`flex flex-row w-full justify-start items-center`} - > - <p - class={`font-sansd text-trellis_ti text-layer-0-glyph-label uppercase`} - > - {`${$ls(`common.location`)}`} - </p> - </div> - <div - class={`flex flex-row h-12 w-full justify-start items-center border-y-line border-layer-0-surface-edge`} - > - <p - class={`font-sans font-[400] text-[1.1rem] text-layer-0-glyph`} - > - {`${geol_c.name}, ${geol_c.admin1_id}, ${geol_c.country_name}`} - </p> - </div> - </div> - <div - class={`flex flex-col w-full gap-1 justify-start items-start`} - > - <p - class={`font-sansd text-trellis_ti text-layer-0-glyph-label uppercase`} - > - {`${$ls(`common.coordinates`)}`} - </p> - <div - class={`flex flex-row h-12 w-full justify-start items-center border-y-line border-layer-0-surface-edge`} - > - <p - class={`font-sans font-[400] text-[1.1rem] text-layer-0-glyph`} - > - {`${fmt_geol_latitude( - geol_pos.lat, - `d`, - 4, - )}, ${fmt_geol_longitude( - geol_pos.lng, - `d`, - 4, - )}`} - </p> - </div> - </div> - <div - class={`flex flex-col w-full gap-1 justify-start items-start`} - > - <div - class={`flex flex-row w-full justify-start items-center`} - > - <p - class={`font-sansd text-trellis_ti text-layer-0-glyph-label uppercase`} - > - {`${$ls(`common.farm`)}/${`${$ls(`common.estate`)}`}`} - </p> - </div> - <div - class={`flex flex-row h-12 w-full justify-start items-center border-y-line border-layer-0-surface-edge`} - > - <InputElement - bind:value={lgcs_label} - basis={{ - id: fmt_id(`label`), - sync: true, - layer: 0, - classes: `h-10 placeholder:text-[1.1rem]`, - placeholder: `${$ls(`common.name_of_farm_or_estate`)}`, - field: { - charset: - location_gcs_form_fields - .label.charset, - validate: - location_gcs_form_fields - .label.validation, - validate_keypress: true, - }, - }} - /> - </div> - </div> - <div - class={`flex flex-col w-full gap-1 justify-start items-start`} - > - <div - class={`flex flex-row w-full gap-1 justify-start items-center`} - > - <p - class={`font-sansd text-trellis_ti text-layer-0-glyph-label uppercase`} - > - {`${$ls(`common.area`)}`} - </p> - <SelectMenu - bind:value={lgcs_area_unit} - basis={{ - layer: 0, - options: [ - { - entries: [ - { - label: `${$ls(`measurement.area.ac`)}`, - value: `ac`, - }, - { - label: `${$ls(`measurement.area.ha`)}`, - value: `ha`, - }, - { - label: `${$ls(`measurement.area.m2`)}`, - value: `m2`, - }, - ], - }, - ], - }} - > - <svelte:fragment slot="element"> - <GlyphTitleSelectLabel - basis={{ - label: `${$ls(`measurement.area.${lgcs_area_unit}_ab`)}`, - }} - /> - </svelte:fragment> - </SelectMenu> - </div> - <div - class={`relative flex flex-row h-12 w-full justify-between items-center border-y-line border-layer-0-surface-edge`} - > - <InputElement - bind:value={lgcs_area} - basis={{ - id: fmt_id(`area`), - sync: true, - layer: 0, - classes: `h-10 placeholder:text-[1.1rem]`, - placeholder: `${$ls(`common.land_area`)}`, - field: { - charset: regex.num, - validate: regex.num, - validate_keypress: true, - }, - }} - /> - </div> - </div> - <div - class={`flex flex-col w-full gap-1 justify-start items-start`} - > - <div - class={`flex flex-row w-full gap-1 justify-start items-center`} - > - <p - class={`font-sansd text-trellis_ti text-layer-0-glyph-label uppercase`} - > - {`${$ls(`common.elevation`)}`} - </p> - <SelectMenu - bind:value={lgcs_elevation_unit} - basis={{ - layer: 0, - options: [ - { - entries: [ - { - label: `${$ls(`measurement.length.m`)}`, - value: `m`, - }, - { - label: `${$ls(`measurement.length.ft`)}`, - value: `ft`, - }, - ], - }, - ], - }} - > - <svelte:fragment slot="element"> - <GlyphTitleSelectLabel - basis={{ - label: `${$ls(`measurement.length.${lgcs_elevation_unit}_ab`)}`, - }} - /> - </svelte:fragment> - </SelectMenu> - </div> - <div - class={`flex flex-row h-12 w-full justify-start items-center border-y-line border-layer-0-surface-edge`} - > - <InputElement - bind:value={lgcs_elevation} - basis={{ - id: fmt_id(`elevation`), - sync: true, - layer: 0, - classes: `h-10 placeholder:text-[1.1rem]`, - placeholder: `${$ls(`common.elevation`)}`, - field: { - charset: regex.num, - validate: regex.num, - validate_keypress: true, - }, - }} - /> - </div> - </div> - <div - class={`flex flex-col w-full gap-1 justify-start items-start`} - > - <div - class={`flex flex-row w-full justify-start items-center`} - > - <p - class={`font-sansd text-trellis_ti text-layer-0-glyph-label uppercase`} - > - {`${$ls(`common.climate`)}`} - </p> - </div> - <div - class={`flex flex-row h-12 w-full justify-start items-center border-y-line border-layer-0-surface-edge`} - > - <InputElement - bind:value={lgcs_climate} - basis={{ - id: fmt_id(`climate`), - sync: true, - layer: 0, - classes: `h-10 placeholder:text-[1.1rem]`, - placeholder: `${$ls(`common.climate`)}`, - field: { - charset: regex.description, - validate: regex.description_ch, - validate_keypress: true, - }, - }} - /> - </div> - </div> - <div - class={`flex flex-row w-full pt-2 justify-center items-center`} - > - <ButtonGlyphSimple - basis={{ - label: `${$ls(`icu.add_*`, { value: `${$ls(`common.location`)}` })}`, - callback: async () => { - await submit(); - }, - }} - /> - </div> - {/if} - </div> - </div> - </div> - </div> - </div> -</LayoutView> diff --git a/src/routes/(app)/farm/land/view/+page.svelte b/src/routes/(app)/farm/land/view/+page.svelte @@ -1,90 +0,0 @@ -<script lang="ts"> - import { db } from "$lib/client"; - import MapPointDisplay from "$lib/components/map_point_display.svelte"; - import type { LocationGcs } from "@radroots/models"; - import { - app_notify, - ButtonGlyphSimple, - catch_err, - LayoutView, - ls, - NavToolbar, - PageHeader, - qp_id, - } from "@radroots/svelte-lib"; - import { onMount } from "svelte"; - - type LoadData = { - location_gcs: LocationGcs; - }; - let ld: LoadData | undefined = undefined; - - onMount(async () => { - try { - if (!$qp_id) app_notify.set(`${$ls(`error.client.page.load`)}`); - ld = await load_data(); - } catch (e) { - } finally { - } - }); - - const load_data = async (): Promise<LoadData | undefined> => { - try { - const location_gcs = await db.location_gcs_get_one({ id: $qp_id }); - if (`err` in location_gcs) - return void app_notify.set(`${$ls(`error.client.page.load`)}`); - return { - location_gcs: location_gcs.result, - } satisfies LoadData; - } catch (e) { - await catch_err(e, `load_data`); - } - }; -</script> - -<LayoutView> - <NavToolbar /> - <PageHeader - basis={{ - label: [ - `${$ls(`common.farm_land`)}`, - { - route: `/farm/land`, - }, - ], - }} - > - <div slot="option" class={`flex flex-row justify-start items-center`}> - <ButtonGlyphSimple - basis={{ - label: `${$ls(`common.edit`)}`, - callback: async () => { - alert(`@todo!`); - }, - }} - /> - </div> - </PageHeader> - {#if ld?.location_gcs} - <div class={`flex flex-col w-full px-4 justify-center items-center`}> - <div - class={`flex flex-row h-[20rem] w-full justify-center items-center bg-layer-2-surface round-44 overflow-hidden`} - > - <MapPointDisplay - basis={{ - zoom: 12, - point: { - lat: ld.location_gcs.lat, - lng: ld.location_gcs.lng, - }, - }} - /> - </div> - <div class={`flex flex-col w-full justify-center items-center`}> - <div class={`flex flex-row w-full justify-start items-center`}> - <p class={`font-sans font-[400] text-layer-0-glyph`}>hi</p> - </div> - </div> - </div> - {/if} -</LayoutView> diff --git a/src/routes/(app)/models/location-gcs/+page.svelte b/src/routes/(app)/models/location-gcs/+page.svelte @@ -1,164 +0,0 @@ -<script lang="ts"> - import { db } from "$lib/client"; - import { type LocationGcs } from "@radroots/models"; - import { - app_notify, - catch_err, - LayoutTrellis, - LayoutView, - ls, - Nav, - Trellis, - } from "@radroots/svelte-lib"; - import { onMount } from "svelte"; - - type LoadData = { - location_gcss: LocationGcs[]; - }; - let ld: LoadData | undefined = undefined; - - onMount(async () => { - try { - ld = await load_data(); - } catch (e) { - } finally { - } - }); - - const load_data = async (): Promise<LoadData | undefined> => { - try { - const location_gcss = await db.location_gcs_get({ - list: [`all`], - }); - if (`err` in location_gcss) { - app_notify.set(`${$ls(`error.client.page.load`)}`); - return; - } - return { - location_gcss: location_gcss.results, - } satisfies LoadData; - } catch (e) { - await catch_err(e, `load_data`); - } - }; - - const handle_add_location_gcs = async (): Promise<void> => { - try { - console.log(`@todo`); - } catch (e) { - await catch_err(e, `handle_add_location_gcs`); - } - }; -</script> - -<LayoutView> - <LayoutTrellis> - {#if ld && ld.location_gcss?.length > 0} - {#each ld.location_gcss as li} - <Trellis - basis={{ - args: { - layer: 1, - title: { - value: `${$ls(`icu.your_*`, { value: `${$ls(`common.locations`)}` })}`, - }, - list: [ - { - hide_active: true, - touch: { - label: { - left: [ - { - value: `Location:`, - classes: `capitalize`, - }, - ], - right: [ - { - value: li.label || `test`, - }, - ], - }, - callback: async () => {}, - }, - }, - { - hide_active: true, - touch: { - label: { - left: [ - { - value: `Coordinates:`, - classes: `capitalize`, - }, - ], - right: [ - { - value: `${li.lat.toFixed(3)} ${li.lng.toFixed(3)}`, - }, - ], - }, - callback: async () => {}, - }, - }, - ], - }, - }} - /> - {/each} - {:else if ld && ld?.location_gcss?.length === 0} - <div - class={`flex flex-col w-full justify-center items-center px-4 gap-3`} - > - <p class={`font-sans font-[400] text-layer-2-glyph`}> - {`No items to display.`} - </p> - - <button - class={`flex flex-row justify-center items-center`} - on:click={async () => { - await handle_add_location_gcs(); - }} - > - <p - class={`font-sans font-[400] text-layer-2-glyph-hl text-sm`} - > - {`Click to add a new location`} - </p> - </button> - </div> - {/if} - </LayoutTrellis> -</LayoutView> -<Nav - basis={{ - prev: { - label: `${$ls(`common.home`)}`, - route: `/`, - }, - title: { - label: { - value: `${$ls(`common.locations`)}`, - }, - }, - option: - ld && ld?.location_gcss?.length > 0 - ? { - label: { - value: `${$ls(`common.add`)}`, - classes: `tap-color`, - }, - callback: async () => { - await handle_add_location_gcs(); - }, - } - : undefined, - }} -/> - -<style> - :global(.map-card) { - height: 100px; - width: 160px; - } -</style> diff --git a/src/routes/(app)/models/nostr-profile/+page.svelte b/src/routes/(app)/models/nostr-profile/+page.svelte @@ -1,340 +0,0 @@ -<script lang="ts"> - import { db, keystore, nostr } from "$lib/client"; - import { type NostrProfile } from "@radroots/models"; - import { - app_nostr_key, - app_notify, - ascii, - catch_err, - Glyph, - type ISelectOption, - LayoutTrellis, - LayoutView, - ls, - Nav, - nav_prev, - route, - SelectMenu, - TrellisTitle, - } from "@radroots/svelte-lib"; - import { onMount } from "svelte"; - - type OptionsListKey = - | `view-key` - | `set-key-active` - | `edit-profile-name` - | `add-profile-name` - | `delete-key`; - const page_param: { - options_list: ISelectOption<OptionsListKey>[]; - } = { - options_list: [ - { - value: `view-key`, - label: `${$ls(`icu.view_*`, { value: `${$ls(`common.details`)}`.toLowerCase() })}`, - }, - { - value: `edit-profile-name`, - label: `${$ls(`icu.edit_*`, { value: `${$ls(`common.profile_name`)}`.toLowerCase() })}`, - }, - { - value: `add-profile-name`, - label: `${$ls(`icu.add_*`, { value: `${$ls(`common.profile_name`)}`.toLowerCase() })}`, - }, - { - value: `set-key-active`, - label: `${$ls(`icu.set_as_*`, { value: `${$ls(`common.active`)}`.toLowerCase() })}`, - }, - { - value: `delete-key`, - label: `${$ls(`icu.delete_*`, { value: `${$ls(`common.key`)}`.toLowerCase() })}`, - }, - ], - }; - - type LoadData = { - nostr_profiles: NostrProfile[]; - }; - let ld: LoadData | undefined = undefined; - - onMount(async () => { - try { - ld = await load_data(); - } catch (e) { - } finally { - } - }); - - const load_data = async (): Promise<LoadData | undefined> => { - try { - const nostr_profiles = await db.nostr_profile_get({ - list: [`all`], - }); - console.log( - JSON.stringify(nostr_profiles, null, 4), - `nostr_profiles`, - ); - if (`err` in nostr_profiles) { - app_notify.set(`${$ls(`error.client.page.load`)}`); - return; - } - return { - nostr_profiles: nostr_profiles.results, - } satisfies LoadData; - } catch (e) { - await catch_err(e, `load_data`); - } - }; - - const handle_add_location_gcs = async (): Promise<void> => { - try { - console.log(`@todo`); - } catch (e) { - await catch_err(e, `handle_add_location_gcs`); - } - }; - - const handle_add_nostr_profile = async (): Promise<void> => { - try { - const ks_keys = await keystore.entries(); - console.log(JSON.stringify(ks_keys, null, 4), `ks_keys`); - } catch (e) { - await catch_err(e, `handle_add_nostr_profile`); - } - }; - - const handle_key_options_press = async (opts: { - option: string; - public_key: string; - }): Promise<void> => { - try { - switch (opts.option) { - case `view-key`: - case `add-profile-name`: - case `edit-profile-name`: { - $nav_prev.push({ - route: `/models/nostr-profile/view`, - label: `Keys`, - }); - } - - case `view-key`: - { - await route(`/models/nostr-profile/view`, [ - [`nostr_pk`, opts.public_key], - ]); - } - break; - case `add-profile-name`: - case `edit-profile-name`: - { - await route(`/models/nostr-profile/edit/field`, [ - [`nostr_pk`, opts.public_key], - [`rkey`, `name`], - ]); - } - break; - } - } catch (e) { - await catch_err(e, `handle_key_options_press`); - } - }; -</script> - -<LayoutView> - {#if ld} - <LayoutTrellis> - <div - class={`flex flex-col w-full gap-[2px] justify-start items-center`} - > - <TrellisTitle - layer={0} - basis={{ - value: `${$ls(`icu.*_list`, { value: `${$ls(`common.profile`)}`.toLowerCase() })}`, - }} - /> - {#if ld.nostr_profiles.length} - <div - class={`flex flex-col w-full gap-4 justify-center items-center`} - > - {#each ld.nostr_profiles as li (li.public_key)} - <div - class={`relative flex flex-col h-24 pt-5 px-3 bg-layer-1-surface rounded-touch overflow-hidden active:ring-4 active:ring-layer-2-surface/80 transition-all tap-rise-1 active:opacity-60`} - > - <button - class={`flex flex-col h-full w-full pt-[2px] pl-1 gap-1 items-start`} - on:click|preventDefault={async () => { - $nav_prev.push({ - route: `/models/nostr-profile`, - label: `${$ls(`common.profiles`)}`, - }); - await route( - `/models/nostr-profile/view`, - [[`nostr_pk`, li.public_key]], - ); - }} - > - <div - class={`flex flex-row w-full pl-1 gap-4 justify-start items-center`} - > - <p - class={`font-mono text-[1.1rem] text-layer-1-glyph-shade text-ellipsis overflow-hidden`} - > - {li.name - ? li.name - : `(${`${$ls(`icu.no_*`, { value: `${$ls(`common.profile`)}` })}`})`} - </p> - {#if li.public_key === $app_nostr_key} - <div class={`flex flex-row`}> - <div - class={`flex flex-row h-4 justify-center items-center px-[6px] bg-success/70 rounded-md -translate-y-[1px]`} - > - <p - class={`font-mono font-[900] text-[0.7rem] text-white text-ellipsis overflow-hidden`} - > - {`${$ls(`common.active`)}`} - </p> - </div> - </div> - {/if} - </div> - <div - class={`grid grid-cols-12 flex flex-row h-6 w-full pt-2 gap-2 items-center`} - > - <div - class={`col-span-2 flex flex-row h-full items-center `} - > - <div - class={`flex flex-row h-[1rem] px-[9px] justify-start items-center bg-zinc-800/90 rounded-[5px] translate-y-[1px]`} - > - <p - class={`font-mono font-[600] text-[0.9rem] text-layer-2-glyph lowercase line-clamp-1`} - > - {`${$ls(`common.key`)}`} - </p> - </div> - </div> - <div - class={`col-span-10 flex flex-row h-full pr-2 justify-end items-center overflow-x-hidden`} - > - <p - class={`font-mono text-[0.9rem] text-layer-1-glyph line-clamp-1`} - > - {`${`${nostr.lib.npub(li.public_key) || ""}`.slice( - 0, - 24, - )}...`} - </p> - </div> - </div> - </button> - <div - class={`z-10 absolute top-2 right-3 flex flex-row h-full justify-end pr-1`} - > - <SelectMenu - value={ascii.bullet} - basis={{ - layer: 0, - //mask: true, - callback: async ({ value }) => { - await handle_key_options_press({ - option: value, - public_key: li.public_key, - }); - }, - options: [ - { - entries: - page_param.options_list.filter( - (i) => - !( - !li.name && - i.value === - `edit-profile-name` - ) && - !( - li.name && - i.value === - `add-profile-name` - ) && - !( - li.public_key === - $app_nostr_key && - i.value === - `set-key-active` - ), - ), - }, - ], - }} - > - <svelte:fragment slot="element"> - <Glyph - basis={{ - key: `dots-three`, - dim: `md`, - classes: `text-layer-1-glyph`, - weight: `bold`, - }} - /> - </svelte:fragment> - </SelectMenu> - </div> - </div> - {/each} - </div> - {/if} - </div> - </LayoutTrellis> - {:else} - <div - class={`flex flex-col w-full justify-center items-center px-4 gap-3`} - > - <p class={`font-sans font-[400] text-layer-2-glyph`}> - {`No items to display.`} - </p> - - <button - class={`flex flex-row justify-center items-center`} - on:click={async () => { - await handle_add_location_gcs(); - }} - > - <p class={`font-sans font-[400] text-layer-2-glyph-hl text-sm`}> - {`Click to add a new location`} - </p> - </button> - </div> - {/if} -</LayoutView> -<Nav - basis={{ - prev: { - label: `${$ls(`common.back`)}`, - route: `/`, - }, - title: { - label: { - value: `${$ls(`common.profiles`)}`, - }, - }, - option: ld?.nostr_profiles?.length - ? { - label: { - value: `${$ls(`common.add`)}`, - classes: `tap-color`, - }, - callback: async () => { - await handle_add_nostr_profile(); - }, - } - : undefined, - }} -/> - -<style> - :global(.map-card) { - height: 100px; - width: 160px; - } -</style> diff --git a/src/routes/(app)/models/nostr-profile/edit/field/+page.svelte b/src/routes/(app)/models/nostr-profile/edit/field/+page.svelte @@ -1,243 +0,0 @@ -<script lang="ts"> - import { db, dialog } from "$lib/client"; - import { - nostr_profile_form_fields, - parse_nostr_profile_form_keys, - type NostrProfile, - type NostrProfileFields, - type NostrProfileFormFields, - } from "@radroots/models"; - import { - app_notify, - catch_err, - Fill, - fmt_id, - kv, - LayoutTrellis, - LayoutView, - ls, - Nav, - qp_nostr_pk, - qp_rkey, - route, - route_prev, - sleep, - Trellis, - } from "@radroots/svelte-lib"; - import { onDestroy, onMount } from "svelte"; - - let el_input: HTMLInputElement | null = null; - let el_input_loaded = false; - - type LoadData = { - nostr_profile: NostrProfile; - field_key: keyof NostrProfileFields; - }; - let ld: LoadData | undefined = undefined; - - onMount(async () => { - try { - if (!$qp_rkey || !$qp_nostr_pk) { - app_notify.set( - `${$ls(`icu.error_loading_*`, { value: `${$ls(`common.page`)}` })}`, - ); - return; - } - - ld = await load_page(); - } catch (e) { - } finally { - } - }); - - onDestroy(async () => { - try { - qp_rkey.set(``); - qp_nostr_pk.set(``); - } catch (e) { - } finally { - } - }); - - let val_field_valid = false; - - $: translated_field_key = ld?.field_key - ? `${$ls(`models.nostr_profile.fields.${ld.field_key}.label`)}`.toLowerCase() - : ``; - - $: if (el_input_loaded && el_input) { - el_input.focus(); - } - - const load_page = async (): Promise<LoadData | undefined> => { - try { - const nostr_profiles = await db.nostr_profile_get({ - public_key: $qp_nostr_pk, - }); - if (`err` in nostr_profiles) { - app_notify.set( - `${$ls(`icu.error_loading_*`, { value: `${$ls(`common.profile`)}` })}`, - ); - return; - } else if (!nostr_profiles.results.length) { - app_notify.set( - `${$ls(`icu.error_loading_*`, { value: `${$ls(`common.page`)}` })}`, - ); - return; - } - const field_key = parse_nostr_profile_form_keys($qp_rkey); - if (!field_key) { - app_notify.set(`${$ls(`error.client.page.load`)}`); - return; - } - return { - nostr_profile: nostr_profiles.results[0], - field_key, - } satisfies LoadData; - } catch (e) { - await catch_err(e, `load_page`); - } - }; - - const submit = async (): Promise<void> => { - try { - if (!ld?.field_key || !ld?.nostr_profile) return; - - const val = await kv.get(fmt_id($qp_rkey)); - if (!val) { - await route(`/models/nostr-profile`); - return; - } - const validated = - nostr_profile_form_fields[ld?.field_key].validation.test(val); - if (!validated) { - dialog.alert( - `${$ls(`icu.invalid_*_entry`, { value: translated_field_key })}`, - ); - return; - } - - const fields: Partial<NostrProfileFormFields> = {}; - fields[ld?.field_key] = val; - - const update_res = await db.nostr_profile_update({ - on: { - public_key: $qp_nostr_pk, - }, - fields, - }); - if (`err` in update_res) { - await dialog.alert(`${$ls(`error.client.unhandled`)}`); - return; - } - - // @todo sync to nostr - if (ld) - await route_prev(`/models/nostr-profile/view`, [ - [`id`, ld.nostr_profile.id], - ]); - else await route_prev(`/models/nostr-profile`); - } catch (e) { - await catch_err(e, `submit`); - } - }; -</script> - -<LayoutView> - <LayoutTrellis> - {#if ld} - <Trellis - basis={{ - args: { - hide_offset: true, - layer: 1, - title: { - value: ld?.nostr_profile[ld?.field_key] - ? `${$ls(`icu.edit_*`, { value: translated_field_key })}` - : `${$ls(`icu.add_a_*`, { value: translated_field_key.toLowerCase() })}`, - }, - list: [ - { - hide_active: true, - input: { - basis: { - id: fmt_id($qp_rkey), - sync: true, - /* - @todo - sync_init: ld?.nostr_profile[ - ld?.field_key - ] - ? ld.nostr_profile[ld.field_key] - : true, - */ - classes: `placeholder:font-[300]`, - placeholder: ld?.nostr_profile[ - ld?.field_key - ] - ? `${$ls(`icu.enter_new_*`, { value: translated_field_key.toLowerCase() })}` - : `${$ls(`icu.add_a_*`, { value: translated_field_key.toLowerCase() })}`, - field: { - charset: - nostr_profile_form_fields[ - ld?.field_key - ].charset, - validate: - nostr_profile_form_fields[ - ld?.field_key - ].validation, - validate_keypress: false, - }, - callback: async ({ pass }) => { - val_field_valid = pass; - }, - callback_keydown: async ({ key }) => { - if (key === `Enter`) await submit(); - }, - on_mount: async (el) => { - el_input = el; - await sleep(600); //@todo - el_input_loaded = true; - }, - }, - }, - }, - ], - }, - }} - > - <div - slot="offset" - class={`flex flex-row w-4 justify-start items-center`} - > - <Fill /> - </div> - </Trellis> - {/if} - </LayoutTrellis> -</LayoutView> -<Nav - basis={{ - prev: { - label: `${$ls(`common.back`)}`, - route: `/models/nostr-profile`, - }, - title: { - label: { - classes: `capitalize`, - value: translated_field_key, - }, - }, - option: { - label: { - classes: val_field_valid ? `` : `opacity-60`, - value: ld?.nostr_profile[ld?.field_key] - ? `${$ls(`common.update`)}` - : `${$ls(`common.add`)}`, - }, - callback: async () => { - if (val_field_valid) await submit(); - }, - }, - }} -/> diff --git a/src/routes/(app)/models/nostr-profile/view/+page.svelte b/src/routes/(app)/models/nostr-profile/view/+page.svelte @@ -1,500 +0,0 @@ -<script lang="ts"> - import { db, keystore, nostr } from "$lib/client"; - import { ks } from "$lib/conf"; - import type { NostrProfile, NostrRelay } from "@radroots/models"; - import { - app_nostr_key, - app_notify, - app_submit_route, - as_glyph_key, - catch_err, - clipboard_copy, - LayoutTrellis, - LayoutView, - ls, - Nav, - nav_prev, - nostr_relays_connected, - qp_nostr_pk, - route, - show_toast, - Trellis, - type ITrellisKindTouch, - } from "@radroots/svelte-lib"; - import { onMount } from "svelte"; - - type LoadData = { - nostr_profile: NostrProfile; - nostr_relays: NostrRelay[]; - nostr_relays_unconnected: NostrRelay[]; - secret_key: string; - }; - let ld: LoadData | undefined = undefined; - - let show_public_key_hex = false; - let show_secret_key_hex = false; - let vl_secret_key_unlock = false; - - onMount(async () => { - try { - if (!$qp_nostr_pk) - app_notify.set(`${$ls(`error.client.page.load`)}`); - ld = await load_data(); - } catch (e) { - } finally { - } - }); - - const load_data = async (): Promise<LoadData | undefined> => { - try { - const nostr_profiles = await db.nostr_profile_get({ - public_key: $qp_nostr_pk, - }); - if (`err` in nostr_profiles) { - app_notify.set(`${$ls(`error.client.page.load`)}`); - return; - } else if (nostr_profiles.results) { - app_notify.set(`${$ls(`error.client.page.load`)}`); - return; - } - const ks_secret_key = await keystore.get( - ks.keys.nostr_secretkey($qp_nostr_pk), - ); - if (`err` in ks_secret_key) { - app_notify.set(`Error loading profile`); - return; - } - const nostr_relays = await db.nostr_relay_get({ - list: [`on_profile`, { public_key: $qp_nostr_pk }], - sort: `oldest`, - }); - const nostr_relays_unconnected = await db.nostr_relay_get({ - list: [`off_profile`, { public_key: $qp_nostr_pk }], - sort: `oldest`, - }); - return { - nostr_profile: nostr_profiles.results[0], - secret_key: ks_secret_key.result, - nostr_relays: - `results` in nostr_relays ? nostr_relays.results : [], - nostr_relays_unconnected: - `results` in nostr_relays_unconnected - ? nostr_relays_unconnected.results - : [], - } satisfies LoadData; - } catch (e) { - await catch_err(e, `load_data`); - } - }; - - let tr_nostr_relays: ITrellisKindTouch[] = []; - $: tr_nostr_relays = - ld?.nostr_profile && ld?.nostr_relays.length - ? ld.nostr_relays.map((nostr_relay) => ({ - layer: 1, - touch: { - label: { - left: [ - { - value: nostr_relay.url, - }, - ], - }, - callback: async () => { - if (!ld) return; - nav_prev.set([ - ...$nav_prev, - { - route: `/models/nostr-profile/view`, - label: `Profile`, - params: [ - [`nostr_pk`, ld.nostr_profile.public_key], - ], - }, - ]); - await route(`/models/nostr-relay/view`, [ - [`id`, nostr_relay.id], - ]); - }, - }, - offset: { - mod: { - glyph_circle: { - classes_wrap: $nostr_relays_connected.includes( - nostr_relay.id, - ) - ? `bg-layer-1-glyph-hl/60 group-active:opacity-40` - : `bg-yellow-600/90 group-active:opacity-40`, - glyph: { - classes: $nostr_relays_connected.includes( - nostr_relay.id, - ) - ? `text-white/80 group-active:opacity-60 fade-in` - : `text-yellow-100/80 group-active:opacity-60 fade-in`, - key: $nostr_relays_connected.includes( - nostr_relay.id, - ) - ? `check` - : `exclamation-mark`, - weight: `bold`, - dim: `xs-`, - }, - }, - }, - callback: async (ev) => { - ev.stopPropagation(); - }, - }, - })) - : []; - - const toggle_hex_pk = (): void => { - show_public_key_hex = !show_public_key_hex; - }; - - const toggle_hex_sk = (): void => { - show_secret_key_hex = !show_secret_key_hex; - }; -</script> - -<LayoutView> - <LayoutTrellis> - {#if ld} - <Trellis - basis={{ - args: { - layer: 1, - title: { - value: `${$ls(`common.profile_name`)}`, - }, - list: [ - { - hide_active: vl_secret_key_unlock, - touch: { - label: { - left: [ - { - classes: ld.nostr_profile.name - ? `` - : `text-layer-1-glyph-shade`, - value: - ld.nostr_profile.name || - `${$ls(`icu.no_*_published`, { value: `${$ls(`common.profile`)}`.toLowerCase() })}`, - }, - ], - }, - end: { - glyph: { - key: as_glyph_key(`caret-right`), - }, - }, - callback: async () => { - if (!ld) return; - app_submit_route.set({ - route: `/models/nostr-profile/view`, - params: [ - [ - `nostr_pk`, - ld.nostr_profile.public_key, - ], - ], - }); - $nav_prev.push({ - route: `/models/nostr-profile/view`, - label: `Key`, - params: [ - [ - `nostr_pk`, - ld.nostr_profile.public_key, - ], - ], - }); - await route( - `/models/nostr-profile/edit/field`, - [ - [ - `nostr_pk`, - ld.nostr_profile.public_key, - ], - [`rkey`, `name`], - ], - ); - }, - }, - }, - ], - }, - }} - /> - <Trellis - basis={{ - args: { - layer: 1, - title: { - value: `${$ls(`common.public_key`)}`, - link: { - label: { - swap: { - on: { - classes: `pl-1 text-trellis_tiNote uppercase -translate-y-[1px] -translate-x-[2px] text-layer-0-glyph-shade`, - value: `${$ls(`common.npub`)}`, - }, - off: { - classes: `pl-1 text-trellis_tiNote uppercase -translate-y-[1px] -translate-x-[6px] text-layer-0-glyph-shade`, - value: `${$ls(`common.hex`)}`, - }, - toggle: show_public_key_hex, - }, - }, - callback: async () => { - toggle_hex_pk(); - }, - }, - }, - list: [ - { - offset: { - mod: `sm`, - }, - touch: { - label: { - left: [ - { - value: show_public_key_hex - ? ld.nostr_profile - .public_key - : nostr.lib.npub( - ld.nostr_profile - .public_key, - ), - }, - ], - }, - callback: async () => { - if (!ld) return; - await show_toast({ - args: { - position: `bottom-center`, - label: { - value: `${`${$ls( - `icu.*_as`, - { - value: `${$ls( - `icu.*_copied`, - { - value: `${$ls( - `common.public_key`, - )}`, - }, - )}`, - }, - )}`} ${show_public_key_hex ? `${$ls(`common.hex`)}`.toLowerCase() : `${$ls(`common.npub`)}`.toLowerCase()}.`, - }, - }, - callback: async () => { - if (!ld) return; - await clipboard_copy( - show_public_key_hex - ? ld?.nostr_profile - .public_key - : nostr.lib.npub( - ld.nostr_profile - .public_key, - ), - ); - }, - }); - }, - }, - }, - ], - }, - }} - /> - <Trellis - basis={{ - args: { - layer: 1, - title: { - value: `${$ls(`common.secret_key`)}`, - link: { - label: { - swap: { - on: { - classes: `pl-1 text-trellis_tiNote uppercase -translate-y-[1px] -translate-x-[2px] text-layer-0-glyph-shade`, - value: `${$ls(`common.nsec`)}`, - }, - off: { - classes: `pl-1 text-trellis_tiNote uppercase -translate-y-[1px] -translate-x-[6px] text-layer-0-glyph-shade`, - value: `${$ls(`common.hex`)}`, - }, - toggle: vl_secret_key_unlock - ? show_secret_key_hex - : false, - }, - }, - callback: async () => { - if (vl_secret_key_unlock) toggle_hex_sk(); - }, - }, - }, - list: [ - { - offset: { - mod: `sm`, - }, - touch: { - label: { - left: [ - { - value: vl_secret_key_unlock - ? show_secret_key_hex - ? ld.secret_key - : nostr.lib.nsec( - ld.secret_key, - ) - : `•`.repeat(40), - }, - ], - }, - callback: async () => { - if (!ld) return; - await show_toast({ - args: { - position: `bottom-center`, - label: { - value: `${`${$ls( - `icu.*_as`, - { - value: `${$ls( - `icu.*_copied`, - { - value: `${$ls( - `common.secret_key`, - )}`, - }, - )}`, - }, - )}`} ${show_secret_key_hex ? `${$ls(`common.hex`)}`.toLowerCase() : `${$ls(`common.nsec`)}`.toLowerCase()}.`, - }, - }, - callback: async () => { - if (!ld) return; - await clipboard_copy( - show_secret_key_hex - ? ld.secret_key - : nostr.lib.npub( - ld.secret_key, - ), - ); - }, - }); - }, - }, - }, - ], - }, - }} - /> - <Trellis - basis={{ - args: { - layer: 1, - title: { - value: `${$ls(`common.status`)}`, - }, - list: [ - { - hide_active: vl_secret_key_unlock, - touch: { - label: { - left: [ - { - classes: - $app_nostr_key === - ld.nostr_profile.public_key - ? `text-success font-[400]` - : undefined, - value: - $app_nostr_key === - ld.nostr_profile.public_key - ? `${$ls(`common.active`)}` - : `${$ls( - `common.public_key`, - )}`, - }, - ], - }, - }, - }, - ], - }, - }} - /> - <Trellis - basis={{ - args: { - layer: 1, - title: { - value: `${$ls(`icu.connected_*`, { value: `${$ls(`common.relays`)}` })}`, - }, - list: tr_nostr_relays.length - ? tr_nostr_relays - : [ - { - hide_active: vl_secret_key_unlock, - touch: { - label: { - left: [ - { - classes: ld.nostr_profile - .name - ? `` - : `text-layer-1-glyph-shade`, - value: `${$ls(`icu.no_*_published`, { value: `${$ls(`common.relays`)}`.toLowerCase() })}`, - }, - ], - }, - }, - }, - ], - }, - }} - /> - {/if} - </LayoutTrellis> -</LayoutView> -<Nav - basis={{ - prev: { - label: `${$ls(`common.back`)}`, - route: `/models/nostr-profile`, - }, - title: { - label: { - classes: `capitalize`, - value: `${$ls(`common.profile`)}`, - }, - }, - option: { - label: { - swap: { - on: { - value: `${$ls(`common.done`)}`, - }, - off: { - value: `${$ls(`common.unlock`)}`, - }, - toggle: vl_secret_key_unlock, - }, - }, - callback: async (el) => { - if (el) { - vl_secret_key_unlock = - !el.classList.contains(`swap-active`); - } - if (vl_secret_key_unlock) show_secret_key_hex = false; - }, - }, - }} -/> diff --git a/src/routes/(app)/models/nostr-relay/+page.svelte b/src/routes/(app)/models/nostr-relay/+page.svelte @@ -1,217 +0,0 @@ -<script lang="ts"> - import { db, dialog } from "$lib/client"; - import type { NostrRelay } from "@radroots/models"; - import { - app_nostr_key, - app_notify, - catch_err, - GlyphCircle, - LayoutTrellis, - LayoutView, - ls, - Nav, - nav_prev, - nostr_relays_connected, - route, - Trellis, - type ITrellisKindTouch, - } from "@radroots/svelte-lib"; - import { onMount } from "svelte"; - - type LoadData = { - nostr_relays: NostrRelay[]; - nostr_relays_other: NostrRelay[]; - }; - let ld: LoadData | undefined = undefined; - - let loading_edit_id = ``; - let show_edit = false; - - onMount(async () => { - try { - ld = await load_data(); - } catch (e) { - } finally { - } - }); - - const load_data = async (): Promise<LoadData | undefined> => { - try { - const nostr_relays = await db.nostr_relay_get({ - list: [`on_profile`, { public_key: $app_nostr_key }], - sort: `oldest`, - }); - if (`err` in nostr_relays) { - app_notify.set(`${$ls(`error.client.page.load`)}`); - return; - } - const nostr_relays_other = await db.nostr_relay_get({ - list: [`off_profile`, { public_key: $app_nostr_key }], - sort: `oldest`, - }); - if (`err` in nostr_relays_other) { - app_notify.set(`${$ls(`error.client.page.load`)}`); - return; - } - return { - nostr_relays: nostr_relays.results, - nostr_relays_other: nostr_relays_other.results, - } satisfies LoadData; - } catch (e) { - await catch_err(e, `load_data`); - } - }; - - let tr_list_relays: ITrellisKindTouch[] = []; - $: tr_list_relays = ld?.nostr_relays.length - ? ld.nostr_relays.map((nostr_relay) => ({ - layer: 1, - hide_active: show_edit, - touch: { - label: { - left: [ - { - value: nostr_relay.url, - }, - ], - }, - callback: async () => { - if (show_edit) return; - nav_prev.set([ - ...$nav_prev, - { - route: `/models/nostr-relay`, - label: `Relays`, - }, - ]); - await route(`/models/nostr-relay/view`, [ - [`id`, nostr_relay.id], - ]); - }, - }, - offset: show_edit - ? { - mod: { - glyph_circle: { - classes_wrap: `bg-red-400/80`, - glyph: { - classes: `text-white fade-in tap-scale`, - key: `minus`, - weight: `bold`, - dim: `xs-`, - loading: loading_edit_id === nostr_relay.id, - }, - }, - }, - callback: async () => { - await handle_disconnect_relay(nostr_relay); - }, - } - : { - mod: { - glyph_circle: { - classes_wrap: $nostr_relays_connected.includes( - nostr_relay.id, - ) - ? `bg-layer-1-glyph-hl/60 group-active:opacity-40` - : `bg-yellow-600/90 group-active:opacity-40`, - glyph: { - classes: $nostr_relays_connected.includes( - nostr_relay.id, - ) - ? `text-white/80 group-active:opacity-60 fade-in` - : `text-yellow-100/80 group-active:opacity-60 fade-in`, - key: $nostr_relays_connected.includes( - nostr_relay.id, - ) - ? `check` - : `exclamation-mark`, - weight: `bold`, - dim: `xs-`, - }, - }, - }, - callback: async (ev) => { - ev.stopPropagation(); - }, - }, - })) - : []; - - const handle_disconnect_relay = async ( - nostr_relay: NostrRelay, - ): Promise<void> => { - try { - loading_edit_id = nostr_relay.id; - const confirm = await dialog.confirm( - `This action will disconnect relay ${nostr_relay.url}`, - ); - if (confirm === false) return; - - alert(`@todo`); - } catch (e) { - await catch_err(e, `handle_disconnect_relay`); - } finally { - loading_edit_id = ``; - show_edit = false; - } - }; -</script> - -<LayoutView> - <LayoutTrellis> - <GlyphCircle - basis={{ - classes_wrap: `bg-red-400`, - glyph: { - classes: `text-white`, - key: `arrow-circle-up`, - dim: `md`, - }, - }} - /> - {#if tr_list_relays.length} - <Trellis - basis={{ - args: { - layer: 1, - title: { - value: `${$ls(`icu.nostr_*`, { value: `${$ls(`common.relays`)}` })}`, - }, - list: tr_list_relays, - }, - }} - /> - {/if} - </LayoutTrellis> -</LayoutView> -<Nav - basis={{ - prev: { - label: `${$ls(`common.back`)}`, - route: `/models/nostr-profile`, - }, - title: { - label: { - classes: `capitalize`, - value: `${$ls(`common.relays`)}`, - }, - }, - option: { - label: { - swap: { - on: { - value: `${$ls(`common.done`)}`, - }, - off: { - value: `${$ls(`common.edit`)}`, - }, - toggle: show_edit, - }, - }, - callback: async (el) => { - if (el) show_edit = !el.classList.contains(`swap-active`); - }, - }, - }} -/> diff --git a/src/routes/(app)/models/nostr-relay/view/+page.svelte b/src/routes/(app)/models/nostr-relay/view/+page.svelte @@ -1,363 +0,0 @@ -<script lang="ts"> - import { db, nostr } from "$lib/client"; - import type { NostrProfile, NostrRelay } from "@radroots/models"; - import { - app_blur, - app_notify, - catch_err, - EnvelopeButtons, - LayoutTrellis, - LayoutView, - ls, - Nav, - nostr_relays_connected, - qp_id, - Trellis, - } from "@radroots/svelte-lib"; - import { onMount } from "svelte"; - - type LoadData = { - nostr_relay: NostrRelay; - nostr_profiles: NostrProfile[]; - nostr_profiles_unconnected: NostrProfile[]; - }; - let ld: LoadData | undefined = undefined; - let show_edit = false; - - onMount(async () => { - try { - if (!$qp_id) app_notify.set(`${$ls(`error.client.page.load`)}`); - ld = await load_data(); - } catch (e) { - } finally { - } - }); - - const load_data = async (): Promise<LoadData | undefined> => { - try { - const nostr_relays = await db.nostr_relay_get({ - id: $qp_id, - }); - if (`err` in nostr_relays) { - app_notify.set(`${$ls(`error.client.page.load`)}`); - return; - } else if (!nostr_relays.results.length) { - app_notify.set(`${$ls(`error.client.page.load`)}`); - return; - } - const nostr_relay = nostr_relays.results[0]; - const nostr_profiles = await db.nostr_profile_get({ - list: [`on_relay`, { id: nostr_relay.id }], - }); - const nostr_profiles_unconnected = await db.nostr_profile_get({ - list: [`off_relay`, { id: nostr_relay.id }], - }); - return { - nostr_relay, - nostr_profiles: - `results` in nostr_profiles ? nostr_profiles.results : [], - nostr_profiles_unconnected: - `results` in nostr_profiles_unconnected - ? nostr_profiles_unconnected.results - : [], - } satisfies LoadData; - } catch (e) { - await catch_err(e, `load_data`); - } - }; - - $: app_blur.set(show_edit); - - const relay_connect = async (): Promise<void> => { - try { - } catch (e) { - await catch_err(e, `relay_connect`); - } - }; - - const relay_disconnect = async (): Promise<void> => { - try { - } catch (e) { - await catch_err(e, `relay_disconnect`); - } - }; -</script> - -<LayoutView> - <LayoutTrellis> - {#if ld} - <Trellis - basis={{ - args: { - layer: 1, - title: { - value: `${$ls(`common.url`)}`, - }, - list: [ - { - hide_active: true, - touch: { - label: { - left: [ - { - classes: `text-layer-1-glyph`, - value: ld.nostr_relay.url, - }, - ], - }, - }, - }, - ], - }, - }} - /> - <Trellis - basis={{ - args: { - layer: 1, - title: { - value: `${$ls(`common.status`)}`, - }, - list: [ - { - hide_active: true, - touch: { - label: { - left: $nostr_relays_connected.includes( - ld.nostr_relay.id, - ) - ? [ - { - glyph: { - classes: `text-success`, - key: `globe`, - }, - }, - { - classes: `text-success font-[500]`, - value: `${$ls(`common.connected`)}`, - }, - ] - : [ - { - glyph: { - classes: `text-yellow-700/80`, - key: `globe-x`, - }, - }, - { - classes: `text-yellow-700/80 font-[500] capitalize`, - value: `${$ls(`common.not_connected`)}`, - }, - ], - }, - }, - }, - ], - }, - }} - /> - <Trellis - basis={{ - args: { - layer: 1, - title: { - value: `${$ls(`models.nostr_relay.fields.pubkey.label`)}`, - }, - list: [ - { - hide_active: true, - touch: { - label: { - left: [ - { - classes: `text-layer-1-glyph`, - value: ld.nostr_relay.pubkey - ? nostr.lib.npub( - ld.nostr_relay.pubkey, - true, - ) - : `${$ls(`icu.no_*_published`, { value: `${$ls(`pubkey`)}`.toLowerCase() })}`, - }, - ], - }, - }, - }, - ], - }, - }} - /> - <Trellis - basis={{ - args: { - layer: 1, - title: { - value: `${$ls(`models.nostr_relay.fields.description.label`)}`, - }, - list: [ - { - hide_active: true, - touch: { - label: { - left: [ - { - classes: `text-layer-1-glyph`, - value: - ld.nostr_relay - .description || - `${$ls(`icu.no_*_published`, { value: `${$ls(`models.nostr_relay.fields.description.label`)}`.toLowerCase() })}`, - }, - ], - }, - }, - }, - ], - }, - }} - /> - <Trellis - basis={{ - args: { - layer: 1, - title: { - value: `${$ls(`models.nostr_relay.fields.software.label`)}`, - }, - list: [ - { - hide_active: true, - touch: { - label: { - left: [ - { - classes: `text-layer-1-glyph`, - value: - ld.nostr_relay.software || - `${$ls(`icu.no_*_published`, { value: `${$ls(`models.fields.nostr_relay.software.label`)}`.toLowerCase() })}`, - }, - ], - }, - }, - }, - ], - }, - }} - /> - <Trellis - basis={{ - args: { - layer: 1, - title: { - value: `${$ls(`models.nostr_relay.fields.version.label`)}`, - }, - list: [ - { - hide_active: true, - touch: { - label: { - left: [ - { - classes: `text-layer-1-glyph`, - value: - ld.nostr_relay.version || - `${$ls(`icu.no_*_published`, { value: `${$ls(`models.nostr_relay.fields.version.label`)}`.toLowerCase() })}`, - }, - ], - }, - }, - }, - ], - }, - }} - /> - <Trellis - basis={{ - args: { - layer: 1, - title: { - value: `${$ls(`models.nostr_relay.fields.supported_nips.label`)}`, - }, - list: [ - { - hide_active: true, - touch: { - label: { - left: [ - { - classes: `text-layer-1-glyph`, - value: - ld.nostr_relay - .supported_nips || - `${$ls(`icu.no_*_published`, { value: `${$ls(`models.nostr_relay.fields.supported_nips.label`)}`.toLowerCase() })}`, - }, - ], - }, - }, - }, - ], - }, - }} - /> - {/if} - </LayoutTrellis> -</LayoutView> -<Nav - basis={{ - prev: { - label: `${$ls(`common.back`)}`, - route: `/models/nostr-relay`, - }, - title: { - label: { - classes: `capitalize`, - value: `${$ls(`common.relay`)}`, - }, - }, - option: { - label: { - swap: { - on: { - value: `${$ls(`common.done`)}`, - }, - off: { - value: `${$ls(`common.edit`)}`, - }, - toggle: show_edit, - }, - }, - callback: async (el) => { - if (el) show_edit = !el.classList.contains(`swap-active`); - }, - }, - }} -/> -{#if ld} - <EnvelopeButtons - basis={{ - visible: show_edit, - buttons: [ - { - classes: `text-env_btnc text-layer-1-glyph-hl`, - label: `${$ls(`common.cancel`)}`, - callback: async () => { - show_edit = false; - }, - }, - { - classes: $nostr_relays_connected.includes(ld.nostr_relay.id) - ? `text-env_btnl text-red-400` - : `text-env_btnl text-success`, - label: $nostr_relays_connected.includes(ld.nostr_relay.id) - ? `${$ls(`icu.disconnect_*`, { value: `${$ls(`common.relay`)}` })}` - : `${$ls(`icu.connect_*`, { value: `${$ls(`common.relay`)}` })}`, - callback: async () => { - if (!ld) return; - if ($nostr_relays_connected.includes(ld.nostr_relay.id)) - await relay_disconnect(); - else await relay_connect(); - show_edit = false; - }, - }, - ], - }} - /> -{/if} diff --git a/src/routes/(app)/models/trade-product/+page.svelte b/src/routes/(app)/models/trade-product/+page.svelte @@ -1,109 +0,0 @@ -<script lang="ts"> - import { db } from "$lib/client"; - import TradeProductListCard from "$lib/components/trade_product_list_card.svelte"; - import type { TradeProductBundle } from "$lib/types"; - import { - app_notify, - catch_err, - LayoutTrellis, - LayoutView, - ls, - Nav, - route, - } from "@radroots/svelte-lib"; - import { onMount } from "svelte"; - - type LoadData = { - results: TradeProductBundle[]; - }; - let ld: LoadData | undefined = undefined; - - onMount(async () => { - try { - ld = await load_data(); - } catch (e) { - } finally { - } - }); - - const load_data = async (): Promise<LoadData | undefined> => { - try { - const trade_products = await db.trade_product_get({ - list: [`all`], - }); - if (`err` in trade_products) { - app_notify.set( - `${$ls(`icu.error_loading_*`, { value: `${$ls(`common.page`)}` })}`, - ); - return; - } - const results: TradeProductBundle[] = []; - for (const trade_product of trade_products.results) { - const location_gcs_res = await db.location_gcs_get({ - list: [`on_trade_product`, { id: trade_product.id }], - }); - if (`err` in location_gcs_res) { - //@todo - return; - } - const location_gcs = location_gcs_res.results[0]; - const media_uploads_res = await db.media_upload_get({ - list: [`on_trade_product`, { id: trade_product.id }], - }); - results.push({ - trade_product, - location_gcs, - media_uploads: - `results` in media_uploads_res - ? media_uploads_res.results - : undefined, - }); - } - return { - results, - } satisfies LoadData; - } catch (e) { - await catch_err(e, `load_data`); - } - }; -</script> - -{#if ld && ld.results.length > 0} - <LayoutView> - <LayoutTrellis basis={{ classes: `pt-8` }}> - {#each ld.results as li, li_i} - <TradeProductListCard - basis={{ - index: li_i, - result: li, - }} - /> - {/each} - </LayoutTrellis> - </LayoutView> -{/if} -<Nav - basis={{ - prev: { - label: `${$ls(`common.home`)}`, - route: `/`, - }, - title: { - label: { - value: `${$ls(`common.products`)}`, - }, - }, - option: - ld && ld?.results?.length > 0 - ? { - label: { - value: `${$ls(`common.add_new`)}`, - classes: `tap-color capitalize`, - }, - callback: async () => { - await route(`/models/trade-product/add`); - }, - } - : undefined, - }} -/> diff --git a/src/routes/(app)/models/trade-product/add/+page.svelte b/src/routes/(app)/models/trade-product/add/+page.svelte @@ -1,1795 +0,0 @@ -<script lang="ts"> - import { db, dialog, geol } from "$lib/client"; - import ImageUploadControl from "$lib/components/image_upload_control.svelte"; - import ImageUploadEditEnvelope from "$lib/components/image_upload_edit_envelope.svelte"; - import MapPointSelectEnvelope from "$lib/components/map_point_select_envelope.svelte"; - import TradeFieldDisplayEl from "$lib/components/trade_field_display_el.svelte"; - import TradeFieldDisplayKv from "$lib/components/trade_field_display_kv.svelte"; - import { el_focus } from "$lib/util/client"; - import { location_gcs_to_geoc } from "$lib/util/geocode"; - import { kv_init_page, kv_sync } from "$lib/util/kv"; - import { model_location_gcs_add_geocode } from "$lib/util/models-location-gcs"; - import { model_media_upload_add_list } from "$lib/util/models-media-upload"; - import { - trade_product_fields_validate, - tradeproduct_init_kv, - tradeproduct_validate_fields, - } from "$lib/util/models-trade-product"; - import type { GeocoderReverseResult } from "@radroots/geocoder"; - import { - trade_product_form_fields, - type LocationGcs, - } from "@radroots/models"; - import { - app_layout, - ascii, - carousel_dec, - carousel_inc, - carousel_index, - carousel_index_max, - carousel_num, - catch_err, - el_id, - EntryLine, - EntryMultiline, - EntrySelect, - EntryWrap, - fmt_id, - fmt_price, - Glyph, - InputElement, - int_step, - kv, - layout_view_cover, - LayoutTrellis, - LayoutTrellisLine, - LayoutView, - Loading, - locale, - ls, - Nav, - route, - SelectElement, - sleep, - view_effect, - } from "@radroots/svelte-lib"; - import { - err_system, - fiat_currencies, - fmt_currency_price, - fmt_trade_quantity_tup, - mass_tf_u, - mass_units, - num_str, - parse_currency_marker, - parse_currency_price, - parse_trade_key, - parse_trade_quantity_tup, - sum_currency_price, - trade, - trade_keys, - year_curr, - type CurrencyPrice, - type GeolocationCoordinatesPoint, - type TradeKey, - type TradeQuantity, - } from "@radroots/utils"; - import { onMount } from "svelte"; - import { writable } from "svelte/store"; - - type CarouselParam = { - label_prev?: string; - label_next: string; - }; - const page_param: { - carousel: Record<View, Map<number, CarouselParam>>; - default: { - tradepr_key: TradeKey; - }; - } = { - carousel: { - c_1: new Map<number, CarouselParam>([ - [ - 0, - { - label_next: `${$ls(`common.add`)}`, - }, - ], - [ - 1, - { - label_next: `${$ls(`common.add`)}`, - }, - ], - [ - 2, - { - label_next: ``, - }, - ], - ]), - success: new Map<number, CarouselParam>([ - [ - 0, - { - label_next: ``, - }, - ], - ]), - }, - default: { - tradepr_key: `coffee`, - }, - }; - - let view_init: View = `c_1`; - type View = `c_1` | `success`; - let view: View = view_init; - $: { - view_effect<View>(view); - } - - let load_page = false; - let load_submit = false; - - let tradepr_success_id = ``; - let tradepr_photo_paths: string[] = []; - let tradepr_photo_edit: { index: number; file_path: string } | undefined = - undefined; - let tradepr_key_sel_toggle = false; - let tradepr_key_sel = ``; - $: tradepr_key_parsed = parse_trade_key(tradepr_key_sel); - $: tradepr_key_quantities_list = tradepr_key_parsed - ? trade.key[tradepr_key_parsed].quantity - : trade.default.quantity; - - let tradepr_process_sel = ``; - let tradepr_process_sel_toggle = false; - $: tradepr_process_list = tradepr_key_parsed - ? trade.key[tradepr_key_parsed].process - : []; - - const tradepr_lgc_sel = writable<string>(``); - let tradepr_lgc_sel_geoc: GeocoderReverseResult | undefined = undefined; - let tradepr_lgc_map_vis = false; - let tradepr_lgc_map_point: GeolocationCoordinatesPoint | undefined = - undefined; - let tradepr_lgc_map_geoc: GeocoderReverseResult | undefined = undefined; - let tradepr_lgc_list: LocationGcs[] = []; - - let tradepr_price_amt_val = ``; - let tradepr_price_curr_sel = ``; - let tradepr_price_qty_unit_sel = ``; - - const tradepr_qty_tup_sel = writable<string>(``); - let tradepr_qty_tup_sel_toggle = false; - let tradepr_qty_unit_sel = ``; - const tradepr_qty_avail = writable<number>(1); - - let tradepr_parsed_quantity: TradeQuantity | undefined = undefined; - - onMount(async () => { - try { - await init_page(); - await setup_tests(); - } catch (e) { - } finally { - load_page = true; - } - }); - - const init_page = async (): Promise<void> => { - try { - await kv_init_page(); - await handle_view(view_init); - layout_view_cover.set(false); - carousel_index.set(0); - carousel_index_max.set(page_param.carousel[view].size - 1); - carousel_num.set(0); - tradepr_price_curr_sel = `eur`; - tradepr_price_qty_unit_sel = `kg`; - tradepr_qty_unit_sel = `kg`; - const location_gcs_get_ls = await db.location_gcs_get({ - list: [`all`], - }); - if (`results` in location_gcs_get_ls) - tradepr_lgc_list = location_gcs_get_ls.results; - } catch (e) { - await catch_err(e, `init_page`); - } - }; - - const setup_tests = async (): Promise<void> => { - try { - tradepr_key_sel = page_param.default.tradepr_key; - tradepr_process_sel = `washed`; - tradepr_price_amt_val = `4.30`; - tradepr_qty_tup_sel.set(`60-kg-bag`); - await kv_sync([ - [fmt_id(`title`), `Green Coffee Beans`], - [fmt_id(`lot`), `Ancestor slope`], - [ - fmt_id(`summary`), - `Good coffee, an amazing year, wonderful flavour, we love it!`, - ], - ]); - } catch (e) { - await catch_err(e, `setup_tests`); - } - }; - - let tradepr_cprice_amt: CurrencyPrice | undefined = undefined; - $: tradepr_cprice_amt = - tradepr_price_amt_val && tradepr_price_curr_sel - ? parse_currency_price( - $locale, - tradepr_price_curr_sel, - tradepr_price_amt_val, - ) - : undefined; - - let tradepr_cprice_total: CurrencyPrice | undefined = undefined; - $: tradepr_cprice_total = - tradepr_cprice_amt && tradepr_parsed_quantity - ? parse_currency_price( - $locale, - tradepr_cprice_amt.cur, - sum_currency_price(tradepr_cprice_amt) * - mass_tf_u( - tradepr_parsed_quantity?.mass_unit, - tradepr_price_qty_unit_sel, - tradepr_parsed_quantity?.mass, - ) * - $tradepr_qty_avail, - ) - : undefined; - - tradepr_qty_avail.subscribe(async (_tradepr_qty_avail) => { - if (_tradepr_qty_avail < 1) { - tradepr_qty_avail.set(Math.max(_tradepr_qty_avail, 1)); - return; - } - }); - - tradepr_qty_tup_sel.subscribe(async (_tradepr_qty_tup_sel) => { - tradepr_parsed_quantity = - parse_trade_quantity_tup(_tradepr_qty_tup_sel); - if (tradepr_parsed_quantity) { - await kv.set( - fmt_id(`qty_amt`), - num_str(tradepr_parsed_quantity.mass), - ); - await kv.set(fmt_id(`qty_unit`), tradepr_parsed_quantity.mass_unit); - } - }); - - tradepr_lgc_sel.subscribe(async (_tradepr_lgc_sel) => { - if (!_tradepr_lgc_sel) return; - else if (_tradepr_lgc_sel.startsWith(`*`) && tradepr_lgc_map_geoc) { - tradepr_lgc_sel_geoc = tradepr_lgc_map_geoc; - } else { - const location_gcs_sel = await db.location_gcs_get_one({ - id: _tradepr_lgc_sel, - }); - if (`result` in location_gcs_sel) - tradepr_lgc_sel_geoc = location_gcs_to_geoc( - location_gcs_sel.result, - ); - } - }); - - const handle_view = async (view_new: View): Promise<void> => { - try { - carousel_index.set(0); - view = view_new; - } catch (e) { - await catch_err(e, `handle_view`); - } - }; - - const handle_tradepr_key_toggle = async ( - vis_input: boolean, - ): Promise<void> => { - try { - const kv_curr = await kv.get(fmt_id(`key`)); - if (kv_curr) { - const confirm = await dialog.confirm({ - message: `${$ls(`icu.the_current_entry_*_will_be_deleted`, { value: kv_curr })}. ${$ls(`common.do_you_want_to_continue_q`)}`, - }); - if (!confirm) return; - } - tradepr_key_sel_toggle = vis_input; - if (vis_input) tradepr_key_sel = ``; - } catch (e) { - await catch_err(e, `handle_tradepr_key_toggle`); - } - }; - - const handle_tradepr_process_toggle = async ( - vis_input: boolean, - ): Promise<void> => { - try { - const kv_curr = await kv.get(fmt_id(`process`)); - if (kv_curr) { - const confirm = await dialog.confirm({ - message: `${$ls(`icu.the_current_entry_*_will_be_deleted`, { value: kv_curr })}. ${$ls(`common.do_you_want_to_continue_q`)}`, - }); - if (!confirm) return; - } - tradepr_process_sel_toggle = vis_input; - if (vis_input) tradepr_process_sel = ``; - } catch (e) { - await catch_err(e, `handle_tradepr_process_toggle`); - } - }; - - const handle_tradepr_qty_amt_toggle = async ( - vis_input: boolean, - ): Promise<void> => { - try { - if (vis_input) { - tradepr_qty_tup_sel.set(``); - } else { - const kv_curr = await kv.get(fmt_id(`qty_amt`)); - if (kv_curr) { - const kv_curr_unit = await kv.get(fmt_id(`qty_unit`)); - const confirm = await dialog.confirm({ - message: `${$ls(`icu.the_current_entry_*_will_be_deleted`, { value: `${kv_curr}${kv_curr_unit ? ` ${kv_curr_unit}` : `${$ls(`measurement.mass.unit.${kv_curr_unit}_ab`)}`}` })}. ${$ls(`common.do_you_want_to_continue_q`)}`, - }); - if (!confirm) return; - await kv.delete(fmt_id(`qty_amt`)); - } - } - tradepr_qty_tup_sel_toggle = vis_input; - } catch (e) { - await catch_err(e, `handle_tradepr_qty_amt_toggle`); - } - }; - - const handle_tradepr_lgc_sel_map = async (): Promise<void> => { - try { - const geolc = await geol.current(); - if (`err` in geolc) { - await dialog.alert( - `${$ls(`icu.failure_*`, { value: `${$ls(`icu.reading_*`, { value: `${$ls(`common.geocode`)}`.toLowerCase() })}` })}`, - ); - return; - } - tradepr_lgc_map_vis = true; - tradepr_lgc_map_point = { - lat: geolc.lat, - lng: geolc.lng, - }; - } catch (e) { - await catch_err(e, `handle_tradepr_lgc_sel_map`); - } - }; - - const handle_back = async (num?: number): Promise<void> => { - try { - switch (view) { - case `c_1`: - { - switch ($carousel_index) { - default: - { - await carousel_dec(view); - } - break; - } - } - break; - } - if (num) { - carousel_num.set(Math.max(num, 0)); - carousel_index.set(Math.max($carousel_index - (num - 1), 0)); - } - } catch (e) { - await catch_err(e, `handle_back`); - } - }; - - const handle_continue = async (): Promise<void> => { - try { - switch (view) { - case `c_1`: - { - switch ($carousel_index) { - case 0: - { - const validate_fields = - await tradeproduct_validate_fields({ - kv_pref: fmt_id(), - fields: [`title`, `key`, `process`], - }); - if (`err` in validate_fields) { - await dialog.alert( - `${$ls(`icu.enter_the_*`, { value: `${$ls(`models.trade_product.fields.${validate_fields.err}.label`)}`.toLowerCase() })}`, - ); - return; - } - await carousel_inc(view); - } - break; - case 1: - { - const validate_fields = - await tradeproduct_validate_fields({ - kv_pref: fmt_id(), - fields: [ - `price_amt`, - `key`, - `process`, - ], - }); - if (`err` in validate_fields) { - await dialog.alert( - `${$ls(`icu.enter_the_*`, { value: `${$ls(`models.trade_product.fields.${validate_fields.err}.label`)}`.toLowerCase() })}`, - ); - return; - } - await carousel_inc(view); - } - break; - } - } - break; - } - } catch (e) { - await catch_err(e, `handle_continue`); - } - }; - - const submit = async (): Promise<void> => { - try { - if (load_submit) return; - load_submit = true; - if (!tradepr_photo_paths.length) { - const confirm = await dialog.confirm({ - message: `${`${$ls(`icu.the_listing_will_be_created_without_a_*`, { value: `${$ls(`common.photo`)}`.toLowerCase() })}`}. ${$ls(`common.do_you_want_to_continue_q`)}`, - cancel_label: `${$ls(`icu.add_*`, { value: `${$ls(`common.photo`)}` })}`, - ok_label: `${$ls(`common.continue`)}`, - }); - if (!confirm) { - await el_focus( - fmt_id(`image-upload-control`), - async () => await handle_back(2), - ); - return; - } - } - let location_gcs_id = ``; - const location_gcs_get_i = await db.location_gcs_get_one({ - id: $tradepr_lgc_sel, - }); - if (`result` in location_gcs_get_i) { - location_gcs_id = location_gcs_get_i.result.id; - } else { - //@todo add check for existing geohash - if (tradepr_lgc_map_point && tradepr_lgc_map_geoc) { - const location_gcs_add_geocode = - await model_location_gcs_add_geocode({ - geo_code: tradepr_lgc_map_geoc, - point: tradepr_lgc_map_point, - kind: `trade_product`, //@todo - }); - if ( - `err` in location_gcs_add_geocode || - `err_s` in location_gcs_add_geocode - ) { - await dialog.alert( - `err` in location_gcs_add_geocode - ? err_system(location_gcs_add_geocode.err) - ? `${$ls(`error.client.database_read_failure`)}` - : `${$ls(`icu.the_*_is_incomplete`, { value: `${$ls(`models.location_gcs.fields.${location_gcs_add_geocode.err}.label`)}`.toLowerCase() })}` - : `${$ls(`${location_gcs_add_geocode.err_s[0]}`)}`, - ); - return; - } - location_gcs_id = location_gcs_add_geocode.id; - } - } - const location_gcs_get_c = await db.location_gcs_get_one({ - id: location_gcs_id, - }); - if (`err` in location_gcs_get_c) { - const confirm = await dialog.confirm({ - message: `${$ls(`icu.a_*_is_required`, { value: `${$ls(`common.location`)}`.toLowerCase() })}`, - ok_label: `${$ls(`icu.add_*`, { value: `${$ls(`common.location`)}` })}`, - }); - if (confirm) { - await el_focus( - fmt_id(`tradepr_location_wrap`), - async () => await handle_back(2), - ); - } - return; - } - //@todo edit here - // photos - /*const photo_path_uploads: { - file_path: FilePath; - res_base: string; - res_path: string; - }[] = []; - const photo_path_uploads_err: { - file_path: FilePath; - err_msg: string; - }[] = []; - const photo_path_uploads_error: IClientHttpResponseError[] = []; - if (tradepr_photo_paths.length) { - for (const photo_path of tradepr_photo_paths) { - const file_path = parse_file_path(photo_path); - if (!file_path) continue; - const url = `${PUBLIC_RADROOTS_URL}/public/upload/image`; //@todo - const put_upload = await fetch_radroots_upload({ - url, - file_path, - }); - if (`err` in put_upload) { - photo_path_uploads_err.push({ - file_path, - err_msg: put_upload.err, - }); - continue; - } else if (`error` in put_upload) { - photo_path_uploads_error.push(put_upload.error); - continue; - } - photo_path_uploads.push({ - file_path, - res_base: put_upload.res_base, - res_path: put_upload.res_path, - }); - } - } - if (photo_path_uploads_error.length) { - const confirm = await dialog.confirm({ - message: `${$ls(photo_path_uploads_error[0].message)}`, //@todo - ok_label: photo_path_uploads_error[0].label_ok - ? `${$ls(photo_path_uploads_error[0].label_ok)}` || - undefined - : undefined, - cancel_label: photo_path_uploads_error[0].label_cancel - ? `${$ls(photo_path_uploads_error[0].label_cancel)}` || - undefined - : undefined, - }); - if (confirm) { - console.log(`@todo add profile name`); - return; - } - } - if (photo_path_uploads_err.length) { - await dialog.alert( - `${$ls(`icu.there_was_a_failure_while_*`, { - value: `${$ls(`icu.uploading_*_photos`, { - value: - photo_path_uploads_err.length === - tradepr_photo_paths.length - ? `${$ls(`common.all`)}` - : `${photo_path_uploads_err.length}`, - })}`.toLowerCase(), - })}`, - ); - return; - }*/ - - const media_uploads = await model_media_upload_add_list({ - photo_paths: tradepr_photo_paths, - }); - - if (`alert` in media_uploads) { - await dialog.alert(media_uploads.alert); - return; - } else if (`confirm` in media_uploads) { - await dialog.confirm(media_uploads.confirm); - return; - } - - // trade product - const trade_product_fields = await trade_product_fields_validate({ - field_defaults: [ - [`price_qty_amt`, num_str(1)], - [`profile`, `natural`], - [`qty_avail`, num_str(Math.max($tradepr_qty_avail, 1))], - [`year`, year_curr()], - ], - }); - if (`err` in trade_product_fields) { - await dialog.alert( - `${$ls(`icu.the_*_is_incomplete`, { value: `${$ls(`models.trade_product.fields.${trade_product_fields.err}.label`)}`.toLowerCase() })}`, - ); - return; - } - const trade_product_add = - await db.trade_product_add(trade_product_fields); - if (`err` in trade_product_add || `err_s` in trade_product_add) { - await dialog.alert( - `err` in trade_product_add - ? `${$ls(`icu.the_*_is_incomplete`, { value: `${$ls(`models.trade_product.fields.${trade_product_add.err}.label`)}`.toLowerCase() })}` - : `${$ls(`${trade_product_add.err_s[0]}`)}`, - ); - return; - } - let trade_product_location_set_err: string = ``; - const trade_product_location_set = - await db.trade_product_location_set({ - trade_product: { - id: trade_product_add.id, - }, - location_gcs: { - id: location_gcs_get_c.result.id, - }, - }); - if (`err` in trade_product_location_set) { - trade_product_location_set_err = trade_product_location_set.err; - } - const trade_product_media_set_err: string[] = []; - for (const media_upload of media_uploads.results) { - const trade_product_media_set = - await db.trade_product_media_set({ - trade_product: { - id: trade_product_add.id, - }, - media_upload: { - id: media_upload, - }, - }); - if (`err` in trade_product_media_set) { - trade_product_media_set_err.push( - trade_product_media_set.err, - ); - } - } - - if (trade_product_location_set_err) { - //@todo - } - - if (trade_product_media_set_err.length) { - //@todo - } - - handle_view(`success`); - await sleep(2000); - await route(`/`); - } catch (e) { - await catch_err(e, `submit`); - } finally { - load_submit = false; - } - }; -</script> - -{#if load_page} - <LayoutView> - <div - data-view={`c_1`} - class={`flex flex-col h-full w-full justify-start items-center`} - > - <div - data-carousel-container={`c_1`} - class={`carousel-container flex h-full w-full`} - > - <div - data-carousel-item={`c_1`} - class={`carousel-item flex flex-col w-full justify-start items-center`} - > - <LayoutTrellis> - <ImageUploadControl - bind:photo_paths={tradepr_photo_paths} - bind:photo_edit={tradepr_photo_edit} - basis={{ - id: fmt_id(`image-upload-control`), - }} - /> - <LayoutTrellisLine - basis={{ - label: { - value: `${$ls(`icu.listing_*`, { value: `${$ls(`common.title`)}` })}`, - }, - }} - > - <EntryLine - basis={{ - wrap: { - id: fmt_id(`title_wrap`), - layer: 1, - }, - el: { - id: fmt_id(`title`), - layer: 1, - sync: true, - classes: `fade-in-long`, - placeholder: `${$ls(`icu.enter_*`, { value: `${$ls(`common.title`)}`.toLowerCase() })}`, - field: { - charset: - trade_product_form_fields.title - .charset, - validate: - trade_product_form_fields.title - .validation, - validate_keypress: true, - }, - }, - }} - /> - </LayoutTrellisLine> - <LayoutTrellisLine - basis={{ - label: { - value: `${$ls(`common.product`)}`, - }, - notify: tradepr_key_sel_toggle - ? { - label: { - value: `${$ls(`common.close`)}`, - }, - callback: async () => { - await handle_tradepr_key_toggle( - false, - ); - }, - } - : undefined, - }} - > - {#if !tradepr_key_sel_toggle} - <EntrySelect - bind:value={tradepr_key_sel} - basis={{ - wrap: { - id: fmt_id(`key_wrap`), - layer: 1, - }, - el: { - id: fmt_id(`key`), - sync: true, - layer: 1, - options: [ - { - entries: [ - { - value: ``, - label: `${$ls(`icu.choose_*`, { value: `${$ls(`common.product`)}`.toLowerCase() })}`, - disabled: true, - }, - ...trade_keys.map( - (i) => ({ - value: i, - label: `${$ls(`trade.product.key.${i}.name`)}`, - }), - ), - { - value: ``, - label: `${$ls(`common.other`)}`, - }, - ], - }, - ], - callback: async (opt) => { - el_id( - fmt_id(`key_wrap`), - )?.classList.remove( - `layer-1-ring-apply`, - ); - if (!opt.value) { - await handle_tradepr_key_toggle( - true, - ); - tradepr_key_sel = ``; - tradepr_process_sel = ``; - } else { - tradepr_process_sel = ``; - } - }, - }, - }} - /> - {:else} - <EntryLine - basis={{ - wrap: { - id: fmt_id(`key_wrap`), - layer: 1, - }, - el: { - id: fmt_id(`key`), - layer: 1, - sync: true, - classes: `fade-in-long`, - placeholder: `${$ls(`icu.enter_the_*`, { value: `${$ls(`icu.*_name`, { value: `${$ls(`common.product`)}` })}`.toLowerCase() })}`, - field: { - charset: - trade_product_form_fields - .key.charset, - validate: - trade_product_form_fields - .key.validation, - validate_keypress: true, - }, - }, - }} - /> - {/if} - </LayoutTrellisLine> - <LayoutTrellisLine - basis={{ - label: { - value: `${$ls(`common.process`)}`, - }, - notify: tradepr_process_sel_toggle - ? { - label: { - value: `${$ls(`common.close`)}`, - }, - callback: async () => { - await handle_tradepr_process_toggle( - false, - ); - }, - } - : undefined, - }} - > - {#if !tradepr_process_sel_toggle} - <EntrySelect - bind:value={tradepr_process_sel} - basis={{ - wrap: { - id: fmt_id(`process_wrap`), - layer: 1, - }, - el: { - id: fmt_id(`process`), - sync: true, - layer: 1, - options: [ - { - entries: - tradepr_process_list.length - ? [ - { - value: ``, - label: `${$ls(`icu.choose_*`, { value: `${$ls(`common.process`)}`.toLowerCase() })}`, - disabled: true, - }, - ...tradepr_process_list.map( - (i) => ({ - value: i, - label: `${$ls(`trade.product.key.${tradepr_key_parsed}.process.${i}`)}`, - }), - ), - { - value: ``, - label: `${$ls(`common.other`)}`, - }, - ] - : [ - { - value: ``, - label: `${$ls(`icu.choose_*`, { value: `${$ls(`common.process`)}`.toLowerCase() })}`, - disabled: true, - }, - - { - value: `*choose-product`, - label: `${$ls(`icu.choose_*`, { value: `${$ls(`common.product`)}`.toLowerCase() })}`, - }, - ], - }, - ], - callback: async ({ value }) => { - el_id( - fmt_id(`process_wrap`), - )?.classList.remove( - `layer-1-ring-apply`, - ); - if (!value) { - await handle_tradepr_process_toggle( - true, - ); - } - }, - }, - }} - /> - {:else} - <EntryLine - basis={{ - wrap: { - id: fmt_id(`process_wrap`), - layer: 1, - }, - el: { - id: fmt_id(`process`), - layer: 1, - sync: true, - classes: `fade-in-long`, - placeholder: `${$ls(`icu.enter_the_*`, { value: `${$ls(`common.process`)}`.toLowerCase() })}`, - field: { - charset: - trade_product_form_fields - .process.charset, - validate: - trade_product_form_fields - .process.validation, - validate_keypress: true, - }, - }, - }} - /> - {/if} - </LayoutTrellisLine> - <LayoutTrellisLine - basis={{ - label: { - value: `${$ls(`common.location`)}`, - }, - }} - > - <EntrySelect - bind:value={$tradepr_lgc_sel} - basis={{ - wrap: { - id: fmt_id(`tradepr_location_wrap`), - layer: 1, - }, - el: { - id: fmt_id(`tradepr_location`), - sync: true, - layer: 1, - options: [ - { - entries: tradepr_lgc_map_geoc - ? [ - { - value: ``, - label: `${$ls(`icu.choose_*`, { value: `${$ls(`common.location`)}`.toLowerCase() })}`, - disabled: true, - }, - { - value: `*map`, - label: `${$ls(`icu.choose_on_*`, { value: `${$ls(`common.map`)}`.toLowerCase() })}`, - }, - { - value: `*geoc`, - label: `${tradepr_lgc_map_geoc.name}, ${tradepr_lgc_map_geoc.admin1_name}, ${tradepr_lgc_map_geoc.country_id}`, - }, - ...tradepr_lgc_list.map( - (i) => ({ - value: i.id, - label: `${i.gc_name}, ${i.gc_admin1_name}, ${i.gc_country_id}`, - }), - ), - ] - : [ - { - value: ``, - label: `${$ls(`icu.choose_*`, { value: `${$ls(`common.location`)}`.toLowerCase() })}`, - disabled: true, - }, - { - value: `*map`, - label: `${$ls(`icu.choose_on_*`, { value: `${$ls(`common.map`)}`.toLowerCase() })}`, - }, - ...tradepr_lgc_list.map( - (i) => ({ - value: i.id, - label: `${i.gc_name}, ${i.gc_admin1_name}, ${i.gc_country_id}`, - }), - ), - ], - }, - ], - callback: async ({ value }) => { - el_id( - fmt_id(`process_wrap`), - )?.classList.remove( - `layer-1-ring-apply`, - ); - if (value === `*map`) { - await handle_tradepr_lgc_sel_map(); - } - }, - }, - }} - /> - </LayoutTrellisLine> - </LayoutTrellis> - </div> - <div - data-carousel-item={`c_1`} - class={`carousel-item flex flex-col w-full justify-start items-center`} - > - <LayoutTrellis> - <div - class={`flex flex-col w-full justify-center items-center`} - > - <div - class={`flex flex-col h-[11rem] w-${$app_layout} justify-start items-start bg-layer-1-surface rounded-[2rem] overflow-hidden`} - > - <div - class={`flex flex-row h-[2.5rem] w-full justify-center items-center`} - > - <p - class={`font-sans font-[400] text-[1.05rem] text-layer-1-glyph_d`} - > - {`${$ls(`common.listing`)}`} - </p> - </div> - <div - class={`flex flex-col h-[8.5rem] w-full px-4 justify-start items-start`} - > - <div - class={`flex flex-col h-full w-full gap-[5px] justify-center items-center border-t-line border-layer-0-glyph_d`} - > - <TradeFieldDisplayKv - basis={{ - visible: $carousel_index === 1, - label: `${$ls(`common.title`)}`, - display: { - kv: `title`, - undef: `${$ls(`icu.no_*`, { value: `${$ls(`common.title`)}`.toLowerCase() })}`, - }, - handle_back: async () => { - await handle_back(1); - }, - }} - /> - <TradeFieldDisplayKv - basis={{ - visible: $carousel_index === 1, - label: `${$ls(`common.product`)}`, - display: { - kv: `key`, - undef: `${$ls(`icu.no_*`, { value: `${$ls(`common.product`)}`.toLowerCase() })}`, - }, - handle_back: async () => { - await handle_back(1); - }, - }} - /> - <TradeFieldDisplayKv - basis={{ - visible: $carousel_index === 1, - label: `${$ls(`common.process`)}`, - display: { - kv: `process`, - undef: `${$ls(`icu.no_*`, { value: `${$ls(`common.process`)}`.toLowerCase() })}`, - }, - handle_back: async () => { - await handle_back(1); - }, - }} - /> - <TradeFieldDisplayKv - basis={{ - visible: $carousel_index === 1, - kv_wrap: `tradepr_location_wrap`, - label: `${$ls(`common.location`)}`, - display: { - value: tradepr_lgc_sel_geoc - ? `${tradepr_lgc_sel_geoc.name}, ${tradepr_lgc_sel_geoc.admin1_name}, ${tradepr_lgc_sel_geoc.country_id}` - : ``, - undef: `${$ls(`icu.no_*`, { value: `${$ls(`common.location`)}`.toLowerCase() })}`, - }, - handle_back: async () => { - await handle_back(1); - }, - }} - /> - </div> - </div> - </div> - </div> - <LayoutTrellisLine - basis={{ - label: { - value: `${$ls(`common.lot`)}`, - }, - }} - > - <EntryLine - basis={{ - wrap: { - id: fmt_id(`lot_wrap`), - layer: 1, - }, - el: { - id: fmt_id(`lot`), - layer: 1, - sync: true, - placeholder: `${$ls(`icu.enter_the_*`, { value: `${$ls(`icu.*_name`, { value: `${$ls(`common.lot`)}` })}`.toLowerCase() })}`, - field: { - charset: - trade_product_form_fields.lot - .charset, - validate: - trade_product_form_fields.lot - .validation, - validate_keypress: true, - }, - }, - }} - /> - </LayoutTrellisLine> - <LayoutTrellisLine - basis={{ - label: { - value: `${$ls(`icu.*_price`, { value: `${$ls(`common.product`)}` })} (${tradepr_price_curr_sel}/${`${$ls(`measurement.mass.unit.${tradepr_price_qty_unit_sel}_ab`)}`})`, - }, - }} - > - <EntryWrap - basis={{ - id: fmt_id(`price_wrap`), - layer: 1, - }} - > - <div - class={`flex flex-row justify-start pr-1 items-center`} - > - <SelectElement - bind:value={tradepr_price_curr_sel} - basis={{ - id: fmt_id(`price_currency`), - layer: 1, - sync: true, - classes: `w-fit font-sans font-[400] text-[1.1rem] ${tradepr_price_amt_val ? `text-layer-1-glyph_d` : `text-layer-1-glyph_pl`} el-re`, - options: [ - { - entries: - fiat_currencies.map( - (i) => ({ - value: `${i}`, - label: parse_currency_marker( - $locale, - i, - ), - }), - ), - }, - ], - }} - /> - </div> - <InputElement - bind:value={tradepr_price_amt_val} - basis={{ - id: fmt_id(`price_amt`), - layer: 1, - sync: true, - placeholder: `${$ls(`icu.enter_the_*`, { value: `${$ls(`common.price`)}`.toLowerCase() })}`, - field: { - charset: - trade_product_form_fields - .price_amt.charset, - validate: - trade_product_form_fields - .price_amt.validation, - validate_keypress: true, - }, - callback: async ({ value, pass }) => { - const lastchar = - value[value.length - 1]; - const period_count = - value.split(".").length - 1; - if ( - (pass && - lastchar !== `.` && - period_count < 2) || - value.length < 1 - ) { - el_id( - fmt_id(`price_wrap`), - )?.classList.remove( - `layer-1-ring-apply`, - ); - } else { - el_id( - fmt_id(`price_wrap`), - )?.classList.add( - `layer-1-ring-apply`, - ); - } - }, - callback_blur: async ({ el }) => { - if (!el.value) return; - el.value = fmt_price( - tradepr_price_curr_sel, - el.value, - ).slice(1); //@todo fmt handles 'en' only - }, - }} - /> - <div - class={`flex flex-row gap-2 justify-end items-center`} - > - <p - class={`font-sans font-[400] text-[1.05rem] text-layer-1-glyph_d lowercase`} - > - {num_str(1)} - </p> - <SelectElement - bind:value={tradepr_price_qty_unit_sel} - basis={{ - id: fmt_id(`price_qty_unit`), - sync: true, - layer: 1, - classes: `w-fit font-sans font-[400] text-[1.05rem]`, - show_arrows: `r`, - options: [ - { - entries: mass_units.map( - (i) => ({ - value: i, - label: `${$ls(`measurement.mass.unit.${i}_ab`)}`.toLowerCase(), - }), - ), - }, - ], - }} - /> - </div> - </EntryWrap> - </LayoutTrellisLine> - <LayoutTrellisLine - basis={{ - label: { - value: `${$ls(`icu.*_quantity`, { value: `${$ls(`common.order`)}` })}`, - }, - notify: tradepr_qty_tup_sel_toggle - ? { - label: { - value: `${$ls(`common.close`)}`, - }, - callback: async () => { - await handle_tradepr_qty_amt_toggle( - false, - ); - }, - } - : undefined, - }} - > - {#if !tradepr_qty_tup_sel_toggle} - <EntrySelect - bind:value={$tradepr_qty_tup_sel} - basis={{ - wrap: { - id: fmt_id(`qty_wrap`), - layer: 1, - }, - el: { - layer: 1, - options: [ - { - entries: [ - { - value: ``, - label: `${$ls(`icu.choose_*`, { value: `${$ls(`common.quantity`)}`.toLowerCase() })}`, - disabled: true, - }, - ...tradepr_key_quantities_list.map( - (i) => ({ - value: fmt_trade_quantity_tup( - i, - ), - label: `${i.mass} ${$ls(`measurement.mass.unit.${i.mass_unit}_ab`)} ${i.label}`, - }), - ), - { - value: ``, - label: `${$ls(`common.other`)}`, - }, - ], - }, - ], - callback: async ({ value }) => { - el_id( - fmt_id(`qty_wrap`), - )?.classList.remove( - `layer-1-ring-apply`, - ); - if (value === ``) { - await handle_tradepr_qty_amt_toggle( - true, - ); - } - }, - }, - }} - /> - {:else} - <EntryWrap - basis={{ - id: fmt_id(`qty_wrap`), - layer: 1, - }} - > - <InputElement - basis={{ - id: fmt_id(`qty_amt`), - sync: true, - layer: 1, - placeholder: `${$ls(`icu.enter_*_per_order`, { value: `${$ls(`measurement.mass.unit.${tradepr_qty_unit_sel}_ab`)}`.toLowerCase() })}`, - field: { - charset: - trade_product_form_fields - .qty_amt.charset, - validate: - trade_product_form_fields - .qty_amt.validation, - validate_keypress: true, - }, - }} - /> - <div - class={`flex flex-row gap-2 justify-end items-center`} - > - <SelectElement - bind:value={tradepr_qty_unit_sel} - basis={{ - id: fmt_id(`qty_unit`), - sync: true, - layer: 1, - classes: `w-fit font-sans font-[400] text-[1.05rem] text-layer-1-glyph_d`, - show_arrows: `r`, - options: [ - { - entries: mass_units.map( - (i) => ({ - value: i, - label: `${$ls(`measurement.mass.unit.${i}_ab`)}`, - }), - ), - }, - ], - }} - /> - </div> - </EntryWrap> - {/if} - </LayoutTrellisLine> - <LayoutTrellisLine - basis={{ - label: { - value: `${$ls(`common.description`)}`, - }, - }} - > - <EntryMultiline - basis={{ - wrap: { - id: fmt_id(`summary_wrap`), - }, - el: { - classes: `h-[7rem]`, - id: fmt_id(`summary`), - sync: true, - placeholder: `${$ls(`icu.enter_the_*`, { value: `${$ls(`icu.*_description`, { value: `${$ls(`common.listing`)}` })}`.toLowerCase() })}`, - field: { - charset: - trade_product_form_fields - .summary.charset, - validate: - trade_product_form_fields - .summary.validation, - validate_keypress: true, - }, - }, - }} - /> - </LayoutTrellisLine> - </LayoutTrellis> - </div> - <div - data-carousel-item={`c_1`} - class={`carousel-item flex flex-col w-full justify-start items-center`} - > - <LayoutTrellis> - <div - class={`flex flex-col w-full justify-center items-center`} - > - <div - class={`flex flex-col h-auto w-${$app_layout} justify-start items-start bg-layer-1-surface rounded-[2rem] overflow-hidden`} - > - <div - class={`flex flex-row h-[3rem] w-full justify-center items-center`} - > - <p - class={`font-sans font-[400] text-[1.05rem] text-layer-1-glyph_d`} - > - {`${$ls(`common.listing`)}`} - </p> - </div> - <div - class={`flex flex-col w-full px-4 justify-start items-center`} - > - <div - class={`flex flex-col h-auto w-full py-6 gap-[5px] justify-center items-start border-t-line border-layer-0-glyph_d`} - > - <TradeFieldDisplayKv - basis={{ - visible: $carousel_index === 2, - label: `${$ls(`common.title`)}`, - display: { - kv: `title`, - undef: `${$ls(`icu.no_*`, { value: `${$ls(`common.title`)}`.toLowerCase() })}`, - }, - handle_back: async () => { - await handle_back(2); - }, - }} - /> - <TradeFieldDisplayKv - basis={{ - visible: $carousel_index === 2, - label: `${$ls(`common.product`)}`, - display: { - kv: `key`, - undef: `${$ls(`icu.no_*`, { value: `${$ls(`common.product`)}`.toLowerCase() })}`, - }, - handle_back: async () => { - await handle_back(2); - }, - }} - /> - <TradeFieldDisplayKv - basis={{ - visible: $carousel_index === 2, - label: `${$ls(`common.process`)}`, - display: { - kv: `process`, - undef: `${$ls(`icu.no_*`, { value: `${$ls(`common.process`)}`.toLowerCase() })}`, - }, - handle_back: async () => { - await handle_back(2); - }, - }} - /> - <TradeFieldDisplayKv - basis={{ - visible: $carousel_index === 2, - kv_wrap: `tradepr_location_wrap`, - label: `${$ls(`common.location`)}`, - display: { - value: tradepr_lgc_sel_geoc - ? `${tradepr_lgc_sel_geoc.name}, ${tradepr_lgc_sel_geoc.admin1_name}, ${tradepr_lgc_sel_geoc.country_id}` - : ``, - undef: `${$ls(`icu.no_*`, { value: `${$ls(`common.location`)}`.toLowerCase() })}`, - }, - handle_back: async () => { - await handle_back(2); - }, - }} - /> - <TradeFieldDisplayKv - basis={{ - visible: $carousel_index === 2, - label: `${$ls(`common.lot`)}`, - display: { - kv: `lot`, - undef: `${$ls(`icu.no_*`, { value: `${$ls(`common.lot`)}`.toLowerCase() })}`, - }, - handle_back: async () => { - await handle_back(1); - }, - }} - /> - <TradeFieldDisplayKv - basis={{ - visible: $carousel_index === 2, - label: `${$ls(`common.description`)}`, - display: { - kv: `summary`, - undef: `${$ls(`icu.no_*`, { value: `${$ls(`common.description`)}`.toLowerCase() })}`, - }, - handle_back: async () => { - await handle_back(1); - }, - }} - /> - </div> - <div - class={`flex flex-col h-auto w-full py-6 gap-[5px] justify-center items-start border-t-line border-layer-0-glyph_d`} - > - <TradeFieldDisplayKv - basis={{ - visible: $carousel_index === 2, - label: `${$ls(`common.price`)}`, - kv_wrap: `price_wrap`, - display: { - value: - tradepr_cprice_amt && - tradepr_price_qty_unit_sel - ? `${fmt_currency_price(tradepr_cprice_amt)} / ${`${$ls(`measurement.mass.unit.${tradepr_price_qty_unit_sel}_ab`)}`.toLowerCase()}` - : ``, - undef: `${$ls(`icu.no_*`, { value: `${$ls(`common.price`)}`.toLowerCase() })}`, - nostyle: true, - }, - handle_back: async () => { - await handle_back(1); - }, - }} - /> - <TradeFieldDisplayKv - basis={{ - visible: $carousel_index === 2, - label: `${$ls(`icu.*_quantity`, { value: `${$ls(`common.order`)}` })}`, - display: { - value: tradepr_parsed_quantity - ? `${tradepr_parsed_quantity.mass} ${`${$ls(`measurement.mass.unit.${tradepr_parsed_quantity.mass_unit}_ab`)}`.toLowerCase()} ${tradepr_parsed_quantity.label || `${$ls(`common.bag`)}`}` - : ``, - undef: `${$ls(`icu.no_*`, { value: `${$ls(`icu.*_quantity`, { value: `${$ls(`common.order`)}` })}` })}`, - nostyle: - !!tradepr_parsed_quantity, - }, - kv_wrap: `qty_wrap`, - handle_back: async () => { - await handle_back(1); - }, - }} - /> - <TradeFieldDisplayEl - basis={{ - visible: $carousel_index === 2, - label: `${$ls(`icu.*_available`, { value: `${$ls(`common.quantity`)}` })}${tradepr_parsed_quantity ? ` (${$tradepr_qty_avail})` : ``}`, - display: { - classes: - tradepr_parsed_quantity - ? `` - : `pr-4`, - value: tradepr_parsed_quantity - ? ascii.bullet - : ``, - hide: !!tradepr_parsed_quantity, - undef: ascii.dash, - }, - }} - > - {#if tradepr_parsed_quantity} - <div - class={`flex flex-row gap-2 pl-2 pr-1 justify-center items-center`} - > - <button - class={`group flex flex-row justify-center items-center`} - on:click={async () => { - tradepr_qty_avail.set( - int_step( - $tradepr_qty_avail, - `-`, - 1, - ), - ); - }} - > - <Glyph - basis={{ - key: `arrow-down`, - dim: `xs`, - classes: `h-[1.3rem] w-[1.3rem] text-layer-1-glyph_d bg-layer-2-surface/60 rounded-full el-re`, - }} - /> - </button> - <button - class={`group flex flex-row justify-center items-center`} - on:click={async () => { - tradepr_qty_avail.set( - int_step( - $tradepr_qty_avail, - `+`, - ), - ); - }} - > - <Glyph - basis={{ - key: `arrow-up`, - dim: `xs`, - classes: `h-[1.3rem] w-[1.3rem] text-layer-1-glyph_d bg-layer-2-surface/60 rounded-full el-re`, - }} - /> - </button> - </div> - {/if} - </TradeFieldDisplayEl> - <TradeFieldDisplayEl - basis={{ - visible: $carousel_index === 2, - label: `${$ls(`icu.*_total`, { value: `${$ls(`common.quantity`)}` })}`, - display: { - classes: - tradepr_parsed_quantity - ? `pr-2` - : `pr-4`, - value: tradepr_parsed_quantity - ? `${Number( - $tradepr_qty_avail * - tradepr_parsed_quantity.mass, - ).toFixed( - 2, - )} ${`${$ls(`measurement.mass.unit.${tradepr_parsed_quantity.mass_unit}_ab`)}`.toLowerCase()}` - : ``, - undef: ascii.dash, - nostyle: true, - }, - }} - /> - </div> - <div - class={`flex flex-col h-auto w-full pb-6 gap-[5px] justify-center items-start`} - > - <TradeFieldDisplayEl - basis={{ - visible: $carousel_index === 2, - label: `${$ls(`icu.*_total`, { value: `${$ls(`common.order`)}` })}`, - display: { - classes: - tradepr_parsed_quantity && - tradepr_cprice_amt - ? `pr-2` - : `pr-4`, - value: tradepr_cprice_total - ? `${fmt_currency_price(tradepr_cprice_total)}` - : ``, - undef: ascii.dash, - }, - }} - /> - </div> - </div> - </div> - </div> - <button - class={`flex flex-row h-12 w-${$app_layout} justify-center items-center bg-layer-1-glyph-hl active:bg-layer-1-glyph-hl_a round-40 font-sans font-[600] text-[1.1rem] text-white capitalize el-re`} - on:click={async () => { - await submit(); - }} - > - {#if load_submit} - <Loading /> - {:else} - {`${$ls(`common.post`)}`} - {/if} - </button> - </LayoutTrellis> - </div> - </div> - </div> - <div - data-view={`success`} - class={`hidden flex flex-col h-full w-full justify-start items-center`} - > - <div - data-carousel-container={`success`} - class={`carousel-container flex h-full w-full`} - > - <div - data-carousel-item={`success`} - class={`carousel-item flex flex-col w-full justify-start items-center ${view === `success` ? `fade-in-long` : ``}`} - > - <LayoutTrellis> - <div - class={`flex flex-col h-trellis_centered_${$app_layout} w-full justify-center items-center`} - > - <Glyph - basis={{ - classes: `text-success text-[72px]`, - weight: `bold`, - key: `seal-check`, - }} - /> - <div - class={`flex flex-col pt-1 justify-start items-center`} - > - <div - class={`flex flex-row w-full justify-center items-center`} - > - <p - class={`font-sans font-[400] text-[1.3rem] text-layer-0-glyph`} - > - {`${$ls(`common.complete`)}`} - </p> - </div> - <div - class={`flex flex-row w-full justify-center items-center`} - > - <button - class={`flex flex-row justify-center items-center`} - on:click={async () => { - if (tradepr_success_id) - await route( - `/models/trade-product/view`, - [ - [ - `id`, - tradepr_success_id, - ], - ], - ); - }} - > - <p - class={`font-sans font-[400] text-[1.1rem] text-layer-0-glyph`} - > - {`${$ls(`icu.click_to_*`, { value: `${$ls(`icu.view_the_*`, { value: `${$ls(`common.product`)}` })}`.toLowerCase() })}`} - </p> - </button> - </div> - </div> - </div> - </LayoutTrellis> - </div> - </div> - </div> - </LayoutView> -{/if} -<Nav - basis={{ - prev: { - label: - view === `success` - ? `${$ls(`common.home`)}` - : `${$ls(`common.back`)}`, - route: view === `success` ? `/` : `/models/trade-product`, - prevent_route: - (view === `c_1` && $carousel_index === 0) || view === `success` - ? undefined - : { - callback: async () => { - await handle_back(); - }, - }, - callback: async () => { - await tradeproduct_init_kv(fmt_id()); - }, - }, - title: { - label: { - value: - view === `success` - ? `` - : `${$ls(`icu.new_*`, { value: `${$ls(`common.product`)}` })}`, - }, - callback: async () => {}, - }, - option: { - label: { - value: - $carousel_num > 1 - ? `${$ls(`common.back`)}` - : page_param.carousel[view].get($carousel_index) - ?.label_next || ``, - glyph: - $carousel_index === $carousel_index_max || - view === `success` - ? undefined - : { - key: `caret-right`, - classes: `text-layer-1-glyph-hl`, - }, - }, - callback: async () => { - await handle_continue(); - }, - }, - }} -/> -<MapPointSelectEnvelope - bind:map_point_select={tradepr_lgc_map_point} - bind:map_point_select_geoc={tradepr_lgc_map_geoc} - basis={{ - visible: tradepr_lgc_map_vis, - close: async () => { - tradepr_lgc_sel.set(`*geoc`); - tradepr_lgc_map_vis = false; - }, - }} -/> -<ImageUploadEditEnvelope - bind:photo_paths={tradepr_photo_paths} - bind:photo_edit={tradepr_photo_edit} -/> diff --git a/src/routes/(app)/models/trade-product/view/+page.svelte b/src/routes/(app)/models/trade-product/view/+page.svelte @@ -1,75 +0,0 @@ -<script lang="ts"> - import { db } from "$lib/client"; - import type { TradeProductBundle } from "$lib/types"; - import { - app_notify, - catch_err, - LayoutTrellis, - LayoutView, - ls, - Nav, - qp_id, - } from "@radroots/svelte-lib"; - import { onMount } from "svelte"; - - type LoadData = TradeProductBundle; - let ld: LoadData | undefined = undefined; - - onMount(async () => { - try { - if (!$qp_id) app_notify.set(`${$ls(`error.client.page.load`)}`); - ld = await load_data(); - } catch (e) { - } finally { - } - }); - - const load_data = async (): Promise<LoadData | undefined> => { - try { - const _trade_product = await db.trade_product_get_one({ - id: $qp_id, - }); - if (`err` in _trade_product) { - app_notify.set(`${$ls(`error.client.page.load`)}`); //@todo - return; - } - const { result: trade_product } = _trade_product; - const location_gcs_res = await db.location_gcs_get({ - list: [`on_trade_product`, { id: trade_product.id }], - }); - if (`err` in location_gcs_res) { - return; //@todo - } - const location_gcs = location_gcs_res.results[0]; - return { - trade_product, - location_gcs, - } satisfies LoadData; - } catch (e) { - await catch_err(e, `load_data`); - } - }; -</script> - -{#if ld} - <LayoutView> - <LayoutTrellis> - <p class={`font-sans font-[400] text-layer-0-glyph break-all`}> - {JSON.stringify(ld)} - </p> - </LayoutTrellis> - </LayoutView> -{/if} -<Nav - basis={{ - prev: { - label: `${$ls(`common.back`)}`, - route: `/models/trade-product`, - }, - title: { - label: { - value: `${$ls(`common.product`)}`, - }, - }, - }} -/> diff --git a/src/routes/(app)/notifications/+page.svelte b/src/routes/(app)/notifications/+page.svelte @@ -1,36 +0,0 @@ -<script lang="ts"> - import { - LayoutView, - ls, - NavToolbar, - PageHeader, - TabsFloat, - } from "@radroots/svelte-lib"; - - let notifications: any[] = []; -</script> - -<LayoutView> - <NavToolbar /> - <PageHeader - basis={{ - label: `${$ls(`common.notifications`)} (${notifications.length})`, - }} - ></PageHeader> - <div class={`flex flex-col w-full px-4 gap-6 justify-center items-center`}> - {#if notifications.length} - {#each notifications as li} - <div class={`flex flex-row w-full justify-center items-center`}> - {li} - </div> - {/each} - {:else} - <div class={`flex flex-row w-full justify-center items-center`}> - <p class={`font-sans font-[500] text-layer-0-glyph capitalize`}> - {`${$ls(`icu.no_*`, { value: `${$ls(`common.notifications`)}` })}`} - </p> - </div> - {/if} - </div> -</LayoutView> -<TabsFloat /> diff --git a/src/routes/(app)/search/+page.svelte b/src/routes/(app)/search/+page.svelte @@ -1,118 +0,0 @@ -<script lang="ts"> - import { db } from "$lib/client"; - import SearchResultDisplay from "$lib/components/search_result_display.svelte"; - import { cfg } from "$lib/conf"; - import type { - LocationGcs, - NostrProfile, - NostrRelay, - TradeProduct, - } from "@radroots/models"; - import { - catch_err, - debounce_input, - fmt_id, - Glyph, - InputElement, - LayoutView, - ls, - NavToolbar, - PageHeader, - TabsFloat, - } from "@radroots/svelte-lib"; - import { SearchService, type SearchServiceResult } from "@radroots/utils"; - import { onMount } from "svelte"; - - type LoadData = { - location_gcs: LocationGcs[]; - nostr_profile: NostrProfile[]; - nostr_relay: NostrRelay[]; - trade_product: TradeProduct[]; - }; - let ld: LoadData | undefined = undefined; - - let client_search: SearchService | undefined = undefined; - let search_val = ``; - let search_results: SearchServiceResult[] = []; - - onMount(async () => { - try { - await init_page(); - } catch (e) { - } finally { - } - }); - - const init_page = async (): Promise<void> => { - try { - ld = await load_data(); - if (ld) client_search = new SearchService(ld); - } catch (e) { - await catch_err(e, `init_page`); - } - }; - - const load_data = async (): Promise<LoadData | undefined> => { - try { - const location_gcs = await db.location_gcs_get({ list: [`all`] }); - const nostr_profile = await db.nostr_profile_get({ - list: [`all`], - }); - const nostr_relay = await db.nostr_relay_get({ list: [`all`] }); - const trade_product = await db.trade_product_get({ - list: [`all`], - }); - return { - location_gcs: - `results` in location_gcs ? location_gcs.results : [], - nostr_profile: - `results` in nostr_profile ? nostr_profile.results : [], - nostr_relay: - `results` in nostr_relay ? nostr_relay.results : [], - trade_product: - `results` in trade_product ? trade_product.results : [], - } satisfies LoadData; - } catch (e) { - await catch_err(e, `load_page`); - } - }; - - const handle_search_input = debounce_input((val: string) => { - if (client_search) search_results = client_search.search(val); - }, cfg.debounce.search); -</script> - -<LayoutView> - <NavToolbar /> - <PageHeader basis={{ label: `${$ls(`common.search`)}` }}></PageHeader> - <div class={`flex flex-col w-full px-4 gap-6 justify-center items-center`}> - <div - class={`relative flex flex-row w-full justify-center items-center bg-layer-1-surface rounded-2xl`} - > - <Glyph - basis={{ - classes: `absolute left-4 text-layer-0-glyph-shade`, - dim: `sm`, - weight: `bold`, - key: `magnifying-glass`, - }} - /> - <InputElement - bind:value={search_val} - basis={{ - id: fmt_id(`search`), - sync: true, - classes: `pl-12 text-layer-0-glyph`, - placeholder: `Enter search query`, - callback: async ({ value }) => handle_search_input(value), - }} - /> - </div> - <div class={`flex flex-col w-full gap-4 justify-center items-center`}> - {#each search_results as li (li.id)} - <SearchResultDisplay basis={li} /> - {/each} - </div> - </div> -</LayoutView> -<TabsFloat /> diff --git a/src/routes/(app)/settings/+page.svelte b/src/routes/(app)/settings/+page.svelte @@ -1,424 +0,0 @@ -<script lang="ts"> - import { dialog, geol, haptics, keystore } from "$lib/client"; - import { ks } from "$lib/conf"; - import { reset_device } from "$lib/util/client"; - import { - app_thc, - ascii, - LayoutTrellis, - LayoutView, - ls, - NavToolbar, - PageHeader, - route, - Trellis, - } from "@radroots/svelte-lib"; - import { parse_color_mode } from "@radroots/theme"; -</script> - -<LayoutView> - <NavToolbar /> - <PageHeader - basis={{ - label: `${$ls(`common.settings`)}`, - }} - /> - <LayoutTrellis> - <Trellis - basis={{ - args: { - layer: 1, - title: { - value: `Appearance`, - }, - list: [ - { - hide_active: true, - select: { - label: { - left: [ - { - value: `${$ls(`common.color_mode`)}`, - classes: `capitalize`, - }, - ], - }, - display: { - label: { - value: `${$app_thc}`, - classes: `capitalize`, - }, - }, - el: { - value: $app_thc, - options: [ - { - entries: [ - { - value: ascii.bullet, - label: `${$ls(`icu.choose_*`, { value: `${$ls(`common.color_mode`)}`.toLowerCase() })}`, - disabled: true, - }, - { - value: `light`, - label: `${$ls(`common.light`)}`, - }, - { - value: `dark`, - label: `${$ls(`common.dark`)}`, - }, - ], - }, - ], - callback: async ({ value }) => { - if (value) - app_thc.set( - parse_color_mode(value), - ); - }, - }, - end: { - glyph: { - key: `caret-right`, - }, - }, - }, - }, - ], - }, - }} - /> - <Trellis - basis={{ - args: { - layer: 1, - title: { - value: `Nostr Keys`, - }, - list: [ - { - touch: { - label: { - left: [ - { - value: `Nostr Key (public)`, - classes: `capitalize`, - }, - ], - }, - end: { - glyph: { - key: `caret-right`, - }, - }, - callback: async () => { - const public_key = await keystore.get( - ks.keys.nostr_publickey, - ); - if (`err` in public_key) return; - await dialog.alert( - `Hi! This is your nostr public key ${public_key.result}`, - ); - }, - }, - }, - { - touch: { - label: { - left: [ - { - value: `Nostr Key (secret)`, - classes: `capitalize`, - }, - ], - }, - end: { - glyph: { - key: `caret-right`, - }, - }, - callback: async () => { - const public_key = await keystore.get( - ks.keys.nostr_publickey, - ); - if (`err` in public_key) return; - const secret_key = await keystore.get( - ks.keys.nostr_secretkey( - public_key.result, - ), - ); - if (`err` in secret_key) return; - await dialog.alert( - `Hi! This is your nostr secret key ${secret_key.result}`, - ); - }, - }, - }, - ], - }, - }} - /> - <Trellis - basis={{ - args: { - layer: 1, - title: { - value: `Configuration`, - }, - list: [ - { - touch: { - label: { - left: [ - { - value: `Nostr Settings`, - classes: `capitalize`, - }, - ], - }, - end: { - glyph: { - key: `caret-right`, - }, - }, - callback: async () => { - await route(`/settings/nostr`); - }, - }, - }, - { - touch: { - label: { - left: [ - { - value: `Reset Device`, - classes: `capitalize`, - }, - ], - }, - end: { - glyph: { - key: `caret-right`, - }, - }, - callback: async () => { - const confirm = await dialog.confirm( - `Hi! This will delete your saved keys.`, - ); - if (confirm === true) await reset_device(); - }, - }, - }, - ], - }, - }} - /> - <Trellis - basis={{ - args: { - layer: 1, - title: { - value: `Location`, - }, - list: [ - { - touch: { - label: { - left: [ - { - value: `Geolocation Current`, - classes: `capitalize`, - }, - ], - }, - callback: async () => { - const pos = await geol.current(); - await dialog.alert(JSON.stringify(pos)); - }, - }, - }, - ], - }, - }} - /> - <Trellis - basis={{ - args: { - layer: 1, - title: { - value: `Web`, - }, - list: [ - { - touch: { - label: { - left: [ - { - value: `Radroots.Org (Open Homepage)`, - }, - ], - }, - callback: async () => { - //const url = `https://radroots.org`; - //await browser.open(url); - }, - }, - }, - { - touch: { - label: { - left: [ - { - value: `Radroots.Org (Share Homepage)`, - }, - ], - }, - callback: async () => { - //await share.open({ - // title: `Radroots Home Page`, - // text: `Find farmers around the world.`, - // url: `https://radroots.org`, - // dialog_title: `This is the dialog title`, - //}); - }, - }, - }, - { - touch: { - label: { - left: [ - { - value: `Primal.Net (Open Profile)`, - }, - ], - }, - callback: async () => { - //const public_key = await keystore.get( - // ks.keys.nostr_publickey, - //); - //const npub = nostr.lib.npub(public_key); - //const url = `https://primal.net/p/${npub}`; - //await browser.open(url); - }, - }, - }, - ], - }, - }} - /> - <Trellis - basis={{ - args: { - layer: 1, - title: { - value: `Device`, - }, - list: [ - { - touch: { - label: { - left: [ - { - value: `Device (Info)`, - }, - ], - }, - callback: async () => { - //const data = await device.info(); - //await dialog.alert(JSON.stringify(data)); - }, - }, - }, - { - touch: { - label: { - left: [ - { - value: `Device (Battery)`, - }, - ], - }, - callback: async () => { - //const data = await device.battery(); - //await dialog.alert(JSON.stringify(data)); - }, - }, - }, - ], - }, - }} - /> - <Trellis - basis={{ - args: { - layer: 1, - title: { - value: `Tests`, - }, - list: [ - { - touch: { - label: { - left: [ - { - value: `Haptics Touch (less)`, - classes: `capitalize`, - }, - ], - }, - end: { - glyph: { - key: `caret-right`, - }, - }, - callback: async () => { - await haptics.impact(`light`); - }, - }, - }, - { - touch: { - label: { - left: [ - { - value: `Haptics Touch (more)`, - classes: `capitalize`, - }, - ], - }, - end: { - glyph: { - key: `caret-right`, - }, - }, - callback: async () => { - await haptics.impact(`heavy`); - }, - }, - }, - { - touch: { - label: { - left: [ - { - value: `Haptics Vibrate (500ms)`, - classes: `capitalize`, - }, - ], - }, - end: { - glyph: { - key: `caret-right`, - }, - }, - callback: async () => { - await haptics.vibrate(500); - }, - }, - }, - ], - }, - }} - /> - </LayoutTrellis> -</LayoutView> diff --git a/src/routes/(app)/settings/nostr/+page.svelte b/src/routes/(app)/settings/nostr/+page.svelte @@ -1,142 +0,0 @@ -<script lang="ts"> - import { dialog } from "$lib/client"; - import { - LayoutTrellis, - LayoutView, - Nav, - Trellis, - ascii, - catch_err, - ls, - type ISelectOption, - } from "@radroots/svelte-lib"; - import { onMount } from "svelte"; - - const page_param: { - select_options: ISelectOption<string>[]; - } = { - select_options: [ - { - value: ascii.bullet, - label: `${$ls(`icu.choose_*`, { value: `${$ls(`common.photo_hosting`)}`.toLowerCase() })}`, - disabled: true, - }, - { - value: `^radroots`, - label: `https://radroots.net`, - }, - { - value: `*add`, - label: `${$ls(`icu.add_*`, { value: `${$ls(`common.upload_url`)}`.toLowerCase() })}`, - }, - { - value: `*disable`, - label: `Disable uploads`, - }, - ], - }; - - let nostr_photohosting_sel_val = ``; - let nostr_photohosting_sel_label = ``; - - onMount(async () => { - try { - await init_page(); - } catch (e) { - } finally { - } - }); - - const init_page = async (): Promise<void> => { - try { - nostr_photohosting_sel_val = `^radroots`; //@todo - } catch (e) { - await catch_err(e, `init_page`); - } - }; - - $: nostr_photohosting_sel_label = nostr_photohosting_sel_val - ? page_param.select_options.filter( - (i) => i.value === nostr_photohosting_sel_val, - )?.[0].label - : ``; - - const handle_select_option = async ( - option_value: string, - ): Promise<void> => { - try { - if (!option_value.startsWith(`*`)) { - nostr_photohosting_sel_val = option_value; - return; - } - nostr_photohosting_sel_val = ``; - - await dialog.alert(`@todo`); //@todo - nostr_photohosting_sel_val = `^radroots`; - } catch (e) { - await catch_err(e, `handle_select_option`); - } - }; -</script> - -<LayoutView> - <LayoutTrellis> - <Trellis - basis={{ - args: { - layer: 1, - list: [ - { - hide_active: true, - select: { - label: { - left: [ - { - value: `Photo Hosting`, - classes: `capitalize`, - }, - ], - }, - display: { - loading: !nostr_photohosting_sel_val, - label: { - value: nostr_photohosting_sel_label, - }, - }, - el: { - value: nostr_photohosting_sel_val, - options: [ - { - entries: page_param.select_options, - }, - ], - callback: async ({ value }) => { - await handle_select_option(value); - }, - }, - end: { - glyph: { - key: `caret-right`, - }, - }, - }, - }, - ], - }, - }} - /> - </LayoutTrellis> -</LayoutView> -<Nav - basis={{ - prev: { - label: `${$ls(`common.settings`)}`, - route: `/settings`, - }, - title: { - label: { - value: `${$ls(`common.nostr`)}`, - }, - }, - }} -/> diff --git a/src/routes/(app)/settings/profile/+page.svelte b/src/routes/(app)/settings/profile/+page.svelte @@ -1,398 +0,0 @@ -<script lang="ts"> - import { db, dialog, fs } from "$lib/client"; - import ImageUploadAddPhoto from "$lib/components/image_upload_add_photo.svelte"; - import { throw_err } from "$lib/util/client"; - import { kv_init_page } from "$lib/util/kv"; - import { model_media_upload_add_list } from "$lib/util/models-media-upload"; - import { nostr_sync_metadata } from "$lib/util/nostr-sync"; - import { fmt_media_upload_url, type NostrProfile } from "@radroots/models"; - import { - app_nostr_key, - ascii, - catch_err, - Glyph, - ImageBlob, - ImagePath, - Loading, - ls, - route, - TabsFloat, - } from "@radroots/svelte-lib"; - import { onDestroy, onMount } from "svelte"; - - type LoadData = { - nostr_profile: NostrProfile; - }; - let ld: LoadData | undefined = undefined; - - let loading_photo_upload = false; - let opt_photo_path = ``; - type ViewDisplay = `photos` | `following` | `followers`; - let view_display: ViewDisplay = `photos`; - - $: { - console.log(JSON.stringify(ld, null, 4), `ld`); - } - - onMount(async () => { - try { - await init_page(); - } catch (e) { - await catch_err(e, `mount`); - } finally { - } - }); - - onDestroy(async () => { - try { - await nostr_sync_metadata(); - } catch (e) { - await catch_err(e, `destroy`); - } finally { - } - }); - - $: photo_overlay_visible = ld?.nostr_profile?.picture || opt_photo_path; - $: classes_photo_overlay_glyph = photo_overlay_visible - ? `text-white` - : `text-layer-0-glyph`; - $: classes_photo_overlay_glyph_opt = photo_overlay_visible - ? `text-gray-300` - : `text-layer-0-glyph`; - $: classes_photo_overlay_glyph_opt_selected = photo_overlay_visible - ? `text-white` - : `text-layer-1-glyph`; - - const init_page = async (): Promise<void> => { - try { - await kv_init_page(); - ld = await load_data(); - } catch (e) { - await catch_err(e, `init_page`); - } - }; - - const load_data = async (): Promise<LoadData | undefined> => { - try { - let nostr_profile_get_one = await db.nostr_profile_get_one({ - public_key: $app_nostr_key, - }); - if (`err` in nostr_profile_get_one) - return throw_err(nostr_profile_get_one); - return { - nostr_profile: nostr_profile_get_one.result, - } satisfies LoadData; - } catch (e) { - await catch_err(e, `load_data`); - } - }; - - const handle_back = async (): Promise<void> => { - try { - if (opt_photo_path) await handle_profile_photo_add(opt_photo_path); - await route(`/`); - } catch (e) { - await catch_err(e, `handle_back`); - } - }; - - const handle_profile_photo_add = async ( - file_path: string, - ): Promise<void> => { - try { - const confirm = await dialog.confirm({ - message: `The photo will be used for your profile. Do you want to continue?`, - }); - if (!confirm) return; - loading_photo_upload = true; - let photo_url = ``; - const media_upload_existing = await db.media_upload_get_one({ - file_path, - }); - if (`result` in media_upload_existing) - photo_url = fmt_media_upload_url(media_upload_existing.result); - else { - const media_uploads = await model_media_upload_add_list({ - photo_paths: [file_path], - }); - if (`alert` in media_uploads) { - await dialog.alert(media_uploads.alert); - return; - } else if (`confirm` in media_uploads) { - await dialog.confirm(media_uploads.confirm); - return; - } - if ( - `results` in media_uploads && - media_uploads.results.length - ) { - const media_upload = await db.media_upload_get_one({ - id: media_uploads.results[0], - }); - if (`result` in media_upload) - photo_url = fmt_media_upload_url(media_upload.result); - } - } - if (photo_url) { - const nostr_profile_update = await db.nostr_profile_update({ - on: { - public_key: $app_nostr_key, - }, - fields: { - picture: photo_url, - }, - }); - if (`err` in nostr_profile_update) { - await dialog.alert(`${$ls(`error.client.unhandled`)}`); - return; - } - } - } catch (e) { - await catch_err(e, `handle_profile_photo_add`); - } finally { - loading_photo_upload = false; - } - }; - - const handle_edit_display_name = async (): Promise<void> => { - try { - await route(`/settings/profile/edit`, [ - [`nostr_pk`, $app_nostr_key], - [`rkey`, `display_name`], - ]); - } catch (e) { - await catch_err(e, `handle_edit_display_name`); - } - }; - - const handle_edit_name = async (): Promise<void> => { - try { - if (ld?.nostr_profile.name) { - const confirm = await dialog.confirm({ - message: `Updating your username will result in public links on your profile being updated. Do you want to continue?`, - }); - if (!confirm) return; - } - await route(`/settings/profile/edit`, [ - [`nostr_pk`, $app_nostr_key], - [`rkey`, `name`], - ]); - } catch (e) { - await catch_err(e, `handle_edit_name`); - } - }; - - const handle_edit_about = async (): Promise<void> => { - try { - await route(`/settings/profile/edit`, [ - [`nostr_pk`, $app_nostr_key], - [`rkey`, `about`], - ]); - } catch (e) { - await catch_err(e, `handle_edit_about`); - } - }; -</script> - -<div - class={`relative flex flex-col min-h-[525px] h-[525px] w-full justify-center items-center bg-layer-2-surface fade-in`} -> - {#if ld?.nostr_profile?.picture} - <ImagePath - basis={{ - path: ld.nostr_profile.picture, - }} - /> - {:else if opt_photo_path} - {#await fs.read_bin(opt_photo_path) then file_data} - <ImageBlob - basis={{ - data: file_data, - }} - /> - {/await} - {:else} - <div class={`flex flex-row justify-start items-center -translate-y-8`}> - <ImageUploadAddPhoto bind:photo_path={opt_photo_path} /> - </div> - {/if} - <div class={`absolute top-[3.8rem] left-6 flex flex-row`}> - <button - class={`flex flex-row h-12 w-12 justify-center items-center ${photo_overlay_visible ? `bg-layer-2-surface` : `bg-layer-1-surface`} layer-1-active-surface rounded-full el-re`} - on:click={async () => { - await handle_back(); - }} - > - {#if loading_photo_upload} - <Loading /> - {:else} - <Glyph - basis={{ - classes: `text-layer-0-glyph`, - dim: `sm+`, - weight: `bold`, - key: `arrow-left`, - }} - /> - {/if} - </button> - </div> - <div class={`absolute top-[3.8rem] right-6 flex flex-row`}> - <button - class={`flex flex-row h-12 w-12 justify-center items-center ${photo_overlay_visible ? `bg-layer-2-surface` : `bg-layer-1-surface`} layer-1-active-surface rounded-full el-re`} - on:click={async () => { - alert(`@todo!`); - }} - > - <Glyph - basis={{ - classes: `text-layer-0-glyph`, - dim: `md-`, - weight: `bold`, - key: `images-square`, - }} - /> - </button> - </div> - <div - class={`absolute bottom-0 left-0 flex flex-col h-[calc(100%-100%/1.618)] w-full px-6 gap-2 justify-end items-center`} - > - <div - class={`flex flex-col w-full gap-[2px] justify-center items-center`} - > - <div class={`flex flex-row h-10 w-full justify-start items-center`}> - <button - class={`group flex flex-row justify-center items-center`} - on:click={async () => { - await handle_edit_display_name(); - }} - > - <p - class={`font-sansd font-[600] text-[2rem] ${classes_photo_overlay_glyph} ${ld?.nostr_profile.display_name ? `` : `capitalize opacity-active`} el-re`} - > - {#if ld?.nostr_profile.display_name} - {ld.nostr_profile.display_name} - {:else} - {`+ ${`${$ls(`icu.add_*`, { value: `${$ls(`common.profile_name`)}` })}`}`} - {/if} - </p> - </button> - </div> - <div - class={`flex flex-row w-full gap-[6px] justify-start items-center`} - > - <button - class={`group flex flex-row justify-center items-center`} - on:click={async () => { - await handle_edit_name(); - }} - > - <p - class={`font-sans font-[600] text-[1.1rem] ${classes_photo_overlay_glyph} ${ld?.nostr_profile.name ? `` : `capitalize opacity-active`} el-re`} - > - {#if ld?.nostr_profile.name} - <span class={`font-[900] -mr-[3px]`}>{`@`}</span> - {ld.nostr_profile.name} - {:else} - {`+ ${`${$ls(`icu.add_*`, { value: `${$ls(`common.username`)}` })}`}`} - {/if} - </p> - </button> - <p - class={`font-sans font-[400] ${classes_photo_overlay_glyph}`} - > - {ascii.bullet} - </p> - <button - class={`flex flex-row justify-center items-center`} - on:click={async () => { - alert(`@todo!`); - }} - > - <Glyph - basis={{ - classes: `${classes_photo_overlay_glyph}`, - dim: `xs`, - weight: `bold`, - key: `link-simple`, - }} - /> - </button> - </div> - <div class={`flex flex-row w-full justify-start items-center`}> - <button - class={`group flex flex-row justify-center items-center`} - on:click={async () => { - await handle_edit_about(); - }} - > - <p - class={`font-sansd font-[400] text-[1.1rem] ${classes_photo_overlay_glyph} ${ld?.nostr_profile.about ? `` : `capitalize opacity-active`}`} - > - {#if ld?.nostr_profile.about} - {ld.nostr_profile.about} - {:else} - {`+ ${`${$ls(`icu.add_*`, { value: `${$ls(`common.bio`)}` })}`}`} - {/if} - </p> - </button> - </div> - </div> - <div - class={`flex flex-row w-full pt-2 pb-6 gap-2 justify-start items-center`} - > - <button - class={`flex flex-row justify-center items-center`} - on:click={async () => { - view_display = `photos`; - }} - > - <p - class={`font-sans text-[1.1rem] font-[600] capitalize ${view_display === `photos` ? classes_photo_overlay_glyph_opt_selected : classes_photo_overlay_glyph_opt} el-re`} - > - {`photos`} - </p> - </button> - <button - class={`flex flex-row justify-center items-center`} - on:click={async () => { - view_display = `following`; - }} - > - <p - class={`font-sans text-[1.1rem] font-[600] capitalize ${view_display === `following` ? classes_photo_overlay_glyph_opt_selected : classes_photo_overlay_glyph_opt} el-re`} - > - {`following`} - </p> - </button> - <button - class={`flex flex-row justify-center items-center`} - on:click={async () => { - view_display = `followers`; - }} - > - <p - class={`font-sans text-[1.1rem] font-[600] capitalize ${view_display === `followers` ? classes_photo_overlay_glyph_opt_selected : classes_photo_overlay_glyph_opt} el-re`} - > - {`followers`} - </p> - </button> - </div> - </div> -</div> -<div class={`flex flex-col w-full justify-start items-center`}> - {#if view_display === `photos`} - <p class={`font-sans font-[400] text-layer-0-glyph`}> - {view_display} - </p> - {:else if view_display === `following`} - <p class={`font-sans font-[400] text-layer-0-glyph`}> - {view_display} - </p> - {:else if view_display === `followers`} - <p class={`font-sans font-[400] text-layer-0-glyph`}> - {view_display} - </p> - {/if} -</div> -<TabsFloat /> diff --git a/src/routes/(app)/settings/profile/edit/+page.svelte b/src/routes/(app)/settings/profile/edit/+page.svelte @@ -1,210 +0,0 @@ -<script lang="ts"> - import { db, dialog } from "$lib/client"; - import { nostr_sync_metadata } from "$lib/util/nostr-sync"; - import { - nostr_profile_form_fields, - type NostrProfile, - type NostrProfileFields, - type NostrProfileFormFields, - parse_nostr_profile_form_keys, - } from "@radroots/models"; - import { - app_notify, - catch_err, - fmt_id, - InputElement, - kv, - LayoutView, - ls, - Nav, - qp_nostr_pk, - qp_rkey, - route, - TextareaElement, - } from "@radroots/svelte-lib"; - import { onDestroy, onMount } from "svelte"; - - type LoadData = { - nostr_profile: NostrProfile; - field_key: keyof NostrProfileFields; - }; - let ld: LoadData | undefined = undefined; - - let page_initial_value = ``; - let page_input_value = ``; - - onMount(async () => { - try { - console.log(`$qp_rkey `, $qp_rkey); - console.log(`$qp_nostr_pk `, $qp_nostr_pk); - if (!$qp_rkey || !$qp_nostr_pk) { - app_notify.set( - `${$ls(`icu.error_loading_*`, { value: `${$ls(`common.page`)}` })}`, - ); - return; - } - ld = await load_page(); - } catch (e) { - } finally { - } - }); - - onDestroy(async () => { - try { - await nostr_sync_metadata(); - } catch (e) { - } finally { - } - }); - - $: translated_field_key = ld?.field_key - ? `${$ls(`models.nostr_profile.fields.${ld.field_key}.label`)}` - : ``; - $: input_value_del = page_initial_value !== page_input_value; - - const load_page = async (): Promise<LoadData | undefined> => { - try { - const nostr_profile = await db.nostr_profile_get_one({ - public_key: $qp_nostr_pk, - }); - if (`err` in nostr_profile) { - app_notify.set( - `${$ls(`icu.error_loading_*`, { value: `${$ls(`common.profile`)}` })}`, - ); - return; - } - const field_key = parse_nostr_profile_form_keys($qp_rkey); - if (!field_key) { - app_notify.set(`${$ls(`error.client.page.load`)}`); - return; - } - const field_val = nostr_profile.result[field_key]; - if (field_val) { - page_input_value = field_val; - page_initial_value = field_val; - } - return { - nostr_profile: nostr_profile.result, - field_key, - } satisfies LoadData; - } catch (e) { - await catch_err(e, `load_page`); - } - }; - - const submit = async (): Promise<void> => { - try { - if (!ld?.field_key || !ld?.nostr_profile) return; - const val = await kv.get(fmt_id($qp_rkey)); - if (!val) { - await route(`/settings/profile`); - return; - } - const validated = - nostr_profile_form_fields[ld.field_key].validation.test(val); - if (!validated) { - dialog.alert( - `${$ls(`icu.invalid_*_entry`, { value: translated_field_key })}`, - ); - return; - } - const fields: Partial<NostrProfileFormFields> = {}; - fields[ld.field_key] = val; - const update_res = await db.nostr_profile_update({ - on: { - public_key: $qp_nostr_pk, - }, - fields, - }); - if (`err` in update_res) { - await dialog.alert(`${$ls(`error.client.unhandled`)}`); - return; - } - await route(`/settings/profile`); - } catch (e) { - await catch_err(e, `submit`); - } - }; -</script> - -<LayoutView> - {#if ld} - <div - class={`flex flex-col w-full pt-4 px-4 gap-1 justify-start items-center fade-in`} - > - <div class={`flex flex-row w-full pl-2 justify-start items-center`}> - <p - class={`font-sans text-trellis_ti text-layer-0-glyph-label uppercase`} - > - {translated_field_key.replace(/profile /i, ``)} - </p> - </div> - {#if ld.field_key === `about`} - <TextareaElement - bind:value={page_input_value} - basis={{ - id: fmt_id(ld.field_key), - classes: `min-h-[12rem] pl-4`, - sync: true, - layer: 1, - placeholder: `${$ls(`icu.enter_*`, { value: `${translated_field_key}`.toLowerCase() })}`, - field: { - charset: - nostr_profile_form_fields[ld.field_key].charset, - validate: - nostr_profile_form_fields[ld.field_key] - .validation, - validate_keypress: true, - }, - callback_keydown: async ({ key_s }) => { - if (key_s && input_value_del) await submit(); - }, - }} - /> - {:else} - <InputElement - bind:value={page_input_value} - basis={{ - id: fmt_id(ld.field_key), - classes: `rounded-touch pl-4`, - sync: true, - layer: 1, - placeholder: `${$ls(`icu.enter_*`, { value: `${translated_field_key}`.toLowerCase() })}`, - field: { - charset: - nostr_profile_form_fields[ld.field_key].charset, - validate: - nostr_profile_form_fields[ld.field_key] - .validation, - validate_keypress: true, - }, - callback_keydown: async ({ key_s }) => { - if (key_s && input_value_del) await submit(); - }, - }} - /> - {/if} - </div> - {/if} -</LayoutView> -<Nav - basis={{ - prev: { - label: `${$ls(`common.profile`)}`, - route: `/settings/profile`, - prevent_route: input_value_del - ? { - callback: async () => { - if (input_value_del) await submit(); - }, - } - : undefined, - }, - title: { - label: { - classes: `capitalize`, - value: `${$ls(`icu.edit_*`, { value: `${$ls(`common.profile`)}` })}`, - }, - }, - }} -/> diff --git a/src/routes/(app)/test/+page.svelte b/src/routes/(app)/test/+page.svelte @@ -1,4 +0,0 @@ -<script lang="ts"> -</script> - -<div></div> diff --git a/src/routes/(cfg)/cfg/+layout.ts b/src/routes/(cfg)/cfg/+layout.ts @@ -1,27 +0,0 @@ -import { keystore } from '$lib/client'; -import { ks } from '$lib/conf'; -import { app_splash, catch_err, route } from '@radroots/svelte-lib'; -import type { LayoutLoad, LayoutLoadEvent } from './$types'; - -export const load: LayoutLoad = async (_: LayoutLoadEvent) => { - try { - await keystore.init(); - const ks_nostr_publickey = await keystore.get( - ks.keys.nostr_publickey, - ); - if (`result` in ks_nostr_publickey) { - const ks_nostr_secretkey = await keystore.get( - ks.keys.nostr_secretkey(ks_nostr_publickey.result), - ); - if (`result` in ks_nostr_secretkey) { - await route(`/`); - return; - } - } - app_splash.set(false); - } catch (e) { - await catch_err(e, `(cfg)load`) - } finally { - return {}; - }; -}; diff --git a/src/routes/(cfg)/cfg/error/+page.svelte b/src/routes/(cfg)/cfg/error/+page.svelte @@ -1,25 +0,0 @@ -<script lang="ts"> - import { restart } from "$lib/util/client"; - import { LayoutView } from "@radroots/svelte-lib"; -</script> - -<LayoutView> - <div class={`flex flex-col w-full gap-2 justify-start items-center`}> - <p class={`font-sans font-[400] text-layer-0-glyph`}> - The device is improperly configured. - </p> - <button - class={`flex flex-row justify-center items-center`} - on:click={async () => { - await restart({ - route: `/`, - notify_message: `The device was restarted`, - }); - }} - > - <p class={`font-sans font-[400] text-layer-0-glyph-hl`}> - Click to restart - </p> - </button> - </div> -</LayoutView> diff --git a/src/routes/(cfg)/cfg/init/+page.svelte b/src/routes/(cfg)/cfg/init/+page.svelte @@ -1,1143 +0,0 @@ -<script lang="ts"> - import { PUBLIC_NOSTR_RELAY_DEFAULTS } from "$env/static/public"; - import { db, dialog, keystore, nostr } from "$lib/client"; - import { cfg, ks } from "$lib/conf"; - import { restart } from "$lib/util/client"; - import { - fetch_radroots_profile_confirm, - fetch_radroots_profile_init, - fetch_radroots_profile_status, - fetch_radroots_profile_validate, - } from "$lib/util/fetch-radroots-profile"; - import { kv_init_page } from "$lib/util/kv"; - import type { IClientUnlisten } from "@radroots/client"; - import { - app_layout, - app_loading, - app_splash, - ButtonLayoutPair, - carousel_dec, - carousel_inc, - carousel_index, - carousel_index_max, - catch_err, - DisplayLine, - el_id, - EntryLine, - fmt_id, - Glyph, - InputElement, - kv, - LayoutView, - Loading, - LogoCircle, - ls, - sleep, - view_effect, - } from "@radroots/svelte-lib"; - import { regex } from "@radroots/utils"; - import { onDestroy, onMount } from "svelte"; - - const page_carousel: Record<View, { max_index: number }> = { - cfg_init: { - max_index: 2, - }, - cfg_main: { - max_index: 2, - }, - eula: { - max_index: 1, - }, - }; - - let el_eula: HTMLDivElement; - let el_eula_scrolled = false; - - let loading_submit = false; - - type CfgInitKeyOption = `key_gen` | `kv_nostr_secretkey`; - let cgf_init_key_option: CfgInitKeyOption | undefined = undefined; - - type CfgMainOptionIndex1 = `farmer` | `personal`; - let cfg_main_opt_idx1: CfgMainOptionIndex1 | undefined = undefined; - let cfg_main_nostr_publickey = ``; - let cfg_main_nostr_publickey_npub = ``; - $: cfg_main_nostr_publickey_npub = cfg_main_nostr_publickey - ? nostr.lib.npub(cfg_main_nostr_publickey) || `` - : ``; - let cfg_main_profilename_valid = false; - let cfg_main_profilename_loading = false; - - type View = `cfg_init` | `cfg_main` | `eula`; - let initial_view: View = `cfg_init`; - let view: View = initial_view; - $: { - view_effect<View>(view); - } - - let unlisten_cfg_init_nostr_secretkey: IClientUnlisten | undefined = - undefined; - - onMount(async () => { - try { - await init_page(); - } catch (e) { - console.log(`e mount`, e); - } finally { - app_splash.set(false); - } - }); - - onDestroy(async () => { - try { - await kv_init_page(); - if (unlisten_cfg_init_nostr_secretkey) - unlisten_cfg_init_nostr_secretkey(); - el_eula?.removeEventListener(`scroll`, () => null); - } catch (e) { - console.log(`e destroy`, e); - } finally { - } - }); - - const init_page = async (): Promise<void> => { - try { - handle_view(initial_view); - const unlisten_1 = await keystore.on_key_change( - ks.cfg_init.nostr_secretkey, - async (_cfg_init_nostr_secretkey) => { - if (_cfg_init_nostr_secretkey) { - cfg_main_nostr_publickey = - nostr.lib.secretkey_to_publickey( - _cfg_init_nostr_secretkey, - ) || ``; - } else cfg_main_nostr_publickey = ``; - }, - ); - if (!(`err` in unlisten_1)) - unlisten_cfg_init_nostr_secretkey = unlisten_1; - el_eula?.addEventListener(`scroll`, () => { - const client_h = el_eula?.clientHeight; - const scroll_h = el_eula?.scrollHeight; - const scroll_top = el_eula?.scrollTop; - if (scroll_top + client_h >= scroll_h) el_eula_scrolled = true; - }); - await lookup_ks(); - await kv_init_page(); - } catch (e) { - await catch_err(e, `init_page`); - } - }; - - const lookup_ks = async (): Promise<void> => { - try { - app_splash.set(false); - const ks_nostr_secretkey = await keystore.get( - ks.cfg_init.nostr_secretkey, - ); - const ks_radroots_tok = await keystore.get( - ks.cfg_init.radroots_tok, - ); - if (`result` in ks_nostr_secretkey) { - app_loading.set(true); - cfg_main_nostr_publickey; - if (`result` in ks_radroots_tok) { - cfg_main_nostr_publickey = nostr.lib.public_key( - ks_nostr_secretkey.result, - ); - const profile_status = await fetch_radroots_profile_status( - ks_radroots_tok.result, - ); - if (`active` in profile_status) { - if ( - profile_status.active.public_key !== - cfg_main_nostr_publickey - ) { - await keystore.remove(ks.cfg_init.nostr_secretkey); - await keystore.remove(ks.cfg_init.radroots_tok); - await keystore.remove( - ks.cfg_init.nostr_profilename, - ); - return; - } - const confirm = await dialog.confirm({ - message: `There is an existing configuration in progress${`nip_05` in profile_status.active ? ` for "${profile_status.active.nip_05}".` : `.`} ${`${$ls(`common.do_you_want_to_continue_q`)}`}`, //@todo - cancel_label: `${$ls(`common.reset`)}`, - ok_label: `${$ls(`common.yes`)}`, - }); - if (confirm === false) { - await keystore.remove(ks.cfg_init.nostr_secretkey); - await keystore.remove(ks.cfg_init.radroots_tok); - await keystore.remove( - ks.cfg_init.nostr_profilename, - ); - return; - } else { - if (`nip_05` in profile_status.active) { - await kv.set( - fmt_id(`nostr_profilename`), - profile_status.active.nip_05, - ); - } - await kv.set( - fmt_id(`nostr_profilename`), - profile_status.active.nip_05, - ); - handle_view(`eula`); - } - } - } else { - await keystore.remove(ks.cfg_init.nostr_secretkey); - } - } - } catch (e) { - await catch_err(e, `lookup_ks`); - } finally { - app_loading.set(false); - } - }; - - const reset_page = async (alert_message?: string): Promise<void> => { - try { - app_loading.set(true); - handle_view(`cfg_init`); - if (alert_message) await dialog.alert(alert_message); - await sleep(cfg.delay.load); - } catch (e) { - await catch_err(e, `reset_page`); - } finally { - app_loading.set(false); - } - }; - - const reset_ks = async (): Promise<void> => { - try { - const ks_entries = await keystore.entries(); - if (`results` in ks_entries) { - for (const [ks_key] of ks_entries.results.filter(([k]) => - k.startsWith(`cfg:init:`), - )) - await keystore.remove(ks_key); - } - } catch (e) { - await catch_err(e, `reset_ks`); - } - }; - - const handle_view = (new_view: View): void => { - if (new_view === `cfg_init` && view === `cfg_main`) { - const offset = cgf_init_key_option === `key_gen` ? 1 : 0; - carousel_index.set(page_carousel[new_view].max_index - offset); - } else { - carousel_index.set(0); - carousel_index_max.set(page_carousel[new_view].max_index); - } - view = new_view; - }; - - const handle_back = async (): Promise<void> => { - try { - switch (view) { - case `cfg_init`: - { - switch ($carousel_index) { - case 1: - { - cgf_init_key_option = undefined; - await carousel_dec(view); - } - break; - case 2: - { - await carousel_dec(view); - } - break; - } - } - break; - case `cfg_main`: { - switch ($carousel_index) { - case 0: - { - handle_view(`cfg_init`); - } - break; - case 1: - { - carousel_dec(view); - } - break; - } - } - } - } catch (e) { - await catch_err(e, `handle_back`); - } - }; - - const handle_continue = async (): Promise<void> => { - try { - switch (view) { - case `cfg_init`: - { - switch ($carousel_index) { - case 0: - { - await carousel_inc(view); - } - break; - case 1: - { - if ( - cgf_init_key_option === - `kv_nostr_secretkey` - ) { - await carousel_inc(view); - return; - } - const ks_nostr_secretkey = - await keystore.get( - ks.cfg_init.nostr_secretkey, - ); - if (`result` in ks_nostr_secretkey) { - handle_view(`cfg_main`); - return; - } - await keystore.set( - ks.cfg_init.nostr_secretkey, - nostr.lib.generate_key(), - ); - handle_view(`cfg_main`); - } - break; - case 2: - { - const kv_nostr_secretkey = await kv.get( - fmt_id(`nostr_secretkey`), - ); - if (!kv_nostr_secretkey) { - await dialog.alert( - `${$ls(`icu.enter_a_*`, { value: `${$ls(`icu.valid_*`, { value: `${$ls(`common.key`)}` })}`.toLowerCase() })}`, - ); - return; - } - const nostr_secretkey_valid_nsec = - nostr.lib.nsec_decode( - kv_nostr_secretkey, - ); - const nostr_secretkey_valid_hex = - nostr.lib.public_key( - kv_nostr_secretkey, - ); - if ( - nostr_secretkey_valid_nsec || - nostr_secretkey_valid_hex - ) { - await keystore.set( - ks.cfg_init.nostr_secretkey, - kv_nostr_secretkey, - ); - handle_view(`cfg_main`); - return; - } - await dialog.alert( - `${$ls(`icu.invalid_*`, { value: `${$ls(`common.key`)}`.toLowerCase() })}`, - ); - } - break; - } - } - break; - case `cfg_main`: - { - switch ($carousel_index) { - case 0: - { - if (cfg_main_profilename_loading) return; - const ks_nostr_secretkey = - await keystore.get( - ks.cfg_init.nostr_secretkey, - ); - if (`err` in ks_nostr_secretkey) { - await reset_page( - `${$ls(`error.device.configuration_failure`)}`, - ); - return; - } - const kv_profile_name = await kv.get( - fmt_id(`nostr_profilename`), - ); - if (!kv_profile_name) { - await dialog.alert( - `${$ls(`icu.enter_a_*`, { value: `${$ls(`common.profile_name`)}`.toLowerCase() })}`, - ); - return; - } - - cfg_main_profilename_loading = true; - const profilename_validated = - await fetch_radroots_profile_validate({ - profile_name: kv_profile_name, - }); - if (`err` in profilename_validated) { - cfg_main_profilename_loading = false; - await dialog.alert( - profilename_validated.err, - ); - return; - } - const confirm = await dialog.confirm({ - message: `${`${$ls(`icu.the_*_is_available`, { value: `${$ls(`common.profile_name`).toLowerCase()} "${profilename_validated.profile_name}"` })}`}. Would you like to use it?`, //@todo - cancel_label: `${$ls(`common.no`)}`, - ok_label: `${$ls(`common.yes`)}`, - }); - if (!confirm) { - cfg_main_profilename_loading = false; - return; - } - const profilename_added = - await fetch_radroots_profile_init({ - profile_name: - profilename_validated.profile_name, - secret_key: - ks_nostr_secretkey.result, - }); - cfg_main_profilename_loading = false; - if (`err` in profilename_added) { - await dialog.alert( - profilename_added.err, - ); - return; - } - await keystore.set( - ks.cfg_init.nostr_profilename, - profilename_validated.profile_name, - ); - await keystore.set( - ks.cfg_init.radroots_tok, - profilename_added.tok, - ); - - carousel_inc(view); - } - break; - case 1: { - if (!cfg_main_opt_idx1) - cfg_main_opt_idx1 = `personal`; - await keystore.set( - ks.pref.cfg_type, - cfg_main_opt_idx1, - ); - handle_view(`eula`); - } - } - } - break; - } - } catch (e) { - await catch_err(e, `handle_continue`); - } - }; - - const submit = async (): Promise<void> => { - try { - loading_submit = true; - const ks_nostr_secretkey = await keystore.get( - ks.cfg_init.nostr_secretkey, - ); - if (`err` in ks_nostr_secretkey) { - await dialog.alert( - `${$ls(`error.device.configuration_failure`)}`, - ); - return; //@todo - } - const ks_radroots_tok = await keystore.get( - ks.cfg_init.radroots_tok, - ); - if (`result` in ks_radroots_tok) { - const profile_activated = await fetch_radroots_profile_confirm( - ks_radroots_tok.result, - ); - if (`err` in profile_activated) { - await dialog.alert( - `${$ls(`icu.*_failure`, { value: `${$ls(`common.activation`)}` })}`, - ); - return; //@todo - } - } - const ks_nostr_profilename = await keystore.get( - ks.cfg_init.nostr_profilename, - ); - const nostr_publickey = nostr.lib.public_key( - ks_nostr_secretkey.result, - ); - if (!nostr_publickey) { - await dialog.alert( - `${$ls(`error.device.public_key_not_derived`)}`, - ); - return; //@todo - } - const nostr_profile_add = await db.nostr_profile_add({ - public_key: nostr_publickey, - name: - `result` in ks_nostr_profilename - ? ks_nostr_profilename.result - : undefined, - }); - if (`err` in nostr_profile_add || `err_s` in nostr_profile_add) { - await dialog.alert( - `${$ls(`icu.failure_saving_*_to_the_database`, { value: `${$ls(`common.profile`)}`.toLowerCase() })}`, - ); - return; //@todo - } - await keystore.set(ks.keys.nostr_publickey, nostr_publickey); - await keystore.set( - ks.keys.nostr_secretkey(nostr_publickey), - ks_nostr_secretkey.result, - ); - for (const url of Array.from( - new Set([...PUBLIC_NOSTR_RELAY_DEFAULTS.split(",")]), - )) { - const nostr_relay_add = await db.nostr_relay_add({ url }); - if (`err` in nostr_relay_add || `err_s` in nostr_relay_add) { - await dialog.alert( - `${$ls(`icu.failure_saving_*_to_the_database`, { value: `${$ls(`icu.default_*`, { value: `${$ls(`common.relays`)}` })}`.toLowerCase() })}`, - ); - return; // @todo - } - await db.nostr_profile_relay_set({ - nostr_profile: { - id: nostr_profile_add.id, - }, - nostr_relay: { - id: nostr_relay_add.id, - }, - }); - } - await reset_ks(); - await restart({ - route: `/`, - notify_message: `${$ls(`app.cfg.init.notification.welcome`)}`, - }); - } catch (e) { - await catch_err(e, `submit`); - } finally { - loading_submit = false; - } - }; -</script> - -<LayoutView> - <div - data-view={`cfg_init`} - class={`flex flex-col h-full w-full max-mobile_base:pt-12 pt-16 justify-start items-center`} - > - <div - data-carousel-container={`cfg_init`} - class={`carousel-container flex h-full w-full`} - > - <div - data-carousel-item={`cfg_init`} - class={`carousel-item flex flex-col w-full max-mobile_y:pt-28 pt-36 pb-4 justify-start items-center`} - > - <div - class={`flex flex-col flex-grow justify-between pt-4 pb-24 items-center`} - > - <div - class={`flex flex-row w-full justify-center items-center`} - > - <LogoCircle /> - </div> - <div - class={`flex flex-col w-full pt-4 px-12 gap-2 justify-start items-center`} - > - <div - class={`flex flex-row w-full justify-start items-center`} - > - <p - class={`font-sans font-[400] text-sm text-layer-0-glyph-label uppercase`} - > - {`${$ls(`common.configure`)}`} - </p> - </div> - <div - class={`flex flex-col w-full gap-2 justify-start items-center`} - > - <div - class={`flex flex-row w-full justify-start items-center`} - > - <p - class={`font-mono font-[400] text-[1.1rem] text-layer-0-glyph`} - > - {`${$ls(`app.cfg.init.greeting`)}`} - </p> - </div> - <div - class={`flex flex-row w-full justify-start items-center`} - > - <p - class={`font-mono font-[400] text-[1.1rem] text-layer-0-glyph`} - > - {`${$ls(`app.cfg.init.message`)}`} - </p> - </div> - </div> - </div> - </div> - </div> - <button - data-carousel-item={`cfg_init`} - class={`carousel-item flex flex-col w-full max-mobile_y:pt-32 pt-36 pb-4 justify-start items-center`} - on:click={async () => { - cgf_init_key_option = undefined; - }} - > - <div - class={`flex flex-col w-full gap-10 justify-start items-center`} - > - <div - class={`flex flex-row w-full justify-center items-center`} - > - <p - class={`font-mono font-[600] text-layer-0-glyph text-3xl`} - > - {`${$ls(`icu.configure_*`, { value: `${$ls(`common.device`)}` })}`} - </p> - </div> - <div - class={`flex flex-col w-full gap-5 justify-center items-center`} - > - <button - class={`flex flex-col h-touch_bold w-${$app_layout} justify-center items-center rounded-touch ${cgf_init_key_option === `key_gen` ? `layer-1-surface-apply-active layer-1-raise-apply layer-1-ring-apply` : `bg-layer-1-surface`} el-re`} - on:click|stopPropagation={async () => { - cgf_init_key_option = `key_gen`; - }} - > - <p - class={`font-sans font-[600] text-layer-0-glyph text-xl`} - > - {`${$ls(`icu.create_new_*`, { value: `${$ls(`common.keypair`)}`.toLowerCase() })}`} - </p> - </button> - <button - class={`flex flex-col h-touch_bold w-${$app_layout} justify-center items-center rounded-touch ${cgf_init_key_option === `kv_nostr_secretkey` ? `layer-1-surface-apply-active layer-1-raise-apply layer-1-ring-apply` : `bg-layer-1-surface`} el-re`} - on:click|stopPropagation={async () => { - cgf_init_key_option = `kv_nostr_secretkey`; - }} - > - <p - class={`font-sans font-[600] text-layer-0-glyph text-xl`} - > - {`${$ls(`icu.use_existing_*`, { - value: `${$ls(`common.keypair`)}`.toLowerCase(), - })}`} - </p> - </button> - </div> - </div> - </button> - <div - data-carousel-item={`cfg_init`} - class={`carousel-item flex flex-col w-full max-mobile_y:pt-32 pt-36 pb-4 justify-start items-center`} - > - <div - class={`flex flex-col w-full gap-8 justify-start items-center`} - > - <div - class={`flex flex-col w-full gap-6 justify-center items-center`} - > - {#if cfg_main_nostr_publickey} - <p - class={`font-mono font-[600] text-layer-0-glyph text-3xl`} - > - {`${$ls(`common.using_public_key`)}`} - </p> - <DisplayLine - basis={{ - classes: `w-${$app_layout}`, - label: { - classes: `pl-4 font-mono text-lg text-start truncate`, - value: - cfg_main_nostr_publickey_npub || - cfg_main_nostr_publickey, - }, - style: `guide`, - }} - /> - {:else} - <p - class={`font-mono font-[600] text-layer-0-glyph text-3xl`} - > - {`${$ls(`icu.add_existing_*`, { value: `${$ls(`common.key`)}`.toLowerCase() })}`} - </p> - <InputElement - basis={{ - classes: `h-entry_guide w-${$app_layout} bg-layer-1-surface rounded-touch font-mono text-lg placeholder:opacity-60 items-end text-center`, - id: fmt_id(`nostr_secretkey`), - sync: true, - placeholder: `${$ls(`icu.enter_*`, { value: `nostr nsec/hex` })}`, - field: { - charset: regex.profile_name_ch, - validate: regex.profile_name, - validate_keypress: true, - }, - callback_keydown: async ({ key, el }) => { - if (key === `Enter`) { - el.blur(); - await handle_continue(); - } - }, - }} - /> - {/if} - </div> - </div> - </div> - </div> - <div - class={`absolute max-mobile_base:bottom-0 bottom-8 left-0 flex flex-col w-full justify-center items-center`} - > - <ButtonLayoutPair - basis={{ - continue: { - disabled: $carousel_index === 1 && !cgf_init_key_option, - callback: async () => await handle_continue(), - }, - back: { - visible: $carousel_index > 0, - callback: async () => await handle_back(), - }, - }} - /> - </div> - </div> - <div - data-view={`cfg_main`} - class={`hidden flex flex-col h-full w-full max-mobile_base:pt-12 pt-16 justify-start items-center`} - > - <div - data-carousel-container={`cfg_main`} - class={`carousel-container flex h-full w-full`} - > - <div - data-carousel-item={`cfg_main`} - class={`carousel-item flex flex-col w-full max-mobile_y:pt-32 pt-36 pb-4 justify-start items-center`} - > - <div - class={`flex flex-col w-full gap-8 justify-start items-center`} - > - <div - class={`flex flex-col w-full gap-6 justify-center items-center`} - > - <p - class={`font-mono font-[600] text-layer-0-glyph text-3xl`} - > - {`${$ls(`icu.add_*`, { value: `${$ls(`common.profile`)}` })}`} - </p> - <EntryLine - basis={{ - loading: cfg_main_profilename_loading, - wrap: { - layer: 1, - classes: `w-${$app_layout}`, - style: `guide`, - }, - el: { - classes: `font-sans text-[1.25rem] text-center placeholder:opacity-60`, - id: fmt_id(`nostr_profilename`), - sync: true, - layer: 1, - placeholder: `${$ls(`icu.enter_*`, { value: `${$ls(`common.profile_name`)}`.toLowerCase() })}`, - field: { - charset: regex.profile_name_ch, - validate: regex.profile_name, - validate_keypress: true, - }, - callback: async ({ pass }) => { - cfg_main_profilename_valid = pass; - }, - callback_keydown: async ({ key, el }) => { - if (key === `Enter`) { - el.blur(); - await handle_continue(); - } - }, - }, - }} - /> - </div> - </div> - </div> - <button - data-carousel-item={`cfg_main`} - class={`carousel-item flex flex-col w-full max-mobile_y:pt-32 pt-36 pb-4 justify-start items-center`} - on:click={async () => { - cfg_main_opt_idx1 = undefined; - }} - > - <div - class={`flex flex-col w-full gap-10 justify-start items-center`} - > - <div - class={`flex flex-row w-full justify-center items-center`} - > - <p - class={`font-mono font-[600] text-layer-0-glyph text-3xl`} - > - {`${$ls(`common.setup_for_farmer`)}`} - </p> - </div> - <div - class={`flex flex-col w-full gap-5 justify-center items-center`} - > - <button - class={`flex flex-col h-touch_bold w-${$app_layout} justify-center items-center rounded-touch ${cfg_main_opt_idx1 === `farmer` ? `layer-1-surface-apply-active layer-1-raise-apply layer-1-ring-apply` : `bg-layer-1-surface`} el-re`} - on:click|stopPropagation={async () => { - cfg_main_opt_idx1 = `farmer`; - }} - > - <p - class={`font-sans font-[600] text-layer-0-glyph text-xl`} - > - {`${$ls(`common.yes`)}`} - </p> - </button> - <button - class={`flex flex-col h-touch_bold w-${$app_layout} justify-center items-center rounded-touch ${cfg_main_opt_idx1 === `personal` ? `layer-1-surface-apply-active layer-1-raise-apply layer-1-ring-apply` : `bg-layer-1-surface`} el-re`} - on:click|stopPropagation={async () => { - cfg_main_opt_idx1 = `personal`; - }} - > - <p - class={`font-sans font-[600] text-layer-0-glyph text-xl`} - > - {`${$ls(`common.no`)}`} - </p> - </button> - </div> - </div> - </button> - </div> - <div - class={`absolute top-16 left-4 flex flex-row gap-2 justify-start items-center ${view === `cfg_main` ? `fade-in-long` : ``}`} - > - <button - class={`group flex flex-row justify-center items-center`} - on:click={async () => { - await handle_back(); - }} - > - <Glyph - basis={{ - classes: `text-layer-1-glyph-shade group-active:opacity-60 transition-all`, - key: `caret-left`, - dim: `lg`, - weight: `bold`, - }} - /> - <p - class={`font-sans font-[400] text-layer-0-glyph text-lg capitalize group-active:opacity-60 transition-all`} - > - {`${$ls(`icu.go_*`, { value: `${$ls(`common.back`)}` })}`} - </p> - </button> - </div> - <div - class={`absolute max-mobile_base:bottom-0 bottom-8 left-0 flex flex-col w-full justify-center items-center ${view === `cfg_main` ? `fade-in-long` : ``}`} - > - <ButtonLayoutPair - basis={{ - continue: { - disabled: - ($carousel_index === 0 && - !cfg_main_profilename_valid) || - ($carousel_index === 1 && !cfg_main_opt_idx1), - callback: async () => await handle_continue(), - }, - back: { - visible: true, - label: - $carousel_index === 0 - ? `${$ls(`common.skip`)}` - : `${$ls(`common.back`)}`, - callback: async () => { - if ($carousel_index === 0) { - const confirm = await dialog.confirm({ - message: `${$ls(`app.cfg.init.notification.no_profile_name`)}`, - cancel_label: `${$ls(`icu.add_*`, { value: `${$ls(`common.profile`)}` })}`, - ok_label: `${$ls(`common.continue`)}`, - }); - if (confirm === false) { - el_id(fmt_id(`nostr_profilename`))?.focus(); - return; - } - carousel_inc(view); - return; - } - await handle_back(); - }, - }, - }} - /> - </div> - </div> - <div - data-view={`eula`} - class={`hidden flex flex-col h-full w-full max-mobile_base:pt-12 pt-12 justify-start items-center`} - > - <div - data-carousel-container={`eula`} - class={`carousel-container flex h-full w-full rounded-2xl scroll-hide`} - > - <div - data-carousel-item={`eula`} - class={`carousel-item flex flex-col w-full max-mobile_base:pt-16 justify-start items-center`} - > - <div - class={`flex flex-col w-full px-4 pb-2 justify-start items-center ${view === `eula` ? `fade-in-long` : ``} overflow-hidden`} - > - <div - class={`flex flex-col w-full px-4 gap-4 justify-start items-center`} - > - <div - class={`flex flex-row w-full justify-center items-center`} - > - <p - class={`font-mono font-[600] text-layer-0-glyph text-2xl`} - > - {`${$ls(`eula.title`)}`} - </p> - </div> - <div - bind:this={el_eula} - class={`flex flex-col h-[34rem] w-full gap-6 justify-start items-center overflow-y-scroll scroll-hide`} - > - <div - class={`flex flex-col w-full gap-2 justify-start items-start`} - > - <p - class={`font-mono font-[600] text-layer-0-glyph`} - > - {`**${$ls(`eula.introduction.title`)}**`} - </p> - <p - class={`font-mono font-[500] text-layer-0-glyph text-sm text-justify break-word`} - > - {`${$ls(`eula.introduction.body`)}`} - </p> - </div> - <div - class={`flex flex-col w-full gap-2 justify-start items-start`} - > - <p - class={`font-mono font-[600] text-layer-0-glyph`} - > - {`**${$ls(`eula.prohibited_content.title`)}**`} - </p> - <p - class={`font-mono font-[500] text-sm text-layer-0-glyph text-justify break-word`} - > - {`${$ls(`eula.prohibited_content.body_0_title`)}`} - </p> - <div - class={`flex flex-col w-full justify-start items-start`} - > - {#each [0, 1, 2, 3, 4, 5] as li} - <div - class={`flex flex-row w-full justify-start items-center`} - > - <div - class={`flex flex-row h-full w-8 justify-start items-start`} - > - <p - class={` font-mono font-[500] text-sm text-layer-0-glyph text-justify break-word`} - > - {`*`} - </p> - </div> - <div - class={`flex flex-row h-full w-full justify-start items-start`} - > - <p - class={`col-span-10 font-mono font-[500] text-sm text-layer-0-glyph text-justify break-word`} - > - {`${$ls(`eula.prohibited_content.body_li_0_${li}`)}`} - </p> - </div> - </div> - {/each} - </div> - </div> - <div - class={`flex flex-col w-full gap-2 justify-start items-start`} - > - <p - class={`font-mono font-[600] text-layer-0-glyph`} - > - {`**${$ls(`eula.prohibited_conduct.title`)}**`} - </p> - <div - class={`flex flex-col w-full justify-start items-start`} - > - {#each [0, 1, 2, 3] as li} - <div - class={`flex flex-row w-full justify-start items-center`} - > - <div - class={`flex flex-row h-full w-8 justify-start items-start`} - > - <p - class={` font-mono font-[500] text-sm text-layer-0-glyph text-justify break-word`} - > - {`*`} - </p> - </div> - <div - class={`flex flex-row h-full w-full justify-start items-start`} - > - <p - class={`col-span-10 font-mono font-[500] text-sm text-layer-0-glyph text-justify break-word`} - > - {`${$ls(`eula.prohibited_conduct.body_li_0_${li}`)}`} - </p> - </div> - </div> - {/each} - </div> - </div> - <div - class={`flex flex-col w-full gap-2 justify-start items-start`} - > - <p - class={`font-mono font-[600] text-layer-0-glyph`} - > - {`**${$ls(`eula.consequences_of_violation.title`)}**`} - </p> - <p - class={`font-mono font-[500] text-layer-0-glyph text-sm text-justify break-word`} - > - {`${$ls(`eula.consequences_of_violation.body`)}`} - </p> - </div> - <div - class={`flex flex-col w-full gap-2 justify-start items-start`} - > - <p - class={`font-mono font-[600] text-layer-0-glyph`} - > - {`**${$ls(`eula.disclaimer.title`)}**`} - </p> - <p - class={`font-mono font-[500] text-layer-0-glyph text-sm text-justify break-word`} - > - {`${$ls(`eula.disclaimer.body`)}`} - </p> - </div> - <div - class={`flex flex-col w-full gap-2 justify-start items-start`} - > - <p - class={`font-mono font-[600] text-layer-0-glyph`} - > - {`**${$ls(`eula.changes.title`)}**`} - </p> - <p - class={`font-mono font-[500] text-layer-0-glyph text-sm text-justify break-word`} - > - {`${$ls(`eula.changes.body`)}`} - </p> - </div> - <div - class={`flex flex-col w-full gap-2 justify-start items-start`} - > - <p - class={`font-mono font-[600] text-layer-0-glyph`} - > - {`**${$ls(`eula.contact.title`)}**`} - </p> - <p - class={`font-mono font-[500] text-layer-0-glyph text-sm text-justify break-word`} - > - {`${$ls(`eula.contact.body`)}`} - </p> - </div> - <div - class={`flex flex-col w-full gap-2 justify-start items-start`} - > - <p - class={`font-mono font-[600] text-layer-0-glyph`} - > - {`**${$ls(`eula.acceptance_of_terms.title`)}**`} - </p> - <p - class={`font-mono font-[500] text-layer-0-glyph text-sm text-justify break-word`} - > - {`${$ls(`eula.acceptance_of_terms.body`)}`} - </p> - </div> - </div> - </div> - <div - class={`flex flex-row w-full pt-8 justify-center items-center`} - > - <button - class={`group flex flex-row basis-1/2 gap-4 justify-center items-center`} - on:click={async () => { - const confirm = await dialog.confirm({ - message: `${$ls(`eula.error.required`)}`, - cancel_label: `${$ls(`common.quit`)}`, - }); - if (confirm === false) location.reload(); //@todo - }} - > - <p - class={`font-mono font-[400] text-sm text-layer-0-glyph/60 group-active:text-layer-0-glyph transition-all`} - > - {`-`} - </p> - <p - class={`font-mono font-[400] text-sm text-layer-0-glyph/60 group-active:text-layer-0-glyph transition-all`} - > - {`${`${$ls(`common.disagree`)}`}`} - </p> - <p - class={`font-mono font-[400] text-sm text-layer-0-glyph/60 group-active:text-layer-0-glyph transition-all`} - > - {`-`} - </p> - </button> - <button - class={`relative group flex flex-row basis-1/2 gap-4 justify-center items-center ${el_eula_scrolled ? `` : `opacity-40`}`} - on:click={async () => { - if (el_eula_scrolled) await submit(); - }} - > - <p - class={`font-mono font-[400] text-sm text-layer-0-glyph-hl group-active:text-layer-0-glyph-hl/80 transition-all`} - > - {`-`} - </p> - <p - class={`font-mono font-[400] text-sm text-layer-0-glyph-hl group-active:text-layer-0-glyph-hl/80 transition-all`} - > - {`${`${$ls(`common.agree`)}`}`} - </p> - <p - class={`font-mono font-[400] text-sm text-layer-0-glyph-hl group-active:text-layer-0-glyph-hl/80 transition-all`} - > - {`- `} - </p> - {#if loading_submit} - <div - class={`absolute right-2 flex flex-row justify-start items-center`} - > - <Loading basis={{ dim: `xs` }} /> - </div> - {/if} - </button> - </div> - </div> - </div> - </div> - </div> -</LayoutView> diff --git a/src/routes/+error.svelte b/src/routes/+error.svelte @@ -1,29 +0,0 @@ -<script lang="ts"> - import { page } from "$app/stores"; - import { Glyph, LayoutArea, Nav, ls } from "@radroots/svelte-lib"; -</script> - -<LayoutArea> - <div class={`flex flex-col h-full w-full justify-center items-center`}> - <div class={`flex flex-col justify-start items-center`}> - <Glyph - basis={{ - classes: `text-layer-1-glyph-shade`, - key: `subtitles-slash`, - dim: `xl`, - }} - /> - <p class={`font-sans font-[400] text-layer-0-glyph`}> - {`${$ls(`error.client.page.status.${$page.status}`)}`} - </p> - </div> - </div> -</LayoutArea> -<Nav - basis={{ - prev: { - label: `${$ls(`common.home`)}`, - route: `/`, - }, - }} -/> diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte @@ -1,123 +0,0 @@ -<script lang="ts"> - import { device, dialog, http, logger, os } from "$lib/client"; - import { cfg } from "$lib/conf"; - import { kv_init_app } from "$lib/util/kv"; - import type { - IClientDeviceMetadata, - IClientUnlisten, - } from "@radroots/client"; - import { - app_db, - app_geoc, - app_loading, - app_notify, - app_splash, - app_th, - app_thc, - catch_err, - Controls, - CssStatic, - CssStyles, - LayoutWindow, - LoadingView, - locale, - route, - sleep, - SplashScreen, - theme_set, - type NavigationRoute, - } from "@radroots/svelte-lib"; - import { parse_color_mode, parse_theme_key } from "@radroots/theme"; - import "css-paint-polyfill"; - import { onDestroy, onMount } from "svelte"; - import "../app.css"; - - let route_render: NavigationRoute | undefined = undefined; - let log_unlisten: IClientUnlisten | undefined = undefined; - - onMount(async () => { - try { - await init_app(); - } catch (e) { - } finally { - } - }); - - onDestroy(async () => { - try { - route_render = undefined; - if (log_unlisten) log_unlisten(); - } catch (e) { - } finally { - } - }); - - app_thc.subscribe((_app_thc) => { - const color_mode = parse_color_mode(_app_thc); - theme_set(parse_theme_key($app_th), color_mode); - }); - - app_th.subscribe((_app_th) => { - const color_mode = parse_color_mode($app_thc); - theme_set(parse_theme_key(_app_th), color_mode); - }); - - app_db.subscribe((_app_db) => { - if (!_app_db) return; - console.log(`(app_db) success`); - }); - - app_geoc.subscribe((_app_geoc) => { - if (!_app_geoc) return; - console.log(`(app_geoc) success`); - }); - - app_notify.subscribe(async (_app_notify) => { - if (!_app_notify) { - return; - } - route(`/`); - await sleep(cfg.delay.notify); - dialog.alert(_app_notify); - app_notify.set(``); - }); - - const init_app = async (): Promise<void> => { - try { - if (`paintWorklet` in CSS) - (CSS as any).paintWorklet.addModule(`/squircle.min.js`); - const metadata: IClientDeviceMetadata = { - version: cfg.app.version, - platform: os.platform(), - locale: $locale, - }; - await device.init(metadata); - await http.init(metadata); - log_unlisten = await logger.init(); - await kv_init_app(); - } catch (e) { - await catch_err(e, `init_app`); - } - }; -</script> - -<svelte:head> - <meta name="description" content={cfg.app.description} /> - <meta property="og:title" content={cfg.app.title} /> - <meta property="og:description" content={cfg.app.description} /> -</svelte:head> - -<LayoutWindow> - {#if $app_splash} - <SplashScreen /> - {:else if $app_loading} - <LoadingView /> - {/if} - <slot /> -</LayoutWindow> -<Controls /> -<CssStatic /> -<CssStyles /> -<div - class="hidden h-entry_guide h-entry_line h-[calc(100vh-12%)] h-trellis_centered_mobile_base h-trellis_centered_mobile_y bg-white/40 backdrop-blur-lg backdrop-blur-sm" -/> diff --git a/src/routes/+layout.ts b/src/routes/+layout.ts @@ -1,22 +0,0 @@ -import { catch_err, default_locale, load_translations, locales, translations_loading } from '@radroots/svelte-lib'; -import type { LayoutLoad, LayoutLoadEvent } from './$types'; - -export const prerender = true; -export const ssr = false; -export const trailingSlash = 'always'; - -export const load: LayoutLoad = async ({ url }: LayoutLoadEvent) => { - try { - const { language: nav_locale } = navigator; - let locale = default_locale.toString(); - const locales_avail = locales.get(); - if (locales_avail.some(i => i === nav_locale.toLowerCase())) locale = navigator.language; - else if (locales_avail.some(i => i === nav_locale.slice(0, 2).toLowerCase())) locale = nav_locale.slice(0, 2); - await load_translations(locale.toLowerCase(), url.pathname); - await translations_loading.toPromise(); - } catch (e) { - await catch_err(e, `(root)load`) - } finally { - return {}; - }; -}; diff --git a/static/geonames b/static/geonames @@ -1 +0,0 @@ -Subproject commit 49ba0e0ee53d69636c8d991fa1448890405b4bb0 diff --git a/static/keyva.min.js b/static/keyva.min.js @@ -1 +0,0 @@ -"use strict"; class Keyva { static unbound = IDBKeyRange.lowerBound(Number.MIN_SAFE_INTEGER); static prefix(prefix) { return IDBKeyRange.bound(prefix, prefix + "￿") } static async each() { const databases = await indexedDB.databases(); return databases.map((db => db.name)).filter((s => !!s && s.startsWith(this.kvPrefix))) } static async delete(...names) { names = names.length ? names.map((n => n.startsWith(this.kvPrefix) ? n : this.kvPrefix + n)) : await this.each(); Promise.all(names.map((n => this.asPromise(indexedDB.deleteDatabase(n))))) } static kvPrefix = "-radroots-"; constructor(options = {}) { const idx = options.indexes || []; this.indexes = (Array.isArray(idx) ? idx : [idx]).sort(); this.name = Keyva.kvPrefix + (options.name || "") } indexes; name; async get(k) { const store = await this.getStore("readonly"); return Array.isArray(k) ? Promise.all(k.map((key => Keyva.asPromise(store.get(key))))) : Keyva.asPromise(store.get(k)) } async each(options = {}, only) { const store = await this.getStore("readonly"); const target = options.index ? store.index(options.index) : store; const limit = options.limit; const range = options.range; if (only === "keys") return Keyva.asPromise(target.getAllKeys(range, limit)); if (only === "values") return Keyva.asPromise(target.getAll(range, limit)); let keys = []; let values = []; await Promise.allSettled([new Promise((async r => { const results = await Keyva.asPromise(target.getAllKeys(range, limit)); keys.push(...results); r() })), new Promise((async r => { const results = await Keyva.asPromise(target.getAll(range, limit)); values.push(...results); r() }))]); const tuples = []; for (let i = -1; ++i < keys.length;)tuples.push([keys[i], values[i]]); return tuples } async set(a, b) { const store = await this.getStore("readwrite"); if (Array.isArray(a)) { for (const entry of a) store.put(entry[1], entry[0]); return Keyva.asPromise(store.transaction) } store.put(b, a); return Keyva.asPromise(store.transaction) } async delete(arg) { const store = await this.getStore("readwrite"); arg ??= Keyva.unbound; if (Array.isArray(arg)) { for (const key of arg) store.delete(key) } else store.delete(arg); return Keyva.asPromise(store.transaction) } async getStore(mode) { const db = await this.getDatabase(); return db.transaction(this.name, mode).objectStore(this.name) } async getDatabase() { if (!this.database) { await this.maybeFixSafari(); let quit = false; let version; let indexNamesAdded = []; let indexNamesRemoved = []; for (; ;) { const request = indexedDB.open(this.name, version); request.onupgradeneeded = () => { const db = request.result; const tx = request.transaction; const store = tx.objectStoreNames.contains(this.name) ? tx.objectStore(this.name) : db.createObjectStore(this.name); for (const index of indexNamesAdded) store.createIndex(index, index); for (const index of indexNamesRemoved) store.deleteIndex(index) }; this.database = await Keyva.asPromise(request); if (quit) break; const tx = this.database.transaction(this.name, "readonly"); const store = tx.objectStore(this.name); const indexNames = Array.from(store.indexNames).sort(); tx.abort(); indexNamesAdded = this.indexes.filter((n => !indexNames.includes(n))); indexNamesRemoved = indexNames.filter((n => !this.indexes.includes(n))); if (indexNamesAdded.length + indexNamesRemoved.length === 0) break; quit = true; this.database.close(); version = this.database.version + 1 } } return this.database } database = null; async maybeFixSafari() { if (!/Version\/14\.\d*\s*Safari\//.test(navigator.userAgent)) return; let id = 0; return new Promise((resolve => { const hit = () => indexedDB.databases().finally(resolve); id = setInterval(hit, 50); hit() })).finally((() => clearInterval(id))) } static asPromise(request) { return new Promise(((resolve, reject) => { request.oncomplete = request.onsuccess = () => resolve(request.result); request.onabort = request.onerror = () => reject(request.error) })) } } if (typeof module === "object") Object.assign(module.exports, { Keyva: Keyva }); diff --git a/static/phosphor-icons b/static/phosphor-icons @@ -1 +0,0 @@ -Subproject commit d763205d561e4eff8eeae253ece6d46ef651bf79 diff --git a/static/splashscreen.html b/static/splashscreen.html @@ -1,26 +0,0 @@ -<!DOCTYPE html> -<html> - <head> - <title>Radroots</title> - <style> - body { - margin: 0; - } - - .splashscreen-image { - background-color: #444; - background-image: url('logo.png'); - background-position: center; - background-repeat: no-repeat; - background-size: 50%; - width: 100vw !important; - height: 100vh !important; - overflow: hidden; - } - </style> - </head> - - <body> - <div class="splashscreen-image" /> - </body> -</html> -\ No newline at end of file diff --git a/static/sql-wasm.wasm b/static/sql-wasm.wasm Binary files differ. diff --git a/static/squircle.min.js b/static/squircle.min.js @@ -1 +0,0 @@ -const drawSquircle = (ctx, geom, radii, smooth, lineWidth, color) => { const defaultFill = color; const lineWidthOffset = lineWidth / 2; ctx.beginPath(); ctx.lineTo(radii[0], lineWidthOffset); ctx.lineTo(geom.width - radii[1], lineWidthOffset); ctx.bezierCurveTo(geom.width - radii[1] / smooth, lineWidthOffset, geom.width - lineWidthOffset, radii[1] / smooth, geom.width - lineWidthOffset, radii[1]); ctx.lineTo(geom.width - lineWidthOffset, geom.height - radii[2]); ctx.bezierCurveTo(geom.width - lineWidthOffset, geom.height - radii[2] / smooth, geom.width - radii[2] / smooth, geom.height - lineWidthOffset, geom.width - radii[2], geom.height - lineWidthOffset); ctx.lineTo(radii[3], geom.height - lineWidthOffset); ctx.bezierCurveTo(radii[3] / smooth, geom.height - lineWidthOffset, lineWidthOffset, geom.height - radii[3] / smooth, lineWidthOffset, geom.height - radii[3]); ctx.lineTo(lineWidthOffset, radii[0]); ctx.bezierCurveTo(lineWidthOffset, radii[0] / smooth, radii[0] / smooth, lineWidthOffset, radii[0], lineWidthOffset); ctx.closePath(); if (lineWidth) { ctx.strokeStyle = defaultFill; ctx.lineWidth = lineWidth; ctx.stroke() } else { ctx.fillStyle = defaultFill; ctx.fill() } }; class SquircleClass { static get contextOptions() { return { alpha: true } } static get inputProperties() { return ["--squircle-radius", "--squircle-radius-top-left", "--squircle-radius-top-right", "--squircle-radius-bottom-right", "--squircle-radius-bottom-left", "--squircle-smooth", "--squircle-outline", "--squircle-fill"] } paint(ctx, geom, properties) { const smoothRatio = 10; const distanceRatio = 1.8; const squircleSmooth = parseFloat(properties.get("--squircle-smooth") * smoothRatio); const individualRadiiProps = SquircleClass.inputProperties.slice(1, 5); let squircleRadii = individualRadiiProps.map(prop => { const value = properties.get(prop); return value ? parseInt(value, 10) * distanceRatio : NaN }); let shorthand_R; if (squircleRadii.some(isNaN)) { const radiusRegex = /([0-9]+[a-z%]*)/g; const radius_shorthand = properties.get("--squircle-radius").toString(); const matches = radius_shorthand.match(radiusRegex); if (matches) { shorthand_R = matches.map(val => parseInt(val, 10) * distanceRatio); while (shorthand_R.length < 4) { if (shorthand_R.length === 1) { shorthand_R.push(shorthand_R[0]) } else if (shorthand_R.length === 2) { shorthand_R = [shorthand_R[0], shorthand_R[1], shorthand_R[0], shorthand_R[1]] } else if (shorthand_R.length === 3) { shorthand_R = [shorthand_R[0], shorthand_R[1], shorthand_R[2], shorthand_R[1]] } } } else { const defaultRadius = squircleRadii.every(isNaN) ? 8 * distanceRatio : 0; shorthand_R = [defaultRadius, defaultRadius, defaultRadius, defaultRadius] } } squircleRadii = squircleRadii.map((val, i) => isNaN(val) ? shorthand_R[i] : val); const squrcleOutline = parseFloat(properties.get("--squircle-outline"), 10); const squrcleColor = properties.get("--squircle-fill").toString(); const isSmooth = () => { if (typeof properties.get("--squircle-smooth")[0] !== "undefined") { if (squircleSmooth === 0) { return 1 } return squircleSmooth } else { return 10 } }; const isOutline = () => { if (squrcleOutline) { return squrcleOutline } else { return 0 } }; const isColor = () => { if (squrcleColor) { return squrcleColor } else { return "#f45" } }; const maxRadius = Math.max(...squircleRadii); if (maxRadius < geom.width / 2 && maxRadius < geom.height / 2) { drawSquircle(ctx, geom, squircleRadii, isSmooth(), isOutline(), isColor()) } else { const minRadius = Math.min(geom.width / 2, geom.height / 2); drawSquircle(ctx, geom, squircleRadii.map(() => minRadius), isSmooth(), isOutline(), isColor()) } } } if (typeof registerPaint !== "undefined") { registerPaint("squircle", SquircleClass) } -\ No newline at end of file diff --git a/static/stylesheets b/static/stylesheets @@ -1 +0,0 @@ -Subproject commit 4187cb5a9dde2f03416834d7dc4e627fd638f98e diff --git a/static/webfonts b/static/webfonts @@ -1 +0,0 @@ -Subproject commit e03efc1901fa3cea33c32bc686ae64ade99eb6bc diff --git a/tailwind.config.ts b/tailwind.config.ts @@ -1,192 +0,0 @@ -import { theme_colors, themes } from "@radroots/theme"; -import { wind } from "@radroots/utils"; -import aspect_ratio from "@tailwindcss/aspect-ratio"; -import daisyui from "daisyui"; -import type { Config } from "tailwindcss"; -import heropatterns from "tailwindcss-hero-patterns"; -import tailwind_default from "tailwindcss/defaultTheme"; -const { fontFamily: tw_font } = tailwind_default; - -const heights_form = { - line: `46px`, -}; - -const heights_responsive = { - tabs_mobile_base: `64px`, - tabs_mobile_y: `86px`, - nav_mobile_base: `71px`, - nav_mobile_y: `100px`, - view_mobile_base: `31rem`, - view_mobile_y: `42rem`, - view_offset_mobile_base: `1rem`, - view_offset_mobile_y: `2rem`, - trellis_centered_mobile_base: `32rem`, - trellis_centered_mobile_y: `35rem` -}; - -const heights = { - ...heights_responsive, - entry_bold: `3.75rem`, - entry_guide: `3.6rem`, - entry_line: `46px`, - touch_bold: `4.25rem`, - touch_guide: `3.4rem`, - line: `46px`, - envelope_top: `56px`, - toast_min: `56px`, - envelope_button: `50px`, - line_label: `1.75rem`, -}; - - -const widths_responsive = { - mobile_base: `335px`, - mobile_y: `345px` -}; - -const widths = { - ...widths_responsive, - line: `320px`, - trellis_line: `349px`, - trellis_value: `180px`, - trellis_display: `286px`, -}; - -const dimensions_responsive = { - map_offset_top_mobile_base: `32px`, - map_offset_top_mobile_y: `64px`, -}; - -const dimensions = { - ...dimensions_responsive, - glyph_btn_sm: `28px`, - map_circle: `16px`, - map_circle_inner: `10px`, -}; - -const config: Config = { - content: [ - `src/**/*.{ts,svelte}`, - `../../packages/svelte-lib/src/**/*.{ts,svelte}`, - ], - theme: { - screens: { - mobile_y: { raw: `(orientation: portrait) and (min-height: ${wind.app.layout.mobile_y}px)` }, - mobile_base: { raw: `(orientation: portrait) and (max-height: ${wind.app.layout.mobile_base}px)` }, - }, - aspectRatio: { - auto: `auto`, - square: `1 / 1`, - video: `16 / 9`, - 1: `1`, - 2: `2`, - 3: `3`, - 4: `4`, - 5: `5`, - 6: `6`, - 7: `7`, - 8: `8`, - 9: `9`, - 10: `10`, - 11: `11`, - 12: `12`, - 13: `13`, - 14: `14`, - 15: `15`, - 16: `16`, - }, - extend: { - colors: { - ...theme_colors, - 'chart-green': `var(--chart-color-green)`, - 'chart-red': `var(--chart-color-red)`, - }, - fontFamily: { - sans: [`SF Pro Rounded`, ...tw_font.sans], - sansd: [`SF Pro Display`], - serif: [...tw_font.serif], - mono: [...tw_font.mono], - apercu: [`Apercu Mono Pro`], - magda: [`Magda Text`], - lust: [`Lust`], - circ: [`Circular`], - arch: [`Archivo`], - sg: [`Space Grotesk`], - sp: [`Spartan`], - of: [`Outfit`] - }, - fontSize: { - guide: [`1.2rem`, { lineHeight: `1.2rem` }], - line_label: [`1.3rem`, { lineHeight: `1.3rem` }], - trellis_ti: [`0.8rem`, { lineHeight: `1rem`, fontWeight: 300 }], - line_d: [`1.05rem`, { lineHeight: `1.33rem`, fontWeight: 400 }], - nav_prev: [`1.09rem`, { lineHeight: `1.33rem`, fontWeight: 400 }], - nav_curr: [`1.09rem`, { lineHeight: `1.33rem`, fontWeight: 500 }], - env_ti: [`1.05rem`, { lineHeight: `1.75rem`, fontWeight: 600 }], - env_btnc: [`1.1rem`, { lineHeight: `1.75rem`, fontWeight: 600 }], - env_btnl: [`1.1rem`, { lineHeight: `1.75rem`, fontWeight: 500 }], - }, - gridTemplateColumns: { - '16': `repeat(16, minmax(0, 1fr))`, - '24': `repeat(24, minmax(0, 1fr))`, - }, - height: { - ...heights, - ...dimensions, - ...Object.fromEntries(Object.entries(heights_form).map(([k, v]) => [`form_${k}`, v])), - }, - width: { - ...widths, - ...dimensions, - }, - minHeight: { - ...heights - }, - minWidth: { - ...widths - }, - maxHeight: { - ...heights - }, - maxWidth: { - ...widths - }, - padding: { - ...Object.fromEntries(Object.entries(heights).map(([k, v]) => [`h_${k}`, v])), - ...Object.fromEntries(Object.entries(widths).map(([k, v]) => [`w_${k}`, v])), - ...Object.fromEntries(Object.entries(dimensions).map(([k, v]) => [`dim_${k}`, v])), - }, - translate: { - ...Object.fromEntries(Object.entries(heights).map(([k, v]) => [`h_${k}`, v])), - ...Object.fromEntries(Object.entries(widths).map(([k, v]) => [`w_${k}`, v])), - }, - spacing: { - ...Object.fromEntries(Object.entries(dimensions).map(([k, v]) => [`dim_${k}`, v])), - }, - borderWidth: { - line: `1px`, - edge: `2px` - }, - borderRadius: { - input_form: `8px`, - entry: `1.05rem`, - touch: `1.25rem` - }, - } - }, - plugins: [ - daisyui, - aspect_ratio, - heropatterns - ], - daisyui: { - themes: [ - themes.theme_os_dark, - themes.theme_os_light, - themes.theme_garden_light, - themes.theme_garden_dark - ], - }, -}; - -export default config; diff --git a/turbo.json b/turbo.json @@ -0,0 +1,35 @@ +{ + "$schema": "https://turbo.build/schema.json", + "ui": "tui", + "globalEnv": [], + "tasks": { + "build": { + "env": [], + "dependsOn": [ + "^build" + ], + "inputs": [ + "$TURBO_DEFAULT$", + ".env*" + ], + "outputs": [ + ".svelte-kit/**", + "dist" + ] + }, + "lint": { + "dependsOn": [ + "^lint" + ] + }, + "check-types": { + "dependsOn": [ + "^check-types" + ] + }, + "dev": { + "cache": false, + "persistent": true + } + } +} +\ No newline at end of file