0.7.27 +Refactor
14
CHANGELOG.md
@@ -9,13 +9,13 @@
|
|||||||
0.3.1 - Ajout des helpers subscribe/unsubscribe à WsClient
|
0.3.1 - Ajout des helpers subscribe/unsubscribe à WsClient
|
||||||
0.3.2 - Ajout des helpers typed et du parsing typed basé sur solana-rpc-client-api
|
0.3.2 - Ajout des helpers typed et du parsing typed basé sur solana-rpc-client-api
|
||||||
0.3.3 - Ajout du suffixe _raw aux helpers raw pour distinguer typed et raw
|
0.3.3 - Ajout du suffixe _raw aux helpers raw pour distinguer typed et raw
|
||||||
0.3.4 - Ajout de la fenêtre Demo Ws dans kb_app pour tester les souscriptions live
|
0.3.4 - Ajout de la fenêtre Demo Ws dans kb_demo_app pour tester les souscriptions live
|
||||||
0.3.5 - Stabilisation de Demo Ws, lecture correcte des endpoints activés depuis la config, limitation/throttling de l’affichage UI sous fort débit
|
0.3.5 - Stabilisation de Demo Ws, lecture correcte des endpoints activés depuis la config, limitation/throttling de l’affichage UI sous fort débit
|
||||||
0.4.0 - Socle HttpClient générique async clonable, JSON-RPC HTTP 2.0, résolution d’URL avec api_key_env_var, limiteur local req/sec + burst, helpers initiaux getHealth/getVersion/getSlot
|
0.4.0 - Socle HttpClient générique async clonable, JSON-RPC HTTP 2.0, résolution d’URL avec api_key_env_var, limiteur local req/sec + burst, helpers initiaux getHealth/getVersion/getSlot
|
||||||
0.4.1 - Ajout des premiers helpers HTTP Solana haut niveau, dans la continuité de l’API du client WebSocket
|
0.4.1 - Ajout des premiers helpers HTTP Solana haut niveau, dans la continuité de l’API du client WebSocket
|
||||||
0.4.2 - Préparation de la politique HTTP avancée : états de pause avant envoi, quotas par famille de méthodes et futur pool d’endpoints
|
0.4.2 - Préparation de la politique HTTP avancée : états de pause avant envoi, quotas par famille de méthodes et futur pool d’endpoints
|
||||||
0.4.3 - Pool d’endpoints HTTP
|
0.4.3 - Pool d’endpoints HTTP
|
||||||
0.4.4 - Ajout de la fenêtre Demo Http dans kb_app, exécution manuelle des méthodes HTTP via le pool, snapshot des endpoints et amélioration des presets UI
|
0.4.4 - Ajout de la fenêtre Demo Http dans kb_demo_app, exécution manuelle des méthodes HTTP via le pool, snapshot des endpoints et amélioration des presets UI
|
||||||
0.5.0 - Début du socle SQLite : configuration database, ouverture/validation de la base et premières briques de persistance
|
0.5.0 - Début du socle SQLite : configuration database, ouverture/validation de la base et premières briques de persistance
|
||||||
0.5.1 - Ajout des premières tables métier SQLite pour les endpoints connus HTTP/WS et les événements runtime, avec séparation entities/dtos/queries/types
|
0.5.1 - Ajout des premières tables métier SQLite pour les endpoints connus HTTP/WS et les événements runtime, avec séparation entities/dtos/queries/types
|
||||||
0.5.2 - Ajout de la table des tokens observés, de leur statut local et des premières requêtes de persistance associées
|
0.5.2 - Ajout de la table des tokens observés, de leur statut local et des premières requêtes de persistance associées
|
||||||
@@ -29,7 +29,7 @@
|
|||||||
0.6.3 - Enrichissement des notifications WebSocket utiles : extraction améliorée de pubkey, signature, owner, parsed account type et slot pour account/logs/signature notifications
|
0.6.3 - Enrichissement des notifications WebSocket utiles : extraction améliorée de pubkey, signature, owner, parsed account type et slot pour account/logs/signature notifications
|
||||||
0.6.4 - Premières règles de détection technique pour candidats pools/listings depuis programNotification en s’appuyant sur les DEX connus en base
|
0.6.4 - Premières règles de détection technique pour candidats pools/listings depuis programNotification en s’appuyant sur les DEX connus en base
|
||||||
0.6.5 - Ajout de ws_manager.rs pour l’orchestration multi-clients WebSocket, le bus d’événements unifié et le branchement centralisé du relais de détection
|
0.6.5 - Ajout de ws_manager.rs pour l’orchestration multi-clients WebSocket, le bus d’événements unifié et le branchement centralisé du relais de détection
|
||||||
0.6.6 - Ajout de la fenêtre Demo Ws Manager dans kb_app pour piloter plusieurs WsClient, visualiser le snapshot consolidé, tester le démarrage/arrêt par rôle et valider le flux unifié de WsEvent
|
0.6.6 - Ajout de la fenêtre Demo Ws Manager dans kb_demo_app pour piloter plusieurs WsClient, visualiser le snapshot consolidé, tester le démarrage/arrêt par rôle et valider le flux unifié de WsEvent
|
||||||
0.7.0 - Ajout du socle de résolution transactionnelle orientée DEX : relais WS vers file de résolution, récupération getTransaction via HttpEndpointPool et persistance des résolutions dans les observations/signaux
|
0.7.0 - Ajout du socle de résolution transactionnelle orientée DEX : relais WS vers file de résolution, récupération getTransaction via HttpEndpointPool et persistance des résolutions dans les observations/signaux
|
||||||
0.7.1 - Ajout du modèle transactionnel enrichi : tables slots/transactions/instructions, requêtes d’accès et projection structurée des transactions résolues
|
0.7.1 - Ajout du modèle transactionnel enrichi : tables slots/transactions/instructions, requêtes d’accès et projection structurée des transactions résolues
|
||||||
0.7.2 - Ajout du premier décodeur DEX spécifique Raydium AmmV4 / initialize2, persistance des événements DEX décodés et branchement automatique du décodage après résolution/projection transactionnelle
|
0.7.2 - Ajout du premier décodeur DEX spécifique Raydium AmmV4 / initialize2, persistance des événements DEX décodés et branchement automatique du décodage après résolution/projection transactionnelle
|
||||||
@@ -52,9 +52,9 @@
|
|||||||
0.7.19 - Ajout d’une première couche holdings observés avec agrégation par couple wallet/token et branchement automatique dans le pipeline de résolution transactionnelle
|
0.7.19 - Ajout d’une première couche holdings observés avec agrégation par couple wallet/token et branchement automatique dans le pipeline de résolution transactionnelle
|
||||||
0.7.20 - Ajout d’une première couche candles / OHLCV avec matérialisation en base des timeframes usuels et régénération à la demande pour un timeframe arbitraire depuis les trade events
|
0.7.20 - Ajout d’une première couche candles / OHLCV avec matérialisation en base des timeframes usuels et régénération à la demande pour un timeframe arbitraire depuis les trade events
|
||||||
0.7.21 - Ajout d’une première couche de signaux analytiques enrichis par paire avec persistance dédiée et détection de first trade, trade burst, buy/sell imbalance, price jump et volume spike
|
0.7.21 - Ajout d’une première couche de signaux analytiques enrichis par paire avec persistance dédiée et détection de first trade, trade burst, buy/sell imbalance, price jump et volume spike
|
||||||
0.7.22 - Ajout d’une première fenêtre `Demo Pipeline` dans `kb_app` pour l’inspection en lecture seule du pipeline `0.7.x`, avec recherche par signature, token mint, pair id ou pool address, affichage structuré des transactions résolues, événements DEX décodés, pools, paires, listings, launch origins, pool origins, wallets et holdings observés, trade events, pair metrics, candles et signaux analytiques déjà persistés, ainsi que conservation d’une instance partagée de la base SQLite pour éviter la réouverture et la réinitialisation du schéma à chaque commande UI
|
0.7.22 - Ajout d’une première fenêtre `Demo Pipeline` dans `kb_demo_app` pour l’inspection en lecture seule du pipeline `0.7.x`, avec recherche par signature, token mint, pair id ou pool address, affichage structuré des transactions résolues, événements DEX décodés, pools, paires, listings, launch origins, pool origins, wallets et holdings observés, trade events, pair metrics, candles et signaux analytiques déjà persistés, ainsi que conservation d’une instance partagée de la base SQLite pour éviter la réouverture et la réinitialisation du schéma à chaque commande UI
|
||||||
0.7.23 - Ajout du pilotage UI du backfill historique ciblé par `token mint` dans `kb_app`, avec saisie du rôle HTTP et des limites de signatures, affichage du résumé de backfill, réinspection automatique du token dans `Demo Pipeline` lorsque des objets persistés sont effectivement reconstruits, et gestion explicite du cas où le backfill réussit sans matérialiser de token exploitable dans la base locale
|
0.7.23 - Ajout du pilotage UI du backfill historique ciblé par `token mint` dans `kb_demo_app`, avec saisie du rôle HTTP et des limites de signatures, affichage du résumé de backfill, réinspection automatique du token dans `Demo Pipeline` lorsque des objets persistés sont effectivement reconstruits, et gestion explicite du cas où le backfill réussit sans matérialiser de token exploitable dans la base locale
|
||||||
0.7.24 - Ajout de l’affichage graphique des candles / OHLCV dans `kb_app` via `echarts`, avec sélection de paire et de timeframe, rendu chandelier + volume, et prise en charge des candles matérialisées ou régénérées à la demande depuis `Demo Pipeline`
|
0.7.24 - Ajout de l’affichage graphique des candles / OHLCV dans `kb_demo_app` via `echarts`, avec sélection de paire et de timeframe, rendu chandelier + volume, et prise en charge des candles matérialisées ou régénérées à la demande depuis `Demo Pipeline`
|
||||||
0.7.25 - Enrichissement metadata des tokens, avec résolution locale limitée à SOL / WSOL, résolution des autres mints via comptes on-chain, Token-2022, Metaplex ou payloads DEX, et conservation explicite des cas non résolus
|
0.7.25 - Enrichissement metadata des tokens, avec résolution locale limitée à SOL / WSOL, résolution des autres mints via comptes on-chain, Token-2022, Metaplex ou payloads DEX, et conservation explicite des cas non résolus
|
||||||
0.7.26 - Diagnostics locaux du pipeline persisté, correction de l’agrégation instruction-scoped des swaps Raydium, clarification des compteurs de replay/upsert, et validation qu’aucun trade candidate issu d’une transaction OK n’est perdu
|
0.7.26 - Diagnostics locaux du pipeline persisté, correction de l’agrégation instruction-scoped des swaps Raydium, clarification des compteurs de replay/upsert, et validation qu’aucun trade candidate issu d’une transaction OK n’est perdu
|
||||||
|
0.7.27 - Validation multi-DEX et non-régression du pipeline sur Pump.fun, PumpSwap, Raydium CPMM et Raydium CLMM, avec corpus de tests, diagnostics de référence et garanties sur les événements non pricés
|
||||||
|
|||||||
10
Cargo.toml
@@ -4,11 +4,11 @@
|
|||||||
resolver = "3"
|
resolver = "3"
|
||||||
members = [
|
members = [
|
||||||
"kb_lib",
|
"kb_lib",
|
||||||
"kb_app",
|
"kb_demo_app",
|
||||||
]
|
]
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "0.7.26"
|
version = "0.7.27"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
repository = "https://git.sasedev.com/Sasedev/khadhroony-bobobot"
|
repository = "https://git.sasedev.com/Sasedev/khadhroony-bobobot"
|
||||||
@@ -32,7 +32,7 @@ serde = { version = "^1.0", features = ["derive"] }
|
|||||||
serde_json = { version = "^1.0", features = [] }
|
serde_json = { version = "^1.0", features = [] }
|
||||||
solana-account-decoder-client-types = { version = ">=4.0.0-rc.0", features = ["zstd"] }
|
solana-account-decoder-client-types = { version = ">=4.0.0-rc.0", features = ["zstd"] }
|
||||||
solana-address-lookup-table-interface = { version = "^3.1", features = ["bincode", "serde"] }
|
solana-address-lookup-table-interface = { version = "^3.1", features = ["bincode", "serde"] }
|
||||||
solana-client = { version = ">=4.0.0-beta.7", features = [] }
|
solana-client = { version = ">=4.0.0-rc.0", features = [] }
|
||||||
solana-compute-budget-interface = { version = "^3.0", features = ["borsh", "serde"] }
|
solana-compute-budget-interface = { version = "^3.0", features = ["borsh", "serde"] }
|
||||||
solana-rpc-client-api = { version = ">=4.0.0-rc.0", features = [] }
|
solana-rpc-client-api = { version = ">=4.0.0-rc.0", features = [] }
|
||||||
solana-rpc-client-types = { version = ">=4.0.0-rc.0", features = [] }
|
solana-rpc-client-types = { version = ">=4.0.0-rc.0", features = [] }
|
||||||
@@ -45,7 +45,7 @@ spl-memo-interface = { version = "^2.0", features = [] }
|
|||||||
spl-token-interface = { version = "^2.0", features = [] }
|
spl-token-interface = { version = "^2.0", features = [] }
|
||||||
spl-token-2022-interface = { version = "^2.1", features = [] }
|
spl-token-2022-interface = { version = "^2.1", features = [] }
|
||||||
sqlx = { version = "^0.8", features = ["chrono", "uuid", "bigdecimal", "json", "sqlite", "runtime-tokio-rustls"] }
|
sqlx = { version = "^0.8", features = ["chrono", "uuid", "bigdecimal", "json", "sqlite", "runtime-tokio-rustls"] }
|
||||||
tauri = { version = "^2.10", features = ["default", "tray-icon"] }
|
tauri = { version = "^2.11", features = ["default", "tray-icon"] }
|
||||||
tauri-build = { version = "2", features = [] }
|
tauri-build = { version = "2", features = [] }
|
||||||
tauri-plugin-tracing = { version = "^0.3", default-features = false, features = [] }
|
tauri-plugin-tracing = { version = "^0.3", default-features = false, features = [] }
|
||||||
tempfile = { version = "^3", features = [] }
|
tempfile = { version = "^3", features = [] }
|
||||||
@@ -84,3 +84,5 @@ manual_ok_err = "allow"
|
|||||||
manual_unwrap_or = "allow"
|
manual_unwrap_or = "allow"
|
||||||
manual_map = "allow"
|
manual_map = "allow"
|
||||||
match_like_matches_macro = "allow"
|
match_like_matches_macro = "allow"
|
||||||
|
single_match = "allow"
|
||||||
|
manual_unwrap_or_default = "allow"
|
||||||
|
|||||||
12
README.md
@@ -35,7 +35,7 @@ La liste exacte pourra évoluer au fil du projet.
|
|||||||
Le workspace Rust est organisé autour de deux sous-crates principales :
|
Le workspace Rust est organisé autour de deux sous-crates principales :
|
||||||
|
|
||||||
- `kb_lib`
|
- `kb_lib`
|
||||||
- `kb_app`
|
- `kb_demo_app`
|
||||||
|
|
||||||
### `kb_lib`
|
### `kb_lib`
|
||||||
|
|
||||||
@@ -51,9 +51,9 @@ Le workspace Rust est organisé autour de deux sous-crates principales :
|
|||||||
- gestion des wallets,
|
- gestion des wallets,
|
||||||
- logique de détection et de filtrage.
|
- logique de détection et de filtrage.
|
||||||
|
|
||||||
### `kb_app`
|
### `kb_demo_app`
|
||||||
|
|
||||||
`kb_app` est l’application Tauri V2 avec frontend TypeScript.
|
`kb_demo_app` est l’application Demo Tauri V2 avec frontend TypeScript.
|
||||||
|
|
||||||
Son rôle est de :
|
Son rôle est de :
|
||||||
|
|
||||||
@@ -62,7 +62,7 @@ Son rôle est de :
|
|||||||
- déléguer la logique à `kb_lib`,
|
- déléguer la logique à `kb_lib`,
|
||||||
- afficher les états, logs et événements remontés par la bibliothèque.
|
- afficher les états, logs et événements remontés par la bibliothèque.
|
||||||
|
|
||||||
Le crate expose une bibliothèque interne `kb_app_lib` afin de limiter le couplage avec `main.rs` et de préparer une évolution mobile ultérieure.
|
Le crate expose une bibliothèque interne `kb_demo_app_lib` afin de limiter le couplage avec `main.rs` et de préparer une évolution mobile ultérieure.
|
||||||
|
|
||||||
## 3. Contraintes techniques
|
## 3. Contraintes techniques
|
||||||
|
|
||||||
@@ -239,8 +239,8 @@ cargo test export_bindings
|
|||||||
|
|
||||||
Exemple déjà présent :
|
Exemple déjà présent :
|
||||||
|
|
||||||
- `kb_app/src/splash.rs`
|
- `kb_demo_app/src/splash.rs`
|
||||||
- `kb_app/frontend/ts/bindings/SplashOrder.ts`
|
- `kb_demo_app/frontend/ts/bindings/SplashOrder.ts`
|
||||||
|
|
||||||
## 12. État du projet
|
## 12. État du projet
|
||||||
|
|
||||||
|
|||||||
104
ROADMAP.md
@@ -21,7 +21,7 @@ Le projet vise en priorité :
|
|||||||
Le workspace est organisé autour de deux sous-crates principales :
|
Le workspace est organisé autour de deux sous-crates principales :
|
||||||
|
|
||||||
- `kb_lib` : bibliothèque métier, réseau, config, tracing, stockage, analyse et logique applicative.
|
- `kb_lib` : bibliothèque métier, réseau, config, tracing, stockage, analyse et logique applicative.
|
||||||
- `kb_app` : application Tauri V2 avec frontend TypeScript, chargée de l’interface et de la délégation vers `kb_lib`.
|
- `kb_demo_app` : application Demo Tauri V2 avec frontend TypeScript, chargée de l’interface et de la délégation vers `kb_lib`.
|
||||||
|
|
||||||
### 2.2. Contraintes de code
|
### 2.2. Contraintes de code
|
||||||
|
|
||||||
@@ -40,8 +40,8 @@ Le socle du projet doit respecter les contraintes suivantes :
|
|||||||
|
|
||||||
### 2.3. Règles de responsabilité
|
### 2.3. Règles de responsabilité
|
||||||
|
|
||||||
- `kb_app` ne doit pas embarquer la logique métier réseau ou Solana.
|
- `kb_demo_app` ne doit pas embarquer la logique métier réseau ou Solana.
|
||||||
- `kb_app` doit seulement orchestrer l’UI, les commandes Tauri et les appels vers `kb_lib`.
|
- `kb_demo_app` doit seulement orchestrer l’UI, les commandes Tauri et les appels vers `kb_lib`.
|
||||||
- `kb_lib` doit porter les clients réseau, la config, le tracing, les types partagés, les registres et la logique métier.
|
- `kb_lib` doit porter les clients réseau, la config, le tracing, les types partagés, les registres et la logique métier.
|
||||||
|
|
||||||
## 3. Vision fonctionnelle
|
## 3. Vision fonctionnelle
|
||||||
@@ -146,13 +146,13 @@ Objectif : corriger le squelette et poser la base de travail.
|
|||||||
Réalisé :
|
Réalisé :
|
||||||
|
|
||||||
- correction de `kb_lib/src/lib.rs`,
|
- correction de `kb_lib/src/lib.rs`,
|
||||||
- création de `KbError`,
|
- création de `kb_lib::Error`,
|
||||||
- création de `KbConfig`,
|
- création de `kb_lib::Config`,
|
||||||
- création de `init_tracing`,
|
- création de `kb_lib::init_tracing`,
|
||||||
- création des constantes Solana officielles,
|
- création des constantes Solana officielles,
|
||||||
- préparation des modules `ws_client` et `http_client`,
|
- préparation des modules `ws_client` et `http_client`,
|
||||||
- remise de `kb_app/src/lib.rs` en conformité,
|
- remise de `kb_demo_app/src/lib.rs` en conformité,
|
||||||
- documentation de `kb_app/src/splash.rs`,
|
- documentation de `kb_demo_app/src/splash.rs`,
|
||||||
- UI Tauri minimale.
|
- UI Tauri minimale.
|
||||||
|
|
||||||
### 6.002. Version `0.1.x` — Transport WebSocket générique
|
### 6.002. Version `0.1.x` — Transport WebSocket générique
|
||||||
@@ -174,7 +174,7 @@ Objectif : valider le transport via l’application desktop.
|
|||||||
|
|
||||||
Réalisé :
|
Réalisé :
|
||||||
|
|
||||||
- intégration minimale de `WsClient` dans `kb_app`,
|
- intégration minimale de `WsClient` dans `kb_demo_app`,
|
||||||
- boutons start/stop,
|
- boutons start/stop,
|
||||||
- zone de logs,
|
- zone de logs,
|
||||||
- validation du flux `frontend -> tauri -> kb_lib -> frontend`.
|
- validation du flux `frontend -> tauri -> kb_lib -> frontend`.
|
||||||
@@ -229,7 +229,7 @@ Réalisé :
|
|||||||
- conservation des helpers typed comme interface plus propre,
|
- conservation des helpers typed comme interface plus propre,
|
||||||
- préparation d’une hiérarchie API plus explicite.
|
- préparation d’une hiérarchie API plus explicite.
|
||||||
|
|
||||||
### 6.009. Version `0.3.4` — Fenêtre `Demo Ws` dans `kb_app`
|
### 6.009. Version `0.3.4` — Fenêtre `Demo Ws` dans `kb_demo_app`
|
||||||
Objectif : tester manuellement les souscriptions live dans une fenêtre dédiée.
|
Objectif : tester manuellement les souscriptions live dans une fenêtre dédiée.
|
||||||
|
|
||||||
Réalisé :
|
Réalisé :
|
||||||
@@ -306,7 +306,7 @@ Réalisé :
|
|||||||
- prendre en compte la classe de méthode HTTP,
|
- prendre en compte la classe de méthode HTTP,
|
||||||
- préparer le routage multi-RPC et la limitation de concurrence par endpoint.
|
- préparer le routage multi-RPC et la limitation de concurrence par endpoint.
|
||||||
|
|
||||||
### 6.016. Version `0.4.4` — Démo HTTP dans `kb_app`
|
### 6.016. Version `0.4.4` — Démo HTTP dans `kb_demo_app`
|
||||||
Réalisé :
|
Réalisé :
|
||||||
|
|
||||||
- ajout d’une fenêtre `Demo Http`,
|
- ajout d’une fenêtre `Demo Http`,
|
||||||
@@ -325,9 +325,9 @@ Réalisé :
|
|||||||
|
|
||||||
- configuration DB dans `config.json`,
|
- configuration DB dans `config.json`,
|
||||||
- ouverture/validation SQLite,
|
- ouverture/validation SQLite,
|
||||||
- façade `KbDatabase`,
|
- façade `kb_lib::Database`,
|
||||||
- premier schéma technique,
|
- premier schéma technique,
|
||||||
- table `kb_db_metadata`,
|
- table `k_sol_db_metadata`,
|
||||||
- séparation `db/entities`, `db/dtos`, `db/queries`, `db/types`.
|
- séparation `db/entities`, `db/dtos`, `db/queries`, `db/types`.
|
||||||
|
|
||||||
### 6.019. Version `0.5.1` — Premières tables métier de stockage local
|
### 6.019. Version `0.5.1` — Premières tables métier de stockage local
|
||||||
@@ -341,7 +341,7 @@ Réalisé :
|
|||||||
### 6.020. Version `0.5.2` — Stockage des tokens observés
|
### 6.020. Version `0.5.2` — Stockage des tokens observés
|
||||||
Réalisé :
|
Réalisé :
|
||||||
|
|
||||||
- ajout de la table `kb_observed_tokens`,
|
- ajout de la table `k_sol_observed_tokens`,
|
||||||
- stockage minimal des mints, symboles, noms, statuts et dates d’observation,
|
- stockage minimal des mints, symboles, noms, statuts et dates d’observation,
|
||||||
- ajout du `token_program`,
|
- ajout du `token_program`,
|
||||||
- préparation des relations futures avec pools, paires et événements on-chain,
|
- préparation des relations futures avec pools, paires et événements on-chain,
|
||||||
@@ -350,9 +350,9 @@ Réalisé :
|
|||||||
### 6.021. Version `0.5.3` — Événements et signaux locaux
|
### 6.021. Version `0.5.3` — Événements et signaux locaux
|
||||||
Réalisé :
|
Réalisé :
|
||||||
|
|
||||||
- conservation des événements runtime techniques via `kb_db_runtime_events`,
|
- conservation des événements runtime techniques via `k_sol_db_runtime_events`,
|
||||||
- ajout des observations on-chain brutes via `kb_onchain_observations`,
|
- ajout des observations on-chain brutes via `k_sol_onchain_observations`,
|
||||||
- ajout des signaux d’analyse via `kb_analysis_signals`,
|
- ajout des signaux d’analyse via `k_sol_analysis_signals`,
|
||||||
- distinction explicite entre événements runtime, observations on-chain et événements métier,
|
- distinction explicite entre événements runtime, observations on-chain et événements métier,
|
||||||
- préparation de la traçabilité de provenance par type de source et endpoint, sans remettre en cause l’unicité locale d’un token par mint.
|
- préparation de la traçabilité de provenance par type de source et endpoint, sans remettre en cause l’unicité locale d’un token par mint.
|
||||||
|
|
||||||
@@ -436,10 +436,10 @@ Réalisé :
|
|||||||
- branchement optionnel du relais de détection WS sur tous les clients orchestrés,
|
- branchement optionnel du relais de détection WS sur tous les clients orchestrés,
|
||||||
- préparation des futures politiques de répartition, supervision et reconnexion.
|
- préparation des futures politiques de répartition, supervision et reconnexion.
|
||||||
|
|
||||||
### 6.031. Version `0.6.6` — Démo légère `WsManager` dans `kb_app`
|
### 6.031. Version `0.6.6` — Démo légère `WsManager` dans `kb_demo_app`
|
||||||
Réalisé :
|
Réalisé :
|
||||||
|
|
||||||
- ajout d’une fenêtre `Demo Ws Manager` dans `kb_app`,
|
- ajout d’une fenêtre `Demo Ws Manager` dans `kb_demo_app`,
|
||||||
- ouverture depuis la fenêtre principale,
|
- ouverture depuis la fenêtre principale,
|
||||||
- affichage du snapshot consolidé du `WsManager`,
|
- affichage du snapshot consolidé du `WsManager`,
|
||||||
- pilotage des endpoints WS gérés via `start/stop all` et `start/stop role`,
|
- pilotage des endpoints WS gérés via `start/stop all` et `start/stop role`,
|
||||||
@@ -453,13 +453,13 @@ Réalisé :
|
|||||||
- introduction d’une file de résolution transactionnelle alimentée par les signatures issues des flux WS utiles,
|
- introduction d’une file de résolution transactionnelle alimentée par les signatures issues des flux WS utiles,
|
||||||
- corrélation initiale des `logsNotification` et `signatureNotification` avec des appels `getTransaction`,
|
- corrélation initiale des `logsNotification` et `signatureNotification` avec des appels `getTransaction`,
|
||||||
- utilisation du pool HTTP existant pour enrichir les signaux détectés côté WS,
|
- utilisation du pool HTTP existant pour enrichir les signaux détectés côté WS,
|
||||||
- persistance des résolutions transactionnelles dans `kb_onchain_observations` et `kb_analysis_signals`,
|
- persistance des résolutions transactionnelles dans `k_sol_onchain_observations` et `k_sol_analysis_signals`,
|
||||||
- préparation du futur modèle transactionnel enrichi sans bloquer les flux temps réel.
|
- préparation du futur modèle transactionnel enrichi sans bloquer les flux temps réel.
|
||||||
|
|
||||||
### 6.033. Version `0.7.1` — Modèle transactionnel Solana enrichi
|
### 6.033. Version `0.7.1` — Modèle transactionnel Solana enrichi
|
||||||
Réalisé :
|
Réalisé :
|
||||||
|
|
||||||
- ajout des tables techniques `kb_chain_slots`, `kb_chain_transactions` et `kb_chain_instructions`,
|
- ajout des tables techniques `k_sol_chain_slots`, `k_sol_chain_transactions` et `k_sol_chain_instructions`,
|
||||||
- distinction claire entre slot, transaction résolue et instructions normalisées,
|
- distinction claire entre slot, transaction résolue et instructions normalisées,
|
||||||
- support des instructions principales et inner instructions,
|
- support des instructions principales et inner instructions,
|
||||||
- ajout des entités, DTOs et requêtes associées,
|
- ajout des entités, DTOs et requêtes associées,
|
||||||
@@ -481,7 +481,7 @@ Réalisé :
|
|||||||
Réalisé :
|
Réalisé :
|
||||||
|
|
||||||
- transformation des événements DEX décodés en objets métier pool / pair / listing,
|
- transformation des événements DEX décodés en objets métier pool / pair / listing,
|
||||||
- alimentation de `kb_pools`, `kb_pairs`, `kb_pool_tokens` et `kb_pool_listings`,
|
- alimentation de `k_sol_pools`, `k_sol_pairs`, `k_sol_pool_tokens` et `k_sol_pool_listings`,
|
||||||
- première détection métier pour Raydium AmmV4 / initialize2,
|
- première détection métier pour Raydium AmmV4 / initialize2,
|
||||||
- branchement automatique de la détection métier après résolution, projection et décodage DEX,
|
- branchement automatique de la détection métier après résolution, projection et décodage DEX,
|
||||||
- émission de signaux dédiés pour `new_pool`, `new_pair` et `first_listing_seen`,
|
- émission de signaux dédiés pour `new_pool`, `new_pair` et `first_listing_seen`,
|
||||||
@@ -501,7 +501,7 @@ Réalisé :
|
|||||||
Réalisé :
|
Réalisé :
|
||||||
|
|
||||||
- enrichissement du décodeur `PumpSwap` avec extraction des mints et du `pool_v2`,
|
- enrichissement du décodeur `PumpSwap` avec extraction des mints et du `pool_v2`,
|
||||||
- persistance des événements `PumpSwap` enrichis dans `kb_dex_decoded_events`,
|
- persistance des événements `PumpSwap` enrichis dans `k_sol_dex_decoded_events`,
|
||||||
- ajout de la détection métier `PumpSwap` vers `pool / pair / listing`,
|
- ajout de la détection métier `PumpSwap` vers `pool / pair / listing`,
|
||||||
- émission des signaux dédiés `new_pool`, `new_pair` et `first_listing_seen`,
|
- émission des signaux dédiés `new_pool`, `new_pair` et `first_listing_seen`,
|
||||||
- garantie d’idempotence sur une même transaction déjà traitée,
|
- garantie d’idempotence sur une même transaction déjà traitée,
|
||||||
@@ -512,7 +512,7 @@ Réalisé :
|
|||||||
|
|
||||||
- ajout du premier décodeur `Meteora DBC`,
|
- ajout du premier décodeur `Meteora DBC`,
|
||||||
- prise en charge initiale des événements `create_pool` et `swap`,
|
- prise en charge initiale des événements `create_pool` et `swap`,
|
||||||
- persistance des événements `Meteora DBC` dans `kb_dex_decoded_events`,
|
- persistance des événements `Meteora DBC` dans `k_sol_dex_decoded_events`,
|
||||||
- ajout de la détection métier `Meteora DBC` vers `pool / pair / listing`,
|
- ajout de la détection métier `Meteora DBC` vers `pool / pair / listing`,
|
||||||
- émission des signaux dédiés `new_pool`, `new_pair` et `first_listing_seen`,
|
- émission des signaux dédiés `new_pool`, `new_pair` et `first_listing_seen`,
|
||||||
- préparation du lot suivant pour `Meteora DAMM v2`, `Meteora DAMM v1` et `LaunchLab / Fun Launch`.
|
- préparation du lot suivant pour `Meteora DAMM v2`, `Meteora DAMM v1` et `LaunchLab / Fun Launch`.
|
||||||
@@ -523,7 +523,7 @@ Réalisé :
|
|||||||
- ajout du premier décodeur `Meteora DAMM v2`,
|
- ajout du premier décodeur `Meteora DAMM v2`,
|
||||||
- prise en charge initiale des événements de création de pool via `initialize_pool`, `initialize_pool_with_dynamic_config` et `initialize_customizable_pool`,
|
- prise en charge initiale des événements de création de pool via `initialize_pool`, `initialize_pool_with_dynamic_config` et `initialize_customizable_pool`,
|
||||||
- prise en charge initiale des swaps via `swap` et `swap2`,
|
- prise en charge initiale des swaps via `swap` et `swap2`,
|
||||||
- persistance des événements `Meteora DAMM v2` dans `kb_dex_decoded_events`,
|
- persistance des événements `Meteora DAMM v2` dans `k_sol_dex_decoded_events`,
|
||||||
- ajout de la détection métier `Meteora DAMM v2` vers `pool / pair / listing`,
|
- ajout de la détection métier `Meteora DAMM v2` vers `pool / pair / listing`,
|
||||||
- préparation du rattachement futur entre `Meteora DBC` et `Meteora DAMM v2`.
|
- préparation du rattachement futur entre `Meteora DBC` et `Meteora DAMM v2`.
|
||||||
|
|
||||||
@@ -533,7 +533,7 @@ Réalisé :
|
|||||||
- ajout du premier décodeur `Meteora DAMM v1`,
|
- ajout du premier décodeur `Meteora DAMM v1`,
|
||||||
- prise en charge initiale des événements de création de pool via `initialize_pool` et `initialize_pool_with_config`,
|
- prise en charge initiale des événements de création de pool via `initialize_pool` et `initialize_pool_with_config`,
|
||||||
- prise en charge initiale des swaps via `swap`,
|
- prise en charge initiale des swaps via `swap`,
|
||||||
- persistance des événements `Meteora DAMM v1` dans `kb_dex_decoded_events`,
|
- persistance des événements `Meteora DAMM v1` dans `k_sol_dex_decoded_events`,
|
||||||
- ajout de la détection métier `Meteora DAMM v1` vers `pool / pair / listing`,
|
- ajout de la détection métier `Meteora DAMM v1` vers `pool / pair / listing`,
|
||||||
- préparation du rattachement futur entre `Meteora DBC` et `Meteora DAMM v1`.
|
- préparation du rattachement futur entre `Meteora DBC` et `Meteora DAMM v1`.
|
||||||
|
|
||||||
@@ -553,9 +553,9 @@ Réalisé :
|
|||||||
- ajout du premier décodeur `Orca Whirlpools`,
|
- ajout du premier décodeur `Orca Whirlpools`,
|
||||||
- prise en charge initiale des événements de création de pool via `initialize_pool` et `initialize_pool_v2`,
|
- prise en charge initiale des événements de création de pool via `initialize_pool` et `initialize_pool_v2`,
|
||||||
- prise en charge initiale des swaps via `swap` et `swap_v2`,
|
- prise en charge initiale des swaps via `swap` et `swap_v2`,
|
||||||
- persistance des événements `Orca Whirlpools` dans `kb_dex_decoded_events`,
|
- persistance des événements `Orca Whirlpools` dans `k_sol_dex_decoded_events`,
|
||||||
- ajout de la détection métier `Orca Whirlpools` vers `pool / pair / listing`,
|
- ajout de la détection métier `Orca Whirlpools` vers `pool / pair / listing`,
|
||||||
- utilisation de `KbPoolKind::Clmm` pour refléter la nature concentrée de `Whirlpools`.
|
- utilisation de `PoolKind::Clmm` pour refléter la nature concentrée de `Whirlpools`.
|
||||||
|
|
||||||
### 6.043. Version `0.7.11` — FluxBeam
|
### 6.043. Version `0.7.11` — FluxBeam
|
||||||
Réalisé :
|
Réalisé :
|
||||||
@@ -563,7 +563,7 @@ Réalisé :
|
|||||||
- ajout du premier décodeur `FluxBeam`,
|
- ajout du premier décodeur `FluxBeam`,
|
||||||
- prise en charge initiale des événements de création de pool via un premier décodage `create_pool / initialize_pool`,
|
- prise en charge initiale des événements de création de pool via un premier décodage `create_pool / initialize_pool`,
|
||||||
- prise en charge initiale des swaps via `swap`,
|
- prise en charge initiale des swaps via `swap`,
|
||||||
- persistance des événements `FluxBeam` dans `kb_dex_decoded_events`,
|
- persistance des événements `FluxBeam` dans `k_sol_dex_decoded_events`,
|
||||||
- ajout de la détection métier `FluxBeam` vers `pool / pair / listing`,
|
- ajout de la détection métier `FluxBeam` vers `pool / pair / listing`,
|
||||||
- conservation d’un premier décodage heuristique à raffiner ultérieurement avec des transactions FluxBeam réelles.
|
- conservation d’un premier décodage heuristique à raffiner ultérieurement avec des transactions FluxBeam réelles.
|
||||||
|
|
||||||
@@ -573,7 +573,7 @@ Réalisé :
|
|||||||
- ajout du premier décodeur `DexLab Swap/Pool`,
|
- ajout du premier décodeur `DexLab Swap/Pool`,
|
||||||
- prise en charge initiale des événements de création de pool via un premier décodage `create_pool / initialize_pool`,
|
- prise en charge initiale des événements de création de pool via un premier décodage `create_pool / initialize_pool`,
|
||||||
- prise en charge initiale des swaps via `swap`,
|
- prise en charge initiale des swaps via `swap`,
|
||||||
- persistance des événements `DexLab` dans `kb_dex_decoded_events`,
|
- persistance des événements `DexLab` dans `k_sol_dex_decoded_events`,
|
||||||
- ajout de la détection métier `DexLab` vers `pool / pair / listing`,
|
- ajout de la détection métier `DexLab` vers `pool / pair / listing`,
|
||||||
- conservation d’une séparation entre pool DexLab natif et éventuel `OpenBook Market ID` créé ensuite.
|
- conservation d’une séparation entre pool DexLab natif et éventuel `OpenBook Market ID` créé ensuite.
|
||||||
|
|
||||||
@@ -662,33 +662,33 @@ Réalisé :
|
|||||||
- persistance idempotente des signaux par paire et par bucket,
|
- persistance idempotente des signaux par paire et par bucket,
|
||||||
- branchement automatique dans le pipeline de résolution transactionnelle.
|
- branchement automatique dans le pipeline de résolution transactionnelle.
|
||||||
|
|
||||||
### 6.054. Version `0.7.22` — `kb_app` : inspection et tests du pipeline `0.7.x`
|
### 6.054. Version `0.7.22` — `kb_demo_app` : inspection et tests du pipeline `0.7.x`
|
||||||
Réalisé :
|
Réalisé :
|
||||||
|
|
||||||
- ajout d’une fenêtre dédiée `Demo Pipeline` dans `kb_app`,
|
- ajout d’une fenêtre dédiée `Demo Pipeline` dans `kb_demo_app`,
|
||||||
- inspection du pipeline persistant par `signature`,
|
- inspection du pipeline persistant par `signature`,
|
||||||
- inspection du pipeline persistant par `token mint`,
|
- inspection du pipeline persistant par `token mint`,
|
||||||
- inspection du pipeline persistant par `pair id`,
|
- inspection du pipeline persistant par `pair id`,
|
||||||
- inspection du pipeline persistant par `pool address`,
|
- inspection du pipeline persistant par `pool address`,
|
||||||
- affichage structuré des transactions résolues, événements DEX décodés, pools, paires, listings, launch origins, pool origins, wallets observés, holdings observés, trade events, pair metrics, candles et signaux analytiques,
|
- affichage structuré des transactions résolues, événements DEX décodés, pools, paires, listings, launch origins, pool origins, wallets observés, holdings observés, trade events, pair metrics, candles et signaux analytiques,
|
||||||
- possibilité d’utiliser un timeframe custom pour régénérer à la demande les candles non matérialisées,
|
- possibilité d’utiliser un timeframe custom pour régénérer à la demande les candles non matérialisées,
|
||||||
- conservation d’une instance partagée de `KbDatabase` dans `kb_app` afin d’éviter la réouverture de la base et la réinitialisation du schéma à chaque commande UI,
|
- conservation d’une instance partagée de `Database` dans `kb_demo_app` afin d’éviter la réouverture de la base et la réinitialisation du schéma à chaque commande UI,
|
||||||
- validation pratique de l’inspection du pipeline `0.7.x` sans dépendre uniquement des logs bruts ou de la consultation manuelle de SQLite.
|
- validation pratique de l’inspection du pipeline `0.7.x` sans dépendre uniquement des logs bruts ou de la consultation manuelle de SQLite.
|
||||||
|
|
||||||
### 6.055. Version `0.7.23` — `kb_app` : backfill token ciblé
|
### 6.055. Version `0.7.23` — `kb_demo_app` : backfill token ciblé
|
||||||
Réalisé :
|
Réalisé :
|
||||||
|
|
||||||
- ajout d’un pilotage UI du backfill historique ciblé par `token mint` dans `kb_app`,
|
- ajout d’un pilotage UI du backfill historique ciblé par `token mint` dans `kb_demo_app`,
|
||||||
- sélection du `token mint`, du rôle HTTP et des limites de signatures `mint / pool`,
|
- sélection du `token mint`, du rôle HTTP et des limites de signatures `mint / pool`,
|
||||||
- exécution de `KbTokenBackfillService` depuis une commande Tauri dédiée,
|
- exécution de `TokenBackfillService` depuis une commande Tauri dédiée,
|
||||||
- affichage du résumé de backfill dans `Demo Pipeline`,
|
- affichage du résumé de backfill dans `Demo Pipeline`,
|
||||||
- réinspection automatique du token après backfill lorsque des objets persistés exploitables sont effectivement reconstruits,
|
- réinspection automatique du token après backfill lorsque des objets persistés exploitables sont effectivement reconstruits,
|
||||||
- gestion explicite du cas où le backfill réussit sans matérialiser de token exploitable dans la base locale.
|
- gestion explicite du cas où le backfill réussit sans matérialiser de token exploitable dans la base locale.
|
||||||
|
|
||||||
### 6.056. Version `0.7.24` — `kb_app` : visualisation candles / OHLCV
|
### 6.056. Version `0.7.24` — `kb_demo_app` : visualisation candles / OHLCV
|
||||||
Réalisé :
|
Réalisé :
|
||||||
|
|
||||||
- ajout d’un affichage graphique des candles / OHLCV dans `kb_app` via `echarts`,
|
- ajout d’un affichage graphique des candles / OHLCV dans `kb_demo_app` via `echarts`,
|
||||||
- sélection dynamique de la paire inspectée,
|
- sélection dynamique de la paire inspectée,
|
||||||
- sélection dynamique du timeframe disponible,
|
- sélection dynamique du timeframe disponible,
|
||||||
- affichage conjoint des chandeliers OHLC et du volume,
|
- affichage conjoint des chandeliers OHLC et du volume,
|
||||||
@@ -736,7 +736,7 @@ Réalisé :
|
|||||||
- `duplicateCandleBucketCount`.
|
- `duplicateCandleBucketCount`.
|
||||||
- Correction de l’agrégation des trades Raydium via extraction instruction-scoped des transferts SPL Token depuis `meta.innerInstructions`.
|
- Correction de l’agrégation des trades Raydium via extraction instruction-scoped des transferts SPL Token depuis `meta.innerInstructions`.
|
||||||
- Correction des cas CPMM contenant plusieurs swaps dans une même transaction, sans mélange des montants entre instructions.
|
- Correction des cas CPMM contenant plusieurs swaps dans une même transaction, sans mélange des montants entre instructions.
|
||||||
- Conservation des transactions échouées comme événements décodés traçables, sans génération de `kb_trade_events`.
|
- Conservation des transactions échouées comme événements décodés traçables, sans génération de `k_sol_trade_events`.
|
||||||
- Clarification des compteurs de replay :
|
- Clarification des compteurs de replay :
|
||||||
- `pairCandleUpsertCount`,
|
- `pairCandleUpsertCount`,
|
||||||
- `analyticSignalUpsertCount`.
|
- `analyticSignalUpsertCount`.
|
||||||
@@ -763,15 +763,15 @@ Objectif : verrouiller la non-régression du pipeline actuel avant d’ajouter d
|
|||||||
- ajouter ou compléter les tests unitaires sur `dex_decode`, `dex_detect`, `trade_aggregation`, `pair_candle_aggregation`, `pair_analytic_signal`, `local_pipeline_replay` et `local_pipeline_diagnostics`,
|
- ajouter ou compléter les tests unitaires sur `dex_decode`, `dex_detect`, `trade_aggregation`, `pair_candle_aggregation`, `pair_analytic_signal`, `local_pipeline_replay` et `local_pipeline_diagnostics`,
|
||||||
- ajouter des requêtes SQL de diagnostic de référence pour contrôler rapidement les tables clés après backfill ou replay local,
|
- ajouter des requêtes SQL de diagnostic de référence pour contrôler rapidement les tables clés après backfill ou replay local,
|
||||||
- conserver la tolérance aux événements DEX partiels tout en refusant les trades sans montant ou prix exploitable,
|
- conserver la tolérance aux événements DEX partiels tout en refusant les trades sans montant ou prix exploitable,
|
||||||
- valider que les transactions échouées restent traçables dans les événements décodés sans produire de `kb_trade_events`.
|
- valider que les transactions échouées restent traçables dans les événements décodés sans produire de `k_sol_trade_events`.
|
||||||
|
|
||||||
### 6.060. Version `0.7.28` — Matérialisation des événements liquidité et cycle de vie pool
|
### 6.060. Version `0.7.28` — Matérialisation des événements liquidité et cycle de vie pool
|
||||||
Objectif : exploiter les événements non buy/sell utiles à l’analyse et au trading semi-automatique sans les mélanger avec les trades/candles.
|
Objectif : exploiter les événements non buy/sell utiles à l’analyse et au trading semi-automatique sans les mélanger avec les trades/candles.
|
||||||
|
|
||||||
À faire :
|
À faire :
|
||||||
|
|
||||||
- stabiliser ou ajouter la table `kb_liquidity_events`,
|
- stabiliser ou ajouter la table `k_sol_liquidity_events`,
|
||||||
- stabiliser ou ajouter la table `kb_pool_lifecycle_events`,
|
- stabiliser ou ajouter la table `k_sol_pool_lifecycle_events`,
|
||||||
- matérialiser les événements de type `increase_liquidity`, `decrease_liquidity`, `add_liquidity`, `remove_liquidity`, `open_position`, `close_position`, `initialize`, `create_pool`, `migrate` et assimilés,
|
- matérialiser les événements de type `increase_liquidity`, `decrease_liquidity`, `add_liquidity`, `remove_liquidity`, `open_position`, `close_position`, `initialize`, `create_pool`, `migrate` et assimilés,
|
||||||
- rattacher chaque événement métier à `dex_id`, `pool_id`, `pair_id`, `transaction_id`, `decoded_event_id`, `signature` et `slot`,
|
- rattacher chaque événement métier à `dex_id`, `pool_id`, `pair_id`, `transaction_id`, `decoded_event_id`, `signature` et `slot`,
|
||||||
- conserver le `payload_json` source pour audit et extension future,
|
- conserver le `payload_json` source pour audit et extension future,
|
||||||
@@ -784,9 +784,9 @@ Objectif : conserver les événements non price-action utiles au risque, à l’
|
|||||||
|
|
||||||
À faire :
|
À faire :
|
||||||
|
|
||||||
- ajouter ou stabiliser `kb_fee_events`,
|
- ajouter ou stabiliser `k_sol_fee_events`,
|
||||||
- ajouter ou stabiliser `kb_reward_events`,
|
- ajouter ou stabiliser `k_sol_reward_events`,
|
||||||
- ajouter ou stabiliser `kb_pool_admin_events`,
|
- ajouter ou stabiliser `k_sol_pool_admin_events`,
|
||||||
- matérialiser les événements `collect_protocol_fee`, `collect_fund_fee`, `collect_creator_fee`, `collect_fee` et assimilés,
|
- matérialiser les événements `collect_protocol_fee`, `collect_fund_fee`, `collect_creator_fee`, `collect_fee` et assimilés,
|
||||||
- matérialiser les événements `set_reward_params`, `initialize_reward`, `collect_reward`, `update_reward_infos` et assimilés,
|
- matérialiser les événements `set_reward_params`, `initialize_reward`, `collect_reward`, `update_reward_infos` et assimilés,
|
||||||
- matérialiser les événements `set_config`, `update_config`, `set_authority`, `set_fee_rate`, `pause`, `resume` et assimilés,
|
- matérialiser les événements `set_config`, `update_config`, `set_authority`, `set_fee_rate`, `pause`, `resume` et assimilés,
|
||||||
@@ -801,7 +801,7 @@ Objectif : ajouter ou stabiliser les connecteurs Meteora avec une séparation cl
|
|||||||
- vérifier les programmes et discriminants réellement utilisés pour `Meteora DBC`, `Meteora DAMM v1` et `Meteora DAMM v2`,
|
- vérifier les programmes et discriminants réellement utilisés pour `Meteora DBC`, `Meteora DAMM v1` et `Meteora DAMM v2`,
|
||||||
- constituer un petit corpus local de pools et signatures fiables pour chaque variante,
|
- constituer un petit corpus local de pools et signatures fiables pour chaque variante,
|
||||||
- décoder les créations de pool, swaps et événements de liquidité exploitables,
|
- décoder les créations de pool, swaps et événements de liquidité exploitables,
|
||||||
- alimenter `kb_dex_decoded_events`, les tables métier pool/pair/listing et les nouvelles tables d’événements non-trade,
|
- alimenter `k_sol_dex_decoded_events`, les tables métier pool/pair/listing et les nouvelles tables d’événements non-trade,
|
||||||
- vérifier l’idempotence du replay local sur un corpus mixte Meteora,
|
- vérifier l’idempotence du replay local sur un corpus mixte Meteora,
|
||||||
- documenter les limites connues des variantes insuffisamment couvertes par le corpus.
|
- documenter les limites connues des variantes insuffisamment couvertes par le corpus.
|
||||||
|
|
||||||
@@ -854,7 +854,7 @@ Objectif : traiter le vrai Raydium AMM v4 historique après les autres DEX, afin
|
|||||||
- renommer et stabiliser les fonctions internes autour de `raydium_amm_v4` afin d’éviter l’ambiguïté avec `raydium_cpmm` et `raydium_clmm`,
|
- renommer et stabiliser les fonctions internes autour de `raydium_amm_v4` afin d’éviter l’ambiguïté avec `raydium_cpmm` et `raydium_clmm`,
|
||||||
- documenter les limites connues si le corpus AMM v4 reste trop faible.
|
- documenter les limites connues si le corpus AMM v4 reste trop faible.
|
||||||
|
|
||||||
### 6.067. Version `0.7.35` — `kb_app` : overlays analytiques
|
### 6.067. Version `0.7.35` — `kb_demo_app` : overlays analytiques
|
||||||
Objectif : rendre visibles les signaux analytiques directement sur les graphes et vues de marché.
|
Objectif : rendre visibles les signaux analytiques directement sur les graphes et vues de marché.
|
||||||
|
|
||||||
À faire :
|
À faire :
|
||||||
@@ -865,7 +865,7 @@ Objectif : rendre visibles les signaux analytiques directement sur les graphes e
|
|||||||
- afficher un panneau latéral listant les signaux liés à une paire et à un timeframe,
|
- afficher un panneau latéral listant les signaux liés à une paire et à un timeframe,
|
||||||
- préparer l’extension future vers des indicateurs Ichimoku, Kumo, projections ABCD et égalités temps/prix sans les mélanger au pipeline de décodage DEX.
|
- préparer l’extension future vers des indicateurs Ichimoku, Kumo, projections ABCD et égalités temps/prix sans les mélanger au pipeline de décodage DEX.
|
||||||
|
|
||||||
### 6.068. Version `0.7.36` — `kb_app` : vues consolidées token / pair / pool
|
### 6.068. Version `0.7.36` — `kb_demo_app` : vues consolidées token / pair / pool
|
||||||
Objectif : fournir une lecture métier plus confortable du modèle `0.7.x`.
|
Objectif : fournir une lecture métier plus confortable du modèle `0.7.x`.
|
||||||
|
|
||||||
À faire :
|
À faire :
|
||||||
@@ -882,7 +882,7 @@ Objectif : stabiliser la couche desktop de validation avant l’ouverture de `0.
|
|||||||
|
|
||||||
À faire :
|
À faire :
|
||||||
|
|
||||||
- consolider les vues ajoutées dans `kb_app`,
|
- consolider les vues ajoutées dans `kb_demo_app`,
|
||||||
- améliorer la navigation, les filtres et la pagination,
|
- améliorer la navigation, les filtres et la pagination,
|
||||||
- ajouter les derniers raffinements de confort et de lisibilité,
|
- ajouter les derniers raffinements de confort et de lisibilité,
|
||||||
- préparer une base UI suffisamment stable pour la future phase d’analyse et filtrage `0.8.x`,
|
- préparer une base UI suffisamment stable pour la future phase d’analyse et filtrage `0.8.x`,
|
||||||
@@ -996,7 +996,7 @@ Modules cibles à court terme :
|
|||||||
- `db/dtos/*`
|
- `db/dtos/*`
|
||||||
- `db/queries/*`
|
- `db/queries/*`
|
||||||
|
|
||||||
### 7.2. `kb_app`
|
### 7.2. `kb_demo_app`
|
||||||
Responsabilités cibles :
|
Responsabilités cibles :
|
||||||
|
|
||||||
- lancement Tauri,
|
- lancement Tauri,
|
||||||
@@ -1060,12 +1060,12 @@ La priorité immédiate est désormais la suivante :
|
|||||||
2. vérifier sur bases neuves et après replay local les invariants bloquants du pipeline : `diagnosticsClean = true`, `blockingIssueCount = 0`, aucun trade candidate exploitable perdu, aucun trade event invalide, aucun doublon réel par `decoded_event_id`, aucune candle dupliquée par bucket,
|
2. vérifier sur bases neuves et après replay local les invariants bloquants du pipeline : `diagnosticsClean = true`, `blockingIssueCount = 0`, aucun trade candidate exploitable perdu, aucun trade event invalide, aucun doublon réel par `decoded_event_id`, aucune candle dupliquée par bucket,
|
||||||
3. documenter les requêtes SQL de diagnostic de référence et les résultats attendus pour les tables clés du pipeline,
|
3. documenter les requêtes SQL de diagnostic de référence et les résultats attendus pour les tables clés du pipeline,
|
||||||
4. matérialiser ensuite les événements non buy/sell utiles au trading et à l’analyse : liquidité, cycle de vie des pools, fees, rewards et administration,
|
4. matérialiser ensuite les événements non buy/sell utiles au trading et à l’analyse : liquidité, cycle de vie des pools, fees, rewards et administration,
|
||||||
5. garantir que ces événements non-trade restent séparés des `kb_trade_events` et des candles tout en restant rattachés aux transactions, decoded events, pools, pairs et wallets observés,
|
5. garantir que ces événements non-trade restent séparés des `k_sol_trade_events` et des candles tout en restant rattachés aux transactions, decoded events, pools, pairs et wallets observés,
|
||||||
6. ajouter ou stabiliser les autres DEX par lots vérifiables : Meteora, surfaces de lancement, Orca, FluxBeam et DexLab,
|
6. ajouter ou stabiliser les autres DEX par lots vérifiables : Meteora, surfaces de lancement, Orca, FluxBeam et DexLab,
|
||||||
7. traiter `raydium_amm_v4` legacy seulement après les autres DEX, avec un corpus dédié prouvant le programme `675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8`,
|
7. traiter `raydium_amm_v4` legacy seulement après les autres DEX, avec un corpus dédié prouvant le programme `675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8`,
|
||||||
8. effectuer une validation DEX v1 consolidée sur tous les connecteurs supportés avant de considérer la couche DEX `0.7.x` comme stable,
|
8. effectuer une validation DEX v1 consolidée sur tous les connecteurs supportés avant de considérer la couche DEX `0.7.x` comme stable,
|
||||||
9. ajouter ensuite les overlays des signaux analytiques sur les candles,
|
9. ajouter ensuite les overlays des signaux analytiques sur les candles,
|
||||||
10. consolider les vues métier `token / pair / pool` dans `kb_app`, y compris les événements liquidité, lifecycle, fees, rewards et admin,
|
10. consolider les vues métier `token / pair / pool` dans `kb_demo_app`, y compris les événements liquidité, lifecycle, fees, rewards et admin,
|
||||||
11. stabiliser l’ergonomie, les filtres, la pagination et la navigation de l’UI d’inspection,
|
11. stabiliser l’ergonomie, les filtres, la pagination et la navigation de l’UI d’inspection,
|
||||||
12. préparer ensuite l’ouverture de `0.8.x` pour l’analyse, les filtres, les patterns et les projections graphiques,
|
12. préparer ensuite l’ouverture de `0.8.x` pour l’analyse, les filtres, les patterns et les projections graphiques,
|
||||||
13. préparer enfin Yellowstone gRPC comme extension de capacité, et non comme remplacement du socle HTTP / WS existant.
|
13. préparer enfin Yellowstone gRPC comme extension de capacité, et non comme remplacement du socle HTTP / WS existant.
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
|
||||||
import type { KbDemoPipeline2PairItem } from "./KbDemoPipeline2PairItem";
|
|
||||||
import type { KbDemoPipeline2PoolItem } from "./KbDemoPipeline2PoolItem";
|
|
||||||
import type { KbDemoPipeline2TokenItem } from "./KbDemoPipeline2TokenItem";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Full local catalog payload.
|
|
||||||
*/
|
|
||||||
export type KbDemoPipeline2CatalogPayload = {
|
|
||||||
/**
|
|
||||||
* Open database URL.
|
|
||||||
*/
|
|
||||||
databaseUrl: string,
|
|
||||||
/**
|
|
||||||
* Observed token list.
|
|
||||||
*/
|
|
||||||
tokens: Array<KbDemoPipeline2TokenItem>,
|
|
||||||
/**
|
|
||||||
* Known pool list.
|
|
||||||
*/
|
|
||||||
pools: Array<KbDemoPipeline2PoolItem>,
|
|
||||||
/**
|
|
||||||
* Known pair list.
|
|
||||||
*/
|
|
||||||
pairs: Array<KbDemoPipeline2PairItem>, };
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Static endpoint summary enriched with current manager state.
|
|
||||||
*/
|
|
||||||
export type KbDemoWsManagerEndpointSummary = { name: string, resolvedUrl: string, provider: string, roles: Array<string>, connectionState: string, activeSubscriptionCount: number, };
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
|
||||||
import type { KbDemoWsManagerEndpointSummary } from "./KbDemoWsManagerEndpointSummary";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Global demo manager snapshot payload.
|
|
||||||
*/
|
|
||||||
export type KbDemoWsManagerSnapshotPayload = { endpointCount: number, startedCount: number, endpoints: Array<KbDemoWsManagerEndpointSummary>, };
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Current demo window runtime status.
|
|
||||||
*/
|
|
||||||
export type KbDemoWsStatusPayload = { connectionState: string, endpointName: string | null, endpointUrl: string | null, currentSubscriptionId: bigint | null, currentSubscribeMethod: string | null, currentUnsubscribeMethod: string | null, currentNotificationMethod: string | null, eventCountTotal: bigint, notificationCountTotal: bigint, uiLogCount: bigint, suppressedLogCount: bigint, lastEventKind: string | null, };
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
# file: kb_app/Cargo.toml
|
# file: kb_demo_app/Cargo.toml
|
||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "kb_app"
|
name = "kb_demo_app"
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
version.workspace = true
|
version.workspace = true
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
@@ -12,7 +12,7 @@ publish.workspace = true
|
|||||||
# The `_lib` suffix may seem redundant, but it is necessary
|
# The `_lib` suffix may seem redundant, but it is necessary
|
||||||
# to make the lib name unique and wouldn't conflict with the bin name.
|
# to make the lib name unique and wouldn't conflict with the bin name.
|
||||||
# This seems to be only an issue on Windows, see https://github.com/rust-lang/cargo/issues/8519
|
# This seems to be only an issue on Windows, see https://github.com/rust-lang/cargo/issues/8519
|
||||||
name = "kb_app_lib"
|
name = "kb_demo_app_lib"
|
||||||
crate-type = ["staticlib", "cdylib", "rlib"]
|
crate-type = ["staticlib", "cdylib", "rlib"]
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// file: kb_app/build.rs
|
// file: kb_demo_app/build.rs
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
tauri_build::build()
|
tauri_build::build()
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
<!-- file: kb_app/frontend/demo_http.html -->
|
<!-- file: kb_demo_app/frontend/demo_http.html -->
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="fr">
|
<html lang="fr">
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
<!-- file: kb_app/frontend/demo_pipeline.html -->
|
<!-- file: kb_demo_app/frontend/demo_pipeline.html -->
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="fr">
|
<html lang="fr">
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
<!-- file: kb_app/frontend/demo_pipeline2.html -->
|
<!-- file: kb_demo_app/frontend/demo_pipeline2.html -->
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="fr">
|
<html lang="fr">
|
||||||
|
|
||||||
@@ -167,6 +167,9 @@
|
|||||||
<button id="demoPipeline2DiagnoseLocalPipelineButton" type="button" class="btn btn-outline-primary">
|
<button id="demoPipeline2DiagnoseLocalPipelineButton" type="button" class="btn btn-outline-primary">
|
||||||
Diagnose local pipeline
|
Diagnose local pipeline
|
||||||
</button>
|
</button>
|
||||||
|
<button id="demoPipeline2ValidateLocalPipelineButton" type="button" class="btn btn-outline-success">
|
||||||
|
Validate 0.7.27
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -249,6 +252,19 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="accordion-item border-0 shadow-sm mb-3">
|
||||||
|
<h2 class="accordion-header" id="demoPipeline2LocalValidationHeading">
|
||||||
|
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#demoPipeline2LocalValidationCollapse" aria-expanded="false" aria-controls="demoPipeline2LocalValidationCollapse">
|
||||||
|
Local pipeline Validation
|
||||||
|
</button>
|
||||||
|
</h2>
|
||||||
|
<div id="demoPipeline2LocalValidationCollapse" class="accordion-collapse collapse" aria-labelledby="demoPipeline2LocalValidationHeading" data-bs-parent="#demoPipeline2ContentAccordion">
|
||||||
|
<div class="accordion-body">
|
||||||
|
<textarea id="demoPipeline2LocalValidationTextarea" class="form-control font-monospace" rows="16" readonly spellcheck="false"></textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="accordion-item border-0 shadow-sm mb-3">
|
<div class="accordion-item border-0 shadow-sm mb-3">
|
||||||
<h2 class="accordion-header" id="demoPipeline2ChartHeading">
|
<h2 class="accordion-header" id="demoPipeline2ChartHeading">
|
||||||
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#demoPipeline2ChartCollapse" aria-expanded="true" aria-controls="demoPipeline2ChartCollapse">
|
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#demoPipeline2ChartCollapse" aria-expanded="true" aria-controls="demoPipeline2ChartCollapse">
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
<!-- file: kb_app/frontend/demo_ws.html -->
|
<!-- file: kb_demo_app/frontend/demo_ws.html -->
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="fr">
|
<html lang="fr">
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
<!-- file: kb_app/frontend/demo_ws_manager.html -->
|
<!-- file: kb_demo_app/frontend/demo_ws_manager.html -->
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="fr">
|
<html lang="fr">
|
||||||
|
|
||||||
|
Before Width: | Height: | Size: 7.6 KiB After Width: | Height: | Size: 7.6 KiB |
|
Before Width: | Height: | Size: 611 KiB After Width: | Height: | Size: 611 KiB |
@@ -1,4 +1,4 @@
|
|||||||
<!-- file: kb_app/frontend/index.html -->
|
<!-- file: kb_demo_app/frontend/index.html -->
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="fr">
|
<html lang="fr">
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// file: kb_app/frontend/sass/_app.scss
|
// file: kb_demo_app/frontend/sass/_app.scss
|
||||||
|
|
||||||
$app-header-height: 48px;
|
$app-header-height: 48px;
|
||||||
$app-footer-height: 48px;
|
$app-footer-height: 48px;
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// file: kb_app/frontend/sass/_bootswatch.scss
|
// file: kb_demo_app/frontend/sass/_bootswatch.scss
|
||||||
|
|
||||||
// Pulse 5.3.8
|
// Pulse 5.3.8
|
||||||
// Bootswatch
|
// Bootswatch
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// file: kb_app/frontend/sass/_fontawesome.scss
|
// file: kb_demo_app/frontend/sass/_fontawesome.scss
|
||||||
|
|
||||||
//@use '@fortawesome/fontawesome-free/scss/variables' with (
|
//@use '@fortawesome/fontawesome-free/scss/variables' with (
|
||||||
// // customizing $font-path - make sure it points to where your webfonts are stored in your project
|
// // customizing $font-path - make sure it points to where your webfonts are stored in your project
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// file: kb_app/frontend/sass/_simplebar.scss
|
// file: kb_demo_app/frontend/sass/_simplebar.scss
|
||||||
|
|
||||||
/* Rtl support */
|
/* Rtl support */
|
||||||
[data-simplebar] {
|
[data-simplebar] {
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// file: kb_app/frontend/sass/_variables.scss
|
// file: kb_demo_app/frontend/sass/_variables.scss
|
||||||
|
|
||||||
// Pulse 5.3.8
|
// Pulse 5.3.8
|
||||||
// Bootswatch
|
// Bootswatch
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// file: kb_app/frontend/sass/main.scss
|
// file: kb_demo_app/frontend/sass/main.scss
|
||||||
|
|
||||||
@import "bootstrap/scss/functions";
|
@import "bootstrap/scss/functions";
|
||||||
@import "variables";
|
@import "variables";
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// file: kb_app/frontend/sass/splash.scss
|
// file: kb_demo_app/frontend/sass/splash.scss
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Dos Amazigh';
|
font-family: 'Dos Amazigh';
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
<!-- file: kb_app/frontend/splash.html -->
|
<!-- file: kb_demo_app/frontend/splash.html -->
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="fr">
|
<html lang="fr">
|
||||||
|
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
/**
|
/**
|
||||||
* Response payload for one demo HTTP execution.
|
* Response payload for one demo HTTP execution.
|
||||||
*/
|
*/
|
||||||
export type KbDemoHttpExecutionPayload = {
|
export type DemoHttpExecutionPayload = {
|
||||||
/**
|
/**
|
||||||
* Selected endpoint name.
|
* Selected endpoint name.
|
||||||
*/
|
*/
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
/**
|
/**
|
||||||
* Request payload for one demo HTTP execution.
|
* Request payload for one demo HTTP execution.
|
||||||
*/
|
*/
|
||||||
export type KbDemoHttpRequest = {
|
export type DemoHttpRequest = {
|
||||||
/**
|
/**
|
||||||
* Logical role used to select one endpoint from the pool.
|
* Logical role used to select one endpoint from the pool.
|
||||||
*/
|
*/
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||||
import type { KbDemoPipeline2CatalogPayload } from "./KbDemoPipeline2CatalogPayload";
|
import type { DemoPipeline2CatalogPayload } from "./DemoPipeline2CatalogPayload";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shared backfill response payload.
|
* Shared backfill response payload.
|
||||||
*/
|
*/
|
||||||
export type KbDemoPipeline2BackfillPayload = {
|
export type DemoPipeline2BackfillPayload = {
|
||||||
/**
|
/**
|
||||||
* Object key used by the backfill.
|
* Object key used by the backfill.
|
||||||
*/
|
*/
|
||||||
@@ -24,4 +24,4 @@ summaryJson: string,
|
|||||||
/**
|
/**
|
||||||
* Refreshed local catalog after backfill.
|
* Refreshed local catalog after backfill.
|
||||||
*/
|
*/
|
||||||
catalog: KbDemoPipeline2CatalogPayload, };
|
catalog: DemoPipeline2CatalogPayload, };
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
/**
|
/**
|
||||||
* Request payload for pool backfill.
|
* Request payload for pool backfill.
|
||||||
*/
|
*/
|
||||||
export type KbDemoPipeline2BackfillPoolRequest = {
|
export type DemoPipeline2BackfillPoolRequest = {
|
||||||
/**
|
/**
|
||||||
* Pool address to backfill.
|
* Pool address to backfill.
|
||||||
*/
|
*/
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
/**
|
/**
|
||||||
* Request payload for token backfill.
|
* Request payload for token backfill.
|
||||||
*/
|
*/
|
||||||
export type KbDemoPipeline2BackfillTokenRequest = {
|
export type DemoPipeline2BackfillTokenRequest = {
|
||||||
/**
|
/**
|
||||||
* Token mint to backfill.
|
* Token mint to backfill.
|
||||||
*/
|
*/
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||||
|
import type { DemoPipeline2PairItem } from "./DemoPipeline2PairItem";
|
||||||
|
import type { DemoPipeline2PoolItem } from "./DemoPipeline2PoolItem";
|
||||||
|
import type { DemoPipeline2TokenItem } from "./DemoPipeline2TokenItem";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Full local catalog payload.
|
||||||
|
*/
|
||||||
|
export type DemoPipeline2CatalogPayload = {
|
||||||
|
/**
|
||||||
|
* Open database URL.
|
||||||
|
*/
|
||||||
|
databaseUrl: string,
|
||||||
|
/**
|
||||||
|
* Observed token list.
|
||||||
|
*/
|
||||||
|
tokens: Array<DemoPipeline2TokenItem>,
|
||||||
|
/**
|
||||||
|
* Known pool list.
|
||||||
|
*/
|
||||||
|
pools: Array<DemoPipeline2PoolItem>,
|
||||||
|
/**
|
||||||
|
* Known pair list.
|
||||||
|
*/
|
||||||
|
pairs: Array<DemoPipeline2PairItem>, };
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
/**
|
/**
|
||||||
* Local decoded-event diagnostics summary for the UI.
|
* Local decoded-event diagnostics summary for the UI.
|
||||||
*/
|
*/
|
||||||
export type KbDemoPipeline2LocalDecodedEventDiagnosticSummary = {
|
export type DemoPipeline2LocalDecodedEventDiagnosticSummary = {
|
||||||
/**
|
/**
|
||||||
* Protocol name.
|
* Protocol name.
|
||||||
*/
|
*/
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
/**
|
/**
|
||||||
* Local DEX diagnostics summary for the UI.
|
* Local DEX diagnostics summary for the UI.
|
||||||
*/
|
*/
|
||||||
export type KbDemoPipeline2LocalDexDiagnosticSummary = {
|
export type DemoPipeline2LocalDexDiagnosticSummary = {
|
||||||
/**
|
/**
|
||||||
* DEX code.
|
* DEX code.
|
||||||
*/
|
*/
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||||
import type { KbDemoPipeline2LocalPipelineDiagnosticSummary } from "./KbDemoPipeline2LocalPipelineDiagnosticSummary";
|
import type { DemoPipeline2LocalPipelineDiagnosticSummary } from "./DemoPipeline2LocalPipelineDiagnosticSummary";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Local diagnostics payload returned to the UI.
|
* Local diagnostics payload returned to the UI.
|
||||||
*/
|
*/
|
||||||
export type KbDemoPipeline2LocalDiagnosticsPayload = {
|
export type DemoPipeline2LocalDiagnosticsPayload = {
|
||||||
/**
|
/**
|
||||||
* Open database URL.
|
* Open database URL.
|
||||||
*/
|
*/
|
||||||
@@ -16,4 +16,4 @@ summaryJson: string,
|
|||||||
/**
|
/**
|
||||||
* Structured diagnostics summary.
|
* Structured diagnostics summary.
|
||||||
*/
|
*/
|
||||||
summary: KbDemoPipeline2LocalPipelineDiagnosticSummary, };
|
summary: DemoPipeline2LocalPipelineDiagnosticSummary, };
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
/**
|
/**
|
||||||
* Local duplicate decoded-event trade diagnostic sample for the UI.
|
* Local duplicate decoded-event trade diagnostic sample for the UI.
|
||||||
*/
|
*/
|
||||||
export type KbDemoPipeline2LocalDuplicateDecodedEventTradeDiagnosticSample = {
|
export type DemoPipeline2LocalDuplicateDecodedEventTradeDiagnosticSample = {
|
||||||
/**
|
/**
|
||||||
* Decoded event id.
|
* Decoded event id.
|
||||||
*/
|
*/
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
/**
|
/**
|
||||||
* Local missing-trade-event diagnostic sample for the UI.
|
* Local missing-trade-event diagnostic sample for the UI.
|
||||||
*/
|
*/
|
||||||
export type KbDemoPipeline2LocalMissingTradeEventDiagnosticSample = {
|
export type DemoPipeline2LocalMissingTradeEventDiagnosticSample = {
|
||||||
/**
|
/**
|
||||||
* Decoded event id.
|
* Decoded event id.
|
||||||
*/
|
*/
|
||||||
@@ -32,6 +32,10 @@ poolAccount: string | null,
|
|||||||
* Whether the source transaction failed.
|
* Whether the source transaction failed.
|
||||||
*/
|
*/
|
||||||
transactionFailed: boolean,
|
transactionFailed: boolean,
|
||||||
|
/**
|
||||||
|
* Whether this missing trade event is actionable for validation.
|
||||||
|
*/
|
||||||
|
actionable: boolean,
|
||||||
/**
|
/**
|
||||||
* Diagnostic reason explaining why no trade event was linked.
|
* Diagnostic reason explaining why no trade event was linked.
|
||||||
*/
|
*/
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Local missing-trade-event reason summary for the UI.
|
||||||
|
*/
|
||||||
|
export type DemoPipeline2LocalMissingTradeEventReasonSummary = {
|
||||||
|
/**
|
||||||
|
* Diagnostic reason.
|
||||||
|
*/
|
||||||
|
reason: string,
|
||||||
|
/**
|
||||||
|
* Whether grouped source transactions failed.
|
||||||
|
*/
|
||||||
|
transactionFailed: boolean,
|
||||||
|
/**
|
||||||
|
* Whether grouped missing trade events are actionable.
|
||||||
|
*/
|
||||||
|
actionable: boolean,
|
||||||
|
/**
|
||||||
|
* Total missing trade events in this group.
|
||||||
|
*/
|
||||||
|
eventCount: number,
|
||||||
|
/**
|
||||||
|
* Total events in this group with an explicit base amount payload.
|
||||||
|
*/
|
||||||
|
hasBaseAmountPayloadCount: number,
|
||||||
|
/**
|
||||||
|
* Total events in this group with an explicit quote amount payload.
|
||||||
|
*/
|
||||||
|
hasQuoteAmountPayloadCount: number,
|
||||||
|
/**
|
||||||
|
* Total events in this group with an explicit price payload.
|
||||||
|
*/
|
||||||
|
hasPricePayloadCount: number, };
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
/**
|
/**
|
||||||
* Local multi-trade signature/pair diagnostic sample for the UI.
|
* Local multi-trade signature/pair diagnostic sample for the UI.
|
||||||
*/
|
*/
|
||||||
export type KbDemoPipeline2LocalMultiTradeSignaturePairDiagnosticSample = {
|
export type DemoPipeline2LocalMultiTradeSignaturePairDiagnosticSample = {
|
||||||
/**
|
/**
|
||||||
* Transaction signature.
|
* Transaction signature.
|
||||||
*/
|
*/
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Local non-actionable pair diagnostic summary for the UI.
|
||||||
|
*/
|
||||||
|
export type DemoPipeline2LocalNonActionablePairDiagnosticSummary = {
|
||||||
|
/**
|
||||||
|
* Pair id.
|
||||||
|
*/
|
||||||
|
pairId: number,
|
||||||
|
/**
|
||||||
|
* Pool address.
|
||||||
|
*/
|
||||||
|
poolAddress: string,
|
||||||
|
/**
|
||||||
|
* DEX code.
|
||||||
|
*/
|
||||||
|
dexCode: string,
|
||||||
|
/**
|
||||||
|
* Base token mint.
|
||||||
|
*/
|
||||||
|
baseMint: string,
|
||||||
|
/**
|
||||||
|
* Base token symbol.
|
||||||
|
*/
|
||||||
|
baseSymbol: string | null,
|
||||||
|
/**
|
||||||
|
* Quote token mint.
|
||||||
|
*/
|
||||||
|
quoteMint: string,
|
||||||
|
/**
|
||||||
|
* Quote token symbol.
|
||||||
|
*/
|
||||||
|
quoteSymbol: string | null,
|
||||||
|
/**
|
||||||
|
* Pair symbol.
|
||||||
|
*/
|
||||||
|
pairSymbol: string | null,
|
||||||
|
/**
|
||||||
|
* Total decoded trade candidates attached to the pool.
|
||||||
|
*/
|
||||||
|
decodedTradeCandidateCount: number,
|
||||||
|
/**
|
||||||
|
* Total non-actionable missing trade events attached to the pair.
|
||||||
|
*/
|
||||||
|
nonActionableMissingTradeEventCount: number,
|
||||||
|
/**
|
||||||
|
* Total missing trade candidates caused by failed transactions.
|
||||||
|
*/
|
||||||
|
failedTransactionCandidateCount: number,
|
||||||
|
/**
|
||||||
|
* Total successful missing trade candidates without token mints.
|
||||||
|
*/
|
||||||
|
okTransactionWithoutTokenMintsCount: number,
|
||||||
|
/**
|
||||||
|
* Total successful missing trade candidates without amount payload.
|
||||||
|
*/
|
||||||
|
okTransactionWithoutAmountPayloadCount: number,
|
||||||
|
/**
|
||||||
|
* Distinct non-actionable diagnostic reasons seen for this pair.
|
||||||
|
*/
|
||||||
|
reasonSummary: string | null,
|
||||||
|
/**
|
||||||
|
* Total trade events attached to the pair.
|
||||||
|
*/
|
||||||
|
tradeEventCount: number,
|
||||||
|
/**
|
||||||
|
* Total candle buckets attached to the pair.
|
||||||
|
*/
|
||||||
|
pairCandleCount: number, };
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
/**
|
/**
|
||||||
* Local pair diagnostics summary for the UI.
|
* Local pair diagnostics summary for the UI.
|
||||||
*/
|
*/
|
||||||
export type KbDemoPipeline2LocalPairDiagnosticSummary = {
|
export type DemoPipeline2LocalPairDiagnosticSummary = {
|
||||||
/**
|
/**
|
||||||
* Pair id.
|
* Pair id.
|
||||||
*/
|
*/
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
/**
|
/**
|
||||||
* Local pair gap diagnostic sample for the UI.
|
* Local pair gap diagnostic sample for the UI.
|
||||||
*/
|
*/
|
||||||
export type KbDemoPipeline2LocalPairGapDiagnosticSample = {
|
export type DemoPipeline2LocalPairGapDiagnosticSample = {
|
||||||
/**
|
/**
|
||||||
* Pair id.
|
* Pair id.
|
||||||
*/
|
*/
|
||||||
@@ -1,16 +1,18 @@
|
|||||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||||
import type { KbDemoPipeline2LocalDecodedEventDiagnosticSummary } from "./KbDemoPipeline2LocalDecodedEventDiagnosticSummary";
|
import type { DemoPipeline2LocalDecodedEventDiagnosticSummary } from "./DemoPipeline2LocalDecodedEventDiagnosticSummary";
|
||||||
import type { KbDemoPipeline2LocalDexDiagnosticSummary } from "./KbDemoPipeline2LocalDexDiagnosticSummary";
|
import type { DemoPipeline2LocalDexDiagnosticSummary } from "./DemoPipeline2LocalDexDiagnosticSummary";
|
||||||
import type { KbDemoPipeline2LocalDuplicateDecodedEventTradeDiagnosticSample } from "./KbDemoPipeline2LocalDuplicateDecodedEventTradeDiagnosticSample";
|
import type { DemoPipeline2LocalDuplicateDecodedEventTradeDiagnosticSample } from "./DemoPipeline2LocalDuplicateDecodedEventTradeDiagnosticSample";
|
||||||
import type { KbDemoPipeline2LocalMissingTradeEventDiagnosticSample } from "./KbDemoPipeline2LocalMissingTradeEventDiagnosticSample";
|
import type { DemoPipeline2LocalMissingTradeEventDiagnosticSample } from "./DemoPipeline2LocalMissingTradeEventDiagnosticSample";
|
||||||
import type { KbDemoPipeline2LocalMultiTradeSignaturePairDiagnosticSample } from "./KbDemoPipeline2LocalMultiTradeSignaturePairDiagnosticSample";
|
import type { DemoPipeline2LocalMissingTradeEventReasonSummary } from "./DemoPipeline2LocalMissingTradeEventReasonSummary";
|
||||||
import type { KbDemoPipeline2LocalPairDiagnosticSummary } from "./KbDemoPipeline2LocalPairDiagnosticSummary";
|
import type { DemoPipeline2LocalMultiTradeSignaturePairDiagnosticSample } from "./DemoPipeline2LocalMultiTradeSignaturePairDiagnosticSample";
|
||||||
import type { KbDemoPipeline2LocalPairGapDiagnosticSample } from "./KbDemoPipeline2LocalPairGapDiagnosticSample";
|
import type { DemoPipeline2LocalNonActionablePairDiagnosticSummary } from "./DemoPipeline2LocalNonActionablePairDiagnosticSummary";
|
||||||
|
import type { DemoPipeline2LocalPairDiagnosticSummary } from "./DemoPipeline2LocalPairDiagnosticSummary";
|
||||||
|
import type { DemoPipeline2LocalPairGapDiagnosticSample } from "./DemoPipeline2LocalPairGapDiagnosticSample";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Local pipeline diagnostics summary for the UI.
|
* Local pipeline diagnostics summary for the UI.
|
||||||
*/
|
*/
|
||||||
export type KbDemoPipeline2LocalPipelineDiagnosticSummary = {
|
export type DemoPipeline2LocalPipelineDiagnosticSummary = {
|
||||||
/**
|
/**
|
||||||
* Total persisted chain transactions.
|
* Total persisted chain transactions.
|
||||||
*/
|
*/
|
||||||
@@ -119,32 +121,44 @@ pairWithoutCandleCount: number,
|
|||||||
/**
|
/**
|
||||||
* Diagnostics grouped by DEX.
|
* Diagnostics grouped by DEX.
|
||||||
*/
|
*/
|
||||||
dexSummaries: Array<KbDemoPipeline2LocalDexDiagnosticSummary>,
|
dexSummaries: Array<DemoPipeline2LocalDexDiagnosticSummary>,
|
||||||
/**
|
/**
|
||||||
* Diagnostics grouped by pair.
|
* Diagnostics grouped by pair.
|
||||||
*/
|
*/
|
||||||
pairSummaries: Array<KbDemoPipeline2LocalPairDiagnosticSummary>,
|
pairSummaries: Array<DemoPipeline2LocalPairDiagnosticSummary>,
|
||||||
/**
|
/**
|
||||||
* Diagnostics grouped by decoded event kind.
|
* Diagnostics grouped by decoded event kind.
|
||||||
*/
|
*/
|
||||||
decodedEventSummaries: Array<KbDemoPipeline2LocalDecodedEventDiagnosticSummary>,
|
decodedEventSummaries: Array<DemoPipeline2LocalDecodedEventDiagnosticSummary>,
|
||||||
|
/**
|
||||||
|
* Missing trade events grouped by diagnostic reason.
|
||||||
|
*/
|
||||||
|
missingTradeEventReasonSummaries: Array<DemoPipeline2LocalMissingTradeEventReasonSummary>,
|
||||||
|
/**
|
||||||
|
* Total pairs with only non-actionable missing trade events.
|
||||||
|
*/
|
||||||
|
nonActionablePairCount: number,
|
||||||
|
/**
|
||||||
|
* Pair summaries for non-actionable missing trade events.
|
||||||
|
*/
|
||||||
|
nonActionablePairSummaries: Array<DemoPipeline2LocalNonActionablePairDiagnosticSummary>,
|
||||||
/**
|
/**
|
||||||
* Samples of decoded trade candidates without linked trade event.
|
* Samples of decoded trade candidates without linked trade event.
|
||||||
*/
|
*/
|
||||||
missingTradeEventSamples: Array<KbDemoPipeline2LocalMissingTradeEventDiagnosticSample>,
|
missingTradeEventSamples: Array<DemoPipeline2LocalMissingTradeEventDiagnosticSample>,
|
||||||
/**
|
/**
|
||||||
* Samples of duplicated trade rows by decoded event id.
|
* Samples of duplicated trade rows by decoded event id.
|
||||||
*/
|
*/
|
||||||
duplicateDecodedEventTradeSamples: Array<KbDemoPipeline2LocalDuplicateDecodedEventTradeDiagnosticSample>,
|
duplicateDecodedEventTradeSamples: Array<DemoPipeline2LocalDuplicateDecodedEventTradeDiagnosticSample>,
|
||||||
/**
|
/**
|
||||||
* Samples of multi-trade signature/pair groups.
|
* Samples of multi-trade signature/pair groups.
|
||||||
*/
|
*/
|
||||||
multiTradeSignaturePairSamples: Array<KbDemoPipeline2LocalMultiTradeSignaturePairDiagnosticSample>,
|
multiTradeSignaturePairSamples: Array<DemoPipeline2LocalMultiTradeSignaturePairDiagnosticSample>,
|
||||||
/**
|
/**
|
||||||
* Samples of pairs without trade.
|
* Samples of pairs without trade.
|
||||||
*/
|
*/
|
||||||
pairWithoutTradeSamples: Array<KbDemoPipeline2LocalPairGapDiagnosticSample>,
|
pairWithoutTradeSamples: Array<DemoPipeline2LocalPairGapDiagnosticSample>,
|
||||||
/**
|
/**
|
||||||
* Samples of pairs without candle.
|
* Samples of pairs without candle.
|
||||||
*/
|
*/
|
||||||
pairWithoutCandleSamples: Array<KbDemoPipeline2LocalPairGapDiagnosticSample>, };
|
pairWithoutCandleSamples: Array<DemoPipeline2LocalPairGapDiagnosticSample>, };
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* One local pipeline validation issue for the UI.
|
||||||
|
*/
|
||||||
|
export type DemoPipeline2LocalPipelineValidationIssue = {
|
||||||
|
/**
|
||||||
|
* Stable machine-readable issue code.
|
||||||
|
*/
|
||||||
|
code: string,
|
||||||
|
/**
|
||||||
|
* Human-readable issue message.
|
||||||
|
*/
|
||||||
|
message: string,
|
||||||
|
/**
|
||||||
|
* Optional subject associated with the issue.
|
||||||
|
*/
|
||||||
|
subject: string | null,
|
||||||
|
/**
|
||||||
|
* Whether the issue blocks the validation report.
|
||||||
|
*/
|
||||||
|
blocking: boolean, };
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||||
|
import type { DemoPipeline2LocalPipelineValidationIssue } from "./DemoPipeline2LocalPipelineValidationIssue";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Local pipeline validation report for the UI.
|
||||||
|
*/
|
||||||
|
export type DemoPipeline2LocalPipelineValidationReport = {
|
||||||
|
/**
|
||||||
|
* Stable validation profile code used to produce the report.
|
||||||
|
*/
|
||||||
|
validationProfileCode: string,
|
||||||
|
/**
|
||||||
|
* Whether the validation passed without blocking issues.
|
||||||
|
*/
|
||||||
|
validationPassed: boolean,
|
||||||
|
/**
|
||||||
|
* Number of blocking issues found by validation.
|
||||||
|
*/
|
||||||
|
blockingIssueCount: number,
|
||||||
|
/**
|
||||||
|
* Number of non-blocking warnings found by validation.
|
||||||
|
*/
|
||||||
|
warningCount: number,
|
||||||
|
/**
|
||||||
|
* Expected DEX codes used by the validation run.
|
||||||
|
*/
|
||||||
|
expectedDexCodes: Array<string>,
|
||||||
|
/**
|
||||||
|
* Observed DEX codes found in diagnostics.
|
||||||
|
*/
|
||||||
|
observedDexCodes: Array<string>,
|
||||||
|
/**
|
||||||
|
* Issues produced by validation.
|
||||||
|
*/
|
||||||
|
issues: Array<DemoPipeline2LocalPipelineValidationIssue>, };
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||||
|
import type { DemoPipeline2LocalPipelineDiagnosticSummary } from "./DemoPipeline2LocalPipelineDiagnosticSummary";
|
||||||
|
import type { DemoPipeline2LocalPipelineValidationReport } from "./DemoPipeline2LocalPipelineValidationReport";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Local pipeline validation run for the UI.
|
||||||
|
*/
|
||||||
|
export type DemoPipeline2LocalPipelineValidationRun = {
|
||||||
|
/**
|
||||||
|
* Stable validation profile code used to produce the run.
|
||||||
|
*/
|
||||||
|
validationProfileCode: string,
|
||||||
|
/**
|
||||||
|
* Whether the validation passed without blocking issues.
|
||||||
|
*/
|
||||||
|
validationPassed: boolean,
|
||||||
|
/**
|
||||||
|
* Number of blocking issues found by validation.
|
||||||
|
*/
|
||||||
|
blockingIssueCount: number,
|
||||||
|
/**
|
||||||
|
* Number of non-blocking warnings found by validation.
|
||||||
|
*/
|
||||||
|
warningCount: number,
|
||||||
|
/**
|
||||||
|
* Diagnostics summary that was validated.
|
||||||
|
*/
|
||||||
|
summary: DemoPipeline2LocalPipelineDiagnosticSummary,
|
||||||
|
/**
|
||||||
|
* Detailed validation report produced from the diagnostics summary.
|
||||||
|
*/
|
||||||
|
report: DemoPipeline2LocalPipelineValidationReport, };
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||||
|
import type { DemoPipeline2LocalPipelineValidationRun } from "./DemoPipeline2LocalPipelineValidationRun";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Local validation payload returned to the UI.
|
||||||
|
*/
|
||||||
|
export type DemoPipeline2LocalValidationPayload = {
|
||||||
|
/**
|
||||||
|
* Open database URL.
|
||||||
|
*/
|
||||||
|
databaseUrl: string,
|
||||||
|
/**
|
||||||
|
* Pretty JSON diagnostics summary used by validation.
|
||||||
|
*/
|
||||||
|
summaryJson: string,
|
||||||
|
/**
|
||||||
|
* Pretty JSON validation run.
|
||||||
|
*/
|
||||||
|
validationJson: string,
|
||||||
|
/**
|
||||||
|
* Structured validation run.
|
||||||
|
*/
|
||||||
|
run: DemoPipeline2LocalPipelineValidationRun, };
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
/**
|
/**
|
||||||
* Candle payload returned to the UI.
|
* Candle payload returned to the UI.
|
||||||
*/
|
*/
|
||||||
export type KbDemoPipeline2PairCandlesPayload = {
|
export type DemoPipeline2PairCandlesPayload = {
|
||||||
/**
|
/**
|
||||||
* Pair id.
|
* Pair id.
|
||||||
*/
|
*/
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
/**
|
/**
|
||||||
* Request payload for pair candles.
|
* Request payload for pair candles.
|
||||||
*/
|
*/
|
||||||
export type KbDemoPipeline2PairCandlesRequest = {
|
export type DemoPipeline2PairCandlesRequest = {
|
||||||
/**
|
/**
|
||||||
* Pair id to load.
|
* Pair id to load.
|
||||||
*/
|
*/
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
/**
|
/**
|
||||||
* One pair item for the local catalog.
|
* One pair item for the local catalog.
|
||||||
*/
|
*/
|
||||||
export type KbDemoPipeline2PairItem = {
|
export type DemoPipeline2PairItem = {
|
||||||
/**
|
/**
|
||||||
* Internal pair id.
|
* Internal pair id.
|
||||||
*/
|
*/
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
/**
|
/**
|
||||||
* One pool item for the local catalog.
|
* One pool item for the local catalog.
|
||||||
*/
|
*/
|
||||||
export type KbDemoPipeline2PoolItem = {
|
export type DemoPipeline2PoolItem = {
|
||||||
/**
|
/**
|
||||||
* Pool address.
|
* Pool address.
|
||||||
*/
|
*/
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
/**
|
/**
|
||||||
* One token item for the local catalog.
|
* One token item for the local catalog.
|
||||||
*/
|
*/
|
||||||
export type KbDemoPipeline2TokenItem = {
|
export type DemoPipeline2TokenItem = {
|
||||||
/**
|
/**
|
||||||
* Token mint.
|
* Token mint.
|
||||||
*/
|
*/
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Response payload for one pool backfill launched from `kb_app`.
|
* Response payload for one pool backfill launched from `kb_demo_app`.
|
||||||
*/
|
*/
|
||||||
export type KbDemoPipelineBackfillPoolPayload = {
|
export type DemoPipelineBackfillPoolPayload = {
|
||||||
/**
|
/**
|
||||||
* Backfilled pool address.
|
* Backfilled pool address.
|
||||||
*/
|
*/
|
||||||
@@ -13,7 +13,7 @@ poolAddress: string,
|
|||||||
*/
|
*/
|
||||||
httpRole: string,
|
httpRole: string,
|
||||||
/**
|
/**
|
||||||
* Pretty JSON summary returned by `KbTokenBackfillService::backfill_pool_by_address`.
|
* Pretty JSON summary returned by `TokenBackfillService::backfill_pool_by_address`.
|
||||||
*/
|
*/
|
||||||
backfillJson: string,
|
backfillJson: string,
|
||||||
/**
|
/**
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Request payload for one pool backfill launched from `kb_app`.
|
* Request payload for one pool backfill launched from `kb_demo_app`.
|
||||||
*/
|
*/
|
||||||
export type KbDemoPipelineBackfillPoolRequest = {
|
export type DemoPipelineBackfillPoolRequest = {
|
||||||
/**
|
/**
|
||||||
* Pool address to backfill.
|
* Pool address to backfill.
|
||||||
*/
|
*/
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Response payload for one token backfill launched from `kb_app`.
|
* Response payload for one token backfill launched from `kb_demo_app`.
|
||||||
*/
|
*/
|
||||||
export type KbDemoPipelineBackfillTokenPayload = {
|
export type DemoPipelineBackfillTokenPayload = {
|
||||||
/**
|
/**
|
||||||
* Backfilled token mint.
|
* Backfilled token mint.
|
||||||
*/
|
*/
|
||||||
@@ -13,7 +13,7 @@ tokenMint: string,
|
|||||||
*/
|
*/
|
||||||
httpRole: string,
|
httpRole: string,
|
||||||
/**
|
/**
|
||||||
* Pretty JSON summary returned by `KbTokenBackfillService`.
|
* Pretty JSON summary returned by `TokenBackfillService`.
|
||||||
*/
|
*/
|
||||||
backfillJson: string,
|
backfillJson: string,
|
||||||
/**
|
/**
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Request payload for one token backfill launched from `kb_app`.
|
* Request payload for one token backfill launched from `kb_demo_app`.
|
||||||
*/
|
*/
|
||||||
export type KbDemoPipelineBackfillTokenRequest = {
|
export type DemoPipelineBackfillTokenRequest = {
|
||||||
/**
|
/**
|
||||||
* Token mint to backfill.
|
* Token mint to backfill.
|
||||||
*/
|
*/
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
/**
|
/**
|
||||||
* Request payload for one pipeline inspection by pair id.
|
* Request payload for one pipeline inspection by pair id.
|
||||||
*/
|
*/
|
||||||
export type KbDemoPipelineInspectPairRequest = {
|
export type DemoPipelineInspectPairRequest = {
|
||||||
/**
|
/**
|
||||||
* Pair id to inspect.
|
* Pair id to inspect.
|
||||||
*/
|
*/
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
/**
|
/**
|
||||||
* Response payload for one pipeline inspection.
|
* Response payload for one pipeline inspection.
|
||||||
*/
|
*/
|
||||||
export type KbDemoPipelineInspectPayload = {
|
export type DemoPipelineInspectPayload = {
|
||||||
/**
|
/**
|
||||||
* Inspected signature.
|
* Inspected signature.
|
||||||
*/
|
*/
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
/**
|
/**
|
||||||
* Request payload for one pipeline inspection by pool address.
|
* Request payload for one pipeline inspection by pool address.
|
||||||
*/
|
*/
|
||||||
export type KbDemoPipelineInspectPoolRequest = {
|
export type DemoPipelineInspectPoolRequest = {
|
||||||
/**
|
/**
|
||||||
* Pool address to inspect.
|
* Pool address to inspect.
|
||||||
*/
|
*/
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
/**
|
/**
|
||||||
* Request payload for one pipeline inspection by signature.
|
* Request payload for one pipeline inspection by signature.
|
||||||
*/
|
*/
|
||||||
export type KbDemoPipelineInspectRequest = {
|
export type DemoPipelineInspectRequest = {
|
||||||
/**
|
/**
|
||||||
* Transaction signature to inspect.
|
* Transaction signature to inspect.
|
||||||
*/
|
*/
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
/**
|
/**
|
||||||
* Request payload for one pipeline inspection by token mint.
|
* Request payload for one pipeline inspection by token mint.
|
||||||
*/
|
*/
|
||||||
export type KbDemoPipelineInspectTokenRequest = {
|
export type DemoPipelineInspectTokenRequest = {
|
||||||
/**
|
/**
|
||||||
* Token mint to inspect.
|
* Token mint to inspect.
|
||||||
*/
|
*/
|
||||||
@@ -3,4 +3,4 @@
|
|||||||
/**
|
/**
|
||||||
* Endpoint summary sent to the demo frontend.
|
* Endpoint summary sent to the demo frontend.
|
||||||
*/
|
*/
|
||||||
export type KbDemoWsEndpointSummary = { name: string, resolvedUrl: string, provider: string, enabled: boolean, roles: Array<string>, };
|
export type DemoWsEndpointSummary = { name: string, resolvedUrl: string, provider: string, enabled: boolean, roles: Array<string>, };
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Static endpoint summary enriched with current manager state.
|
||||||
|
*/
|
||||||
|
export type DemoWsManagerEndpointSummary = { name: string, resolvedUrl: string, provider: string, roles: Array<string>, connectionState: string, activeSubscriptionCount: number, };
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||||
|
import type { DemoWsManagerEndpointSummary } from "./DemoWsManagerEndpointSummary";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Global demo manager snapshot payload.
|
||||||
|
*/
|
||||||
|
export type DemoWsManagerSnapshotPayload = { endpointCount: number, startedCount: number, endpoints: Array<DemoWsManagerEndpointSummary>, };
|
||||||
6
kb_demo_app/frontend/ts/bindings/DemoWsStatusPayload.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Current demo window runtime status.
|
||||||
|
*/
|
||||||
|
export type DemoWsStatusPayload = { connectionState: string, endpointName: string | null, endpointUrl: string | null, currentSubscriptionId: bigint | null, currentSubscribeMethod: string | null, currentUnsubscribeMethod: string | null, currentNotificationMethod: string | null, eventCountTotal: bigint, notificationCountTotal: bigint, uiLogCount: bigint, suppressedLogCount: bigint, lastEventKind: string | null, };
|
||||||
@@ -3,4 +3,4 @@
|
|||||||
/**
|
/**
|
||||||
* Subscribe request sent by the demo frontend.
|
* Subscribe request sent by the demo frontend.
|
||||||
*/
|
*/
|
||||||
export type KbDemoWsSubscribeRequest = { method: string, mode: string, target: string | null, filterJson: string | null, configJson: string | null, };
|
export type DemoWsSubscribeRequest = { method: string, mode: string, target: string | null, filterJson: string | null, configJson: string | null, };
|
||||||
@@ -3,8 +3,8 @@ import "simplebar";
|
|||||||
import ResizeObserver from "resize-observer-polyfill";
|
import ResizeObserver from "resize-observer-polyfill";
|
||||||
import { invoke } from "@tauri-apps/api/core";
|
import { invoke } from "@tauri-apps/api/core";
|
||||||
import { debug, takeoverConsole } from "@fltsci/tauri-plugin-tracing";
|
import { debug, takeoverConsole } from "@fltsci/tauri-plugin-tracing";
|
||||||
import { KbDemoHttpRequest } from './bindings/KbDemoHttpRequest.ts';
|
import { DemoHttpRequest } from './bindings/DemoHttpRequest.ts';
|
||||||
import { KbDemoHttpExecutionPayload } from './bindings/KbDemoHttpExecutionPayload.ts';
|
import { DemoHttpExecutionPayload } from './bindings/DemoHttpExecutionPayload.ts';
|
||||||
|
|
||||||
(window as Window & typeof globalThis & { bootstrap?: typeof bootstrap }).bootstrap = bootstrap;
|
(window as Window & typeof globalThis & { bootstrap?: typeof bootstrap }).bootstrap = bootstrap;
|
||||||
(window as Window & typeof globalThis & { ResizeObserver?: typeof ResizeObserver }).ResizeObserver = ResizeObserver;
|
(window as Window & typeof globalThis & { ResizeObserver?: typeof ResizeObserver }).ResizeObserver = ResizeObserver;
|
||||||
@@ -385,7 +385,7 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
executeButton.addEventListener("click", async () => {
|
executeButton.addEventListener("click", async () => {
|
||||||
const request: KbDemoHttpRequest = {
|
const request: DemoHttpRequest = {
|
||||||
role: roleSelect.value,
|
role: roleSelect.value,
|
||||||
method: methodSelect.value,
|
method: methodSelect.value,
|
||||||
firstArg: firstArgInput.value.trim() === "" ? null : firstArgInput.value.trim(),
|
firstArg: firstArgInput.value.trim() === "" ? null : firstArgInput.value.trim(),
|
||||||
@@ -393,7 +393,7 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await invoke<KbDemoHttpExecutionPayload>("demo_http_execute_request", { request });
|
const response = await invoke<DemoHttpExecutionPayload>("demo_http_execute_request", { request });
|
||||||
|
|
||||||
lastEndpointText.textContent = `${response.endpointName} (${response.endpointUrl})`;
|
lastEndpointText.textContent = `${response.endpointName} (${response.endpointUrl})`;
|
||||||
lastProviderText.textContent = response.provider;
|
lastProviderText.textContent = response.provider;
|
||||||
@@ -1,19 +1,19 @@
|
|||||||
// file: kb_app/frontend/ts/demo_pipeline.ts
|
// file: kb_demo_app/frontend/ts/demo_pipeline.ts
|
||||||
|
|
||||||
import * as bootstrap from "bootstrap";
|
import * as bootstrap from "bootstrap";
|
||||||
import "simplebar";
|
import "simplebar";
|
||||||
import ResizeObserver from "resize-observer-polyfill";
|
import ResizeObserver from "resize-observer-polyfill";
|
||||||
import { invoke } from "@tauri-apps/api/core";
|
import { invoke } from "@tauri-apps/api/core";
|
||||||
import { debug, takeoverConsole } from "@fltsci/tauri-plugin-tracing";
|
import { debug, takeoverConsole } from "@fltsci/tauri-plugin-tracing";
|
||||||
import { KbDemoPipelineInspectRequest } from './bindings/KbDemoPipelineInspectRequest.ts';
|
import { DemoPipelineInspectRequest } from './bindings/DemoPipelineInspectRequest.ts';
|
||||||
import { KbDemoPipelineInspectPayload } from './bindings/KbDemoPipelineInspectPayload.ts';
|
import { DemoPipelineInspectPayload } from './bindings/DemoPipelineInspectPayload.ts';
|
||||||
import { KbDemoPipelineInspectTokenRequest } from './bindings/KbDemoPipelineInspectTokenRequest.ts';
|
import { DemoPipelineInspectTokenRequest } from './bindings/DemoPipelineInspectTokenRequest.ts';
|
||||||
import { KbDemoPipelineInspectPairRequest } from './bindings/KbDemoPipelineInspectPairRequest.ts';
|
import { DemoPipelineInspectPairRequest } from './bindings/DemoPipelineInspectPairRequest.ts';
|
||||||
import { KbDemoPipelineInspectPoolRequest } from './bindings/KbDemoPipelineInspectPoolRequest.ts';
|
import { DemoPipelineInspectPoolRequest } from './bindings/DemoPipelineInspectPoolRequest.ts';
|
||||||
import { KbDemoPipelineBackfillTokenRequest } from './bindings/KbDemoPipelineBackfillTokenRequest.ts';
|
import { DemoPipelineBackfillTokenRequest } from './bindings/DemoPipelineBackfillTokenRequest.ts';
|
||||||
import { KbDemoPipelineBackfillTokenPayload } from './bindings/KbDemoPipelineBackfillTokenPayload.ts';
|
import { DemoPipelineBackfillTokenPayload } from './bindings/DemoPipelineBackfillTokenPayload.ts';
|
||||||
import { KbDemoPipelineBackfillPoolRequest } from './bindings/KbDemoPipelineBackfillPoolRequest.ts';
|
import { DemoPipelineBackfillPoolRequest } from './bindings/DemoPipelineBackfillPoolRequest.ts';
|
||||||
import { KbDemoPipelineBackfillPoolPayload } from './bindings/KbDemoPipelineBackfillPoolPayload.ts';
|
import { DemoPipelineBackfillPoolPayload } from './bindings/DemoPipelineBackfillPoolPayload.ts';
|
||||||
import * as echarts from "echarts";
|
import * as echarts from "echarts";
|
||||||
(window as Window & typeof globalThis & { bootstrap?: typeof bootstrap }).bootstrap = bootstrap;
|
(window as Window & typeof globalThis & { bootstrap?: typeof bootstrap }).bootstrap = bootstrap;
|
||||||
(window as Window & typeof globalThis & { ResizeObserver?: typeof ResizeObserver }).ResizeObserver = ResizeObserver;
|
(window as Window & typeof globalThis & { ResizeObserver?: typeof ResizeObserver }).ResizeObserver = ResizeObserver;
|
||||||
@@ -362,7 +362,7 @@ function renderSelectedCandlesChart(
|
|||||||
}
|
}
|
||||||
|
|
||||||
function applyInspectionPayload(
|
function applyInspectionPayload(
|
||||||
payload: KbDemoPipelineInspectPayload,
|
payload: DemoPipelineInspectPayload,
|
||||||
summaryTextarea: HTMLTextAreaElement,
|
summaryTextarea: HTMLTextAreaElement,
|
||||||
transactionTextarea: HTMLTextAreaElement,
|
transactionTextarea: HTMLTextAreaElement,
|
||||||
decodedEventsTextarea: HTMLTextAreaElement,
|
decodedEventsTextarea: HTMLTextAreaElement,
|
||||||
@@ -688,14 +688,14 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|||||||
`[ui] launching pool backfill for '${poolAddress}' with role '${httpRole ?? "history_backfill"}' (pool=${poolSignatureLimit})`,
|
`[ui] launching pool backfill for '${poolAddress}' with role '${httpRole ?? "history_backfill"}' (pool=${poolSignatureLimit})`,
|
||||||
);
|
);
|
||||||
|
|
||||||
const request: KbDemoPipelineBackfillPoolRequest = {
|
const request: DemoPipelineBackfillPoolRequest = {
|
||||||
poolAddress,
|
poolAddress,
|
||||||
httpRole,
|
httpRole,
|
||||||
poolSignatureLimit,
|
poolSignatureLimit,
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const payload = await invoke<KbDemoPipelineBackfillPoolPayload>(
|
const payload = await invoke<DemoPipelineBackfillPoolPayload>(
|
||||||
"demo_pipeline_backfill_pool_address",
|
"demo_pipeline_backfill_pool_address",
|
||||||
{ request },
|
{ request },
|
||||||
);
|
);
|
||||||
@@ -714,13 +714,13 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const inspectRequest: KbDemoPipelineInspectPoolRequest = {
|
const inspectRequest: DemoPipelineInspectPoolRequest = {
|
||||||
poolAddress: payload.poolAddress,
|
poolAddress: payload.poolAddress,
|
||||||
customTimeframeSeconds: null,
|
customTimeframeSeconds: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const inspectPayload = await invoke<KbDemoPipelineInspectPayload>(
|
const inspectPayload = await invoke<DemoPipelineInspectPayload>(
|
||||||
"demo_pipeline_inspect_pool_address",
|
"demo_pipeline_inspect_pool_address",
|
||||||
{ request: inspectRequest },
|
{ request: inspectRequest },
|
||||||
);
|
);
|
||||||
@@ -787,13 +787,13 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|||||||
`[ui] inspecting signature '${signature}'${customTimeframeSeconds === null ? "" : ` with custom timeframe ${customTimeframeSeconds}s`}`,
|
`[ui] inspecting signature '${signature}'${customTimeframeSeconds === null ? "" : ` with custom timeframe ${customTimeframeSeconds}s`}`,
|
||||||
);
|
);
|
||||||
|
|
||||||
const request: KbDemoPipelineInspectRequest = {
|
const request: DemoPipelineInspectRequest = {
|
||||||
signature,
|
signature,
|
||||||
customTimeframeSeconds,
|
customTimeframeSeconds,
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const payload = await invoke<KbDemoPipelineInspectPayload>("demo_pipeline_inspect_signature", { request });
|
const payload = await invoke<DemoPipelineInspectPayload>("demo_pipeline_inspect_signature", { request });
|
||||||
|
|
||||||
applyInspectionPayload(
|
applyInspectionPayload(
|
||||||
payload,
|
payload,
|
||||||
@@ -844,13 +844,13 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|||||||
`[ui] inspecting token mint '${tokenMint}'${customTimeframeSeconds === null ? "" : ` with custom timeframe ${customTimeframeSeconds}s`}`,
|
`[ui] inspecting token mint '${tokenMint}'${customTimeframeSeconds === null ? "" : ` with custom timeframe ${customTimeframeSeconds}s`}`,
|
||||||
);
|
);
|
||||||
|
|
||||||
const request: KbDemoPipelineInspectTokenRequest = {
|
const request: DemoPipelineInspectTokenRequest = {
|
||||||
tokenMint,
|
tokenMint,
|
||||||
customTimeframeSeconds,
|
customTimeframeSeconds,
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const payload = await invoke<KbDemoPipelineInspectPayload>("demo_pipeline_inspect_token_mint", { request });
|
const payload = await invoke<DemoPipelineInspectPayload>("demo_pipeline_inspect_token_mint", { request });
|
||||||
|
|
||||||
applyInspectionPayload(
|
applyInspectionPayload(
|
||||||
payload,
|
payload,
|
||||||
@@ -901,13 +901,13 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|||||||
`[ui] inspecting pair id '${parsedPairId}'${customTimeframeSeconds === null ? "" : ` with custom timeframe ${customTimeframeSeconds}s`}`,
|
`[ui] inspecting pair id '${parsedPairId}'${customTimeframeSeconds === null ? "" : ` with custom timeframe ${customTimeframeSeconds}s`}`,
|
||||||
);
|
);
|
||||||
|
|
||||||
const request: KbDemoPipelineInspectPairRequest = {
|
const request: DemoPipelineInspectPairRequest = {
|
||||||
pairId: BigInt(parsedPairId),
|
pairId: BigInt(parsedPairId),
|
||||||
customTimeframeSeconds,
|
customTimeframeSeconds,
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const payload = await invoke<KbDemoPipelineInspectPayload>("demo_pipeline_inspect_pair_id", { request });
|
const payload = await invoke<DemoPipelineInspectPayload>("demo_pipeline_inspect_pair_id", { request });
|
||||||
|
|
||||||
applyInspectionPayload(
|
applyInspectionPayload(
|
||||||
payload,
|
payload,
|
||||||
@@ -952,13 +952,13 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|||||||
`[ui] inspecting pool '${poolAddress}'${customTimeframeSeconds === null ? "" : ` with custom timeframe ${customTimeframeSeconds}s`}`,
|
`[ui] inspecting pool '${poolAddress}'${customTimeframeSeconds === null ? "" : ` with custom timeframe ${customTimeframeSeconds}s`}`,
|
||||||
);
|
);
|
||||||
|
|
||||||
const request: KbDemoPipelineInspectPoolRequest = {
|
const request: DemoPipelineInspectPoolRequest = {
|
||||||
poolAddress,
|
poolAddress,
|
||||||
customTimeframeSeconds,
|
customTimeframeSeconds,
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const payload = await invoke<KbDemoPipelineInspectPayload>("demo_pipeline_inspect_pool_address", { request });
|
const payload = await invoke<DemoPipelineInspectPayload>("demo_pipeline_inspect_pool_address", { request });
|
||||||
|
|
||||||
applyInspectionPayload(
|
applyInspectionPayload(
|
||||||
payload,
|
payload,
|
||||||
@@ -1019,7 +1019,7 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|||||||
`[ui] launching token backfill for '${tokenMint}' with role '${httpRole ?? "history_backfill"}' (mint=${mintSignatureLimit}, pool=${poolSignatureLimit})`,
|
`[ui] launching token backfill for '${tokenMint}' with role '${httpRole ?? "history_backfill"}' (mint=${mintSignatureLimit}, pool=${poolSignatureLimit})`,
|
||||||
);
|
);
|
||||||
|
|
||||||
const request: KbDemoPipelineBackfillTokenRequest = {
|
const request: DemoPipelineBackfillTokenRequest = {
|
||||||
tokenMint,
|
tokenMint,
|
||||||
httpRole,
|
httpRole,
|
||||||
mintSignatureLimit,
|
mintSignatureLimit,
|
||||||
@@ -1027,7 +1027,7 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const payload = await invoke<KbDemoPipelineBackfillTokenPayload>(
|
const payload = await invoke<DemoPipelineBackfillTokenPayload>(
|
||||||
"demo_pipeline_backfill_token_mint",
|
"demo_pipeline_backfill_token_mint",
|
||||||
{ request },
|
{ request },
|
||||||
);
|
);
|
||||||
@@ -1046,13 +1046,13 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const inspectRequest: KbDemoPipelineInspectTokenRequest = {
|
const inspectRequest: DemoPipelineInspectTokenRequest = {
|
||||||
tokenMint: payload.tokenMint,
|
tokenMint: payload.tokenMint,
|
||||||
customTimeframeSeconds: null,
|
customTimeframeSeconds: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const inspectPayload = await invoke<KbDemoPipelineInspectPayload>(
|
const inspectPayload = await invoke<DemoPipelineInspectPayload>(
|
||||||
"demo_pipeline_inspect_token_mint",
|
"demo_pipeline_inspect_token_mint",
|
||||||
{ request: inspectRequest },
|
{ request: inspectRequest },
|
||||||
);
|
);
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// file: kb_app/frontend/ts/demo_pipeline2.ts
|
// file: kb_demo_app/frontend/ts/demo_pipeline2.ts
|
||||||
|
|
||||||
import * as bootstrap from "bootstrap";
|
import * as bootstrap from "bootstrap";
|
||||||
import "simplebar";
|
import "simplebar";
|
||||||
@@ -7,13 +7,14 @@ import * as echarts from "echarts";
|
|||||||
import { invoke } from "@tauri-apps/api/core";
|
import { invoke } from "@tauri-apps/api/core";
|
||||||
import { debug, takeoverConsole } from "@fltsci/tauri-plugin-tracing";
|
import { debug, takeoverConsole } from "@fltsci/tauri-plugin-tracing";
|
||||||
|
|
||||||
import type { KbDemoPipeline2CatalogPayload } from "./bindings/KbDemoPipeline2CatalogPayload.ts";
|
import type { DemoPipeline2CatalogPayload } from "./bindings/DemoPipeline2CatalogPayload.ts";
|
||||||
import type { KbDemoPipeline2BackfillTokenRequest } from "./bindings/KbDemoPipeline2BackfillTokenRequest.ts";
|
import type { DemoPipeline2BackfillTokenRequest } from "./bindings/DemoPipeline2BackfillTokenRequest.ts";
|
||||||
import type { KbDemoPipeline2BackfillPoolRequest } from "./bindings/KbDemoPipeline2BackfillPoolRequest.ts";
|
import type { DemoPipeline2BackfillPoolRequest } from "./bindings/DemoPipeline2BackfillPoolRequest.ts";
|
||||||
import type { KbDemoPipeline2BackfillPayload } from "./bindings/KbDemoPipeline2BackfillPayload.ts";
|
import type { DemoPipeline2BackfillPayload } from "./bindings/DemoPipeline2BackfillPayload.ts";
|
||||||
import type { KbDemoPipeline2PairCandlesRequest } from "./bindings/KbDemoPipeline2PairCandlesRequest.ts";
|
import type { DemoPipeline2PairCandlesRequest } from "./bindings/DemoPipeline2PairCandlesRequest.ts";
|
||||||
import type { KbDemoPipeline2PairCandlesPayload } from "./bindings/KbDemoPipeline2PairCandlesPayload.ts";
|
import type { DemoPipeline2PairCandlesPayload } from "./bindings/DemoPipeline2PairCandlesPayload.ts";
|
||||||
import type { KbDemoPipeline2LocalDiagnosticsPayload } from "./bindings/KbDemoPipeline2LocalDiagnosticsPayload.ts";
|
import type { DemoPipeline2LocalDiagnosticsPayload } from "./bindings/DemoPipeline2LocalDiagnosticsPayload.ts";
|
||||||
|
import type { DemoPipeline2LocalValidationPayload } from "./bindings/DemoPipeline2LocalValidationPayload.ts";
|
||||||
|
|
||||||
(window as Window & typeof globalThis & { bootstrap?: typeof bootstrap }).bootstrap = bootstrap;
|
(window as Window & typeof globalThis & { bootstrap?: typeof bootstrap }).bootstrap = bootstrap;
|
||||||
(window as Window & typeof globalThis & { ResizeObserver?: typeof ResizeObserver }).ResizeObserver = ResizeObserver;
|
(window as Window & typeof globalThis & { ResizeObserver?: typeof ResizeObserver }).ResizeObserver = ResizeObserver;
|
||||||
@@ -40,7 +41,7 @@ interface PairCandle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
interface KbLocalPipelineReplayResult {
|
interface LocalPipelineReplayResult {
|
||||||
selectedTransactionCount: number;
|
selectedTransactionCount: number;
|
||||||
replayedTransactionCount: number;
|
replayedTransactionCount: number;
|
||||||
decodeErrorCount: number;
|
decodeErrorCount: number;
|
||||||
@@ -133,7 +134,7 @@ function readOptionalPositiveIntegerInput(
|
|||||||
return parsed;
|
return parsed;
|
||||||
}
|
}
|
||||||
function refreshPairSelect(
|
function refreshPairSelect(
|
||||||
catalog: KbDemoPipeline2CatalogPayload,
|
catalog: DemoPipeline2CatalogPayload,
|
||||||
select: HTMLSelectElement,
|
select: HTMLSelectElement,
|
||||||
): void {
|
): void {
|
||||||
const previousValue = select.value;
|
const previousValue = select.value;
|
||||||
@@ -156,7 +157,7 @@ function refreshPairSelect(
|
|||||||
}
|
}
|
||||||
|
|
||||||
function renderCatalogTextareas(
|
function renderCatalogTextareas(
|
||||||
catalog: KbDemoPipeline2CatalogPayload,
|
catalog: DemoPipeline2CatalogPayload,
|
||||||
tokensTextarea: HTMLTextAreaElement,
|
tokensTextarea: HTMLTextAreaElement,
|
||||||
poolsTextarea: HTMLTextAreaElement,
|
poolsTextarea: HTMLTextAreaElement,
|
||||||
pairsTextarea: HTMLTextAreaElement,
|
pairsTextarea: HTMLTextAreaElement,
|
||||||
@@ -351,6 +352,7 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|||||||
const replayMetadataLimitInput = document.querySelector<HTMLInputElement>("#demoPipeline2ReplayMetadataLimitInput");
|
const replayMetadataLimitInput = document.querySelector<HTMLInputElement>("#demoPipeline2ReplayMetadataLimitInput");
|
||||||
const replayLocalPipelineButton = document.querySelector<HTMLButtonElement>("#demoPipeline2ReplayLocalPipelineButton");
|
const replayLocalPipelineButton = document.querySelector<HTMLButtonElement>("#demoPipeline2ReplayLocalPipelineButton");
|
||||||
const diagnoseLocalPipelineButton = document.querySelector<HTMLButtonElement>("#demoPipeline2DiagnoseLocalPipelineButton");
|
const diagnoseLocalPipelineButton = document.querySelector<HTMLButtonElement>("#demoPipeline2DiagnoseLocalPipelineButton");
|
||||||
|
const validateLocalPipelineButton = document.querySelector<HTMLButtonElement>("#demoPipeline2ValidateLocalPipelineButton");
|
||||||
|
|
||||||
const pairSelect = document.querySelector<HTMLSelectElement>("#demoPipeline2PairSelect");
|
const pairSelect = document.querySelector<HTMLSelectElement>("#demoPipeline2PairSelect");
|
||||||
const timeframeSelect = document.querySelector<HTMLSelectElement>("#demoPipeline2TimeframeSelect");
|
const timeframeSelect = document.querySelector<HTMLSelectElement>("#demoPipeline2TimeframeSelect");
|
||||||
@@ -362,6 +364,7 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|||||||
const chartElement = document.querySelector<HTMLDivElement>("#demoPipeline2Chart");
|
const chartElement = document.querySelector<HTMLDivElement>("#demoPipeline2Chart");
|
||||||
const chartMeta = document.querySelector<HTMLDivElement>("#demoPipeline2ChartMeta");
|
const chartMeta = document.querySelector<HTMLDivElement>("#demoPipeline2ChartMeta");
|
||||||
const localDiagnosticsTextarea = document.querySelector<HTMLTextAreaElement>("#demoPipeline2LocalDiagnosticsTextarea");
|
const localDiagnosticsTextarea = document.querySelector<HTMLTextAreaElement>("#demoPipeline2LocalDiagnosticsTextarea");
|
||||||
|
const localValidationTextarea = document.querySelector<HTMLTextAreaElement>("#demoPipeline2LocalValidationTextarea");
|
||||||
|
|
||||||
const clearLogButton = document.querySelector<HTMLButtonElement>("#demoPipeline2ClearLogButton");
|
const clearLogButton = document.querySelector<HTMLButtonElement>("#demoPipeline2ClearLogButton");
|
||||||
const logTextarea = document.querySelector<HTMLTextAreaElement>("#demoPipeline2LogTextarea");
|
const logTextarea = document.querySelector<HTMLTextAreaElement>("#demoPipeline2LogTextarea");
|
||||||
@@ -384,6 +387,7 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|||||||
!replayMetadataLimitInput ||
|
!replayMetadataLimitInput ||
|
||||||
!replayLocalPipelineButton ||
|
!replayLocalPipelineButton ||
|
||||||
!diagnoseLocalPipelineButton ||
|
!diagnoseLocalPipelineButton ||
|
||||||
|
!validateLocalPipelineButton ||
|
||||||
!pairSelect ||
|
!pairSelect ||
|
||||||
!timeframeSelect ||
|
!timeframeSelect ||
|
||||||
!customTimeframeInput ||
|
!customTimeframeInput ||
|
||||||
@@ -391,6 +395,7 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|||||||
!loadCandlesButton ||
|
!loadCandlesButton ||
|
||||||
!backfillSummaryTextarea ||
|
!backfillSummaryTextarea ||
|
||||||
!localDiagnosticsTextarea ||
|
!localDiagnosticsTextarea ||
|
||||||
|
!localValidationTextarea ||
|
||||||
!chartElement ||
|
!chartElement ||
|
||||||
!chartMeta ||
|
!chartMeta ||
|
||||||
!clearLogButton ||
|
!clearLogButton ||
|
||||||
@@ -420,13 +425,13 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|||||||
logTextarea.value = "";
|
logTextarea.value = "";
|
||||||
});
|
});
|
||||||
|
|
||||||
let currentCatalog: KbDemoPipeline2CatalogPayload | null = null;
|
let currentCatalog: DemoPipeline2CatalogPayload | null = null;
|
||||||
|
|
||||||
async function refreshCatalog(): Promise<void> {
|
async function refreshCatalog(): Promise<void> {
|
||||||
appendLogLine(safeLogTextarea, "[ui] refreshing local catalog");
|
appendLogLine(safeLogTextarea, "[ui] refreshing local catalog");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const catalog = await invoke<KbDemoPipeline2CatalogPayload>("demo_pipeline2_get_catalog");
|
const catalog = await invoke<DemoPipeline2CatalogPayload>("demo_pipeline2_get_catalog");
|
||||||
currentCatalog = catalog;
|
currentCatalog = catalog;
|
||||||
|
|
||||||
renderCatalogTextareas(catalog, safeTokensTextarea, safePoolsTextarea, safePairsTextarea);
|
renderCatalogTextareas(catalog, safeTokensTextarea, safePoolsTextarea, safePairsTextarea);
|
||||||
@@ -478,7 +483,7 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|||||||
`[ui] launching token backfill for '${tokenMint}' with role '${httpRole ?? "history_backfill"}'`,
|
`[ui] launching token backfill for '${tokenMint}' with role '${httpRole ?? "history_backfill"}'`,
|
||||||
);
|
);
|
||||||
|
|
||||||
const request: KbDemoPipeline2BackfillTokenRequest = {
|
const request: DemoPipeline2BackfillTokenRequest = {
|
||||||
tokenMint,
|
tokenMint,
|
||||||
httpRole,
|
httpRole,
|
||||||
mintSignatureLimit,
|
mintSignatureLimit,
|
||||||
@@ -486,7 +491,7 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const payload = await invoke<KbDemoPipeline2BackfillPayload>(
|
const payload = await invoke<DemoPipeline2BackfillPayload>(
|
||||||
"demo_pipeline2_backfill_token_mint",
|
"demo_pipeline2_backfill_token_mint",
|
||||||
{ request },
|
{ request },
|
||||||
);
|
);
|
||||||
@@ -526,14 +531,14 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|||||||
`[ui] launching pool backfill for '${poolAddress}' with role '${httpRole ?? "history_backfill"}'`,
|
`[ui] launching pool backfill for '${poolAddress}' with role '${httpRole ?? "history_backfill"}'`,
|
||||||
);
|
);
|
||||||
|
|
||||||
const request: KbDemoPipeline2BackfillPoolRequest = {
|
const request: DemoPipeline2BackfillPoolRequest = {
|
||||||
poolAddress,
|
poolAddress,
|
||||||
httpRole,
|
httpRole,
|
||||||
poolSignatureLimit,
|
poolSignatureLimit,
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const payload = await invoke<KbDemoPipeline2BackfillPayload>(
|
const payload = await invoke<DemoPipeline2BackfillPayload>(
|
||||||
"demo_pipeline2_backfill_pool_address",
|
"demo_pipeline2_backfill_pool_address",
|
||||||
{ request },
|
{ request },
|
||||||
);
|
);
|
||||||
@@ -574,7 +579,7 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result = await invoke<KbLocalPipelineReplayResult>(
|
const result = await invoke<LocalPipelineReplayResult>(
|
||||||
"demo_pipeline2_replay_local_pipeline",
|
"demo_pipeline2_replay_local_pipeline",
|
||||||
{
|
{
|
||||||
limit: replayLimit,
|
limit: replayLimit,
|
||||||
@@ -602,7 +607,7 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|||||||
diagnoseLocalPipelineButton.disabled = true;
|
diagnoseLocalPipelineButton.disabled = true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const payload = await invoke<KbDemoPipeline2LocalDiagnosticsPayload>(
|
const payload = await invoke<DemoPipeline2LocalDiagnosticsPayload>(
|
||||||
"demo_pipeline2_diagnose_local_pipeline",
|
"demo_pipeline2_diagnose_local_pipeline",
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -610,7 +615,7 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|||||||
|
|
||||||
appendLogLine(
|
appendLogLine(
|
||||||
logTextarea,
|
logTextarea,
|
||||||
`[ui] local pipeline diagnostics completed: ${payload.summary.decodedEventCount.toString()} decoded, ${payload.summary.tradeEventCount.toString()} trades, ${payload.summary.pairCandleCount.toString()} candles`,
|
`[ui] local pipeline diagnostics completed: ${payload.summary.decodedEventCount.toString()} decoded, ${payload.summary.tradeEventCount.toString()} trades, ${payload.summary.pairCandleCount.toString()} candles, actionableMissing='${payload.summary.actionableMissingTradeEventCount.toString()}', nonActionablePairs='${payload.summary.nonActionablePairCount.toString()}', blocking='${payload.summary.blockingIssueCount.toString()}'`,
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
appendLogLine(logTextarea, `[ui] local pipeline diagnostics error: ${String(error)}`);
|
appendLogLine(logTextarea, `[ui] local pipeline diagnostics error: ${String(error)}`);
|
||||||
@@ -619,6 +624,26 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
validateLocalPipelineButton.addEventListener("click", async () => {
|
||||||
|
appendLogLine(logTextarea, "[ui] validating local pipeline with 0.7.27 profile");
|
||||||
|
|
||||||
|
try {
|
||||||
|
const payload = await invoke<DemoPipeline2LocalValidationPayload>(
|
||||||
|
"demo_pipeline2_validate_local_pipeline",
|
||||||
|
);
|
||||||
|
|
||||||
|
localValidationTextarea.value = payload.validationJson;
|
||||||
|
localDiagnosticsTextarea.value = payload.summaryJson;
|
||||||
|
|
||||||
|
appendLogLine(
|
||||||
|
logTextarea,
|
||||||
|
`[ui] local pipeline validation completed: profile='${payload.run.validationProfileCode}' passed='${payload.run.validationPassed ? "yes" : "no"}' blocking='${payload.run.blockingIssueCount.toString()}' warnings='${payload.run.warningCount.toString()}'`,
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
appendLogLine(logTextarea, `[ui] local pipeline validation error: ${String(error)}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
loadCandlesButton.addEventListener("click", async () => {
|
loadCandlesButton.addEventListener("click", async () => {
|
||||||
const pairIdText = pairSelect.value.trim();
|
const pairIdText = pairSelect.value.trim();
|
||||||
if (pairIdText === "") {
|
if (pairIdText === "") {
|
||||||
@@ -648,14 +673,14 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|||||||
`[ui] loading candles for pair '${parsedPairId}' timeframe '${timeframeSeconds}s'`,
|
`[ui] loading candles for pair '${parsedPairId}' timeframe '${timeframeSeconds}s'`,
|
||||||
);
|
);
|
||||||
|
|
||||||
const request: KbDemoPipeline2PairCandlesRequest = {
|
const request: DemoPipeline2PairCandlesRequest = {
|
||||||
pairId: parsedPairId,
|
pairId: parsedPairId,
|
||||||
timeframeSeconds,
|
timeframeSeconds,
|
||||||
preferMaterialized: preferMaterializedInput.checked,
|
preferMaterialized: preferMaterializedInput.checked,
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const payload = await invoke<KbDemoPipeline2PairCandlesPayload>(
|
const payload = await invoke<DemoPipeline2PairCandlesPayload>(
|
||||||
"demo_pipeline2_get_pair_candles",
|
"demo_pipeline2_get_pair_candles",
|
||||||
{ request },
|
{ request },
|
||||||
);
|
);
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// file: kb_app/frontend/ts/demo_ws.ts
|
// file: kb_demo_app/frontend/ts/demo_ws.ts
|
||||||
|
|
||||||
import * as bootstrap from "bootstrap";
|
import * as bootstrap from "bootstrap";
|
||||||
import "simplebar";
|
import "simplebar";
|
||||||
@@ -6,9 +6,9 @@ import ResizeObserver from "resize-observer-polyfill";
|
|||||||
import { invoke } from "@tauri-apps/api/core";
|
import { invoke } from "@tauri-apps/api/core";
|
||||||
import { listen, type UnlistenFn } from "@tauri-apps/api/event";
|
import { listen, type UnlistenFn } from "@tauri-apps/api/event";
|
||||||
import { debug, takeoverConsole } from "@fltsci/tauri-plugin-tracing";
|
import { debug, takeoverConsole } from "@fltsci/tauri-plugin-tracing";
|
||||||
import { KbDemoWsEndpointSummary } from './bindings/KbDemoWsEndpointSummary.ts';
|
import { DemoWsEndpointSummary } from './bindings/DemoWsEndpointSummary.ts';
|
||||||
import { KbDemoWsStatusPayload } from './bindings/KbDemoWsStatusPayload.ts';
|
import { DemoWsStatusPayload } from './bindings/DemoWsStatusPayload.ts';
|
||||||
import { KbDemoWsSubscribeRequest } from './bindings/KbDemoWsSubscribeRequest.ts';
|
import { DemoWsSubscribeRequest } from './bindings/DemoWsSubscribeRequest.ts';
|
||||||
|
|
||||||
(window as Window & typeof globalThis & { bootstrap?: typeof bootstrap }).bootstrap = bootstrap;
|
(window as Window & typeof globalThis & { bootstrap?: typeof bootstrap }).bootstrap = bootstrap;
|
||||||
(window as Window & typeof globalThis & { ResizeObserver?: typeof ResizeObserver }).ResizeObserver = ResizeObserver;
|
(window as Window & typeof globalThis & { ResizeObserver?: typeof ResizeObserver }).ResizeObserver = ResizeObserver;
|
||||||
@@ -168,7 +168,7 @@ function updateFormVisibility(
|
|||||||
}
|
}
|
||||||
|
|
||||||
function applyStatusToUi(
|
function applyStatusToUi(
|
||||||
status: KbDemoWsStatusPayload,
|
status: DemoWsStatusPayload,
|
||||||
statusBadge: HTMLSpanElement,
|
statusBadge: HTMLSpanElement,
|
||||||
stateText: HTMLSpanElement,
|
stateText: HTMLSpanElement,
|
||||||
endpointText: HTMLSpanElement,
|
endpointText: HTMLSpanElement,
|
||||||
@@ -296,7 +296,7 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|||||||
appendLogLine(logTextarea, event.payload);
|
appendLogLine(logTextarea, event.payload);
|
||||||
});
|
});
|
||||||
|
|
||||||
unlistenStatusEvent = await listen<KbDemoWsStatusPayload>("demo-ws-status", (event) => {
|
unlistenStatusEvent = await listen<DemoWsStatusPayload>("demo-ws-status", (event) => {
|
||||||
applyStatusToUi(
|
applyStatusToUi(
|
||||||
event.payload,
|
event.payload,
|
||||||
statusBadge,
|
statusBadge,
|
||||||
@@ -319,7 +319,7 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const endpoints = await invoke<KbDemoWsEndpointSummary[]>("demo_ws_list_endpoints");
|
const endpoints = await invoke<DemoWsEndpointSummary[]>("demo_ws_list_endpoints");
|
||||||
|
|
||||||
endpointSelect.innerHTML = "";
|
endpointSelect.innerHTML = "";
|
||||||
for (const endpoint of endpoints) {
|
for (const endpoint of endpoints) {
|
||||||
@@ -374,7 +374,7 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const status = await invoke<KbDemoWsStatusPayload>("demo_ws_get_status");
|
const status = await invoke<DemoWsStatusPayload>("demo_ws_get_status");
|
||||||
applyStatusToUi(
|
applyStatusToUi(
|
||||||
status,
|
status,
|
||||||
statusBadge,
|
statusBadge,
|
||||||
@@ -399,7 +399,7 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|||||||
|
|
||||||
connectButton.addEventListener("click", async () => {
|
connectButton.addEventListener("click", async () => {
|
||||||
try {
|
try {
|
||||||
const status = await invoke<KbDemoWsStatusPayload>("demo_ws_connect", {
|
const status = await invoke<DemoWsStatusPayload>("demo_ws_connect", {
|
||||||
endpointName: endpointSelect.value,
|
endpointName: endpointSelect.value,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -426,7 +426,7 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|||||||
|
|
||||||
disconnectButton.addEventListener("click", async () => {
|
disconnectButton.addEventListener("click", async () => {
|
||||||
try {
|
try {
|
||||||
const status = await invoke<KbDemoWsStatusPayload>("demo_ws_disconnect");
|
const status = await invoke<DemoWsStatusPayload>("demo_ws_disconnect");
|
||||||
|
|
||||||
applyStatusToUi(
|
applyStatusToUi(
|
||||||
status,
|
status,
|
||||||
@@ -450,7 +450,7 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
subscribeButton.addEventListener("click", async () => {
|
subscribeButton.addEventListener("click", async () => {
|
||||||
const request: KbDemoWsSubscribeRequest = {
|
const request: DemoWsSubscribeRequest = {
|
||||||
method: methodSelect.value,
|
method: methodSelect.value,
|
||||||
mode: modeSelect.value,
|
mode: modeSelect.value,
|
||||||
target: targetInput.value.trim() === "" ? null : targetInput.value.trim(),
|
target: targetInput.value.trim() === "" ? null : targetInput.value.trim(),
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// file: kb_app/frontend/ts/demo_ws_manager.ts
|
// file: kb_demo_app/frontend/ts/demo_ws_manager.ts
|
||||||
|
|
||||||
import * as bootstrap from "bootstrap";
|
import * as bootstrap from "bootstrap";
|
||||||
import "simplebar";
|
import "simplebar";
|
||||||
@@ -6,7 +6,7 @@ import ResizeObserver from "resize-observer-polyfill";
|
|||||||
import { invoke } from "@tauri-apps/api/core";
|
import { invoke } from "@tauri-apps/api/core";
|
||||||
import { listen } from "@tauri-apps/api/event";
|
import { listen } from "@tauri-apps/api/event";
|
||||||
import { debug, takeoverConsole } from "@fltsci/tauri-plugin-tracing";
|
import { debug, takeoverConsole } from "@fltsci/tauri-plugin-tracing";
|
||||||
import { KbDemoWsManagerSnapshotPayload } from './bindings/KbDemoWsManagerSnapshotPayload.ts';
|
import { DemoWsManagerSnapshotPayload } from './bindings/DemoWsManagerSnapshotPayload.ts';
|
||||||
|
|
||||||
(window as Window & typeof globalThis & { bootstrap?: typeof bootstrap }).bootstrap = bootstrap;
|
(window as Window & typeof globalThis & { bootstrap?: typeof bootstrap }).bootstrap = bootstrap;
|
||||||
(window as Window & typeof globalThis & { ResizeObserver?: typeof ResizeObserver }).ResizeObserver = ResizeObserver;
|
(window as Window & typeof globalThis & { ResizeObserver?: typeof ResizeObserver }).ResizeObserver = ResizeObserver;
|
||||||
@@ -33,7 +33,7 @@ function appendLogLine(line: string): void {
|
|||||||
logTextarea.scrollTop = logTextarea.scrollHeight;
|
logTextarea.scrollTop = logTextarea.scrollHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderSnapshot(snapshot: KbDemoWsManagerSnapshotPayload): void {
|
function renderSnapshot(snapshot: DemoWsManagerSnapshotPayload): void {
|
||||||
if (endpointCountText) {
|
if (endpointCountText) {
|
||||||
endpointCountText.textContent = String(snapshot.endpointCount);
|
endpointCountText.textContent = String(snapshot.endpointCount);
|
||||||
}
|
}
|
||||||
@@ -64,7 +64,7 @@ function renderSnapshot(snapshot: KbDemoWsManagerSnapshotPayload): void {
|
|||||||
|
|
||||||
async function refreshSnapshot(): Promise<void> {
|
async function refreshSnapshot(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const snapshot = await invoke<KbDemoWsManagerSnapshotPayload>("demo_ws_manager_get_snapshot");
|
const snapshot = await invoke<DemoWsManagerSnapshotPayload>("demo_ws_manager_get_snapshot");
|
||||||
renderSnapshot(snapshot);
|
renderSnapshot(snapshot);
|
||||||
appendLogLine("[ui] refreshed manager snapshot");
|
appendLogLine("[ui] refreshed manager snapshot");
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -94,7 +94,7 @@ async function loadRoles(): Promise<void> {
|
|||||||
|
|
||||||
async function startAll(): Promise<void> {
|
async function startAll(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const snapshot = await invoke<KbDemoWsManagerSnapshotPayload>("demo_ws_manager_start_all");
|
const snapshot = await invoke<DemoWsManagerSnapshotPayload>("demo_ws_manager_start_all");
|
||||||
renderSnapshot(snapshot);
|
renderSnapshot(snapshot);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
appendLogLine(`[ui] start all error: ${String(error)}`);
|
appendLogLine(`[ui] start all error: ${String(error)}`);
|
||||||
@@ -103,7 +103,7 @@ async function startAll(): Promise<void> {
|
|||||||
|
|
||||||
async function stopAll(): Promise<void> {
|
async function stopAll(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const snapshot = await invoke<KbDemoWsManagerSnapshotPayload>("demo_ws_manager_stop_all");
|
const snapshot = await invoke<DemoWsManagerSnapshotPayload>("demo_ws_manager_stop_all");
|
||||||
renderSnapshot(snapshot);
|
renderSnapshot(snapshot);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
appendLogLine(`[ui] stop all error: ${String(error)}`);
|
appendLogLine(`[ui] stop all error: ${String(error)}`);
|
||||||
@@ -117,7 +117,7 @@ async function startRole(): Promise<void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const snapshot = await invoke<KbDemoWsManagerSnapshotPayload>("demo_ws_manager_start_role", {
|
const snapshot = await invoke<DemoWsManagerSnapshotPayload>("demo_ws_manager_start_role", {
|
||||||
role: roleSelect.value,
|
role: roleSelect.value,
|
||||||
});
|
});
|
||||||
renderSnapshot(snapshot);
|
renderSnapshot(snapshot);
|
||||||
@@ -133,7 +133,7 @@ async function stopRole(): Promise<void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const snapshot = await invoke<KbDemoWsManagerSnapshotPayload>("demo_ws_manager_stop_role", {
|
const snapshot = await invoke<DemoWsManagerSnapshotPayload>("demo_ws_manager_stop_role", {
|
||||||
role: roleSelect.value,
|
role: roleSelect.value,
|
||||||
});
|
});
|
||||||
renderSnapshot(snapshot);
|
renderSnapshot(snapshot);
|
||||||
@@ -215,7 +215,7 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|||||||
appendLogLine(event.payload);
|
appendLogLine(event.payload);
|
||||||
});
|
});
|
||||||
|
|
||||||
await listen<KbDemoWsManagerSnapshotPayload>("kb-demo-ws-manager-snapshot", (event) => {
|
await listen<DemoWsManagerSnapshotPayload>("kb-demo-ws-manager-snapshot", (event) => {
|
||||||
renderSnapshot(event.payload);
|
renderSnapshot(event.payload);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// file: kb_app/frontend/ts/main.ts
|
// file: kb_demo_app/frontend/ts/main.ts
|
||||||
|
|
||||||
import * as bootstrap from "bootstrap";
|
import * as bootstrap from "bootstrap";
|
||||||
import "simplebar";
|
import "simplebar";
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// file: kb_app/frontend/ts/splash.ts
|
// file: kb_demo_app/frontend/ts/splash.ts
|
||||||
|
|
||||||
import { error, info, takeoverConsole } from "@fltsci/tauri-plugin-tracing";
|
import { error, info, takeoverConsole } from "@fltsci/tauri-plugin-tracing";
|
||||||
import { listen } from '@tauri-apps/api/event';
|
import { listen } from '@tauri-apps/api/event';
|
||||||
1
kb_demo_app/gen/schemas/acl-manifests.json
Normal file
@@ -183,10 +183,10 @@
|
|||||||
"markdownDescription": "Default core plugins set.\n#### This default permission set includes:\n\n- `core:path:default`\n- `core:event:default`\n- `core:window:default`\n- `core:webview:default`\n- `core:app:default`\n- `core:image:default`\n- `core:resources:default`\n- `core:menu:default`\n- `core:tray:default`"
|
"markdownDescription": "Default core plugins set.\n#### This default permission set includes:\n\n- `core:path:default`\n- `core:event:default`\n- `core:window:default`\n- `core:webview:default`\n- `core:app:default`\n- `core:image:default`\n- `core:resources:default`\n- `core:menu:default`\n- `core:tray:default`"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-version`\n- `allow-name`\n- `allow-tauri-version`\n- `allow-identifier`\n- `allow-bundle-type`\n- `allow-register-listener`\n- `allow-remove-listener`",
|
"description": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-version`\n- `allow-name`\n- `allow-tauri-version`\n- `allow-identifier`\n- `allow-bundle-type`\n- `allow-register-listener`\n- `allow-remove-listener`\n- `allow-supports-multiple-windows`",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"const": "core:app:default",
|
"const": "core:app:default",
|
||||||
"markdownDescription": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-version`\n- `allow-name`\n- `allow-tauri-version`\n- `allow-identifier`\n- `allow-bundle-type`\n- `allow-register-listener`\n- `allow-remove-listener`"
|
"markdownDescription": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-version`\n- `allow-name`\n- `allow-tauri-version`\n- `allow-identifier`\n- `allow-bundle-type`\n- `allow-register-listener`\n- `allow-remove-listener`\n- `allow-supports-multiple-windows`"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "Enables the app_hide command without any pre-configured scope.",
|
"description": "Enables the app_hide command without any pre-configured scope.",
|
||||||
@@ -260,6 +260,12 @@
|
|||||||
"const": "core:app:allow-set-dock-visibility",
|
"const": "core:app:allow-set-dock-visibility",
|
||||||
"markdownDescription": "Enables the set_dock_visibility command without any pre-configured scope."
|
"markdownDescription": "Enables the set_dock_visibility command without any pre-configured scope."
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"description": "Enables the supports_multiple_windows command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "core:app:allow-supports-multiple-windows",
|
||||||
|
"markdownDescription": "Enables the supports_multiple_windows command without any pre-configured scope."
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"description": "Enables the tauri_version command without any pre-configured scope.",
|
"description": "Enables the tauri_version command without any pre-configured scope.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
@@ -344,6 +350,12 @@
|
|||||||
"const": "core:app:deny-set-dock-visibility",
|
"const": "core:app:deny-set-dock-visibility",
|
||||||
"markdownDescription": "Denies the set_dock_visibility command without any pre-configured scope."
|
"markdownDescription": "Denies the set_dock_visibility command without any pre-configured scope."
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"description": "Denies the supports_multiple_windows command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "core:app:deny-supports-multiple-windows",
|
||||||
|
"markdownDescription": "Denies the supports_multiple_windows command without any pre-configured scope."
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"description": "Denies the tauri_version command without any pre-configured scope.",
|
"description": "Denies the tauri_version command without any pre-configured scope.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
@@ -867,10 +879,10 @@
|
|||||||
"markdownDescription": "Denies the close command without any pre-configured scope."
|
"markdownDescription": "Denies the close command without any pre-configured scope."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-get-by-id`\n- `allow-remove-by-id`\n- `allow-set-icon`\n- `allow-set-menu`\n- `allow-set-tooltip`\n- `allow-set-title`\n- `allow-set-visible`\n- `allow-set-temp-dir-path`\n- `allow-set-icon-as-template`\n- `allow-set-show-menu-on-left-click`",
|
"description": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-get-by-id`\n- `allow-remove-by-id`\n- `allow-set-icon`\n- `allow-set-menu`\n- `allow-set-tooltip`\n- `allow-set-title`\n- `allow-set-visible`\n- `allow-set-temp-dir-path`\n- `allow-set-icon-as-template`\n- `allow-set-icon-with-as-template`\n- `allow-set-show-menu-on-left-click`",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"const": "core:tray:default",
|
"const": "core:tray:default",
|
||||||
"markdownDescription": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-get-by-id`\n- `allow-remove-by-id`\n- `allow-set-icon`\n- `allow-set-menu`\n- `allow-set-tooltip`\n- `allow-set-title`\n- `allow-set-visible`\n- `allow-set-temp-dir-path`\n- `allow-set-icon-as-template`\n- `allow-set-show-menu-on-left-click`"
|
"markdownDescription": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-get-by-id`\n- `allow-remove-by-id`\n- `allow-set-icon`\n- `allow-set-menu`\n- `allow-set-tooltip`\n- `allow-set-title`\n- `allow-set-visible`\n- `allow-set-temp-dir-path`\n- `allow-set-icon-as-template`\n- `allow-set-icon-with-as-template`\n- `allow-set-show-menu-on-left-click`"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "Enables the get_by_id command without any pre-configured scope.",
|
"description": "Enables the get_by_id command without any pre-configured scope.",
|
||||||
@@ -902,6 +914,12 @@
|
|||||||
"const": "core:tray:allow-set-icon-as-template",
|
"const": "core:tray:allow-set-icon-as-template",
|
||||||
"markdownDescription": "Enables the set_icon_as_template command without any pre-configured scope."
|
"markdownDescription": "Enables the set_icon_as_template command without any pre-configured scope."
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"description": "Enables the set_icon_with_as_template command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "core:tray:allow-set-icon-with-as-template",
|
||||||
|
"markdownDescription": "Enables the set_icon_with_as_template command without any pre-configured scope."
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"description": "Enables the set_menu command without any pre-configured scope.",
|
"description": "Enables the set_menu command without any pre-configured scope.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
@@ -968,6 +986,12 @@
|
|||||||
"const": "core:tray:deny-set-icon-as-template",
|
"const": "core:tray:deny-set-icon-as-template",
|
||||||
"markdownDescription": "Denies the set_icon_as_template command without any pre-configured scope."
|
"markdownDescription": "Denies the set_icon_as_template command without any pre-configured scope."
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"description": "Denies the set_icon_with_as_template command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "core:tray:deny-set-icon-with-as-template",
|
||||||
|
"markdownDescription": "Denies the set_icon_with_as_template command without any pre-configured scope."
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"description": "Denies the set_menu command without any pre-configured scope.",
|
"description": "Denies the set_menu command without any pre-configured scope.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
@@ -1227,10 +1251,16 @@
|
|||||||
"markdownDescription": "Denies the webview_size command without any pre-configured scope."
|
"markdownDescription": "Denies the webview_size command without any pre-configured scope."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-get-all-windows`\n- `allow-scale-factor`\n- `allow-inner-position`\n- `allow-outer-position`\n- `allow-inner-size`\n- `allow-outer-size`\n- `allow-is-fullscreen`\n- `allow-is-minimized`\n- `allow-is-maximized`\n- `allow-is-focused`\n- `allow-is-decorated`\n- `allow-is-resizable`\n- `allow-is-maximizable`\n- `allow-is-minimizable`\n- `allow-is-closable`\n- `allow-is-visible`\n- `allow-is-enabled`\n- `allow-title`\n- `allow-current-monitor`\n- `allow-primary-monitor`\n- `allow-monitor-from-point`\n- `allow-available-monitors`\n- `allow-cursor-position`\n- `allow-theme`\n- `allow-is-always-on-top`\n- `allow-internal-toggle-maximize`",
|
"description": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-get-all-windows`\n- `allow-scale-factor`\n- `allow-inner-position`\n- `allow-outer-position`\n- `allow-inner-size`\n- `allow-outer-size`\n- `allow-is-fullscreen`\n- `allow-is-minimized`\n- `allow-is-maximized`\n- `allow-is-focused`\n- `allow-is-decorated`\n- `allow-is-resizable`\n- `allow-is-maximizable`\n- `allow-is-minimizable`\n- `allow-is-closable`\n- `allow-is-visible`\n- `allow-is-enabled`\n- `allow-title`\n- `allow-current-monitor`\n- `allow-primary-monitor`\n- `allow-monitor-from-point`\n- `allow-available-monitors`\n- `allow-cursor-position`\n- `allow-theme`\n- `allow-is-always-on-top`\n- `allow-activity-name`\n- `allow-scene-identifier`\n- `allow-internal-toggle-maximize`",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"const": "core:window:default",
|
"const": "core:window:default",
|
||||||
"markdownDescription": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-get-all-windows`\n- `allow-scale-factor`\n- `allow-inner-position`\n- `allow-outer-position`\n- `allow-inner-size`\n- `allow-outer-size`\n- `allow-is-fullscreen`\n- `allow-is-minimized`\n- `allow-is-maximized`\n- `allow-is-focused`\n- `allow-is-decorated`\n- `allow-is-resizable`\n- `allow-is-maximizable`\n- `allow-is-minimizable`\n- `allow-is-closable`\n- `allow-is-visible`\n- `allow-is-enabled`\n- `allow-title`\n- `allow-current-monitor`\n- `allow-primary-monitor`\n- `allow-monitor-from-point`\n- `allow-available-monitors`\n- `allow-cursor-position`\n- `allow-theme`\n- `allow-is-always-on-top`\n- `allow-internal-toggle-maximize`"
|
"markdownDescription": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-get-all-windows`\n- `allow-scale-factor`\n- `allow-inner-position`\n- `allow-outer-position`\n- `allow-inner-size`\n- `allow-outer-size`\n- `allow-is-fullscreen`\n- `allow-is-minimized`\n- `allow-is-maximized`\n- `allow-is-focused`\n- `allow-is-decorated`\n- `allow-is-resizable`\n- `allow-is-maximizable`\n- `allow-is-minimizable`\n- `allow-is-closable`\n- `allow-is-visible`\n- `allow-is-enabled`\n- `allow-title`\n- `allow-current-monitor`\n- `allow-primary-monitor`\n- `allow-monitor-from-point`\n- `allow-available-monitors`\n- `allow-cursor-position`\n- `allow-theme`\n- `allow-is-always-on-top`\n- `allow-activity-name`\n- `allow-scene-identifier`\n- `allow-internal-toggle-maximize`"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Enables the activity_name command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "core:window:allow-activity-name",
|
||||||
|
"markdownDescription": "Enables the activity_name command without any pre-configured scope."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "Enables the available_monitors command without any pre-configured scope.",
|
"description": "Enables the available_monitors command without any pre-configured scope.",
|
||||||
@@ -1424,6 +1454,12 @@
|
|||||||
"const": "core:window:allow-scale-factor",
|
"const": "core:window:allow-scale-factor",
|
||||||
"markdownDescription": "Enables the scale_factor command without any pre-configured scope."
|
"markdownDescription": "Enables the scale_factor command without any pre-configured scope."
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"description": "Enables the scene_identifier command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "core:window:allow-scene-identifier",
|
||||||
|
"markdownDescription": "Enables the scene_identifier command without any pre-configured scope."
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"description": "Enables the set_always_on_bottom command without any pre-configured scope.",
|
"description": "Enables the set_always_on_bottom command without any pre-configured scope.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
@@ -1688,6 +1724,12 @@
|
|||||||
"const": "core:window:allow-unminimize",
|
"const": "core:window:allow-unminimize",
|
||||||
"markdownDescription": "Enables the unminimize command without any pre-configured scope."
|
"markdownDescription": "Enables the unminimize command without any pre-configured scope."
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"description": "Denies the activity_name command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "core:window:deny-activity-name",
|
||||||
|
"markdownDescription": "Denies the activity_name command without any pre-configured scope."
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"description": "Denies the available_monitors command without any pre-configured scope.",
|
"description": "Denies the available_monitors command without any pre-configured scope.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
@@ -1880,6 +1922,12 @@
|
|||||||
"const": "core:window:deny-scale-factor",
|
"const": "core:window:deny-scale-factor",
|
||||||
"markdownDescription": "Denies the scale_factor command without any pre-configured scope."
|
"markdownDescription": "Denies the scale_factor command without any pre-configured scope."
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"description": "Denies the scene_identifier command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "core:window:deny-scene-identifier",
|
||||||
|
"markdownDescription": "Denies the scene_identifier command without any pre-configured scope."
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"description": "Denies the set_always_on_bottom command without any pre-configured scope.",
|
"description": "Denies the set_always_on_bottom command without any pre-configured scope.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
@@ -183,10 +183,10 @@
|
|||||||
"markdownDescription": "Default core plugins set.\n#### This default permission set includes:\n\n- `core:path:default`\n- `core:event:default`\n- `core:window:default`\n- `core:webview:default`\n- `core:app:default`\n- `core:image:default`\n- `core:resources:default`\n- `core:menu:default`\n- `core:tray:default`"
|
"markdownDescription": "Default core plugins set.\n#### This default permission set includes:\n\n- `core:path:default`\n- `core:event:default`\n- `core:window:default`\n- `core:webview:default`\n- `core:app:default`\n- `core:image:default`\n- `core:resources:default`\n- `core:menu:default`\n- `core:tray:default`"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-version`\n- `allow-name`\n- `allow-tauri-version`\n- `allow-identifier`\n- `allow-bundle-type`\n- `allow-register-listener`\n- `allow-remove-listener`",
|
"description": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-version`\n- `allow-name`\n- `allow-tauri-version`\n- `allow-identifier`\n- `allow-bundle-type`\n- `allow-register-listener`\n- `allow-remove-listener`\n- `allow-supports-multiple-windows`",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"const": "core:app:default",
|
"const": "core:app:default",
|
||||||
"markdownDescription": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-version`\n- `allow-name`\n- `allow-tauri-version`\n- `allow-identifier`\n- `allow-bundle-type`\n- `allow-register-listener`\n- `allow-remove-listener`"
|
"markdownDescription": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-version`\n- `allow-name`\n- `allow-tauri-version`\n- `allow-identifier`\n- `allow-bundle-type`\n- `allow-register-listener`\n- `allow-remove-listener`\n- `allow-supports-multiple-windows`"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "Enables the app_hide command without any pre-configured scope.",
|
"description": "Enables the app_hide command without any pre-configured scope.",
|
||||||
@@ -260,6 +260,12 @@
|
|||||||
"const": "core:app:allow-set-dock-visibility",
|
"const": "core:app:allow-set-dock-visibility",
|
||||||
"markdownDescription": "Enables the set_dock_visibility command without any pre-configured scope."
|
"markdownDescription": "Enables the set_dock_visibility command without any pre-configured scope."
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"description": "Enables the supports_multiple_windows command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "core:app:allow-supports-multiple-windows",
|
||||||
|
"markdownDescription": "Enables the supports_multiple_windows command without any pre-configured scope."
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"description": "Enables the tauri_version command without any pre-configured scope.",
|
"description": "Enables the tauri_version command without any pre-configured scope.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
@@ -344,6 +350,12 @@
|
|||||||
"const": "core:app:deny-set-dock-visibility",
|
"const": "core:app:deny-set-dock-visibility",
|
||||||
"markdownDescription": "Denies the set_dock_visibility command without any pre-configured scope."
|
"markdownDescription": "Denies the set_dock_visibility command without any pre-configured scope."
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"description": "Denies the supports_multiple_windows command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "core:app:deny-supports-multiple-windows",
|
||||||
|
"markdownDescription": "Denies the supports_multiple_windows command without any pre-configured scope."
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"description": "Denies the tauri_version command without any pre-configured scope.",
|
"description": "Denies the tauri_version command without any pre-configured scope.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
@@ -867,10 +879,10 @@
|
|||||||
"markdownDescription": "Denies the close command without any pre-configured scope."
|
"markdownDescription": "Denies the close command without any pre-configured scope."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-get-by-id`\n- `allow-remove-by-id`\n- `allow-set-icon`\n- `allow-set-menu`\n- `allow-set-tooltip`\n- `allow-set-title`\n- `allow-set-visible`\n- `allow-set-temp-dir-path`\n- `allow-set-icon-as-template`\n- `allow-set-show-menu-on-left-click`",
|
"description": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-get-by-id`\n- `allow-remove-by-id`\n- `allow-set-icon`\n- `allow-set-menu`\n- `allow-set-tooltip`\n- `allow-set-title`\n- `allow-set-visible`\n- `allow-set-temp-dir-path`\n- `allow-set-icon-as-template`\n- `allow-set-icon-with-as-template`\n- `allow-set-show-menu-on-left-click`",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"const": "core:tray:default",
|
"const": "core:tray:default",
|
||||||
"markdownDescription": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-get-by-id`\n- `allow-remove-by-id`\n- `allow-set-icon`\n- `allow-set-menu`\n- `allow-set-tooltip`\n- `allow-set-title`\n- `allow-set-visible`\n- `allow-set-temp-dir-path`\n- `allow-set-icon-as-template`\n- `allow-set-show-menu-on-left-click`"
|
"markdownDescription": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-get-by-id`\n- `allow-remove-by-id`\n- `allow-set-icon`\n- `allow-set-menu`\n- `allow-set-tooltip`\n- `allow-set-title`\n- `allow-set-visible`\n- `allow-set-temp-dir-path`\n- `allow-set-icon-as-template`\n- `allow-set-icon-with-as-template`\n- `allow-set-show-menu-on-left-click`"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "Enables the get_by_id command without any pre-configured scope.",
|
"description": "Enables the get_by_id command without any pre-configured scope.",
|
||||||
@@ -902,6 +914,12 @@
|
|||||||
"const": "core:tray:allow-set-icon-as-template",
|
"const": "core:tray:allow-set-icon-as-template",
|
||||||
"markdownDescription": "Enables the set_icon_as_template command without any pre-configured scope."
|
"markdownDescription": "Enables the set_icon_as_template command without any pre-configured scope."
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"description": "Enables the set_icon_with_as_template command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "core:tray:allow-set-icon-with-as-template",
|
||||||
|
"markdownDescription": "Enables the set_icon_with_as_template command without any pre-configured scope."
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"description": "Enables the set_menu command without any pre-configured scope.",
|
"description": "Enables the set_menu command without any pre-configured scope.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
@@ -968,6 +986,12 @@
|
|||||||
"const": "core:tray:deny-set-icon-as-template",
|
"const": "core:tray:deny-set-icon-as-template",
|
||||||
"markdownDescription": "Denies the set_icon_as_template command without any pre-configured scope."
|
"markdownDescription": "Denies the set_icon_as_template command without any pre-configured scope."
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"description": "Denies the set_icon_with_as_template command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "core:tray:deny-set-icon-with-as-template",
|
||||||
|
"markdownDescription": "Denies the set_icon_with_as_template command without any pre-configured scope."
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"description": "Denies the set_menu command without any pre-configured scope.",
|
"description": "Denies the set_menu command without any pre-configured scope.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
@@ -1227,10 +1251,16 @@
|
|||||||
"markdownDescription": "Denies the webview_size command without any pre-configured scope."
|
"markdownDescription": "Denies the webview_size command without any pre-configured scope."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-get-all-windows`\n- `allow-scale-factor`\n- `allow-inner-position`\n- `allow-outer-position`\n- `allow-inner-size`\n- `allow-outer-size`\n- `allow-is-fullscreen`\n- `allow-is-minimized`\n- `allow-is-maximized`\n- `allow-is-focused`\n- `allow-is-decorated`\n- `allow-is-resizable`\n- `allow-is-maximizable`\n- `allow-is-minimizable`\n- `allow-is-closable`\n- `allow-is-visible`\n- `allow-is-enabled`\n- `allow-title`\n- `allow-current-monitor`\n- `allow-primary-monitor`\n- `allow-monitor-from-point`\n- `allow-available-monitors`\n- `allow-cursor-position`\n- `allow-theme`\n- `allow-is-always-on-top`\n- `allow-internal-toggle-maximize`",
|
"description": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-get-all-windows`\n- `allow-scale-factor`\n- `allow-inner-position`\n- `allow-outer-position`\n- `allow-inner-size`\n- `allow-outer-size`\n- `allow-is-fullscreen`\n- `allow-is-minimized`\n- `allow-is-maximized`\n- `allow-is-focused`\n- `allow-is-decorated`\n- `allow-is-resizable`\n- `allow-is-maximizable`\n- `allow-is-minimizable`\n- `allow-is-closable`\n- `allow-is-visible`\n- `allow-is-enabled`\n- `allow-title`\n- `allow-current-monitor`\n- `allow-primary-monitor`\n- `allow-monitor-from-point`\n- `allow-available-monitors`\n- `allow-cursor-position`\n- `allow-theme`\n- `allow-is-always-on-top`\n- `allow-activity-name`\n- `allow-scene-identifier`\n- `allow-internal-toggle-maximize`",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"const": "core:window:default",
|
"const": "core:window:default",
|
||||||
"markdownDescription": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-get-all-windows`\n- `allow-scale-factor`\n- `allow-inner-position`\n- `allow-outer-position`\n- `allow-inner-size`\n- `allow-outer-size`\n- `allow-is-fullscreen`\n- `allow-is-minimized`\n- `allow-is-maximized`\n- `allow-is-focused`\n- `allow-is-decorated`\n- `allow-is-resizable`\n- `allow-is-maximizable`\n- `allow-is-minimizable`\n- `allow-is-closable`\n- `allow-is-visible`\n- `allow-is-enabled`\n- `allow-title`\n- `allow-current-monitor`\n- `allow-primary-monitor`\n- `allow-monitor-from-point`\n- `allow-available-monitors`\n- `allow-cursor-position`\n- `allow-theme`\n- `allow-is-always-on-top`\n- `allow-internal-toggle-maximize`"
|
"markdownDescription": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-get-all-windows`\n- `allow-scale-factor`\n- `allow-inner-position`\n- `allow-outer-position`\n- `allow-inner-size`\n- `allow-outer-size`\n- `allow-is-fullscreen`\n- `allow-is-minimized`\n- `allow-is-maximized`\n- `allow-is-focused`\n- `allow-is-decorated`\n- `allow-is-resizable`\n- `allow-is-maximizable`\n- `allow-is-minimizable`\n- `allow-is-closable`\n- `allow-is-visible`\n- `allow-is-enabled`\n- `allow-title`\n- `allow-current-monitor`\n- `allow-primary-monitor`\n- `allow-monitor-from-point`\n- `allow-available-monitors`\n- `allow-cursor-position`\n- `allow-theme`\n- `allow-is-always-on-top`\n- `allow-activity-name`\n- `allow-scene-identifier`\n- `allow-internal-toggle-maximize`"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Enables the activity_name command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "core:window:allow-activity-name",
|
||||||
|
"markdownDescription": "Enables the activity_name command without any pre-configured scope."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "Enables the available_monitors command without any pre-configured scope.",
|
"description": "Enables the available_monitors command without any pre-configured scope.",
|
||||||
@@ -1424,6 +1454,12 @@
|
|||||||
"const": "core:window:allow-scale-factor",
|
"const": "core:window:allow-scale-factor",
|
||||||
"markdownDescription": "Enables the scale_factor command without any pre-configured scope."
|
"markdownDescription": "Enables the scale_factor command without any pre-configured scope."
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"description": "Enables the scene_identifier command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "core:window:allow-scene-identifier",
|
||||||
|
"markdownDescription": "Enables the scene_identifier command without any pre-configured scope."
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"description": "Enables the set_always_on_bottom command without any pre-configured scope.",
|
"description": "Enables the set_always_on_bottom command without any pre-configured scope.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
@@ -1688,6 +1724,12 @@
|
|||||||
"const": "core:window:allow-unminimize",
|
"const": "core:window:allow-unminimize",
|
||||||
"markdownDescription": "Enables the unminimize command without any pre-configured scope."
|
"markdownDescription": "Enables the unminimize command without any pre-configured scope."
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"description": "Denies the activity_name command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "core:window:deny-activity-name",
|
||||||
|
"markdownDescription": "Denies the activity_name command without any pre-configured scope."
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"description": "Denies the available_monitors command without any pre-configured scope.",
|
"description": "Denies the available_monitors command without any pre-configured scope.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
@@ -1880,6 +1922,12 @@
|
|||||||
"const": "core:window:deny-scale-factor",
|
"const": "core:window:deny-scale-factor",
|
||||||
"markdownDescription": "Denies the scale_factor command without any pre-configured scope."
|
"markdownDescription": "Denies the scale_factor command without any pre-configured scope."
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"description": "Denies the scene_identifier command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"const": "core:window:deny-scene-identifier",
|
||||||
|
"markdownDescription": "Denies the scene_identifier command without any pre-configured scope."
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"description": "Denies the set_always_on_bottom command without any pre-configured scope.",
|
"description": "Denies the set_always_on_bottom command without any pre-configured scope.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
Before Width: | Height: | Size: 448 KiB After Width: | Height: | Size: 448 KiB |
|
Before Width: | Height: | Size: 7.6 KiB After Width: | Height: | Size: 7.6 KiB |
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "kb-app",
|
"name": "kb-demo-app",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.7.26",
|
"version": "0.7.27",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
@@ -12,17 +12,17 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fltsci/tauri-plugin-tracing": "^0.3",
|
"@fltsci/tauri-plugin-tracing": "^0.3",
|
||||||
"@fortawesome/fontawesome-free": "^7.2",
|
"@fortawesome/fontawesome-free": "^7.2",
|
||||||
"@tauri-apps/api": "^2.10",
|
"@tauri-apps/api": "^2.11",
|
||||||
"bootstrap": "^5.3",
|
"bootstrap": "^5.3",
|
||||||
"echarts": "^6.0",
|
"echarts": "^6.0",
|
||||||
"resize-observer-polyfill": "^1.5",
|
"resize-observer-polyfill": "^1.5",
|
||||||
"simplebar": "^6.3"
|
"simplebar": "^6.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tauri-apps/cli": "^2.10",
|
"@tauri-apps/cli": "^2.11",
|
||||||
"@types/bootstrap": "^5.2",
|
"@types/bootstrap": "^5.2",
|
||||||
"@types/node": "^25.3",
|
"@types/node": "^25.6",
|
||||||
"sass-embedded": "^1.97",
|
"sass-embedded": "^1.99",
|
||||||
"typescript": "^5.9",
|
"typescript": "^5.9",
|
||||||
"vite": "^8.0"
|
"vite": "^8.0"
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// file: kb_app/src/demo_http.rs
|
// file: kb_demo_app/src/demo_http.rs
|
||||||
|
|
||||||
//! Tauri commands for the HTTP demo window.
|
//! Tauri commands for the HTTP demo window.
|
||||||
//!
|
//!
|
||||||
@@ -8,9 +8,9 @@ use tauri::Manager;
|
|||||||
|
|
||||||
/// Request payload for one demo HTTP execution.
|
/// Request payload for one demo HTTP execution.
|
||||||
#[derive(Clone, Debug, serde::Deserialize, ts_rs::TS)]
|
#[derive(Clone, Debug, serde::Deserialize, ts_rs::TS)]
|
||||||
#[ts(export, export_to = "../frontend/ts/bindings/KbDemoHttpRequest.ts")]
|
#[ts(export, export_to = "../frontend/ts/bindings/DemoHttpRequest.ts")]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub(crate) struct KbDemoHttpRequest {
|
pub(crate) struct DemoHttpRequest {
|
||||||
/// Logical role used to select one endpoint from the pool.
|
/// Logical role used to select one endpoint from the pool.
|
||||||
pub role: std::string::String,
|
pub role: std::string::String,
|
||||||
/// JSON-RPC HTTP method name.
|
/// JSON-RPC HTTP method name.
|
||||||
@@ -25,9 +25,9 @@ pub(crate) struct KbDemoHttpRequest {
|
|||||||
|
|
||||||
/// Response payload for one demo HTTP execution.
|
/// Response payload for one demo HTTP execution.
|
||||||
#[derive(Clone, Debug, serde::Serialize, ts_rs::TS)]
|
#[derive(Clone, Debug, serde::Serialize, ts_rs::TS)]
|
||||||
#[ts(export, export_to = "../frontend/ts/bindings/KbDemoHttpExecutionPayload.ts")]
|
#[ts(export, export_to = "../frontend/ts/bindings/DemoHttpExecutionPayload.ts")]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub(crate) struct KbDemoHttpExecutionPayload {
|
pub(crate) struct DemoHttpExecutionPayload {
|
||||||
/// Selected endpoint name.
|
/// Selected endpoint name.
|
||||||
pub endpoint_name: std::string::String,
|
pub endpoint_name: std::string::String,
|
||||||
/// Selected endpoint provider.
|
/// Selected endpoint provider.
|
||||||
@@ -71,9 +71,9 @@ pub(crate) fn open_demo_http_window(
|
|||||||
Ok(window) => window,
|
Ok(window) => window,
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
return Err(format!("cannot create demo_http window: {error:?}"));
|
return Err(format!("cannot create demo_http window: {error:?}"));
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
let show_result = demo_window.show();
|
let show_result = demo_window.show();
|
||||||
if let Err(error) = show_result {
|
if let Err(error) = show_result {
|
||||||
@@ -89,17 +89,17 @@ pub(crate) fn open_demo_http_window(
|
|||||||
/// Returns a fresh snapshot of the HTTP endpoint pool.
|
/// Returns a fresh snapshot of the HTTP endpoint pool.
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub(crate) async fn demo_http_list_pool_clients(
|
pub(crate) async fn demo_http_list_pool_clients(
|
||||||
state: tauri::State<'_, crate::KbAppState>,
|
state: tauri::State<'_, crate::AppState>,
|
||||||
) -> Result<std::vec::Vec<kb_lib::KbHttpPoolClientSnapshot>, std::string::String> {
|
) -> Result<std::vec::Vec<kb_lib::HttpPoolClientSnapshot>, std::string::String> {
|
||||||
Ok(state.http_pool.snapshot().await)
|
Ok(state.http_pool.snapshot().await)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Executes one manual HTTP request through the endpoint pool.
|
/// Executes one manual HTTP request through the endpoint pool.
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub(crate) async fn demo_http_execute_request(
|
pub(crate) async fn demo_http_execute_request(
|
||||||
state: tauri::State<'_, crate::KbAppState>,
|
state: tauri::State<'_, crate::AppState>,
|
||||||
request: KbDemoHttpRequest,
|
request: DemoHttpRequest,
|
||||||
) -> Result<KbDemoHttpExecutionPayload, std::string::String> {
|
) -> Result<DemoHttpExecutionPayload, std::string::String> {
|
||||||
let role = request.role.trim().to_string();
|
let role = request.role.trim().to_string();
|
||||||
if role.is_empty() {
|
if role.is_empty() {
|
||||||
return Err("demo http role must not be empty".to_string());
|
return Err("demo http role must not be empty".to_string());
|
||||||
@@ -108,28 +108,26 @@ pub(crate) async fn demo_http_execute_request(
|
|||||||
if method.is_empty() {
|
if method.is_empty() {
|
||||||
return Err("demo http method must not be empty".to_string());
|
return Err("demo http method must not be empty".to_string());
|
||||||
}
|
}
|
||||||
let config_json_value_result = kb_parse_optional_demo_http_json(request.config_json);
|
let config_json_value_result = parse_optional_demo_http_json(request.config_json);
|
||||||
let config_json_value = match config_json_value_result {
|
let config_json_value = match config_json_value_result {
|
||||||
Ok(config_json_value) => config_json_value,
|
Ok(config_json_value) => config_json_value,
|
||||||
Err(error) => return Err(error),
|
Err(error) => return Err(error),
|
||||||
};
|
};
|
||||||
let params_result =
|
let params_result =
|
||||||
kb_build_demo_http_params(&method, request.first_arg.as_deref(), config_json_value);
|
build_demo_http_params(&method, request.first_arg.as_deref(), config_json_value);
|
||||||
let params = match params_result {
|
let params = match params_result {
|
||||||
Ok(params) => params,
|
Ok(params) => params,
|
||||||
Err(error) => return Err(error),
|
Err(error) => return Err(error),
|
||||||
};
|
};
|
||||||
let selected_client_result = state
|
let selected_client_result =
|
||||||
.http_pool
|
state.http_pool.select_client_for_role_and_method(&role, &method).await;
|
||||||
.select_client_for_role_and_method(&role, &method)
|
|
||||||
.await;
|
|
||||||
let selected_client = match selected_client_result {
|
let selected_client = match selected_client_result {
|
||||||
Ok(selected_client) => selected_client,
|
Ok(selected_client) => selected_client,
|
||||||
Err(error) => return Err(error.to_string()),
|
Err(error) => return Err(error.to_string()),
|
||||||
};
|
};
|
||||||
let method_class = kb_lib::HttpClient::classify_method(&method);
|
let method_class = kb_lib::HttpClient::classify_method(&method);
|
||||||
let method_class_text = kb_demo_http_method_class_to_string(method_class);
|
let method_class_text = demo_http_method_class_to_string(method_class);
|
||||||
tracing::info!(
|
tracing::trace!(
|
||||||
endpoint_name = %selected_client.endpoint_name(),
|
endpoint_name = %selected_client.endpoint_name(),
|
||||||
endpoint_url = %selected_client.endpoint_url(),
|
endpoint_url = %selected_client.endpoint_url(),
|
||||||
role = %role,
|
role = %role,
|
||||||
@@ -137,9 +135,8 @@ pub(crate) async fn demo_http_execute_request(
|
|||||||
method_class = %method_class_text,
|
method_class = %method_class_text,
|
||||||
"executing demo http request"
|
"executing demo http request"
|
||||||
);
|
);
|
||||||
let response_value_result = selected_client
|
let response_value_result =
|
||||||
.execute_json_rpc_result_raw(method.clone(), params)
|
selected_client.execute_json_rpc_result_raw(method.clone(), params).await;
|
||||||
.await;
|
|
||||||
let response_value = match response_value_result {
|
let response_value = match response_value_result {
|
||||||
Ok(response_value) => response_value,
|
Ok(response_value) => response_value,
|
||||||
Err(error) => return Err(error.to_string()),
|
Err(error) => return Err(error.to_string()),
|
||||||
@@ -152,9 +149,9 @@ pub(crate) async fn demo_http_execute_request(
|
|||||||
"cannot pretty-print demo http response for method '{}': {}",
|
"cannot pretty-print demo http response for method '{}': {}",
|
||||||
method, error
|
method, error
|
||||||
));
|
));
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
Ok(KbDemoHttpExecutionPayload {
|
Ok(DemoHttpExecutionPayload {
|
||||||
endpoint_name: selected_client.endpoint_name().to_string(),
|
endpoint_name: selected_client.endpoint_name().to_string(),
|
||||||
provider: selected_client.endpoint_config().provider.clone(),
|
provider: selected_client.endpoint_config().provider.clone(),
|
||||||
endpoint_url: selected_client.endpoint_url().to_string(),
|
endpoint_url: selected_client.endpoint_url().to_string(),
|
||||||
@@ -165,14 +162,14 @@ pub(crate) async fn demo_http_execute_request(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn kb_parse_optional_demo_http_json(
|
fn parse_optional_demo_http_json(
|
||||||
config_json: std::option::Option<std::string::String>,
|
config_json: std::option::Option<std::string::String>,
|
||||||
) -> Result<std::option::Option<serde_json::Value>, std::string::String> {
|
) -> Result<std::option::Option<serde_json::Value>, std::string::String> {
|
||||||
let config_json = match config_json {
|
let config_json = match config_json {
|
||||||
Some(config_json) => config_json.trim().to_string(),
|
Some(config_json) => config_json.trim().to_string(),
|
||||||
None => {
|
None => {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
if config_json.is_empty() {
|
if config_json.is_empty() {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
@@ -184,7 +181,7 @@ fn kb_parse_optional_demo_http_json(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn kb_build_demo_http_params(
|
fn build_demo_http_params(
|
||||||
method: &str,
|
method: &str,
|
||||||
first_arg: std::option::Option<&str>,
|
first_arg: std::option::Option<&str>,
|
||||||
config_json: std::option::Option<serde_json::Value>,
|
config_json: std::option::Option<serde_json::Value>,
|
||||||
@@ -219,10 +216,10 @@ fn kb_build_demo_http_params(
|
|||||||
Ok(params)
|
Ok(params)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn kb_demo_http_method_class_to_string(method_class: kb_lib::KbHttpMethodClass) -> &'static str {
|
fn demo_http_method_class_to_string(method_class: kb_lib::HttpMethodClass) -> &'static str {
|
||||||
match method_class {
|
match method_class {
|
||||||
kb_lib::KbHttpMethodClass::GeneralRpc => "GeneralRpc",
|
kb_lib::HttpMethodClass::GeneralRpc => "GeneralRpc",
|
||||||
kb_lib::KbHttpMethodClass::SendTransaction => "SendTransaction",
|
kb_lib::HttpMethodClass::SendTransaction => "SendTransaction",
|
||||||
kb_lib::KbHttpMethodClass::HeavyRead => "HeavyRead",
|
kb_lib::HttpMethodClass::HeavyRead => "HeavyRead",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// file: kb_app/src/demo_ws.rs
|
// file: kb_demo_app/src/demo_ws.rs
|
||||||
|
|
||||||
//! Demo WebSocket window commands and runtime state.
|
//! Demo WebSocket window commands and runtime state.
|
||||||
//!
|
//!
|
||||||
@@ -10,9 +10,9 @@ use tauri::Manager;
|
|||||||
|
|
||||||
/// Endpoint summary sent to the demo frontend.
|
/// Endpoint summary sent to the demo frontend.
|
||||||
#[derive(Clone, Debug, serde::Serialize, ts_rs::TS)]
|
#[derive(Clone, Debug, serde::Serialize, ts_rs::TS)]
|
||||||
#[ts(export, export_to = "../frontend/ts/bindings/KbDemoWsEndpointSummary.ts")]
|
#[ts(export, export_to = "../frontend/ts/bindings/DemoWsEndpointSummary.ts")]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub(crate) struct KbDemoWsEndpointSummary {
|
pub(crate) struct DemoWsEndpointSummary {
|
||||||
name: std::string::String,
|
name: std::string::String,
|
||||||
resolved_url: std::string::String,
|
resolved_url: std::string::String,
|
||||||
provider: std::string::String,
|
provider: std::string::String,
|
||||||
@@ -22,9 +22,9 @@ pub(crate) struct KbDemoWsEndpointSummary {
|
|||||||
|
|
||||||
/// Current demo window runtime status.
|
/// Current demo window runtime status.
|
||||||
#[derive(Clone, Debug, serde::Serialize, ts_rs::TS)]
|
#[derive(Clone, Debug, serde::Serialize, ts_rs::TS)]
|
||||||
#[ts(export, export_to = "../frontend/ts/bindings/KbDemoWsStatusPayload.ts")]
|
#[ts(export, export_to = "../frontend/ts/bindings/DemoWsStatusPayload.ts")]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub(crate) struct KbDemoWsStatusPayload {
|
pub(crate) struct DemoWsStatusPayload {
|
||||||
connection_state: std::string::String,
|
connection_state: std::string::String,
|
||||||
endpoint_name: std::option::Option<std::string::String>,
|
endpoint_name: std::option::Option<std::string::String>,
|
||||||
endpoint_url: std::option::Option<std::string::String>,
|
endpoint_url: std::option::Option<std::string::String>,
|
||||||
@@ -41,9 +41,9 @@ pub(crate) struct KbDemoWsStatusPayload {
|
|||||||
|
|
||||||
/// Subscribe request sent by the demo frontend.
|
/// Subscribe request sent by the demo frontend.
|
||||||
#[derive(Clone, Debug, serde::Deserialize, ts_rs::TS)]
|
#[derive(Clone, Debug, serde::Deserialize, ts_rs::TS)]
|
||||||
#[ts(export, export_to = "../frontend/ts/bindings/KbDemoWsSubscribeRequest.ts")]
|
#[ts(export, export_to = "../frontend/ts/bindings/DemoWsSubscribeRequest.ts")]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub(crate) struct KbDemoWsSubscribeRequest {
|
pub(crate) struct DemoWsSubscribeRequest {
|
||||||
method: std::string::String,
|
method: std::string::String,
|
||||||
mode: std::string::String,
|
mode: std::string::String,
|
||||||
target: std::option::Option<std::string::String>,
|
target: std::option::Option<std::string::String>,
|
||||||
@@ -53,13 +53,13 @@ pub(crate) struct KbDemoWsSubscribeRequest {
|
|||||||
|
|
||||||
/// Runtime state for the demo websocket window.
|
/// Runtime state for the demo websocket window.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct KbDemoWsRuntimeState {
|
pub(crate) struct DemoWsRuntimeState {
|
||||||
client: std::option::Option<kb_lib::WsClient>,
|
client: std::option::Option<kb_lib::WsClient>,
|
||||||
relay_task: std::option::Option<tauri::async_runtime::JoinHandle<()>>,
|
relay_task: std::option::Option<tauri::async_runtime::JoinHandle<()>>,
|
||||||
keepalive_task: std::option::Option<tauri::async_runtime::JoinHandle<()>>,
|
keepalive_task: std::option::Option<tauri::async_runtime::JoinHandle<()>>,
|
||||||
endpoint_name: std::option::Option<std::string::String>,
|
endpoint_name: std::option::Option<std::string::String>,
|
||||||
endpoint_url: std::option::Option<std::string::String>,
|
endpoint_url: std::option::Option<std::string::String>,
|
||||||
connection_state: kb_lib::KbConnectionState,
|
connection_state: kb_lib::ConnectionState,
|
||||||
current_subscription: std::option::Option<kb_lib::WsSubscriptionInfo>,
|
current_subscription: std::option::Option<kb_lib::WsSubscriptionInfo>,
|
||||||
event_count_total: u64,
|
event_count_total: u64,
|
||||||
notification_count_total: u64,
|
notification_count_total: u64,
|
||||||
@@ -69,7 +69,7 @@ pub(crate) struct KbDemoWsRuntimeState {
|
|||||||
last_status_emit_at: std::option::Option<std::time::Instant>,
|
last_status_emit_at: std::option::Option<std::time::Instant>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl KbDemoWsRuntimeState {
|
impl DemoWsRuntimeState {
|
||||||
/// Creates a new empty runtime state.
|
/// Creates a new empty runtime state.
|
||||||
pub(crate) fn new() -> Self {
|
pub(crate) fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
@@ -78,7 +78,7 @@ impl KbDemoWsRuntimeState {
|
|||||||
keepalive_task: None,
|
keepalive_task: None,
|
||||||
endpoint_name: None,
|
endpoint_name: None,
|
||||||
endpoint_url: None,
|
endpoint_url: None,
|
||||||
connection_state: kb_lib::KbConnectionState::Disconnected,
|
connection_state: kb_lib::ConnectionState::Disconnected,
|
||||||
current_subscription: None,
|
current_subscription: None,
|
||||||
event_count_total: 0,
|
event_count_total: 0,
|
||||||
notification_count_total: 0,
|
notification_count_total: 0,
|
||||||
@@ -89,7 +89,7 @@ impl KbDemoWsRuntimeState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_status_payload(&self) -> KbDemoWsStatusPayload {
|
fn to_status_payload(&self) -> DemoWsStatusPayload {
|
||||||
let current_subscription_id = self
|
let current_subscription_id = self
|
||||||
.current_subscription
|
.current_subscription
|
||||||
.as_ref()
|
.as_ref()
|
||||||
@@ -106,8 +106,8 @@ impl KbDemoWsRuntimeState {
|
|||||||
.current_subscription
|
.current_subscription
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|subscription| subscription.notification_method.clone());
|
.map(|subscription| subscription.notification_method.clone());
|
||||||
KbDemoWsStatusPayload {
|
DemoWsStatusPayload {
|
||||||
connection_state: kb_connection_state_to_string(self.connection_state),
|
connection_state: connection_state_to_string(self.connection_state),
|
||||||
endpoint_name: self.endpoint_name.clone(),
|
endpoint_name: self.endpoint_name.clone(),
|
||||||
endpoint_url: self.endpoint_url.clone(),
|
endpoint_url: self.endpoint_url.clone(),
|
||||||
current_subscription_id,
|
current_subscription_id,
|
||||||
@@ -128,7 +128,7 @@ impl KbDemoWsRuntimeState {
|
|||||||
self.keepalive_task = None;
|
self.keepalive_task = None;
|
||||||
self.endpoint_name = None;
|
self.endpoint_name = None;
|
||||||
self.endpoint_url = None;
|
self.endpoint_url = None;
|
||||||
self.connection_state = kb_lib::KbConnectionState::Disconnected;
|
self.connection_state = kb_lib::ConnectionState::Disconnected;
|
||||||
self.current_subscription = None;
|
self.current_subscription = None;
|
||||||
self.event_count_total = 0;
|
self.event_count_total = 0;
|
||||||
self.notification_count_total = 0;
|
self.notification_count_total = 0;
|
||||||
@@ -163,9 +163,9 @@ pub(crate) fn open_demo_ws_window(app_handle: tauri::AppHandle) -> Result<(), st
|
|||||||
Ok(window) => window,
|
Ok(window) => window,
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
return Err(format!("cannot create demo_ws window: {error:?}"));
|
return Err(format!("cannot create demo_ws window: {error:?}"));
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
let show_result = demo_window.show();
|
let show_result = demo_window.show();
|
||||||
if let Err(error) = show_result {
|
if let Err(error) = show_result {
|
||||||
@@ -181,8 +181,8 @@ pub(crate) fn open_demo_ws_window(app_handle: tauri::AppHandle) -> Result<(), st
|
|||||||
/// Returns the list of configured websocket endpoints.
|
/// Returns the list of configured websocket endpoints.
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub(crate) async fn demo_ws_list_endpoints(
|
pub(crate) async fn demo_ws_list_endpoints(
|
||||||
state: tauri::State<'_, crate::KbAppState>,
|
state: tauri::State<'_, crate::AppState>,
|
||||||
) -> Result<std::vec::Vec<KbDemoWsEndpointSummary>, std::string::String> {
|
) -> Result<std::vec::Vec<DemoWsEndpointSummary>, std::string::String> {
|
||||||
let mut endpoints = std::vec::Vec::new();
|
let mut endpoints = std::vec::Vec::new();
|
||||||
for endpoint in &state.config.solana.ws_endpoints {
|
for endpoint in &state.config.solana.ws_endpoints {
|
||||||
if !endpoint.enabled {
|
if !endpoint.enabled {
|
||||||
@@ -198,9 +198,9 @@ pub(crate) async fn demo_ws_list_endpoints(
|
|||||||
error
|
error
|
||||||
);
|
);
|
||||||
format!("UNRESOLVED_ENV [{}] {}", endpoint.name, endpoint.url)
|
format!("UNRESOLVED_ENV [{}] {}", endpoint.name, endpoint.url)
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
endpoints.push(KbDemoWsEndpointSummary {
|
endpoints.push(DemoWsEndpointSummary {
|
||||||
name: endpoint.name.clone(),
|
name: endpoint.name.clone(),
|
||||||
resolved_url,
|
resolved_url,
|
||||||
provider: endpoint.provider.clone(),
|
provider: endpoint.provider.clone(),
|
||||||
@@ -214,8 +214,8 @@ pub(crate) async fn demo_ws_list_endpoints(
|
|||||||
/// Returns the current demo websocket runtime status.
|
/// Returns the current demo websocket runtime status.
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub(crate) async fn demo_ws_get_status(
|
pub(crate) async fn demo_ws_get_status(
|
||||||
state: tauri::State<'_, crate::KbAppState>,
|
state: tauri::State<'_, crate::AppState>,
|
||||||
) -> Result<KbDemoWsStatusPayload, std::string::String> {
|
) -> Result<DemoWsStatusPayload, std::string::String> {
|
||||||
let runtime_guard = state.demo_ws_runtime.lock().await;
|
let runtime_guard = state.demo_ws_runtime.lock().await;
|
||||||
Ok(runtime_guard.to_status_payload())
|
Ok(runtime_guard.to_status_payload())
|
||||||
}
|
}
|
||||||
@@ -224,15 +224,15 @@ pub(crate) async fn demo_ws_get_status(
|
|||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub(crate) async fn demo_ws_connect(
|
pub(crate) async fn demo_ws_connect(
|
||||||
app_handle: tauri::AppHandle,
|
app_handle: tauri::AppHandle,
|
||||||
state: tauri::State<'_, crate::KbAppState>,
|
state: tauri::State<'_, crate::AppState>,
|
||||||
endpoint_name: std::string::String,
|
endpoint_name: std::string::String,
|
||||||
) -> Result<KbDemoWsStatusPayload, std::string::String> {
|
) -> Result<DemoWsStatusPayload, std::string::String> {
|
||||||
let endpoint_option = state.config.find_ws_endpoint(&endpoint_name);
|
let endpoint_option = state.config.find_ws_endpoint(&endpoint_name);
|
||||||
let endpoint = match endpoint_option {
|
let endpoint = match endpoint_option {
|
||||||
Some(endpoint) => endpoint.clone(),
|
Some(endpoint) => endpoint.clone(),
|
||||||
None => {
|
None => {
|
||||||
return Err(format!("unknown websocket endpoint '{}'", endpoint_name));
|
return Err(format!("unknown websocket endpoint '{}'", endpoint_name));
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
let runtime_arc = state.demo_ws_runtime.clone();
|
let runtime_arc = state.demo_ws_runtime.clone();
|
||||||
{
|
{
|
||||||
@@ -246,23 +246,19 @@ pub(crate) async fn demo_ws_connect(
|
|||||||
Ok(client) => client,
|
Ok(client) => client,
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
return Err(format!("cannot create websocket client: {error}"));
|
return Err(format!("cannot create websocket client: {error}"));
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
{
|
{
|
||||||
let mut runtime_guard = runtime_arc.lock().await;
|
let mut runtime_guard = runtime_arc.lock().await;
|
||||||
runtime_guard.endpoint_name = Some(endpoint.name.clone());
|
runtime_guard.endpoint_name = Some(endpoint.name.clone());
|
||||||
runtime_guard.endpoint_url = Some(client.endpoint_url().to_string());
|
runtime_guard.endpoint_url = Some(client.endpoint_url().to_string());
|
||||||
runtime_guard.connection_state = kb_lib::KbConnectionState::Connecting;
|
runtime_guard.connection_state = kb_lib::ConnectionState::Connecting;
|
||||||
runtime_guard.current_subscription = None;
|
runtime_guard.current_subscription = None;
|
||||||
}
|
}
|
||||||
kb_emit_demo_ws_status(&app_handle, &runtime_arc).await;
|
emit_demo_ws_status(&app_handle, &runtime_arc).await;
|
||||||
kb_emit_demo_ws_log(
|
emit_demo_ws_log(
|
||||||
&app_handle,
|
&app_handle,
|
||||||
&format!(
|
&format!("[demo] connecting endpoint '{}' ({})", endpoint.name, client.endpoint_url()),
|
||||||
"[demo] connecting endpoint '{}' ({})",
|
|
||||||
endpoint.name,
|
|
||||||
client.endpoint_url()
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
let mut event_receiver = client.subscribe_events();
|
let mut event_receiver = client.subscribe_events();
|
||||||
let relay_runtime = runtime_arc.clone();
|
let relay_runtime = runtime_arc.clone();
|
||||||
@@ -273,33 +269,30 @@ pub(crate) async fn demo_ws_connect(
|
|||||||
match recv_result {
|
match recv_result {
|
||||||
Ok(event) => {
|
Ok(event) => {
|
||||||
let (emit_ui_log, emit_ui_status) =
|
let (emit_ui_log, emit_ui_status) =
|
||||||
kb_register_demo_ws_event_and_decide_emission(&relay_runtime, &event).await;
|
register_demo_ws_event_and_decide_emission(&relay_runtime, &event).await;
|
||||||
if emit_ui_log {
|
if emit_ui_log {
|
||||||
kb_emit_demo_ws_log(&relay_app_handle, &kb_format_demo_ws_event(&event));
|
emit_demo_ws_log(&relay_app_handle, &format_demo_ws_event(&event));
|
||||||
}
|
}
|
||||||
if emit_ui_status {
|
if emit_ui_status {
|
||||||
kb_emit_demo_ws_status(&relay_app_handle, &relay_runtime).await;
|
emit_demo_ws_status(&relay_app_handle, &relay_runtime).await;
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
Err(tokio::sync::broadcast::error::RecvError::Lagged(skipped)) => {
|
Err(tokio::sync::broadcast::error::RecvError::Lagged(skipped)) => {
|
||||||
kb_emit_demo_ws_log(
|
emit_demo_ws_log(
|
||||||
&relay_app_handle,
|
&relay_app_handle,
|
||||||
&format!(
|
&format!("[demo] event receiver lagged and skipped {} event(s)", skipped),
|
||||||
"[demo] event receiver lagged and skipped {} event(s)",
|
|
||||||
skipped
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
Err(tokio::sync::broadcast::error::RecvError::Closed) => {
|
Err(tokio::sync::broadcast::error::RecvError::Closed) => {
|
||||||
break;
|
break;
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
let keepalive_client = client.clone();
|
let keepalive_client = client.clone();
|
||||||
let keepalive_app_handle = app_handle.clone();
|
let keepalive_app_handle = app_handle.clone();
|
||||||
let keepalive_task = tauri::async_runtime::spawn(async move {
|
let keepalive_task = tauri::async_runtime::spawn(async move {
|
||||||
kb_demo_ws_keepalive_loop(&keepalive_app_handle, &keepalive_client).await;
|
demo_ws_keepalive_loop(&keepalive_app_handle, &keepalive_client).await;
|
||||||
});
|
});
|
||||||
let connect_result = client.connect().await;
|
let connect_result = client.connect().await;
|
||||||
if let Err(error) = connect_result {
|
if let Err(error) = connect_result {
|
||||||
@@ -309,7 +302,7 @@ pub(crate) async fn demo_ws_connect(
|
|||||||
let mut runtime_guard = runtime_arc.lock().await;
|
let mut runtime_guard = runtime_arc.lock().await;
|
||||||
runtime_guard.clear();
|
runtime_guard.clear();
|
||||||
}
|
}
|
||||||
kb_emit_demo_ws_status(&app_handle, &runtime_arc).await;
|
emit_demo_ws_status(&app_handle, &runtime_arc).await;
|
||||||
return Err(format!("cannot connect websocket client: {error}"));
|
return Err(format!("cannot connect websocket client: {error}"));
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
@@ -319,9 +312,9 @@ pub(crate) async fn demo_ws_connect(
|
|||||||
runtime_guard.keepalive_task = Some(keepalive_task);
|
runtime_guard.keepalive_task = Some(keepalive_task);
|
||||||
runtime_guard.endpoint_name = Some(endpoint.name.clone());
|
runtime_guard.endpoint_name = Some(endpoint.name.clone());
|
||||||
runtime_guard.endpoint_url = Some(endpoint.resolved_url().unwrap_or(endpoint.url));
|
runtime_guard.endpoint_url = Some(endpoint.resolved_url().unwrap_or(endpoint.url));
|
||||||
runtime_guard.connection_state = kb_lib::KbConnectionState::Connected;
|
runtime_guard.connection_state = kb_lib::ConnectionState::Connected;
|
||||||
}
|
}
|
||||||
kb_emit_demo_ws_status(&app_handle, &runtime_arc).await;
|
emit_demo_ws_status(&app_handle, &runtime_arc).await;
|
||||||
let runtime_guard = runtime_arc.lock().await;
|
let runtime_guard = runtime_arc.lock().await;
|
||||||
Ok(runtime_guard.to_status_payload())
|
Ok(runtime_guard.to_status_payload())
|
||||||
}
|
}
|
||||||
@@ -330,14 +323,14 @@ pub(crate) async fn demo_ws_connect(
|
|||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub(crate) async fn demo_ws_disconnect(
|
pub(crate) async fn demo_ws_disconnect(
|
||||||
app_handle: tauri::AppHandle,
|
app_handle: tauri::AppHandle,
|
||||||
state: tauri::State<'_, crate::KbAppState>,
|
state: tauri::State<'_, crate::AppState>,
|
||||||
) -> Result<KbDemoWsStatusPayload, std::string::String> {
|
) -> Result<DemoWsStatusPayload, std::string::String> {
|
||||||
let runtime_arc = state.demo_ws_runtime.clone();
|
let runtime_arc = state.demo_ws_runtime.clone();
|
||||||
{
|
{
|
||||||
let mut runtime_guard = runtime_arc.lock().await;
|
let mut runtime_guard = runtime_arc.lock().await;
|
||||||
runtime_guard.connection_state = kb_lib::KbConnectionState::Disconnecting;
|
runtime_guard.connection_state = kb_lib::ConnectionState::Disconnecting;
|
||||||
}
|
}
|
||||||
kb_emit_demo_ws_status(&app_handle, &runtime_arc).await;
|
emit_demo_ws_status(&app_handle, &runtime_arc).await;
|
||||||
let (client_option, relay_task_option, keepalive_task_option) = {
|
let (client_option, relay_task_option, keepalive_task_option) = {
|
||||||
let mut runtime_guard = runtime_arc.lock().await;
|
let mut runtime_guard = runtime_arc.lock().await;
|
||||||
(
|
(
|
||||||
@@ -352,7 +345,7 @@ pub(crate) async fn demo_ws_disconnect(
|
|||||||
if let Some(client) = &client_option {
|
if let Some(client) = &client_option {
|
||||||
let disconnect_result = client.disconnect().await;
|
let disconnect_result = client.disconnect().await;
|
||||||
if let Err(error) = disconnect_result {
|
if let Err(error) = disconnect_result {
|
||||||
kb_emit_demo_ws_log(&app_handle, &format!("[demo] disconnect error: {}", error));
|
emit_demo_ws_log(&app_handle, &format!("[demo] disconnect error: {}", error));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(relay_task) = relay_task_option {
|
if let Some(relay_task) = relay_task_option {
|
||||||
@@ -362,7 +355,7 @@ pub(crate) async fn demo_ws_disconnect(
|
|||||||
let mut runtime_guard = runtime_arc.lock().await;
|
let mut runtime_guard = runtime_arc.lock().await;
|
||||||
runtime_guard.clear();
|
runtime_guard.clear();
|
||||||
}
|
}
|
||||||
kb_emit_demo_ws_status(&app_handle, &runtime_arc).await;
|
emit_demo_ws_status(&app_handle, &runtime_arc).await;
|
||||||
let runtime_guard = runtime_arc.lock().await;
|
let runtime_guard = runtime_arc.lock().await;
|
||||||
Ok(runtime_guard.to_status_payload())
|
Ok(runtime_guard.to_status_payload())
|
||||||
}
|
}
|
||||||
@@ -370,8 +363,8 @@ pub(crate) async fn demo_ws_disconnect(
|
|||||||
/// Sends one demo subscription request.
|
/// Sends one demo subscription request.
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub(crate) async fn demo_ws_subscribe(
|
pub(crate) async fn demo_ws_subscribe(
|
||||||
state: tauri::State<'_, crate::KbAppState>,
|
state: tauri::State<'_, crate::AppState>,
|
||||||
request: KbDemoWsSubscribeRequest,
|
request: DemoWsSubscribeRequest,
|
||||||
) -> Result<u64, std::string::String> {
|
) -> Result<u64, std::string::String> {
|
||||||
let client_option = {
|
let client_option = {
|
||||||
let runtime_guard = state.demo_ws_runtime.lock().await;
|
let runtime_guard = state.demo_ws_runtime.lock().await;
|
||||||
@@ -384,34 +377,31 @@ pub(crate) async fn demo_ws_subscribe(
|
|||||||
Some(client) => client,
|
Some(client) => client,
|
||||||
None => {
|
None => {
|
||||||
return Err("demo websocket client is not connected".to_string());
|
return Err("demo websocket client is not connected".to_string());
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
kb_execute_demo_ws_subscribe(&client, &request).await
|
execute_demo_ws_subscribe(&client, &request).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sends one unsubscribe request for the current active subscription.
|
/// Sends one unsubscribe request for the current active subscription.
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub(crate) async fn demo_ws_unsubscribe_current(
|
pub(crate) async fn demo_ws_unsubscribe_current(
|
||||||
state: tauri::State<'_, crate::KbAppState>,
|
state: tauri::State<'_, crate::AppState>,
|
||||||
) -> Result<u64, std::string::String> {
|
) -> Result<u64, std::string::String> {
|
||||||
let (client_option, subscription_option) = {
|
let (client_option, subscription_option) = {
|
||||||
let runtime_guard = state.demo_ws_runtime.lock().await;
|
let runtime_guard = state.demo_ws_runtime.lock().await;
|
||||||
(
|
(runtime_guard.client.clone(), runtime_guard.current_subscription.clone())
|
||||||
runtime_guard.client.clone(),
|
|
||||||
runtime_guard.current_subscription.clone(),
|
|
||||||
)
|
|
||||||
};
|
};
|
||||||
let client = match client_option {
|
let client = match client_option {
|
||||||
Some(client) => client,
|
Some(client) => client,
|
||||||
None => {
|
None => {
|
||||||
return Err("demo websocket client is not connected".to_string());
|
return Err("demo websocket client is not connected".to_string());
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
let subscription = match subscription_option {
|
let subscription = match subscription_option {
|
||||||
Some(subscription) => subscription,
|
Some(subscription) => subscription,
|
||||||
None => {
|
None => {
|
||||||
return Err("no active subscription is currently registered".to_string());
|
return Err("no active subscription is currently registered".to_string());
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
let params = vec![serde_json::Value::from(subscription.subscription_id)];
|
let params = vec![serde_json::Value::from(subscription.subscription_id)];
|
||||||
let send_result = client
|
let send_result = client
|
||||||
@@ -423,20 +413,20 @@ pub(crate) async fn demo_ws_unsubscribe_current(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn kb_execute_demo_ws_subscribe(
|
async fn execute_demo_ws_subscribe(
|
||||||
client: &kb_lib::WsClient,
|
client: &kb_lib::WsClient,
|
||||||
request: &KbDemoWsSubscribeRequest,
|
request: &DemoWsSubscribeRequest,
|
||||||
) -> Result<u64, std::string::String> {
|
) -> Result<u64, std::string::String> {
|
||||||
let method = request.method.trim();
|
let method = request.method.trim();
|
||||||
let mode = request.mode.trim();
|
let mode = request.mode.trim();
|
||||||
if method == "account" {
|
if method == "account" {
|
||||||
let target_result = kb_required_target(request, "account pubkey");
|
let target_result = required_target(request, "account pubkey");
|
||||||
let target = match target_result {
|
let target = match target_result {
|
||||||
Ok(target) => target,
|
Ok(target) => target,
|
||||||
Err(error) => return Err(error),
|
Err(error) => return Err(error),
|
||||||
};
|
};
|
||||||
if mode == "typed" {
|
if mode == "typed" {
|
||||||
let config_result = kb_parse_optional_json_typed::<
|
let config_result = parse_optional_json_typed::<
|
||||||
solana_rpc_client_api::config::RpcAccountInfoConfig,
|
solana_rpc_client_api::config::RpcAccountInfoConfig,
|
||||||
>(&request.config_json, "account typed config");
|
>(&request.config_json, "account typed config");
|
||||||
let config = match config_result {
|
let config = match config_result {
|
||||||
@@ -447,7 +437,7 @@ async fn kb_execute_demo_ws_subscribe(
|
|||||||
return result.map_err(|error| format!("account typed subscribe failed: {error}"));
|
return result.map_err(|error| format!("account typed subscribe failed: {error}"));
|
||||||
}
|
}
|
||||||
let config_result =
|
let config_result =
|
||||||
kb_parse_optional_json_value(&request.config_json, "account raw config");
|
parse_optional_json_value(&request.config_json, "account raw config");
|
||||||
let config = match config_result {
|
let config = match config_result {
|
||||||
Ok(config) => config,
|
Ok(config) => config,
|
||||||
Err(error) => return Err(error),
|
Err(error) => return Err(error),
|
||||||
@@ -457,14 +447,14 @@ async fn kb_execute_demo_ws_subscribe(
|
|||||||
}
|
}
|
||||||
if method == "block" {
|
if method == "block" {
|
||||||
if mode == "typed" {
|
if mode == "typed" {
|
||||||
let filter_result = kb_parse_required_json_typed::<
|
let filter_result = parse_required_json_typed::<
|
||||||
solana_rpc_client_api::config::RpcBlockSubscribeFilter,
|
solana_rpc_client_api::config::RpcBlockSubscribeFilter,
|
||||||
>(&request.filter_json, "block typed filter");
|
>(&request.filter_json, "block typed filter");
|
||||||
let filter = match filter_result {
|
let filter = match filter_result {
|
||||||
Ok(filter) => filter,
|
Ok(filter) => filter,
|
||||||
Err(error) => return Err(error),
|
Err(error) => return Err(error),
|
||||||
};
|
};
|
||||||
let config_result = kb_parse_optional_json_typed::<
|
let config_result = parse_optional_json_typed::<
|
||||||
solana_rpc_client_api::config::RpcBlockSubscribeConfig,
|
solana_rpc_client_api::config::RpcBlockSubscribeConfig,
|
||||||
>(&request.config_json, "block typed config");
|
>(&request.config_json, "block typed config");
|
||||||
let config = match config_result {
|
let config = match config_result {
|
||||||
@@ -474,14 +464,12 @@ async fn kb_execute_demo_ws_subscribe(
|
|||||||
let result = client.block_subscribe_typed(filter, config).await;
|
let result = client.block_subscribe_typed(filter, config).await;
|
||||||
return result.map_err(|error| format!("block typed subscribe failed: {error}"));
|
return result.map_err(|error| format!("block typed subscribe failed: {error}"));
|
||||||
}
|
}
|
||||||
let filter_result =
|
let filter_result = parse_required_json_value(&request.filter_json, "block raw filter");
|
||||||
kb_parse_required_json_value(&request.filter_json, "block raw filter");
|
|
||||||
let filter = match filter_result {
|
let filter = match filter_result {
|
||||||
Ok(filter) => filter,
|
Ok(filter) => filter,
|
||||||
Err(error) => return Err(error),
|
Err(error) => return Err(error),
|
||||||
};
|
};
|
||||||
let config_result =
|
let config_result = parse_optional_json_value(&request.config_json, "block raw config");
|
||||||
kb_parse_optional_json_value(&request.config_json, "block raw config");
|
|
||||||
let config = match config_result {
|
let config = match config_result {
|
||||||
Ok(config) => config,
|
Ok(config) => config,
|
||||||
Err(error) => return Err(error),
|
Err(error) => return Err(error),
|
||||||
@@ -491,14 +479,14 @@ async fn kb_execute_demo_ws_subscribe(
|
|||||||
}
|
}
|
||||||
if method == "logs" {
|
if method == "logs" {
|
||||||
if mode == "typed" {
|
if mode == "typed" {
|
||||||
let filter_result = kb_parse_required_json_typed::<
|
let filter_result = parse_required_json_typed::<
|
||||||
solana_rpc_client_api::config::RpcTransactionLogsFilter,
|
solana_rpc_client_api::config::RpcTransactionLogsFilter,
|
||||||
>(&request.filter_json, "logs typed filter");
|
>(&request.filter_json, "logs typed filter");
|
||||||
let filter = match filter_result {
|
let filter = match filter_result {
|
||||||
Ok(filter) => filter,
|
Ok(filter) => filter,
|
||||||
Err(error) => return Err(error),
|
Err(error) => return Err(error),
|
||||||
};
|
};
|
||||||
let config_result = kb_parse_optional_json_typed::<
|
let config_result = parse_optional_json_typed::<
|
||||||
solana_rpc_client_api::config::RpcTransactionLogsConfig,
|
solana_rpc_client_api::config::RpcTransactionLogsConfig,
|
||||||
>(&request.config_json, "logs typed config");
|
>(&request.config_json, "logs typed config");
|
||||||
let config = match config_result {
|
let config = match config_result {
|
||||||
@@ -508,14 +496,12 @@ async fn kb_execute_demo_ws_subscribe(
|
|||||||
let result = client.logs_subscribe_typed(filter, config).await;
|
let result = client.logs_subscribe_typed(filter, config).await;
|
||||||
return result.map_err(|error| format!("logs typed subscribe failed: {error}"));
|
return result.map_err(|error| format!("logs typed subscribe failed: {error}"));
|
||||||
}
|
}
|
||||||
let filter_result =
|
let filter_result = parse_required_json_value(&request.filter_json, "logs raw filter");
|
||||||
kb_parse_required_json_value(&request.filter_json, "logs raw filter");
|
|
||||||
let filter = match filter_result {
|
let filter = match filter_result {
|
||||||
Ok(filter) => filter,
|
Ok(filter) => filter,
|
||||||
Err(error) => return Err(error),
|
Err(error) => return Err(error),
|
||||||
};
|
};
|
||||||
let config_result =
|
let config_result = parse_optional_json_value(&request.config_json, "logs raw config");
|
||||||
kb_parse_optional_json_value(&request.config_json, "logs raw config");
|
|
||||||
let config = match config_result {
|
let config = match config_result {
|
||||||
Ok(config) => config,
|
Ok(config) => config,
|
||||||
Err(error) => return Err(error),
|
Err(error) => return Err(error),
|
||||||
@@ -524,13 +510,13 @@ async fn kb_execute_demo_ws_subscribe(
|
|||||||
return result.map_err(|error| format!("logs raw subscribe failed: {error}"));
|
return result.map_err(|error| format!("logs raw subscribe failed: {error}"));
|
||||||
}
|
}
|
||||||
if method == "program" {
|
if method == "program" {
|
||||||
let target_result = kb_required_target(request, "program id");
|
let target_result = required_target(request, "program id");
|
||||||
let target = match target_result {
|
let target = match target_result {
|
||||||
Ok(target) => target,
|
Ok(target) => target,
|
||||||
Err(error) => return Err(error),
|
Err(error) => return Err(error),
|
||||||
};
|
};
|
||||||
if mode == "typed" {
|
if mode == "typed" {
|
||||||
let config_result = kb_parse_optional_json_typed::<
|
let config_result = parse_optional_json_typed::<
|
||||||
solana_rpc_client_api::config::RpcProgramAccountsConfig,
|
solana_rpc_client_api::config::RpcProgramAccountsConfig,
|
||||||
>(&request.config_json, "program typed config");
|
>(&request.config_json, "program typed config");
|
||||||
let config = match config_result {
|
let config = match config_result {
|
||||||
@@ -541,7 +527,7 @@ async fn kb_execute_demo_ws_subscribe(
|
|||||||
return result.map_err(|error| format!("program typed subscribe failed: {error}"));
|
return result.map_err(|error| format!("program typed subscribe failed: {error}"));
|
||||||
}
|
}
|
||||||
let config_result =
|
let config_result =
|
||||||
kb_parse_optional_json_value(&request.config_json, "program raw config");
|
parse_optional_json_value(&request.config_json, "program raw config");
|
||||||
let config = match config_result {
|
let config = match config_result {
|
||||||
Ok(config) => config,
|
Ok(config) => config,
|
||||||
Err(error) => return Err(error),
|
Err(error) => return Err(error),
|
||||||
@@ -554,13 +540,13 @@ async fn kb_execute_demo_ws_subscribe(
|
|||||||
return result.map_err(|error| format!("root subscribe failed: {error}"));
|
return result.map_err(|error| format!("root subscribe failed: {error}"));
|
||||||
}
|
}
|
||||||
if method == "signature" {
|
if method == "signature" {
|
||||||
let target_result = kb_required_target(request, "signature");
|
let target_result = required_target(request, "signature");
|
||||||
let target = match target_result {
|
let target = match target_result {
|
||||||
Ok(target) => target,
|
Ok(target) => target,
|
||||||
Err(error) => return Err(error),
|
Err(error) => return Err(error),
|
||||||
};
|
};
|
||||||
if mode == "typed" {
|
if mode == "typed" {
|
||||||
let config_result = kb_parse_optional_json_typed::<
|
let config_result = parse_optional_json_typed::<
|
||||||
solana_rpc_client_api::config::RpcSignatureSubscribeConfig,
|
solana_rpc_client_api::config::RpcSignatureSubscribeConfig,
|
||||||
>(&request.config_json, "signature typed config");
|
>(&request.config_json, "signature typed config");
|
||||||
let config = match config_result {
|
let config = match config_result {
|
||||||
@@ -571,7 +557,7 @@ async fn kb_execute_demo_ws_subscribe(
|
|||||||
return result.map_err(|error| format!("signature typed subscribe failed: {error}"));
|
return result.map_err(|error| format!("signature typed subscribe failed: {error}"));
|
||||||
}
|
}
|
||||||
let config_result =
|
let config_result =
|
||||||
kb_parse_optional_json_value(&request.config_json, "signature raw config");
|
parse_optional_json_value(&request.config_json, "signature raw config");
|
||||||
let config = match config_result {
|
let config = match config_result {
|
||||||
Ok(config) => config,
|
Ok(config) => config,
|
||||||
Err(error) => return Err(error),
|
Err(error) => return Err(error),
|
||||||
@@ -594,8 +580,8 @@ async fn kb_execute_demo_ws_subscribe(
|
|||||||
Err(format!("unsupported demo subscribe method '{}'", method))
|
Err(format!("unsupported demo subscribe method '{}'", method))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn kb_required_target(
|
fn required_target(
|
||||||
request: &KbDemoWsSubscribeRequest,
|
request: &DemoWsSubscribeRequest,
|
||||||
label: &str,
|
label: &str,
|
||||||
) -> Result<std::string::String, std::string::String> {
|
) -> Result<std::string::String, std::string::String> {
|
||||||
let target_option = request.target.as_ref();
|
let target_option = request.target.as_ref();
|
||||||
@@ -603,7 +589,7 @@ fn kb_required_target(
|
|||||||
Some(target) => target.trim(),
|
Some(target) => target.trim(),
|
||||||
None => {
|
None => {
|
||||||
return Err(format!("{} is required", label));
|
return Err(format!("{} is required", label));
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
if target.is_empty() {
|
if target.is_empty() {
|
||||||
return Err(format!("{} is required", label));
|
return Err(format!("{} is required", label));
|
||||||
@@ -611,7 +597,7 @@ fn kb_required_target(
|
|||||||
Ok(target.to_string())
|
Ok(target.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn kb_parse_optional_json_value(
|
fn parse_optional_json_value(
|
||||||
input: &std::option::Option<std::string::String>,
|
input: &std::option::Option<std::string::String>,
|
||||||
label: &str,
|
label: &str,
|
||||||
) -> Result<std::option::Option<serde_json::Value>, std::string::String> {
|
) -> Result<std::option::Option<serde_json::Value>, std::string::String> {
|
||||||
@@ -625,12 +611,12 @@ fn kb_parse_optional_json_value(
|
|||||||
Ok(value) => Ok(Some(value)),
|
Ok(value) => Ok(Some(value)),
|
||||||
Err(error) => Err(format!("cannot parse {}: {}", label, error)),
|
Err(error) => Err(format!("cannot parse {}: {}", label, error)),
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
None => Ok(None),
|
None => Ok(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn kb_parse_required_json_value(
|
fn parse_required_json_value(
|
||||||
input: &std::option::Option<std::string::String>,
|
input: &std::option::Option<std::string::String>,
|
||||||
label: &str,
|
label: &str,
|
||||||
) -> Result<serde_json::Value, std::string::String> {
|
) -> Result<serde_json::Value, std::string::String> {
|
||||||
@@ -639,7 +625,7 @@ fn kb_parse_required_json_value(
|
|||||||
Some(input) => input.trim(),
|
Some(input) => input.trim(),
|
||||||
None => {
|
None => {
|
||||||
return Err(format!("{} is required", label));
|
return Err(format!("{} is required", label));
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
if input.is_empty() {
|
if input.is_empty() {
|
||||||
return Err(format!("{} is required", label));
|
return Err(format!("{} is required", label));
|
||||||
@@ -651,7 +637,7 @@ fn kb_parse_required_json_value(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn kb_parse_optional_json_typed<T>(
|
fn parse_optional_json_typed<T>(
|
||||||
input: &std::option::Option<std::string::String>,
|
input: &std::option::Option<std::string::String>,
|
||||||
label: &str,
|
label: &str,
|
||||||
) -> Result<std::option::Option<T>, std::string::String>
|
) -> Result<std::option::Option<T>, std::string::String>
|
||||||
@@ -668,12 +654,12 @@ where
|
|||||||
Ok(value) => Ok(Some(value)),
|
Ok(value) => Ok(Some(value)),
|
||||||
Err(error) => Err(format!("cannot parse {}: {}", label, error)),
|
Err(error) => Err(format!("cannot parse {}: {}", label, error)),
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
None => Ok(None),
|
None => Ok(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn kb_parse_required_json_typed<T>(
|
fn parse_required_json_typed<T>(
|
||||||
input: &std::option::Option<std::string::String>,
|
input: &std::option::Option<std::string::String>,
|
||||||
label: &str,
|
label: &str,
|
||||||
) -> Result<T, std::string::String>
|
) -> Result<T, std::string::String>
|
||||||
@@ -685,7 +671,7 @@ where
|
|||||||
Some(input) => input.trim(),
|
Some(input) => input.trim(),
|
||||||
None => {
|
None => {
|
||||||
return Err(format!("{} is required", label));
|
return Err(format!("{} is required", label));
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
if input.is_empty() {
|
if input.is_empty() {
|
||||||
return Err(format!("{} is required", label));
|
return Err(format!("{} is required", label));
|
||||||
@@ -697,13 +683,13 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn kb_register_demo_ws_event_and_decide_emission(
|
async fn register_demo_ws_event_and_decide_emission(
|
||||||
runtime_arc: &std::sync::Arc<tokio::sync::Mutex<KbDemoWsRuntimeState>>,
|
runtime_arc: &std::sync::Arc<tokio::sync::Mutex<DemoWsRuntimeState>>,
|
||||||
event: &kb_lib::WsEvent,
|
event: &kb_lib::WsEvent,
|
||||||
) -> (bool, bool) {
|
) -> (bool, bool) {
|
||||||
let mut runtime_guard = runtime_arc.lock().await;
|
let mut runtime_guard = runtime_arc.lock().await;
|
||||||
runtime_guard.event_count_total = runtime_guard.event_count_total.saturating_add(1);
|
runtime_guard.event_count_total = runtime_guard.event_count_total.saturating_add(1);
|
||||||
runtime_guard.last_event_kind = Some(kb_demo_ws_event_kind_name(event).to_string());
|
runtime_guard.last_event_kind = Some(demo_ws_event_kind_name(event).to_string());
|
||||||
let mut emit_ui_log = true;
|
let mut emit_ui_log = true;
|
||||||
let force_status_emit = matches!(
|
let force_status_emit = matches!(
|
||||||
event,
|
event,
|
||||||
@@ -714,18 +700,15 @@ async fn kb_register_demo_ws_event_and_decide_emission(
|
|||||||
| kb_lib::WsEvent::Error { .. }
|
| kb_lib::WsEvent::Error { .. }
|
||||||
);
|
);
|
||||||
match event {
|
match event {
|
||||||
kb_lib::WsEvent::Connected {
|
kb_lib::WsEvent::Connected { endpoint_name, endpoint_url } => {
|
||||||
endpoint_name,
|
runtime_guard.connection_state = kb_lib::ConnectionState::Connected;
|
||||||
endpoint_url,
|
|
||||||
} => {
|
|
||||||
runtime_guard.connection_state = kb_lib::KbConnectionState::Connected;
|
|
||||||
runtime_guard.endpoint_name = Some(endpoint_name.clone());
|
runtime_guard.endpoint_name = Some(endpoint_name.clone());
|
||||||
runtime_guard.endpoint_url = Some(endpoint_url.clone());
|
runtime_guard.endpoint_url = Some(endpoint_url.clone());
|
||||||
}
|
},
|
||||||
kb_lib::WsEvent::SubscriptionRegistered { subscription, .. } => {
|
kb_lib::WsEvent::SubscriptionRegistered { subscription, .. } => {
|
||||||
runtime_guard.current_subscription = Some(subscription.clone());
|
runtime_guard.current_subscription = Some(subscription.clone());
|
||||||
runtime_guard.notification_count_total = 0;
|
runtime_guard.notification_count_total = 0;
|
||||||
}
|
},
|
||||||
kb_lib::WsEvent::SubscriptionNotification { subscription, .. } => {
|
kb_lib::WsEvent::SubscriptionNotification { subscription, .. } => {
|
||||||
runtime_guard.notification_count_total =
|
runtime_guard.notification_count_total =
|
||||||
runtime_guard.notification_count_total.saturating_add(1);
|
runtime_guard.notification_count_total.saturating_add(1);
|
||||||
@@ -736,7 +719,7 @@ async fn kb_register_demo_ws_event_and_decide_emission(
|
|||||||
} else if subscribe_method == "slotsUpdatesSubscribe" {
|
} else if subscribe_method == "slotsUpdatesSubscribe" {
|
||||||
emit_ui_log = notif_count % 20 == 1;
|
emit_ui_log = notif_count % 20 == 1;
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
kb_lib::WsEvent::TextMessage { .. } | kb_lib::WsEvent::JsonRpcMessage { .. } => {
|
kb_lib::WsEvent::TextMessage { .. } | kb_lib::WsEvent::JsonRpcMessage { .. } => {
|
||||||
let subscribe_method_option = runtime_guard
|
let subscribe_method_option = runtime_guard
|
||||||
.current_subscription
|
.current_subscription
|
||||||
@@ -750,13 +733,11 @@ async fn kb_register_demo_ws_event_and_decide_emission(
|
|||||||
emit_ui_log = false;
|
emit_ui_log = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
kb_lib::WsEvent::Pong { .. } => {
|
kb_lib::WsEvent::Pong { .. } => {
|
||||||
emit_ui_log = false;
|
emit_ui_log = false;
|
||||||
}
|
},
|
||||||
kb_lib::WsEvent::SubscriptionUnregistered {
|
kb_lib::WsEvent::SubscriptionUnregistered { subscription_id, .. } => {
|
||||||
subscription_id, ..
|
|
||||||
} => {
|
|
||||||
let current_subscription_id = runtime_guard
|
let current_subscription_id = runtime_guard
|
||||||
.current_subscription
|
.current_subscription
|
||||||
.as_ref()
|
.as_ref()
|
||||||
@@ -765,16 +746,16 @@ async fn kb_register_demo_ws_event_and_decide_emission(
|
|||||||
runtime_guard.current_subscription = None;
|
runtime_guard.current_subscription = None;
|
||||||
runtime_guard.notification_count_total = 0;
|
runtime_guard.notification_count_total = 0;
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
kb_lib::WsEvent::Disconnected { .. } => {
|
kb_lib::WsEvent::Disconnected { .. } => {
|
||||||
runtime_guard.client = None;
|
runtime_guard.client = None;
|
||||||
runtime_guard.relay_task = None;
|
runtime_guard.relay_task = None;
|
||||||
runtime_guard.keepalive_task = None;
|
runtime_guard.keepalive_task = None;
|
||||||
runtime_guard.connection_state = kb_lib::KbConnectionState::Disconnected;
|
runtime_guard.connection_state = kb_lib::ConnectionState::Disconnected;
|
||||||
runtime_guard.current_subscription = None;
|
runtime_guard.current_subscription = None;
|
||||||
runtime_guard.notification_count_total = 0;
|
runtime_guard.notification_count_total = 0;
|
||||||
}
|
},
|
||||||
_ => {}
|
_ => {},
|
||||||
}
|
}
|
||||||
if emit_ui_log {
|
if emit_ui_log {
|
||||||
runtime_guard.ui_log_count = runtime_guard.ui_log_count.saturating_add(1);
|
runtime_guard.ui_log_count = runtime_guard.ui_log_count.saturating_add(1);
|
||||||
@@ -788,7 +769,7 @@ async fn kb_register_demo_ws_event_and_decide_emission(
|
|||||||
match runtime_guard.last_status_emit_at {
|
match runtime_guard.last_status_emit_at {
|
||||||
Some(last_status_emit_at) => {
|
Some(last_status_emit_at) => {
|
||||||
now.duration_since(last_status_emit_at) >= std::time::Duration::from_millis(250)
|
now.duration_since(last_status_emit_at) >= std::time::Duration::from_millis(250)
|
||||||
}
|
},
|
||||||
None => true,
|
None => true,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -798,9 +779,9 @@ async fn kb_register_demo_ws_event_and_decide_emission(
|
|||||||
(emit_ui_log, emit_ui_status)
|
(emit_ui_log, emit_ui_status)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn kb_emit_demo_ws_status(
|
async fn emit_demo_ws_status(
|
||||||
app_handle: &tauri::AppHandle,
|
app_handle: &tauri::AppHandle,
|
||||||
runtime_arc: &std::sync::Arc<tokio::sync::Mutex<KbDemoWsRuntimeState>>,
|
runtime_arc: &std::sync::Arc<tokio::sync::Mutex<DemoWsRuntimeState>>,
|
||||||
) {
|
) {
|
||||||
let status_payload = {
|
let status_payload = {
|
||||||
let runtime_guard = runtime_arc.lock().await;
|
let runtime_guard = runtime_arc.lock().await;
|
||||||
@@ -811,7 +792,7 @@ async fn kb_emit_demo_ws_status(
|
|||||||
Some(demo_window) => demo_window,
|
Some(demo_window) => demo_window,
|
||||||
None => {
|
None => {
|
||||||
return;
|
return;
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
let emit_result = demo_window.emit("demo-ws-status", status_payload);
|
let emit_result = demo_window.emit("demo-ws-status", status_payload);
|
||||||
if let Err(error) = emit_result {
|
if let Err(error) = emit_result {
|
||||||
@@ -819,14 +800,14 @@ async fn kb_emit_demo_ws_status(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn kb_emit_demo_ws_log(app_handle: &tauri::AppHandle, line: &str) {
|
fn emit_demo_ws_log(emit_demo_ws_log: &tauri::AppHandle, line: &str) {
|
||||||
tracing::debug!("{}", line);
|
tracing::trace!("{}", line);
|
||||||
let demo_window_option = app_handle.get_webview_window("demo_ws");
|
let demo_window_option = emit_demo_ws_log.get_webview_window("demo_ws");
|
||||||
let demo_window = match demo_window_option {
|
let demo_window = match demo_window_option {
|
||||||
Some(demo_window) => demo_window,
|
Some(demo_window) => demo_window,
|
||||||
None => {
|
None => {
|
||||||
return;
|
return;
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
let emit_result = demo_window.emit("demo-ws-log", line.to_string());
|
let emit_result = demo_window.emit("demo-ws-log", line.to_string());
|
||||||
if let Err(error) = emit_result {
|
if let Err(error) = emit_result {
|
||||||
@@ -834,57 +815,35 @@ fn kb_emit_demo_ws_log(app_handle: &tauri::AppHandle, line: &str) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn kb_connection_state_to_string(state: kb_lib::KbConnectionState) -> std::string::String {
|
fn connection_state_to_string(state: kb_lib::ConnectionState) -> std::string::String {
|
||||||
match state {
|
match state {
|
||||||
kb_lib::KbConnectionState::Disconnected => "Disconnected".to_string(),
|
kb_lib::ConnectionState::Disconnected => "Disconnected".to_string(),
|
||||||
kb_lib::KbConnectionState::Connecting => "Connecting".to_string(),
|
kb_lib::ConnectionState::Connecting => "Connecting".to_string(),
|
||||||
kb_lib::KbConnectionState::Connected => "Connected".to_string(),
|
kb_lib::ConnectionState::Connected => "Connected".to_string(),
|
||||||
kb_lib::KbConnectionState::Disconnecting => "Disconnecting".to_string(),
|
kb_lib::ConnectionState::Disconnecting => "Disconnecting".to_string(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn kb_format_demo_ws_event(event: &kb_lib::WsEvent) -> std::string::String {
|
fn format_demo_ws_event(event: &kb_lib::WsEvent) -> std::string::String {
|
||||||
match event {
|
match event {
|
||||||
kb_lib::WsEvent::Connected {
|
kb_lib::WsEvent::Connected { endpoint_name, endpoint_url } => {
|
||||||
endpoint_name,
|
|
||||||
endpoint_url,
|
|
||||||
} => {
|
|
||||||
format!("[demo:{endpoint_name}] connected to {endpoint_url}")
|
format!("[demo:{endpoint_name}] connected to {endpoint_url}")
|
||||||
}
|
},
|
||||||
kb_lib::WsEvent::TextMessage {
|
kb_lib::WsEvent::TextMessage { endpoint_name, text } => {
|
||||||
endpoint_name,
|
format!("[demo:{endpoint_name}] text: {}", shorten_log_text(text, 1200))
|
||||||
text,
|
},
|
||||||
} => {
|
kb_lib::WsEvent::JsonRpcMessage { endpoint_name, message } => {
|
||||||
format!(
|
|
||||||
"[demo:{endpoint_name}] text: {}",
|
|
||||||
kb_shorten_log_text(text, 1200)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
kb_lib::WsEvent::JsonRpcMessage {
|
|
||||||
endpoint_name,
|
|
||||||
message,
|
|
||||||
} => {
|
|
||||||
let rendered = format!("{message:?}");
|
let rendered = format!("{message:?}");
|
||||||
format!(
|
format!("[demo:{endpoint_name}] json-rpc: {}", shorten_log_text(&rendered, 1800))
|
||||||
"[demo:{endpoint_name}] json-rpc: {}",
|
},
|
||||||
kb_shorten_log_text(&rendered, 1800)
|
kb_lib::WsEvent::JsonRpcParseError { endpoint_name, text, error } => {
|
||||||
)
|
|
||||||
}
|
|
||||||
kb_lib::WsEvent::JsonRpcParseError {
|
|
||||||
endpoint_name,
|
|
||||||
text,
|
|
||||||
error,
|
|
||||||
} => {
|
|
||||||
format!(
|
format!(
|
||||||
"[demo:{endpoint_name}] json-rpc parse error: {} | raw={}",
|
"[demo:{endpoint_name}] json-rpc parse error: {} | raw={}",
|
||||||
error,
|
error,
|
||||||
kb_shorten_log_text(text, 1200)
|
shorten_log_text(text, 1200)
|
||||||
)
|
)
|
||||||
}
|
},
|
||||||
kb_lib::WsEvent::SubscriptionRegistered {
|
kb_lib::WsEvent::SubscriptionRegistered { endpoint_name, subscription } => {
|
||||||
endpoint_name,
|
|
||||||
subscription,
|
|
||||||
} => {
|
|
||||||
format!(
|
format!(
|
||||||
"[demo:{endpoint_name}] subscription registered request_id={} subscription_id={} subscribe={} unsubscribe={} notification={}",
|
"[demo:{endpoint_name}] subscription registered request_id={} subscription_id={} subscribe={} unsubscribe={} notification={}",
|
||||||
subscription.request_id,
|
subscription.request_id,
|
||||||
@@ -893,7 +852,7 @@ fn kb_format_demo_ws_event(event: &kb_lib::WsEvent) -> std::string::String {
|
|||||||
subscription.unsubscribe_method,
|
subscription.unsubscribe_method,
|
||||||
subscription.notification_method
|
subscription.notification_method
|
||||||
)
|
)
|
||||||
}
|
},
|
||||||
kb_lib::WsEvent::SubscriptionNotification {
|
kb_lib::WsEvent::SubscriptionNotification {
|
||||||
endpoint_name,
|
endpoint_name,
|
||||||
subscription,
|
subscription,
|
||||||
@@ -901,11 +860,11 @@ fn kb_format_demo_ws_event(event: &kb_lib::WsEvent) -> std::string::String {
|
|||||||
method_matches_registry,
|
method_matches_registry,
|
||||||
} => {
|
} => {
|
||||||
let result_text = notification.params.result.to_string();
|
let result_text = notification.params.result.to_string();
|
||||||
let typed_suffix = match kb_lib::parse_kb_solana_ws_typed_notification(notification) {
|
let typed_suffix = match kb_lib::parse_solana_ws_typed_notification(notification) {
|
||||||
Ok(typed_notification) => {
|
Ok(typed_notification) => {
|
||||||
let rendered = format!("{typed_notification:?}");
|
let rendered = format!("{typed_notification:?}");
|
||||||
format!(" | typed={}", kb_shorten_log_text(&rendered, 1200))
|
format!(" | typed={}", shorten_log_text(&rendered, 1200))
|
||||||
}
|
},
|
||||||
Err(_) => std::string::String::new(),
|
Err(_) => std::string::String::new(),
|
||||||
};
|
};
|
||||||
format!(
|
format!(
|
||||||
@@ -914,30 +873,27 @@ fn kb_format_demo_ws_event(event: &kb_lib::WsEvent) -> std::string::String {
|
|||||||
notification.method,
|
notification.method,
|
||||||
subscription.notification_method,
|
subscription.notification_method,
|
||||||
method_matches_registry,
|
method_matches_registry,
|
||||||
kb_shorten_log_text(&result_text, 1600),
|
shorten_log_text(&result_text, 1600),
|
||||||
typed_suffix
|
typed_suffix
|
||||||
)
|
)
|
||||||
}
|
},
|
||||||
kb_lib::WsEvent::JsonRpcNotificationWithoutSubscription {
|
kb_lib::WsEvent::JsonRpcNotificationWithoutSubscription { endpoint_name, notification } => {
|
||||||
endpoint_name,
|
|
||||||
notification,
|
|
||||||
} => {
|
|
||||||
let result_text = notification.params.result.to_string();
|
let result_text = notification.params.result.to_string();
|
||||||
let typed_suffix = match kb_lib::parse_kb_solana_ws_typed_notification(notification) {
|
let typed_suffix = match kb_lib::parse_solana_ws_typed_notification(notification) {
|
||||||
Ok(typed_notification) => {
|
Ok(typed_notification) => {
|
||||||
let rendered = format!("{typed_notification:?}");
|
let rendered = format!("{typed_notification:?}");
|
||||||
format!(" | typed={}", kb_shorten_log_text(&rendered, 1200))
|
format!(" | typed={}", shorten_log_text(&rendered, 1200))
|
||||||
}
|
},
|
||||||
Err(_) => std::string::String::new(),
|
Err(_) => std::string::String::new(),
|
||||||
};
|
};
|
||||||
format!(
|
format!(
|
||||||
"[demo:{endpoint_name}] untracked notification method={} subscription={} result={}{}",
|
"[demo:{endpoint_name}] untracked notification method={} subscription={} result={}{}",
|
||||||
notification.method,
|
notification.method,
|
||||||
notification.params.subscription,
|
notification.params.subscription,
|
||||||
kb_shorten_log_text(&result_text, 1600),
|
shorten_log_text(&result_text, 1600),
|
||||||
typed_suffix
|
typed_suffix
|
||||||
)
|
)
|
||||||
}
|
},
|
||||||
kb_lib::WsEvent::SubscriptionUnregistered {
|
kb_lib::WsEvent::SubscriptionUnregistered {
|
||||||
endpoint_name,
|
endpoint_name,
|
||||||
subscription_id,
|
subscription_id,
|
||||||
@@ -948,83 +904,57 @@ fn kb_format_demo_ws_event(event: &kb_lib::WsEvent) -> std::string::String {
|
|||||||
"[demo:{endpoint_name}] subscription unregistered subscription_id={} unsubscribe_method={} was_active={}",
|
"[demo:{endpoint_name}] subscription unregistered subscription_id={} unsubscribe_method={} was_active={}",
|
||||||
subscription_id, unsubscribe_method, was_active
|
subscription_id, unsubscribe_method, was_active
|
||||||
)
|
)
|
||||||
}
|
},
|
||||||
kb_lib::WsEvent::BinaryMessage {
|
kb_lib::WsEvent::BinaryMessage { endpoint_name, data } => {
|
||||||
endpoint_name,
|
format!("[demo:{endpoint_name}] binary message ({} bytes)", data.len())
|
||||||
data,
|
},
|
||||||
} => {
|
kb_lib::WsEvent::Ping { endpoint_name, data } => {
|
||||||
format!(
|
|
||||||
"[demo:{endpoint_name}] binary message ({} bytes)",
|
|
||||||
data.len()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
kb_lib::WsEvent::Ping {
|
|
||||||
endpoint_name,
|
|
||||||
data,
|
|
||||||
} => {
|
|
||||||
format!("[demo:{endpoint_name}] ping ({} bytes)", data.len())
|
format!("[demo:{endpoint_name}] ping ({} bytes)", data.len())
|
||||||
}
|
},
|
||||||
kb_lib::WsEvent::Pong {
|
kb_lib::WsEvent::Pong { endpoint_name, data } => {
|
||||||
endpoint_name,
|
|
||||||
data,
|
|
||||||
} => {
|
|
||||||
format!("[demo:{endpoint_name}] pong ({} bytes)", data.len())
|
format!("[demo:{endpoint_name}] pong ({} bytes)", data.len())
|
||||||
}
|
},
|
||||||
kb_lib::WsEvent::CloseReceived {
|
kb_lib::WsEvent::CloseReceived { endpoint_name, code, reason } => {
|
||||||
endpoint_name,
|
format!("[demo:{endpoint_name}] close received code={:?} reason={:?}", code, reason)
|
||||||
code,
|
},
|
||||||
reason,
|
|
||||||
} => {
|
|
||||||
format!(
|
|
||||||
"[demo:{endpoint_name}] close received code={:?} reason={:?}",
|
|
||||||
code, reason
|
|
||||||
)
|
|
||||||
}
|
|
||||||
kb_lib::WsEvent::Disconnected { endpoint_name } => {
|
kb_lib::WsEvent::Disconnected { endpoint_name } => {
|
||||||
format!("[demo:{endpoint_name}] disconnected")
|
format!("[demo:{endpoint_name}] disconnected")
|
||||||
}
|
},
|
||||||
kb_lib::WsEvent::Error {
|
kb_lib::WsEvent::Error { endpoint_name, error } => {
|
||||||
endpoint_name,
|
|
||||||
error,
|
|
||||||
} => {
|
|
||||||
format!("[demo:{endpoint_name}] error: {error}")
|
format!("[demo:{endpoint_name}] error: {error}")
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn kb_shorten_log_text(input: &str, max_chars: usize) -> std::string::String {
|
fn shorten_log_text(input: &str, shorten_log_text: usize) -> std::string::String {
|
||||||
let char_count = input.chars().count();
|
let char_count = input.chars().count();
|
||||||
if char_count <= max_chars {
|
if char_count <= shorten_log_text {
|
||||||
return input.to_string();
|
return input.to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
let shortened: std::string::String = input.chars().take(max_chars).collect();
|
let shortened: std::string::String = input.chars().take(shorten_log_text).collect();
|
||||||
format!("{shortened} …[truncated {} chars]", char_count - max_chars)
|
format!("{shortened} …[truncated {} chars]", char_count - shorten_log_text)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn kb_demo_ws_keepalive_loop(app_handle: &tauri::AppHandle, client: &kb_lib::WsClient) {
|
async fn demo_ws_keepalive_loop(app_handle: &tauri::AppHandle, client: &kb_lib::WsClient) {
|
||||||
loop {
|
loop {
|
||||||
tokio::time::sleep(std::time::Duration::from_secs(30)).await;
|
tokio::time::sleep(std::time::Duration::from_secs(30)).await;
|
||||||
let state = client.connection_state().await;
|
let state = client.connection_state().await;
|
||||||
if state != kb_lib::KbConnectionState::Connected {
|
if state != kb_lib::ConnectionState::Connected {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
let send_result = client.send_ping(b"demo-keepalive".to_vec()).await;
|
let send_result = client.send_ping(b"demo-keepalive".to_vec()).await;
|
||||||
if let Err(error) = send_result {
|
if let Err(error) = send_result {
|
||||||
kb_emit_demo_ws_log(
|
emit_demo_ws_log(
|
||||||
app_handle,
|
app_handle,
|
||||||
&format!(
|
&format!("[demo:{}] keepalive ping failed: {}", client.endpoint_name(), error),
|
||||||
"[demo:{}] keepalive ping failed: {}",
|
|
||||||
client.endpoint_name(),
|
|
||||||
error
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn kb_demo_ws_event_kind_name(event: &kb_lib::WsEvent) -> &'static str {
|
fn demo_ws_event_kind_name(event: &kb_lib::WsEvent) -> &'static str {
|
||||||
match event {
|
match event {
|
||||||
kb_lib::WsEvent::Connected { .. } => "connected",
|
kb_lib::WsEvent::Connected { .. } => "connected",
|
||||||
kb_lib::WsEvent::TextMessage { .. } => "text_message",
|
kb_lib::WsEvent::TextMessage { .. } => "text_message",
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// file: kb_app/src/demo_ws_manager.rs
|
// file: kb_demo_app/src/demo_ws_manager.rs
|
||||||
|
|
||||||
//! Demo WebSocket manager window commands and runtime state.
|
//! Demo WebSocket manager window commands and runtime state.
|
||||||
//!
|
//!
|
||||||
@@ -9,9 +9,9 @@ use tauri::Manager;
|
|||||||
|
|
||||||
/// Static endpoint summary enriched with current manager state.
|
/// Static endpoint summary enriched with current manager state.
|
||||||
#[derive(Clone, Debug, serde::Serialize, ts_rs::TS)]
|
#[derive(Clone, Debug, serde::Serialize, ts_rs::TS)]
|
||||||
#[ts(export, export_to = "../frontend/ts/bindings/KbDemoWsManagerEndpointSummary.ts")]
|
#[ts(export, export_to = "../frontend/ts/bindings/DemoWsManagerEndpointSummary.ts")]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub(crate) struct KbDemoWsManagerEndpointSummary {
|
pub(crate) struct DemoWsManagerEndpointSummary {
|
||||||
name: std::string::String,
|
name: std::string::String,
|
||||||
resolved_url: std::string::String,
|
resolved_url: std::string::String,
|
||||||
provider: std::string::String,
|
provider: std::string::String,
|
||||||
@@ -22,24 +22,24 @@ pub(crate) struct KbDemoWsManagerEndpointSummary {
|
|||||||
|
|
||||||
/// Global demo manager snapshot payload.
|
/// Global demo manager snapshot payload.
|
||||||
#[derive(Clone, Debug, serde::Serialize, ts_rs::TS)]
|
#[derive(Clone, Debug, serde::Serialize, ts_rs::TS)]
|
||||||
#[ts(export, export_to = "../frontend/ts/bindings/KbDemoWsManagerSnapshotPayload.ts")]
|
#[ts(export, export_to = "../frontend/ts/bindings/DemoWsManagerSnapshotPayload.ts")]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub(crate) struct KbDemoWsManagerSnapshotPayload {
|
pub(crate) struct DemoWsManagerSnapshotPayload {
|
||||||
endpoint_count: usize,
|
endpoint_count: usize,
|
||||||
started_count: usize,
|
started_count: usize,
|
||||||
endpoints: std::vec::Vec<KbDemoWsManagerEndpointSummary>,
|
endpoints: std::vec::Vec<DemoWsManagerEndpointSummary>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Runtime state for the demo WebSocket manager window.
|
/// Runtime state for the demo WebSocket manager window.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct KbDemoWsManagerRuntimeState {
|
pub(crate) struct DemoWsManagerRuntimeState {
|
||||||
relay_task: std::option::Option<tauri::async_runtime::JoinHandle<()>>,
|
relay_task: std::option::Option<tauri::async_runtime::JoinHandle<()>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl KbDemoWsManagerRuntimeState {
|
impl DemoWsManagerRuntimeState {
|
||||||
/// Creates a new empty runtime state.
|
/// Creates a new empty runtime state.
|
||||||
pub(crate) fn new() -> Self {
|
pub(crate) fn new() -> DemoWsManagerRuntimeState {
|
||||||
Self { relay_task: None }
|
DemoWsManagerRuntimeState { relay_task: None }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,9 +57,9 @@ struct DemoWsManagerActionResult {
|
|||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub(crate) async fn open_demo_ws_manager_window(
|
pub(crate) async fn open_demo_ws_manager_window(
|
||||||
app_handle: tauri::AppHandle,
|
app_handle: tauri::AppHandle,
|
||||||
state: tauri::State<'_, crate::KbAppState>,
|
state: tauri::State<'_, crate::AppState>,
|
||||||
) -> Result<(), std::string::String> {
|
) -> Result<(), std::string::String> {
|
||||||
kb_ensure_demo_ws_manager_relay(&app_handle, &state).await;
|
ensure_demo_ws_manager_relay(&app_handle, &state).await;
|
||||||
let existing_window_option = app_handle.get_webview_window("demo_ws_manager");
|
let existing_window_option = app_handle.get_webview_window("demo_ws_manager");
|
||||||
let demo_window = match existing_window_option {
|
let demo_window = match existing_window_option {
|
||||||
Some(demo_window) => demo_window,
|
Some(demo_window) => demo_window,
|
||||||
@@ -81,9 +81,9 @@ pub(crate) async fn open_demo_ws_manager_window(
|
|||||||
Ok(window) => window,
|
Ok(window) => window,
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
return Err(format!("cannot create demo_ws_manager window: {error:?}"));
|
return Err(format!("cannot create demo_ws_manager window: {error:?}"));
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
let show_result = demo_window.show();
|
let show_result = demo_window.show();
|
||||||
if let Err(error) = show_result {
|
if let Err(error) = show_result {
|
||||||
@@ -93,23 +93,23 @@ pub(crate) async fn open_demo_ws_manager_window(
|
|||||||
if let Err(error) = focus_result {
|
if let Err(error) = focus_result {
|
||||||
return Err(format!("cannot focus demo_ws_manager window: {error:?}"));
|
return Err(format!("cannot focus demo_ws_manager window: {error:?}"));
|
||||||
}
|
}
|
||||||
kb_emit_demo_ws_manager_log(&app_handle, "[ui] demo_ws_manager window loaded");
|
emit_demo_ws_manager_log(&app_handle, "[ui] demo_ws_manager window loaded");
|
||||||
kb_emit_demo_ws_manager_snapshot(&app_handle, &state).await;
|
emit_demo_ws_manager_snapshot(&app_handle, &state).await;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the current manager snapshot.
|
/// Returns the current manager snapshot.
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub(crate) async fn demo_ws_manager_get_snapshot(
|
pub(crate) async fn demo_ws_manager_get_snapshot(
|
||||||
state: tauri::State<'_, crate::KbAppState>,
|
state: tauri::State<'_, crate::AppState>,
|
||||||
) -> Result<KbDemoWsManagerSnapshotPayload, std::string::String> {
|
) -> Result<DemoWsManagerSnapshotPayload, std::string::String> {
|
||||||
kb_build_demo_ws_manager_snapshot(&state).await
|
build_demo_ws_manager_snapshot(&state).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the distinct configured roles for enabled websocket endpoints.
|
/// Returns the distinct configured roles for enabled websocket endpoints.
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub(crate) async fn demo_ws_manager_list_roles(
|
pub(crate) async fn demo_ws_manager_list_roles(
|
||||||
state: tauri::State<'_, crate::KbAppState>,
|
state: tauri::State<'_, crate::AppState>,
|
||||||
) -> Result<std::vec::Vec<std::string::String>, std::string::String> {
|
) -> Result<std::vec::Vec<std::string::String>, std::string::String> {
|
||||||
let mut roles = std::collections::BTreeSet::new();
|
let mut roles = std::collections::BTreeSet::new();
|
||||||
for endpoint in &state.config.solana.ws_endpoints {
|
for endpoint in &state.config.solana.ws_endpoints {
|
||||||
@@ -127,102 +127,82 @@ pub(crate) async fn demo_ws_manager_list_roles(
|
|||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub(crate) async fn demo_ws_manager_start_all(
|
pub(crate) async fn demo_ws_manager_start_all(
|
||||||
app_handle: tauri::AppHandle,
|
app_handle: tauri::AppHandle,
|
||||||
state: tauri::State<'_, crate::KbAppState>,
|
state: tauri::State<'_, crate::AppState>,
|
||||||
) -> Result<KbDemoWsManagerSnapshotPayload, std::string::String> {
|
) -> Result<DemoWsManagerSnapshotPayload, std::string::String> {
|
||||||
kb_ensure_demo_ws_manager_relay(&app_handle, &state).await;
|
ensure_demo_ws_manager_relay(&app_handle, &state).await;
|
||||||
let matched_count = state.ws_manager.endpoint_names().await.len();
|
let matched_count = state.ws_manager.endpoint_names().await.len();
|
||||||
let start_result = state.ws_manager.start_all().await;
|
let start_result = state.ws_manager.start_all().await;
|
||||||
let changed_count = match start_result {
|
let changed_count = match start_result {
|
||||||
Ok(changed_count) => changed_count,
|
Ok(changed_count) => changed_count,
|
||||||
Err(error) => return Err(error.to_string()),
|
Err(error) => return Err(error.to_string()),
|
||||||
};
|
};
|
||||||
let action_result = kb_build_action_result("start", "all", matched_count, changed_count);
|
let action_result = build_action_result("start", "all", matched_count, changed_count);
|
||||||
kb_emit_demo_ws_manager_log(
|
emit_demo_ws_manager_log(&app_handle, format_action_result_for_log(&action_result).as_str());
|
||||||
&app_handle,
|
emit_demo_ws_manager_snapshot(&app_handle, &state).await;
|
||||||
kb_format_action_result_for_log(&action_result).as_str(),
|
build_demo_ws_manager_snapshot(&state).await
|
||||||
);
|
|
||||||
kb_emit_demo_ws_manager_snapshot(&app_handle, &state).await;
|
|
||||||
kb_build_demo_ws_manager_snapshot(&state).await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Stops all managed websocket endpoints.
|
/// Stops all managed websocket endpoints.
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub(crate) async fn demo_ws_manager_stop_all(
|
pub(crate) async fn demo_ws_manager_stop_all(
|
||||||
app_handle: tauri::AppHandle,
|
app_handle: tauri::AppHandle,
|
||||||
state: tauri::State<'_, crate::KbAppState>,
|
state: tauri::State<'_, crate::AppState>,
|
||||||
) -> Result<KbDemoWsManagerSnapshotPayload, std::string::String> {
|
) -> Result<DemoWsManagerSnapshotPayload, std::string::String> {
|
||||||
let matched_count = state.ws_manager.endpoint_names().await.len();
|
let matched_count = state.ws_manager.endpoint_names().await.len();
|
||||||
let stop_result = state.ws_manager.stop_all().await;
|
let stop_result = state.ws_manager.stop_all().await;
|
||||||
let changed_count = match stop_result {
|
let changed_count = match stop_result {
|
||||||
Ok(changed_count) => changed_count,
|
Ok(changed_count) => changed_count,
|
||||||
Err(error) => return Err(error.to_string()),
|
Err(error) => return Err(error.to_string()),
|
||||||
};
|
};
|
||||||
let action_result = kb_build_action_result("stop", "all", matched_count, changed_count);
|
let action_result = build_action_result("stop", "all", matched_count, changed_count);
|
||||||
kb_emit_demo_ws_manager_log(
|
emit_demo_ws_manager_log(&app_handle, format_action_result_for_log(&action_result).as_str());
|
||||||
&app_handle,
|
emit_demo_ws_manager_snapshot(&app_handle, &state).await;
|
||||||
kb_format_action_result_for_log(&action_result).as_str(),
|
build_demo_ws_manager_snapshot(&state).await
|
||||||
);
|
|
||||||
kb_emit_demo_ws_manager_snapshot(&app_handle, &state).await;
|
|
||||||
kb_build_demo_ws_manager_snapshot(&state).await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Starts all managed websocket endpoints having the selected role.
|
/// Starts all managed websocket endpoints having the selected role.
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub(crate) async fn demo_ws_manager_start_role(
|
pub(crate) async fn demo_ws_manager_start_role(
|
||||||
app_handle: tauri::AppHandle,
|
app_handle: tauri::AppHandle,
|
||||||
state: tauri::State<'_, crate::KbAppState>,
|
state: tauri::State<'_, crate::AppState>,
|
||||||
role: std::string::String,
|
role: std::string::String,
|
||||||
) -> Result<KbDemoWsManagerSnapshotPayload, std::string::String> {
|
) -> Result<DemoWsManagerSnapshotPayload, std::string::String> {
|
||||||
kb_ensure_demo_ws_manager_relay(&app_handle, &state).await;
|
ensure_demo_ws_manager_relay(&app_handle, &state).await;
|
||||||
let matched_count = state
|
let matched_count = state.ws_manager.endpoint_names_for_role(role.as_str()).await.len();
|
||||||
.ws_manager
|
|
||||||
.endpoint_names_for_role(role.as_str())
|
|
||||||
.await
|
|
||||||
.len();
|
|
||||||
let start_result = state.ws_manager.start_role(role.as_str()).await;
|
let start_result = state.ws_manager.start_role(role.as_str()).await;
|
||||||
let changed_count = match start_result {
|
let changed_count = match start_result {
|
||||||
Ok(changed_count) => changed_count,
|
Ok(changed_count) => changed_count,
|
||||||
Err(error) => return Err(error.to_string()),
|
Err(error) => return Err(error.to_string()),
|
||||||
};
|
};
|
||||||
let action_result =
|
let action_result =
|
||||||
kb_build_action_result("start", role.as_str(), matched_count, changed_count);
|
build_action_result("start", role.as_str(), matched_count, changed_count);
|
||||||
kb_emit_demo_ws_manager_log(
|
emit_demo_ws_manager_log(&app_handle, format_action_result_for_log(&action_result).as_str());
|
||||||
&app_handle,
|
emit_demo_ws_manager_snapshot(&app_handle, &state).await;
|
||||||
kb_format_action_result_for_log(&action_result).as_str(),
|
build_demo_ws_manager_snapshot(&state).await
|
||||||
);
|
|
||||||
kb_emit_demo_ws_manager_snapshot(&app_handle, &state).await;
|
|
||||||
kb_build_demo_ws_manager_snapshot(&state).await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Stops all managed websocket endpoints having the selected role.
|
/// Stops all managed websocket endpoints having the selected role.
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub(crate) async fn demo_ws_manager_stop_role(
|
pub(crate) async fn demo_ws_manager_stop_role(
|
||||||
app_handle: tauri::AppHandle,
|
app_handle: tauri::AppHandle,
|
||||||
state: tauri::State<'_, crate::KbAppState>,
|
state: tauri::State<'_, crate::AppState>,
|
||||||
role: std::string::String,
|
role: std::string::String,
|
||||||
) -> Result<KbDemoWsManagerSnapshotPayload, std::string::String> {
|
) -> Result<DemoWsManagerSnapshotPayload, std::string::String> {
|
||||||
let matched_count = state
|
let matched_count = state.ws_manager.endpoint_names_for_role(role.as_str()).await.len();
|
||||||
.ws_manager
|
|
||||||
.endpoint_names_for_role(role.as_str())
|
|
||||||
.await
|
|
||||||
.len();
|
|
||||||
let stop_result = state.ws_manager.stop_role(role.as_str()).await;
|
let stop_result = state.ws_manager.stop_role(role.as_str()).await;
|
||||||
let changed_count = match stop_result {
|
let changed_count = match stop_result {
|
||||||
Ok(changed_count) => changed_count,
|
Ok(changed_count) => changed_count,
|
||||||
Err(error) => return Err(error.to_string()),
|
Err(error) => return Err(error.to_string()),
|
||||||
};
|
};
|
||||||
let action_result = kb_build_action_result("stop", role.as_str(), matched_count, changed_count);
|
let action_result = build_action_result("stop", role.as_str(), matched_count, changed_count);
|
||||||
kb_emit_demo_ws_manager_log(
|
emit_demo_ws_manager_log(&app_handle, format_action_result_for_log(&action_result).as_str());
|
||||||
&app_handle,
|
emit_demo_ws_manager_snapshot(&app_handle, &state).await;
|
||||||
kb_format_action_result_for_log(&action_result).as_str(),
|
build_demo_ws_manager_snapshot(&state).await
|
||||||
);
|
|
||||||
kb_emit_demo_ws_manager_snapshot(&app_handle, &state).await;
|
|
||||||
kb_build_demo_ws_manager_snapshot(&state).await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn kb_build_demo_ws_manager_snapshot(
|
async fn build_demo_ws_manager_snapshot(
|
||||||
state: &tauri::State<'_, crate::KbAppState>,
|
state: &tauri::State<'_, crate::AppState>,
|
||||||
) -> Result<KbDemoWsManagerSnapshotPayload, std::string::String> {
|
) -> Result<DemoWsManagerSnapshotPayload, std::string::String> {
|
||||||
let snapshot_result = state.ws_manager.snapshot().await;
|
let snapshot_result = state.ws_manager.snapshot().await;
|
||||||
let snapshot = match snapshot_result {
|
let snapshot = match snapshot_result {
|
||||||
Ok(snapshot) => snapshot,
|
Ok(snapshot) => snapshot,
|
||||||
@@ -230,9 +210,7 @@ async fn kb_build_demo_ws_manager_snapshot(
|
|||||||
};
|
};
|
||||||
let mut endpoints = std::vec::Vec::new();
|
let mut endpoints = std::vec::Vec::new();
|
||||||
for managed_endpoint in snapshot.endpoints {
|
for managed_endpoint in snapshot.endpoints {
|
||||||
let config_endpoint_option = state
|
let config_endpoint_option = state.config.find_ws_endpoint(&managed_endpoint.endpoint_name);
|
||||||
.config
|
|
||||||
.find_ws_endpoint(&managed_endpoint.endpoint_name);
|
|
||||||
let config_endpoint = match config_endpoint_option {
|
let config_endpoint = match config_endpoint_option {
|
||||||
Some(config_endpoint) => config_endpoint,
|
Some(config_endpoint) => config_endpoint,
|
||||||
None => {
|
None => {
|
||||||
@@ -240,35 +218,35 @@ async fn kb_build_demo_ws_manager_snapshot(
|
|||||||
"managed websocket endpoint '{}' is missing from config",
|
"managed websocket endpoint '{}' is missing from config",
|
||||||
managed_endpoint.endpoint_name
|
managed_endpoint.endpoint_name
|
||||||
));
|
));
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
endpoints.push(KbDemoWsManagerEndpointSummary {
|
endpoints.push(DemoWsManagerEndpointSummary {
|
||||||
name: managed_endpoint.endpoint_name,
|
name: managed_endpoint.endpoint_name,
|
||||||
resolved_url: managed_endpoint.resolved_url,
|
resolved_url: managed_endpoint.resolved_url,
|
||||||
provider: managed_endpoint.provider,
|
provider: managed_endpoint.provider,
|
||||||
roles: config_endpoint.roles.clone(),
|
roles: config_endpoint.roles.clone(),
|
||||||
connection_state: kb_connection_state_to_string(managed_endpoint.state),
|
connection_state: connection_state_to_string(managed_endpoint.state),
|
||||||
active_subscription_count: managed_endpoint.active_subscription_count,
|
active_subscription_count: managed_endpoint.active_subscription_count,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Ok(KbDemoWsManagerSnapshotPayload {
|
Ok(DemoWsManagerSnapshotPayload {
|
||||||
endpoint_count: snapshot.endpoint_count,
|
endpoint_count: snapshot.endpoint_count,
|
||||||
started_count: snapshot.started_count,
|
started_count: snapshot.started_count,
|
||||||
endpoints,
|
endpoints,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn kb_emit_demo_ws_manager_snapshot(
|
async fn emit_demo_ws_manager_snapshot(
|
||||||
app_handle: &tauri::AppHandle,
|
app_handle: &tauri::AppHandle,
|
||||||
state: &tauri::State<'_, crate::KbAppState>,
|
state: &tauri::State<'_, crate::AppState>,
|
||||||
) {
|
) {
|
||||||
let snapshot_result = kb_build_demo_ws_manager_snapshot(state).await;
|
let snapshot_result = build_demo_ws_manager_snapshot(state).await;
|
||||||
let snapshot = match snapshot_result {
|
let snapshot = match snapshot_result {
|
||||||
Ok(snapshot) => snapshot,
|
Ok(snapshot) => snapshot,
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
kb_emit_demo_ws_manager_log(app_handle, &format!("[ui] snapshot error: {error}"));
|
emit_demo_ws_manager_log(app_handle, &format!("[ui] snapshot error: {error}"));
|
||||||
return;
|
return;
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
let emit_result = app_handle.emit("kb-demo-ws-manager-snapshot", snapshot);
|
let emit_result = app_handle.emit("kb-demo-ws-manager-snapshot", snapshot);
|
||||||
if let Err(error) = emit_result {
|
if let Err(error) = emit_result {
|
||||||
@@ -276,9 +254,9 @@ async fn kb_emit_demo_ws_manager_snapshot(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn kb_ensure_demo_ws_manager_relay(
|
async fn ensure_demo_ws_manager_relay(
|
||||||
app_handle: &tauri::AppHandle,
|
app_handle: &tauri::AppHandle,
|
||||||
state: &tauri::State<'_, crate::KbAppState>,
|
state: &tauri::State<'_, crate::AppState>,
|
||||||
) {
|
) {
|
||||||
let mut runtime_guard = state.demo_ws_manager_runtime.lock().await;
|
let mut runtime_guard = state.demo_ws_manager_runtime.lock().await;
|
||||||
if runtime_guard.relay_task.is_some() {
|
if runtime_guard.relay_task.is_some() {
|
||||||
@@ -292,21 +270,21 @@ async fn kb_ensure_demo_ws_manager_relay(
|
|||||||
let recv_result = receiver.recv().await;
|
let recv_result = receiver.recv().await;
|
||||||
match recv_result {
|
match recv_result {
|
||||||
Ok(event) => {
|
Ok(event) => {
|
||||||
let line = kb_format_ws_event(&event);
|
let line = format_ws_event(&event);
|
||||||
kb_emit_demo_ws_manager_log(&relay_app_handle, line.as_str());
|
emit_demo_ws_manager_log(&relay_app_handle, line.as_str());
|
||||||
}
|
},
|
||||||
Err(tokio::sync::broadcast::error::RecvError::Lagged(skipped)) => {
|
Err(tokio::sync::broadcast::error::RecvError::Lagged(skipped)) => {
|
||||||
kb_emit_demo_ws_manager_log(
|
emit_demo_ws_manager_log(
|
||||||
&relay_app_handle,
|
&relay_app_handle,
|
||||||
&format!(
|
&format!(
|
||||||
"[manager] event receiver lagged and skipped {} message(s)",
|
"[manager] event receiver lagged and skipped {} message(s)",
|
||||||
skipped
|
skipped
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
Err(tokio::sync::broadcast::error::RecvError::Closed) => {
|
Err(tokio::sync::broadcast::error::RecvError::Closed) => {
|
||||||
break;
|
break;
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let mut runtime_guard = relay_state.lock().await;
|
let mut runtime_guard = relay_state.lock().await;
|
||||||
@@ -316,75 +294,56 @@ async fn kb_ensure_demo_ws_manager_relay(
|
|||||||
runtime_guard.relay_task = Some(relay_task);
|
runtime_guard.relay_task = Some(relay_task);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn kb_emit_demo_ws_manager_log(app_handle: &tauri::AppHandle, message: &str) {
|
fn emit_demo_ws_manager_log(app_handle: &tauri::AppHandle, message: &str) {
|
||||||
let emit_result = app_handle.emit("kb-demo-ws-manager-log", message.to_string());
|
let emit_result = app_handle.emit("kb-demo-ws-manager-log", message.to_string());
|
||||||
if let Err(error) = emit_result {
|
if let Err(error) = emit_result {
|
||||||
tracing::error!("error emitting demo_ws_manager log event: {error:?}");
|
tracing::error!("error emitting demo_ws_manager log event: {error:?}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn kb_connection_state_to_string(state: kb_lib::KbConnectionState) -> std::string::String {
|
fn connection_state_to_string(state: kb_lib::ConnectionState) -> std::string::String {
|
||||||
match state {
|
match state {
|
||||||
kb_lib::KbConnectionState::Disconnected => "Disconnected".to_string(),
|
kb_lib::ConnectionState::Disconnected => "Disconnected".to_string(),
|
||||||
kb_lib::KbConnectionState::Connecting => "Connecting".to_string(),
|
kb_lib::ConnectionState::Connecting => "Connecting".to_string(),
|
||||||
kb_lib::KbConnectionState::Connected => "Connected".to_string(),
|
kb_lib::ConnectionState::Connected => "Connected".to_string(),
|
||||||
kb_lib::KbConnectionState::Disconnecting => "Disconnecting".to_string(),
|
kb_lib::ConnectionState::Disconnecting => "Disconnecting".to_string(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn kb_format_ws_event(event: &kb_lib::WsEvent) -> std::string::String {
|
fn format_ws_event(event: &kb_lib::WsEvent) -> std::string::String {
|
||||||
match event {
|
match event {
|
||||||
kb_lib::WsEvent::Connected {
|
kb_lib::WsEvent::Connected { endpoint_name, endpoint_url } => {
|
||||||
endpoint_name,
|
|
||||||
endpoint_url,
|
|
||||||
} => {
|
|
||||||
format!("[ws:{endpoint_name}] connected to {endpoint_url}")
|
format!("[ws:{endpoint_name}] connected to {endpoint_url}")
|
||||||
}
|
},
|
||||||
kb_lib::WsEvent::TextMessage {
|
kb_lib::WsEvent::TextMessage { endpoint_name, text } => {
|
||||||
endpoint_name,
|
|
||||||
text,
|
|
||||||
} => {
|
|
||||||
format!("[ws:{endpoint_name}] text: {text}")
|
format!("[ws:{endpoint_name}] text: {text}")
|
||||||
}
|
},
|
||||||
kb_lib::WsEvent::JsonRpcMessage {
|
kb_lib::WsEvent::JsonRpcMessage { endpoint_name, message } => match message {
|
||||||
endpoint_name,
|
kb_lib::JsonRpcWsIncomingMessage::SuccessResponse(response) => {
|
||||||
message,
|
|
||||||
} => match message {
|
|
||||||
kb_lib::KbJsonRpcWsIncomingMessage::SuccessResponse(response) => {
|
|
||||||
format!(
|
format!(
|
||||||
"[ws:{endpoint_name}] json-rpc success id={} result={}",
|
"[ws:{endpoint_name}] json-rpc success id={} result={}",
|
||||||
response.id, response.result
|
response.id, response.result
|
||||||
)
|
)
|
||||||
}
|
},
|
||||||
kb_lib::KbJsonRpcWsIncomingMessage::ErrorResponse(response) => {
|
kb_lib::JsonRpcWsIncomingMessage::ErrorResponse(response) => {
|
||||||
format!(
|
format!(
|
||||||
"[ws:{endpoint_name}] json-rpc error id={} code={} message={}",
|
"[ws:{endpoint_name}] json-rpc error id={} code={} message={}",
|
||||||
response.id, response.error.code, response.error.message
|
response.id, response.error.code, response.error.message
|
||||||
)
|
)
|
||||||
}
|
},
|
||||||
kb_lib::KbJsonRpcWsIncomingMessage::Notification(notification) => {
|
kb_lib::JsonRpcWsIncomingMessage::Notification(notification) => {
|
||||||
format!(
|
format!(
|
||||||
"[ws:{endpoint_name}] json-rpc notification method={} subscription={} result={}",
|
"[ws:{endpoint_name}] json-rpc notification method={} subscription={} result={}",
|
||||||
notification.method,
|
notification.method,
|
||||||
notification.params.subscription,
|
notification.params.subscription,
|
||||||
notification.params.result
|
notification.params.result
|
||||||
)
|
)
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
kb_lib::WsEvent::JsonRpcParseError {
|
kb_lib::WsEvent::JsonRpcParseError { endpoint_name, text, error } => {
|
||||||
endpoint_name,
|
format!("[ws:{endpoint_name}] json-rpc parse error: {} | raw={}", error, text)
|
||||||
text,
|
},
|
||||||
error,
|
kb_lib::WsEvent::SubscriptionRegistered { endpoint_name, subscription } => {
|
||||||
} => {
|
|
||||||
format!(
|
|
||||||
"[ws:{endpoint_name}] json-rpc parse error: {} | raw={}",
|
|
||||||
error, text
|
|
||||||
)
|
|
||||||
}
|
|
||||||
kb_lib::WsEvent::SubscriptionRegistered {
|
|
||||||
endpoint_name,
|
|
||||||
subscription,
|
|
||||||
} => {
|
|
||||||
format!(
|
format!(
|
||||||
"[ws:{endpoint_name}] subscription registered subscribe_method={} unsubscribe_method={} notification_method={} request_id={} subscription_id={}",
|
"[ws:{endpoint_name}] subscription registered subscribe_method={} unsubscribe_method={} notification_method={} request_id={} subscription_id={}",
|
||||||
subscription.subscribe_method,
|
subscription.subscribe_method,
|
||||||
@@ -393,7 +352,7 @@ fn kb_format_ws_event(event: &kb_lib::WsEvent) -> std::string::String {
|
|||||||
subscription.request_id,
|
subscription.request_id,
|
||||||
subscription.subscription_id
|
subscription.subscription_id
|
||||||
)
|
)
|
||||||
}
|
},
|
||||||
kb_lib::WsEvent::SubscriptionNotification {
|
kb_lib::WsEvent::SubscriptionNotification {
|
||||||
endpoint_name,
|
endpoint_name,
|
||||||
subscription,
|
subscription,
|
||||||
@@ -408,16 +367,13 @@ fn kb_format_ws_event(event: &kb_lib::WsEvent) -> std::string::String {
|
|||||||
method_matches_registry,
|
method_matches_registry,
|
||||||
notification.params.result
|
notification.params.result
|
||||||
)
|
)
|
||||||
}
|
},
|
||||||
kb_lib::WsEvent::JsonRpcNotificationWithoutSubscription {
|
kb_lib::WsEvent::JsonRpcNotificationWithoutSubscription { endpoint_name, notification } => {
|
||||||
endpoint_name,
|
|
||||||
notification,
|
|
||||||
} => {
|
|
||||||
format!(
|
format!(
|
||||||
"[ws:{endpoint_name}] untracked notification method={} subscription={} result={}",
|
"[ws:{endpoint_name}] untracked notification method={} subscription={} result={}",
|
||||||
notification.method, notification.params.subscription, notification.params.result
|
notification.method, notification.params.subscription, notification.params.result
|
||||||
)
|
)
|
||||||
}
|
},
|
||||||
kb_lib::WsEvent::SubscriptionUnregistered {
|
kb_lib::WsEvent::SubscriptionUnregistered {
|
||||||
endpoint_name,
|
endpoint_name,
|
||||||
subscription_id,
|
subscription_id,
|
||||||
@@ -428,56 +384,37 @@ fn kb_format_ws_event(event: &kb_lib::WsEvent) -> std::string::String {
|
|||||||
"[ws:{endpoint_name}] subscription unregistered subscription_id={} unsubscribe_method={} was_active={}",
|
"[ws:{endpoint_name}] subscription unregistered subscription_id={} unsubscribe_method={} was_active={}",
|
||||||
subscription_id, unsubscribe_method, was_active
|
subscription_id, unsubscribe_method, was_active
|
||||||
)
|
)
|
||||||
}
|
},
|
||||||
kb_lib::WsEvent::BinaryMessage {
|
kb_lib::WsEvent::BinaryMessage { endpoint_name, data } => {
|
||||||
endpoint_name,
|
|
||||||
data,
|
|
||||||
} => {
|
|
||||||
format!("[{endpoint_name}] binary ({} bytes)", data.len())
|
format!("[{endpoint_name}] binary ({} bytes)", data.len())
|
||||||
}
|
},
|
||||||
kb_lib::WsEvent::Ping {
|
kb_lib::WsEvent::Ping { endpoint_name, data } => {
|
||||||
endpoint_name,
|
|
||||||
data,
|
|
||||||
} => {
|
|
||||||
format!("[{endpoint_name}] ping ({} bytes)", data.len())
|
format!("[{endpoint_name}] ping ({} bytes)", data.len())
|
||||||
}
|
},
|
||||||
kb_lib::WsEvent::Pong {
|
kb_lib::WsEvent::Pong { endpoint_name, data } => {
|
||||||
endpoint_name,
|
|
||||||
data,
|
|
||||||
} => {
|
|
||||||
format!("[{endpoint_name}] pong ({} bytes)", data.len())
|
format!("[{endpoint_name}] pong ({} bytes)", data.len())
|
||||||
}
|
},
|
||||||
kb_lib::WsEvent::CloseReceived {
|
kb_lib::WsEvent::CloseReceived { endpoint_name, code, reason } => {
|
||||||
endpoint_name,
|
format!("[ws:{endpoint_name}] close received code={:?} reason={:?}", code, reason)
|
||||||
code,
|
},
|
||||||
reason,
|
|
||||||
} => {
|
|
||||||
format!(
|
|
||||||
"[ws:{endpoint_name}] close received code={:?} reason={:?}",
|
|
||||||
code, reason
|
|
||||||
)
|
|
||||||
}
|
|
||||||
kb_lib::WsEvent::Disconnected { endpoint_name } => {
|
kb_lib::WsEvent::Disconnected { endpoint_name } => {
|
||||||
format!("[ws:{endpoint_name}] disconnected")
|
format!("[ws:{endpoint_name}] disconnected")
|
||||||
}
|
},
|
||||||
kb_lib::WsEvent::Error {
|
kb_lib::WsEvent::Error { endpoint_name, error } => {
|
||||||
endpoint_name,
|
|
||||||
error,
|
|
||||||
} => {
|
|
||||||
format!("[ws:{endpoint_name}] error: {error}")
|
format!("[ws:{endpoint_name}] error: {error}")
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn kb_build_action_result(
|
fn build_action_result(
|
||||||
action: &str,
|
build_action_result: &str,
|
||||||
target: &str,
|
target: &str,
|
||||||
matched_count: usize,
|
matched_count: usize,
|
||||||
changed_count: usize,
|
changed_count: usize,
|
||||||
) -> DemoWsManagerActionResult {
|
) -> DemoWsManagerActionResult {
|
||||||
let unchanged_count = matched_count.saturating_sub(changed_count);
|
let unchanged_count = matched_count.saturating_sub(changed_count);
|
||||||
DemoWsManagerActionResult {
|
DemoWsManagerActionResult {
|
||||||
action: action.to_string(),
|
action: build_action_result.to_string(),
|
||||||
target: target.to_string(),
|
target: target.to_string(),
|
||||||
matched_count,
|
matched_count,
|
||||||
changed_count,
|
changed_count,
|
||||||
@@ -485,7 +422,7 @@ fn kb_build_action_result(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn kb_action_past_tense(action: &str) -> &'static str {
|
fn action_past_tense(action: &str) -> &'static str {
|
||||||
match action {
|
match action {
|
||||||
"start" => "started",
|
"start" => "started",
|
||||||
"stop" => "stopped",
|
"stop" => "stopped",
|
||||||
@@ -493,37 +430,26 @@ fn kb_action_past_tense(action: &str) -> &'static str {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn kb_format_action_result_for_log(result: &DemoWsManagerActionResult) -> std::string::String {
|
fn format_action_result_for_log(result: &DemoWsManagerActionResult) -> std::string::String {
|
||||||
let is_all = result.target == "all";
|
let is_all = result.target == "all";
|
||||||
let past = kb_action_past_tense(result.action.as_str());
|
let past = action_past_tense(result.action.as_str());
|
||||||
if result.matched_count == 0 {
|
if result.matched_count == 0 {
|
||||||
if is_all {
|
if is_all {
|
||||||
return "[ui] no managed websocket endpoint is configured".to_string();
|
return "[ui] no managed websocket endpoint is configured".to_string();
|
||||||
}
|
}
|
||||||
return format!(
|
return format!("[ui] no managed websocket endpoint matches role '{}'", result.target);
|
||||||
"[ui] no managed websocket endpoint matches role '{}'",
|
|
||||||
result.target
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
if result.changed_count == 0 {
|
if result.changed_count == 0 {
|
||||||
if is_all {
|
if is_all {
|
||||||
return format!(
|
return format!(
|
||||||
"[ui] all managed websocket endpoints were already {}",
|
"[ui] all managed websocket endpoints were already {}",
|
||||||
if result.action == "start" {
|
if result.action == "start" { "started" } else { "stopped" }
|
||||||
"started"
|
|
||||||
} else {
|
|
||||||
"stopped"
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return format!(
|
return format!(
|
||||||
"[ui] role '{}' was already {} on {} endpoint(s)",
|
"[ui] role '{}' was already {} on {} endpoint(s)",
|
||||||
result.target,
|
result.target,
|
||||||
if result.action == "start" {
|
if result.action == "start" { "started" } else { "stopped" },
|
||||||
"started"
|
|
||||||
} else {
|
|
||||||
"stopped"
|
|
||||||
},
|
|
||||||
result.unchanged_count
|
result.unchanged_count
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -545,11 +471,7 @@ fn kb_format_action_result_for_log(result: &DemoWsManagerActionResult) -> std::s
|
|||||||
past,
|
past,
|
||||||
result.changed_count,
|
result.changed_count,
|
||||||
result.unchanged_count,
|
result.unchanged_count,
|
||||||
if result.action == "start" {
|
if result.action == "start" { "started" } else { "stopped" }
|
||||||
"started"
|
|
||||||
} else {
|
|
||||||
"stopped"
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
format!(
|
format!(
|
||||||
@@ -558,10 +480,6 @@ fn kb_format_action_result_for_log(result: &DemoWsManagerActionResult) -> std::s
|
|||||||
result.target,
|
result.target,
|
||||||
result.changed_count,
|
result.changed_count,
|
||||||
result.unchanged_count,
|
result.unchanged_count,
|
||||||
if result.action == "start" {
|
if result.action == "start" { "started" } else { "stopped" }
|
||||||
"started"
|
|
||||||
} else {
|
|
||||||
"stopped"
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// file: kb_app/src/lib.rs
|
// file: kb_demo_app/src/lib.rs
|
||||||
|
|
||||||
//! Tauri application library for `khadhroony-bobobot`.
|
//! Tauri application library for `khadhroony-bobobot`.
|
||||||
//!
|
//!
|
||||||
@@ -21,14 +21,14 @@ use tauri::Emitter;
|
|||||||
use tauri::Manager;
|
use tauri::Manager;
|
||||||
|
|
||||||
/// Runtime state for started WebSocket clients.
|
/// Runtime state for started WebSocket clients.
|
||||||
struct KbWsRuntimeState {
|
struct WsRuntimeState {
|
||||||
clients: std::vec::Vec<kb_lib::WsClient>,
|
clients: std::vec::Vec<kb_lib::WsClient>,
|
||||||
relay_tasks: std::vec::Vec<tauri::async_runtime::JoinHandle<()>>,
|
relay_tasks: std::vec::Vec<tauri::async_runtime::JoinHandle<()>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl KbWsRuntimeState {
|
impl WsRuntimeState {
|
||||||
fn new() -> Self {
|
fn new() -> WsRuntimeState {
|
||||||
Self {
|
WsRuntimeState {
|
||||||
clients: std::vec::Vec::new(),
|
clients: std::vec::Vec::new(),
|
||||||
relay_tasks: std::vec::Vec::new(),
|
relay_tasks: std::vec::Vec::new(),
|
||||||
}
|
}
|
||||||
@@ -36,27 +36,27 @@ impl KbWsRuntimeState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Shared application state stored inside Tauri.
|
/// Shared application state stored inside Tauri.
|
||||||
struct KbAppState {
|
struct AppState {
|
||||||
config: kb_lib::KbConfig,
|
config: kb_lib::Config,
|
||||||
database: std::sync::Arc<kb_lib::KbDatabase>,
|
database: std::sync::Arc<kb_lib::Database>,
|
||||||
ws_runtime: tokio::sync::Mutex<KbWsRuntimeState>,
|
ws_runtime: tokio::sync::Mutex<WsRuntimeState>,
|
||||||
demo_ws_runtime: std::sync::Arc<tokio::sync::Mutex<crate::demo_ws::KbDemoWsRuntimeState>>,
|
demo_ws_runtime: std::sync::Arc<tokio::sync::Mutex<crate::demo_ws::DemoWsRuntimeState>>,
|
||||||
demo_ws_manager_runtime:
|
demo_ws_manager_runtime:
|
||||||
std::sync::Arc<tokio::sync::Mutex<crate::demo_ws_manager::KbDemoWsManagerRuntimeState>>,
|
std::sync::Arc<tokio::sync::Mutex<crate::demo_ws_manager::DemoWsManagerRuntimeState>>,
|
||||||
ws_manager: std::sync::Arc<kb_lib::WsManager>,
|
ws_manager: std::sync::Arc<kb_lib::WsManager>,
|
||||||
http_pool: kb_lib::HttpEndpointPool,
|
http_pool: kb_lib::HttpEndpointPool,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Runs the desktop application.
|
/// Runs the desktop application.
|
||||||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||||
pub async fn run() -> Result<(), kb_lib::KbError> {
|
pub async fn run() -> Result<(), kb_lib::KhError> {
|
||||||
let config_path = kb_lib::KbConfig::default_path();
|
let config_path = kb_lib::Config::default_path();
|
||||||
let config_result = kb_lib::KbConfig::load_from_path(&config_path);
|
let config_result = kb_lib::Config::load_from_path(&config_path);
|
||||||
let config = match config_result {
|
let config = match config_result {
|
||||||
Ok(config) => config,
|
Ok(config) => config,
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"kb_app configuration load error from '{}': {}",
|
"kb_demo_app configuration load error from '{}': {}",
|
||||||
config_path.display(),
|
config_path.display(),
|
||||||
error
|
error
|
||||||
);
|
);
|
||||||
@@ -65,14 +65,14 @@ pub async fn run() -> Result<(), kb_lib::KbError> {
|
|||||||
};
|
};
|
||||||
let prepare_result = config.prepare_filesystem();
|
let prepare_result = config.prepare_filesystem();
|
||||||
if let Err(error) = prepare_result {
|
if let Err(error) = prepare_result {
|
||||||
eprintln!("kb_app filesystem preparation error: {error}");
|
eprintln!("kb_demo_app filesystem preparation error: {error}");
|
||||||
return Err(error);
|
return Err(error);
|
||||||
}
|
}
|
||||||
let tracing_guard_result = kb_lib::init_tracing(&config.logging);
|
let tracing_guard_result = kb_lib::init_tracing(&config.logging);
|
||||||
let _tracing_guard = match tracing_guard_result {
|
let _tracing_guard = match tracing_guard_result {
|
||||||
Ok(guard) => guard,
|
Ok(guard) => guard,
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
eprintln!("kb_app tracing initialization error: {error}");
|
eprintln!("kb_demo_app tracing initialization error: {error}");
|
||||||
return Err(error);
|
return Err(error);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -81,7 +81,7 @@ pub async fn run() -> Result<(), kb_lib::KbError> {
|
|||||||
environment = %config.app.environment,
|
environment = %config.app.environment,
|
||||||
"starting desktop application"
|
"starting desktop application"
|
||||||
);
|
);
|
||||||
let database_result = kb_lib::KbDatabase::connect_and_initialize(&config.database).await;
|
let database_result = kb_lib::Database::connect_and_initialize(&config.database).await;
|
||||||
let database = match database_result {
|
let database = match database_result {
|
||||||
Ok(database) => database,
|
Ok(database) => database,
|
||||||
Err(error) => return Err(error),
|
Err(error) => return Err(error),
|
||||||
@@ -102,15 +102,15 @@ pub async fn run() -> Result<(), kb_lib::KbError> {
|
|||||||
panic!("cannot create websocket manager: {}", error);
|
panic!("cannot create websocket manager: {}", error);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
let app_state = KbAppState {
|
let app_state = AppState {
|
||||||
config: config.clone(),
|
config: config.clone(),
|
||||||
database: std::sync::Arc::new(database),
|
database: std::sync::Arc::new(database),
|
||||||
ws_runtime: tokio::sync::Mutex::new(KbWsRuntimeState::new()),
|
ws_runtime: tokio::sync::Mutex::new(WsRuntimeState::new()),
|
||||||
demo_ws_runtime: std::sync::Arc::new(tokio::sync::Mutex::new(
|
demo_ws_runtime: std::sync::Arc::new(tokio::sync::Mutex::new(
|
||||||
crate::demo_ws::KbDemoWsRuntimeState::new(),
|
crate::demo_ws::DemoWsRuntimeState::new(),
|
||||||
)),
|
)),
|
||||||
demo_ws_manager_runtime: std::sync::Arc::new(tokio::sync::Mutex::new(
|
demo_ws_manager_runtime: std::sync::Arc::new(tokio::sync::Mutex::new(
|
||||||
crate::demo_ws_manager::KbDemoWsManagerRuntimeState::new(),
|
crate::demo_ws_manager::DemoWsManagerRuntimeState::new(),
|
||||||
)),
|
)),
|
||||||
ws_manager: std::sync::Arc::new(ws_manager),
|
ws_manager: std::sync::Arc::new(ws_manager),
|
||||||
http_pool,
|
http_pool,
|
||||||
@@ -152,6 +152,7 @@ pub async fn run() -> Result<(), kb_lib::KbError> {
|
|||||||
crate::demo_pipeline2::demo_pipeline2_get_pair_candles,
|
crate::demo_pipeline2::demo_pipeline2_get_pair_candles,
|
||||||
crate::demo_pipeline2::demo_pipeline2_replay_local_pipeline,
|
crate::demo_pipeline2::demo_pipeline2_replay_local_pipeline,
|
||||||
crate::demo_pipeline2::demo_pipeline2_diagnose_local_pipeline,
|
crate::demo_pipeline2::demo_pipeline2_diagnose_local_pipeline,
|
||||||
|
crate::demo_pipeline2::demo_pipeline2_validate_local_pipeline,
|
||||||
]);
|
]);
|
||||||
tauri_builder = tauri_builder.plugin(tracing_builder.build::<tauri::Wry>());
|
tauri_builder = tauri_builder.plugin(tracing_builder.build::<tauri::Wry>());
|
||||||
tauri_builder = tauri_builder.setup(|app| {
|
tauri_builder = tauri_builder.setup(|app| {
|
||||||
@@ -195,12 +196,12 @@ pub async fn run() -> Result<(), kb_lib::KbError> {
|
|||||||
Some("success"),
|
Some("success"),
|
||||||
);
|
);
|
||||||
tokio::time::sleep(std::time::Duration::from_millis(500)).await;
|
tokio::time::sleep(std::time::Duration::from_millis(500)).await;
|
||||||
tracing::debug!("start splash fadeout");
|
tracing::trace!("start splash fadeout");
|
||||||
if is_debug {
|
if is_debug {
|
||||||
emit_splash_order(&splash_window, "add_log", Some("Start Fade-out"), None);
|
emit_splash_order(&splash_window, "add_log", Some("Start Fade-out"), None);
|
||||||
}
|
}
|
||||||
emit_splash_order(&splash_window, "fadeout", None, None);
|
emit_splash_order(&splash_window, "fadeout", None, None);
|
||||||
tracing::debug!("end splash fadeout");
|
tracing::trace!("end splash fadeout");
|
||||||
tokio::time::sleep(std::time::Duration::from_millis(3100)).await;
|
tokio::time::sleep(std::time::Duration::from_millis(3100)).await;
|
||||||
let close_result = splash_window.destroy();
|
let close_result = splash_window.destroy();
|
||||||
if let Err(error) = close_result {
|
if let Err(error) = close_result {
|
||||||
@@ -221,7 +222,7 @@ pub async fn run() -> Result<(), kb_lib::KbError> {
|
|||||||
let run_result = tauri_builder.run(tauri::generate_context!());
|
let run_result = tauri_builder.run(tauri::generate_context!());
|
||||||
if let Err(error) = run_result {
|
if let Err(error) = run_result {
|
||||||
tracing::error!("error while running tauri application: {error:?}");
|
tracing::error!("error while running tauri application: {error:?}");
|
||||||
return Err(kb_lib::KbError::InvalidState(format!(
|
return Err(kb_lib::KhError::InvalidState(format!(
|
||||||
"error while running tauri application: {error:?}"
|
"error while running tauri application: {error:?}"
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
@@ -248,7 +249,7 @@ fn emit_splash_order(
|
|||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
async fn start_ws_clients(
|
async fn start_ws_clients(
|
||||||
app_handle: tauri::AppHandle,
|
app_handle: tauri::AppHandle,
|
||||||
state: tauri::State<'_, KbAppState>,
|
state: tauri::State<'_, AppState>,
|
||||||
) -> Result<usize, std::string::String> {
|
) -> Result<usize, std::string::String> {
|
||||||
{
|
{
|
||||||
let runtime_guard = state.ws_runtime.lock().await;
|
let runtime_guard = state.ws_runtime.lock().await;
|
||||||
@@ -256,7 +257,7 @@ async fn start_ws_clients(
|
|||||||
return Err("websocket clients are already running".to_string());
|
return Err("websocket clients are already running".to_string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let enabled_endpoints: std::vec::Vec<kb_lib::KbWsEndpointConfig> = state
|
let enabled_endpoints: std::vec::Vec<kb_lib::WsEndpointConfig> = state
|
||||||
.config
|
.config
|
||||||
.solana
|
.solana
|
||||||
.ws_endpoints
|
.ws_endpoints
|
||||||
@@ -267,14 +268,14 @@ async fn start_ws_clients(
|
|||||||
if enabled_endpoints.is_empty() {
|
if enabled_endpoints.is_empty() {
|
||||||
return Err("no enabled websocket endpoint found in config.json".to_string());
|
return Err("no enabled websocket endpoint found in config.json".to_string());
|
||||||
}
|
}
|
||||||
kb_emit_app_log(
|
emit_app_log(
|
||||||
&app_handle,
|
&app_handle,
|
||||||
&format!("[app] starting {} websocket client(s)", enabled_endpoints.len()),
|
&format!("[app] starting {} websocket client(s)", enabled_endpoints.len()),
|
||||||
);
|
);
|
||||||
let mut started_clients: std::vec::Vec<kb_lib::WsClient> = std::vec::Vec::new();
|
let mut started_clients: std::vec::Vec<kb_lib::WsClient> = std::vec::Vec::new();
|
||||||
let mut relay_tasks: std::vec::Vec<tauri::async_runtime::JoinHandle<()>> = std::vec::Vec::new();
|
let mut relay_tasks: std::vec::Vec<tauri::async_runtime::JoinHandle<()>> = std::vec::Vec::new();
|
||||||
for endpoint in enabled_endpoints {
|
for endpoint in enabled_endpoints {
|
||||||
kb_emit_app_log(
|
emit_app_log(
|
||||||
&app_handle,
|
&app_handle,
|
||||||
&format!("[app] preparing websocket endpoint '{}' ({})", endpoint.name, endpoint.url),
|
&format!("[app] preparing websocket endpoint '{}' ({})", endpoint.name, endpoint.url),
|
||||||
);
|
);
|
||||||
@@ -282,7 +283,7 @@ async fn start_ws_clients(
|
|||||||
let client = match client_result {
|
let client = match client_result {
|
||||||
Ok(client) => client,
|
Ok(client) => client,
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
kb_shutdown_started_clients(&started_clients, &mut relay_tasks).await;
|
shutdown_started_clients(&started_clients, &mut relay_tasks).await;
|
||||||
return Err(format!(
|
return Err(format!(
|
||||||
"cannot create websocket client for endpoint '{}': {}",
|
"cannot create websocket client for endpoint '{}': {}",
|
||||||
endpoint.name, error
|
endpoint.name, error
|
||||||
@@ -296,11 +297,11 @@ async fn start_ws_clients(
|
|||||||
let recv_result = event_receiver.recv().await;
|
let recv_result = event_receiver.recv().await;
|
||||||
match recv_result {
|
match recv_result {
|
||||||
Ok(event) => {
|
Ok(event) => {
|
||||||
let line = kb_format_ws_event(&event);
|
let line = format_ws_event(&event);
|
||||||
kb_emit_app_log(&relay_app_handle, &line);
|
emit_app_log(&relay_app_handle, &line);
|
||||||
},
|
},
|
||||||
Err(tokio::sync::broadcast::error::RecvError::Lagged(skipped)) => {
|
Err(tokio::sync::broadcast::error::RecvError::Lagged(skipped)) => {
|
||||||
kb_emit_app_log(
|
emit_app_log(
|
||||||
&relay_app_handle,
|
&relay_app_handle,
|
||||||
&format!(
|
&format!(
|
||||||
"[ws] event receiver lagged and skipped {} message(s)",
|
"[ws] event receiver lagged and skipped {} message(s)",
|
||||||
@@ -317,7 +318,7 @@ async fn start_ws_clients(
|
|||||||
let connect_result = client.connect().await;
|
let connect_result = client.connect().await;
|
||||||
if let Err(error) = connect_result {
|
if let Err(error) = connect_result {
|
||||||
relay_task.abort();
|
relay_task.abort();
|
||||||
kb_shutdown_started_clients(&started_clients, &mut relay_tasks).await;
|
shutdown_started_clients(&started_clients, &mut relay_tasks).await;
|
||||||
return Err(format!(
|
return Err(format!(
|
||||||
"cannot connect websocket client for endpoint '{}': {}",
|
"cannot connect websocket client for endpoint '{}': {}",
|
||||||
endpoint.name, error
|
endpoint.name, error
|
||||||
@@ -329,7 +330,7 @@ async fn start_ws_clients(
|
|||||||
{
|
{
|
||||||
let mut runtime_guard = state.ws_runtime.lock().await;
|
let mut runtime_guard = state.ws_runtime.lock().await;
|
||||||
if !runtime_guard.clients.is_empty() {
|
if !runtime_guard.clients.is_empty() {
|
||||||
kb_shutdown_started_clients(&started_clients, &mut relay_tasks).await;
|
shutdown_started_clients(&started_clients, &mut relay_tasks).await;
|
||||||
return Err("websocket clients were started concurrently".to_string());
|
return Err("websocket clients were started concurrently".to_string());
|
||||||
}
|
}
|
||||||
runtime_guard.clients = started_clients;
|
runtime_guard.clients = started_clients;
|
||||||
@@ -339,14 +340,14 @@ async fn start_ws_clients(
|
|||||||
let runtime_guard = state.ws_runtime.lock().await;
|
let runtime_guard = state.ws_runtime.lock().await;
|
||||||
runtime_guard.clients.len()
|
runtime_guard.clients.len()
|
||||||
};
|
};
|
||||||
kb_emit_app_log(&app_handle, &format!("[app] {} websocket client(s) started", started_count));
|
emit_app_log(&app_handle, &format!("[app] {} websocket client(s) started", started_count));
|
||||||
Ok(started_count)
|
Ok(started_count)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
async fn stop_ws_clients(
|
async fn stop_ws_clients(
|
||||||
app_handle: tauri::AppHandle,
|
app_handle: tauri::AppHandle,
|
||||||
state: tauri::State<'_, KbAppState>,
|
state: tauri::State<'_, AppState>,
|
||||||
) -> Result<usize, std::string::String> {
|
) -> Result<usize, std::string::String> {
|
||||||
let (clients, mut relay_tasks) = {
|
let (clients, mut relay_tasks) = {
|
||||||
let mut runtime_guard = state.ws_runtime.lock().await;
|
let mut runtime_guard = state.ws_runtime.lock().await;
|
||||||
@@ -356,15 +357,15 @@ async fn stop_ws_clients(
|
|||||||
)
|
)
|
||||||
};
|
};
|
||||||
if clients.is_empty() {
|
if clients.is_empty() {
|
||||||
kb_emit_app_log(&app_handle, "[app] websocket clients are already stopped");
|
emit_app_log(&app_handle, "[app] websocket clients are already stopped");
|
||||||
return Ok(0);
|
return Ok(0);
|
||||||
}
|
}
|
||||||
kb_emit_app_log(&app_handle, &format!("[app] stopping {} websocket client(s)", clients.len()));
|
emit_app_log(&app_handle, &format!("[app] stopping {} websocket client(s)", clients.len()));
|
||||||
let stopped_count = clients.len();
|
let stopped_count = clients.len();
|
||||||
for client in &clients {
|
for client in &clients {
|
||||||
let disconnect_result = client.disconnect().await;
|
let disconnect_result = client.disconnect().await;
|
||||||
if let Err(error) = disconnect_result {
|
if let Err(error) = disconnect_result {
|
||||||
kb_emit_app_log(
|
emit_app_log(
|
||||||
&app_handle,
|
&app_handle,
|
||||||
&format!(
|
&format!(
|
||||||
"[app] disconnect error for endpoint '{}': {}",
|
"[app] disconnect error for endpoint '{}': {}",
|
||||||
@@ -377,18 +378,18 @@ async fn stop_ws_clients(
|
|||||||
for relay_task in relay_tasks.drain(..) {
|
for relay_task in relay_tasks.drain(..) {
|
||||||
relay_task.abort();
|
relay_task.abort();
|
||||||
}
|
}
|
||||||
kb_emit_app_log(&app_handle, &format!("[app] {} websocket client(s) stopped", stopped_count));
|
emit_app_log(&app_handle, &format!("[app] {} websocket client(s) stopped", stopped_count));
|
||||||
Ok(stopped_count)
|
Ok(stopped_count)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn kb_emit_app_log(app_handle: &tauri::AppHandle, message: &str) {
|
fn emit_app_log(app_handle: &tauri::AppHandle, message: &str) {
|
||||||
let emit_result = app_handle.emit("kb-log", message.to_string());
|
let emit_result = app_handle.emit("kb-log", message.to_string());
|
||||||
if let Err(error) = emit_result {
|
if let Err(error) = emit_result {
|
||||||
tracing::error!("error emitting app log event: {error:?}");
|
tracing::error!("error emitting app log event: {error:?}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn kb_format_ws_event(event: &kb_lib::WsEvent) -> std::string::String {
|
fn format_ws_event(event: &kb_lib::WsEvent) -> std::string::String {
|
||||||
match event {
|
match event {
|
||||||
kb_lib::WsEvent::Connected { endpoint_name, endpoint_url } => {
|
kb_lib::WsEvent::Connected { endpoint_name, endpoint_url } => {
|
||||||
format!("[ws:{endpoint_name}] connected to {endpoint_url}")
|
format!("[ws:{endpoint_name}] connected to {endpoint_url}")
|
||||||
@@ -397,19 +398,19 @@ fn kb_format_ws_event(event: &kb_lib::WsEvent) -> std::string::String {
|
|||||||
format!("[ws:{endpoint_name}] text: {text}")
|
format!("[ws:{endpoint_name}] text: {text}")
|
||||||
},
|
},
|
||||||
kb_lib::WsEvent::JsonRpcMessage { endpoint_name, message } => match message {
|
kb_lib::WsEvent::JsonRpcMessage { endpoint_name, message } => match message {
|
||||||
kb_lib::KbJsonRpcWsIncomingMessage::SuccessResponse(response) => {
|
kb_lib::JsonRpcWsIncomingMessage::SuccessResponse(response) => {
|
||||||
format!(
|
format!(
|
||||||
"[ws:{endpoint_name}] json-rpc success id={} result={}",
|
"[ws:{endpoint_name}] json-rpc success id={} result={}",
|
||||||
response.id, response.result
|
response.id, response.result
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
kb_lib::KbJsonRpcWsIncomingMessage::ErrorResponse(response) => {
|
kb_lib::JsonRpcWsIncomingMessage::ErrorResponse(response) => {
|
||||||
format!(
|
format!(
|
||||||
"[ws:{endpoint_name}] json-rpc error id={} code={} message={}",
|
"[ws:{endpoint_name}] json-rpc error id={} code={} message={}",
|
||||||
response.id, response.error.code, response.error.message
|
response.id, response.error.code, response.error.message
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
kb_lib::KbJsonRpcWsIncomingMessage::Notification(notification) => {
|
kb_lib::JsonRpcWsIncomingMessage::Notification(notification) => {
|
||||||
format!(
|
format!(
|
||||||
"[ws:{endpoint_name}] json-rpc notification method={} subscription={} result={}",
|
"[ws:{endpoint_name}] json-rpc notification method={} subscription={} result={}",
|
||||||
notification.method,
|
notification.method,
|
||||||
@@ -484,7 +485,7 @@ fn kb_format_ws_event(event: &kb_lib::WsEvent) -> std::string::String {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn kb_shutdown_started_clients(
|
async fn shutdown_started_clients(
|
||||||
started_clients: &[kb_lib::WsClient],
|
started_clients: &[kb_lib::WsClient],
|
||||||
relay_tasks: &mut std::vec::Vec<tauri::async_runtime::JoinHandle<()>>,
|
relay_tasks: &mut std::vec::Vec<tauri::async_runtime::JoinHandle<()>>,
|
||||||
) {
|
) {
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// file: kb_app/src/main.rs
|
// file: kb_demo_app/src/main.rs
|
||||||
|
|
||||||
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
|
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
|
||||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||||
@@ -34,12 +34,12 @@ async fn main() -> std::process::ExitCode {
|
|||||||
match provider_result {
|
match provider_result {
|
||||||
Ok(()) => {}
|
Ok(()) => {}
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
eprintln!("kb_app rustls provider init error: {:?}", error);
|
eprintln!("kb_demo_app rustls provider init error: {:?}", error);
|
||||||
return std::process::ExitCode::FAILURE;
|
return std::process::ExitCode::FAILURE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let run_result = kb_app_lib::run().await;
|
let run_result = kb_demo_app_lib::run().await;
|
||||||
if let Err(error) = run_result {
|
if let Err(error) = run_result {
|
||||||
eprintln!("application error: {}", error);
|
eprintln!("application error: {}", error);
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// file: kb_app/src/splash.rs
|
// file: kb_demo_app/src/splash.rs
|
||||||
|
|
||||||
//! Shared splash-screen payload types.
|
//! Shared splash-screen payload types.
|
||||||
//!
|
//!
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://schema.tauri.app/config/2",
|
"$schema": "https://schema.tauri.app/config/2",
|
||||||
"productName": "kb-bapp",
|
"productName": "kb-demo-app",
|
||||||
"version": "0.7.26",
|
"version": "0.7.27",
|
||||||
"identifier": "com.sasedev.kb-app",
|
"identifier": "com.sasedev.kb-demo-app",
|
||||||
"build": {
|
"build": {
|
||||||
"beforeDevCommand": "npm run dev",
|
"beforeDevCommand": "npm run dev",
|
||||||
"devUrl": "http://localhost:1420",
|
"devUrl": "http://localhost:1420",
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// file: kb_app/vite.config.ts
|
// file: kb_demo_app/vite.config.ts
|
||||||
|
|
||||||
import { defineConfig, normalizePath } from "vite";
|
import { defineConfig, normalizePath } from "vite";
|
||||||
import { NodePackageImporter } from "sass-embedded";
|
import { NodePackageImporter } from "sass-embedded";
|
||||||
@@ -4,33 +4,33 @@
|
|||||||
|
|
||||||
/// Root application configuration loaded from `config.json`.
|
/// Root application configuration loaded from `config.json`.
|
||||||
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
|
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||||
pub struct KbConfig {
|
pub struct Config {
|
||||||
/// Application-level metadata and global behavior.
|
/// Application-level metadata and global behavior.
|
||||||
pub app: KbAppConfig,
|
pub app: AppConfig,
|
||||||
/// Tracing and log output configuration.
|
/// Tracing and log output configuration.
|
||||||
pub logging: KbLoggingConfig,
|
pub logging: LoggingConfig,
|
||||||
/// Data directory configuration.
|
/// Data directory configuration.
|
||||||
pub data: KbDataConfig,
|
pub data: DataConfig,
|
||||||
/// Solana endpoint configuration.
|
/// Solana endpoint configuration.
|
||||||
pub solana: KbSolanaConfig,
|
pub solana: SolanaConfig,
|
||||||
/// Database configuration.
|
/// Database configuration.
|
||||||
pub database: KbDatabaseConfig,
|
pub database: DatabaseConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl KbConfig {
|
impl Config {
|
||||||
/// Returns the default path of the JSON configuration file.
|
/// Returns the default path of the JSON configuration file.
|
||||||
pub fn default_path() -> std::path::PathBuf {
|
pub fn default_path() -> std::path::PathBuf {
|
||||||
return kb_workspace_root_dir().join("config.json");
|
return workspace_root_dir().join("config.json");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Loads a configuration from a JSON file and validates it.
|
/// Loads a configuration from a JSON file and validates it.
|
||||||
pub fn load_from_path<P: AsRef<std::path::Path>>(path: P) -> Result<Self, crate::KbError> {
|
pub fn load_from_path<P: AsRef<std::path::Path>>(path: P) -> Result<Self, crate::Error> {
|
||||||
let path_ref = path.as_ref();
|
let path_ref = path.as_ref();
|
||||||
let content_result = std::fs::read_to_string(path_ref);
|
let content_result = std::fs::read_to_string(path_ref);
|
||||||
let content = match content_result {
|
let content = match content_result {
|
||||||
Ok(content) => content,
|
Ok(content) => content,
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
return Err(crate::KbError::Io(format!(
|
return Err(crate::Error::Io(format!(
|
||||||
"cannot read configuration file '{}': {error}",
|
"cannot read configuration file '{}': {error}",
|
||||||
path_ref.display()
|
path_ref.display()
|
||||||
)));
|
)));
|
||||||
@@ -40,7 +40,7 @@ impl KbConfig {
|
|||||||
let config = match config_result {
|
let config = match config_result {
|
||||||
Ok(config) => config,
|
Ok(config) => config,
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
return Err(crate::KbError::Json(format!(
|
return Err(crate::Error::Json(format!(
|
||||||
"cannot parse configuration file '{}': {error}",
|
"cannot parse configuration file '{}': {error}",
|
||||||
path_ref.display()
|
path_ref.display()
|
||||||
)));
|
)));
|
||||||
@@ -54,29 +54,27 @@ impl KbConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Validates the current configuration.
|
/// Validates the current configuration.
|
||||||
pub fn validate(&self) -> Result<(), crate::KbError> {
|
pub fn validate(&self) -> Result<(), crate::Error> {
|
||||||
if self.app.name.trim().is_empty() {
|
if self.app.name.trim().is_empty() {
|
||||||
return Err(crate::KbError::Config("app.name must not be empty".to_string()));
|
return Err(crate::Error::Config("app.name must not be empty".to_string()));
|
||||||
}
|
}
|
||||||
if self.app.environment.trim().is_empty() {
|
if self.app.environment.trim().is_empty() {
|
||||||
return Err(crate::KbError::Config("app.environment must not be empty".to_string()));
|
return Err(crate::Error::Config("app.environment must not be empty".to_string()));
|
||||||
}
|
}
|
||||||
if self.logging.level.trim().is_empty() {
|
if self.logging.level.trim().is_empty() {
|
||||||
return Err(crate::KbError::Config("logging.level must not be empty".to_string()));
|
return Err(crate::Error::Config("logging.level must not be empty".to_string()));
|
||||||
}
|
}
|
||||||
if self.logging.directory.trim().is_empty() {
|
if self.logging.directory.trim().is_empty() {
|
||||||
return Err(crate::KbError::Config("logging.directory must not be empty".to_string()));
|
return Err(crate::Error::Config("logging.directory must not be empty".to_string()));
|
||||||
}
|
}
|
||||||
if self.logging.file_prefix.trim().is_empty() {
|
if self.logging.file_prefix.trim().is_empty() {
|
||||||
return Err(crate::KbError::Config(
|
return Err(crate::Error::Config("logging.file_prefix must not be empty".to_string()));
|
||||||
"logging.file_prefix must not be empty".to_string(),
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
if self.data.sqlite_path.trim().is_empty() {
|
if self.data.sqlite_path.trim().is_empty() {
|
||||||
return Err(crate::KbError::Config("data.sqlite_path must not be empty".to_string()));
|
return Err(crate::Error::Config("data.sqlite_path must not be empty".to_string()));
|
||||||
}
|
}
|
||||||
if self.data.wallets_directory.trim().is_empty() {
|
if self.data.wallets_directory.trim().is_empty() {
|
||||||
return Err(crate::KbError::Config(
|
return Err(crate::Error::Config(
|
||||||
"data.wallets_directory must not be empty".to_string(),
|
"data.wallets_directory must not be empty".to_string(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@@ -84,7 +82,7 @@ impl KbConfig {
|
|||||||
&& self.logging.rotation != "hourly"
|
&& self.logging.rotation != "hourly"
|
||||||
&& self.logging.rotation != "never"
|
&& self.logging.rotation != "never"
|
||||||
{
|
{
|
||||||
return Err(crate::KbError::Config(format!(
|
return Err(crate::Error::Config(format!(
|
||||||
"unsupported logging.rotation '{}'",
|
"unsupported logging.rotation '{}'",
|
||||||
self.logging.rotation
|
self.logging.rotation
|
||||||
)));
|
)));
|
||||||
@@ -94,7 +92,7 @@ impl KbConfig {
|
|||||||
&& self.logging.message_format != "pretty"
|
&& self.logging.message_format != "pretty"
|
||||||
&& self.logging.message_format != "json"
|
&& self.logging.message_format != "json"
|
||||||
{
|
{
|
||||||
return Err(crate::KbError::Config(format!(
|
return Err(crate::Error::Config(format!(
|
||||||
"unsupported logging.message_format '{}'",
|
"unsupported logging.message_format '{}'",
|
||||||
self.logging.message_format
|
self.logging.message_format
|
||||||
)));
|
)));
|
||||||
@@ -103,7 +101,7 @@ impl KbConfig {
|
|||||||
&& self.logging.time_format != "rfc3339_millis"
|
&& self.logging.time_format != "rfc3339_millis"
|
||||||
&& self.logging.time_format != "none"
|
&& self.logging.time_format != "none"
|
||||||
{
|
{
|
||||||
return Err(crate::KbError::Config(format!(
|
return Err(crate::Error::Config(format!(
|
||||||
"unsupported logging.time_format '{}'",
|
"unsupported logging.time_format '{}'",
|
||||||
self.logging.time_format
|
self.logging.time_format
|
||||||
)));
|
)));
|
||||||
@@ -125,11 +123,11 @@ impl KbConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Creates the basic runtime directories required by the current configuration.
|
/// Creates the basic runtime directories required by the current configuration.
|
||||||
pub fn prepare_filesystem(&self) -> Result<(), crate::KbError> {
|
pub fn prepare_filesystem(&self) -> Result<(), crate::Error> {
|
||||||
let logging_directory = self.logging.directory_path();
|
let logging_directory = self.logging.directory_path();
|
||||||
let create_logs_result = std::fs::create_dir_all(&logging_directory);
|
let create_logs_result = std::fs::create_dir_all(&logging_directory);
|
||||||
if let Err(error) = create_logs_result {
|
if let Err(error) = create_logs_result {
|
||||||
return Err(crate::KbError::Io(format!(
|
return Err(crate::Error::Io(format!(
|
||||||
"cannot create logging directory '{}': {error}",
|
"cannot create logging directory '{}': {error}",
|
||||||
logging_directory.display()
|
logging_directory.display()
|
||||||
)));
|
)));
|
||||||
@@ -137,7 +135,7 @@ impl KbConfig {
|
|||||||
let wallets_directory = self.data.wallets_directory_path();
|
let wallets_directory = self.data.wallets_directory_path();
|
||||||
let create_wallets_result = std::fs::create_dir_all(&wallets_directory);
|
let create_wallets_result = std::fs::create_dir_all(&wallets_directory);
|
||||||
if let Err(error) = create_wallets_result {
|
if let Err(error) = create_wallets_result {
|
||||||
return Err(crate::KbError::Io(format!(
|
return Err(crate::Error::Io(format!(
|
||||||
"cannot create wallets directory '{}': {error}",
|
"cannot create wallets directory '{}': {error}",
|
||||||
wallets_directory.display()
|
wallets_directory.display()
|
||||||
)));
|
)));
|
||||||
@@ -148,7 +146,7 @@ impl KbConfig {
|
|||||||
if !sqlite_parent.as_os_str().is_empty() {
|
if !sqlite_parent.as_os_str().is_empty() {
|
||||||
let create_db_parent_result = std::fs::create_dir_all(sqlite_parent);
|
let create_db_parent_result = std::fs::create_dir_all(sqlite_parent);
|
||||||
if let Err(error) = create_db_parent_result {
|
if let Err(error) = create_db_parent_result {
|
||||||
return Err(crate::KbError::Io(format!(
|
return Err(crate::Error::Io(format!(
|
||||||
"cannot create database parent directory '{}': {error}",
|
"cannot create database parent directory '{}': {error}",
|
||||||
sqlite_parent.display()
|
sqlite_parent.display()
|
||||||
)));
|
)));
|
||||||
@@ -162,7 +160,7 @@ impl KbConfig {
|
|||||||
pub fn find_http_endpoint(
|
pub fn find_http_endpoint(
|
||||||
&self,
|
&self,
|
||||||
endpoint_name: &str,
|
endpoint_name: &str,
|
||||||
) -> std::option::Option<&KbHttpEndpointConfig> {
|
) -> std::option::Option<&HttpEndpointConfig> {
|
||||||
return self
|
return self
|
||||||
.solana
|
.solana
|
||||||
.http_endpoints
|
.http_endpoints
|
||||||
@@ -171,10 +169,7 @@ impl KbConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a named WebSocket endpoint by reference.
|
/// Returns a named WebSocket endpoint by reference.
|
||||||
pub fn find_ws_endpoint(
|
pub fn find_ws_endpoint(&self, endpoint_name: &str) -> std::option::Option<&WsEndpointConfig> {
|
||||||
&self,
|
|
||||||
endpoint_name: &str,
|
|
||||||
) -> std::option::Option<&KbWsEndpointConfig> {
|
|
||||||
return self
|
return self
|
||||||
.solana
|
.solana
|
||||||
.ws_endpoints
|
.ws_endpoints
|
||||||
@@ -184,44 +179,44 @@ impl KbConfig {
|
|||||||
|
|
||||||
fn validate_http_endpoint(
|
fn validate_http_endpoint(
|
||||||
&self,
|
&self,
|
||||||
endpoint: &KbHttpEndpointConfig,
|
endpoint: &HttpEndpointConfig,
|
||||||
endpoint_names: &mut std::vec::Vec<std::string::String>,
|
endpoint_names: &mut std::vec::Vec<std::string::String>,
|
||||||
) -> Result<(), crate::KbError> {
|
) -> Result<(), crate::Error> {
|
||||||
if endpoint.name.trim().is_empty() {
|
if endpoint.name.trim().is_empty() {
|
||||||
return Err(crate::KbError::Config("http endpoint name must not be empty".to_string()));
|
return Err(crate::Error::Config("http endpoint name must not be empty".to_string()));
|
||||||
}
|
}
|
||||||
if endpoint_names.iter().any(|name| return name == &endpoint.name) {
|
if endpoint_names.iter().any(|name| return name == &endpoint.name) {
|
||||||
return Err(crate::KbError::Config(format!(
|
return Err(crate::Error::Config(format!(
|
||||||
"duplicated endpoint name '{}'",
|
"duplicated endpoint name '{}'",
|
||||||
endpoint.name
|
endpoint.name
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
if !endpoint.url.starts_with("http://") && !endpoint.url.starts_with("https://") {
|
if !endpoint.url.starts_with("http://") && !endpoint.url.starts_with("https://") {
|
||||||
return Err(crate::KbError::Config(format!(
|
return Err(crate::Error::Config(format!(
|
||||||
"http endpoint '{}' must start with http:// or https://",
|
"http endpoint '{}' must start with http:// or https://",
|
||||||
endpoint.name
|
endpoint.name
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
if endpoint.requests_per_second == 0 {
|
if endpoint.requests_per_second == 0 {
|
||||||
return Err(crate::KbError::Config(format!(
|
return Err(crate::Error::Config(format!(
|
||||||
"http endpoint '{}' requests_per_second must be > 0",
|
"http endpoint '{}' requests_per_second must be > 0",
|
||||||
endpoint.name
|
endpoint.name
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
if endpoint.burst_capacity == 0 {
|
if endpoint.burst_capacity == 0 {
|
||||||
return Err(crate::KbError::Config(format!(
|
return Err(crate::Error::Config(format!(
|
||||||
"http endpoint '{}' burst_capacity must be > 0",
|
"http endpoint '{}' burst_capacity must be > 0",
|
||||||
endpoint.name
|
endpoint.name
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
if endpoint.connect_timeout_ms == 0 {
|
if endpoint.connect_timeout_ms == 0 {
|
||||||
return Err(crate::KbError::Config(format!(
|
return Err(crate::Error::Config(format!(
|
||||||
"http endpoint '{}' connect_timeout_ms must be > 0",
|
"http endpoint '{}' connect_timeout_ms must be > 0",
|
||||||
endpoint.name
|
endpoint.name
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
if endpoint.request_timeout_ms == 0 {
|
if endpoint.request_timeout_ms == 0 {
|
||||||
return Err(crate::KbError::Config(format!(
|
return Err(crate::Error::Config(format!(
|
||||||
"http endpoint '{}' request_timeout_ms must be > 0",
|
"http endpoint '{}' request_timeout_ms must be > 0",
|
||||||
endpoint.name
|
endpoint.name
|
||||||
)));
|
)));
|
||||||
@@ -232,56 +227,56 @@ impl KbConfig {
|
|||||||
|
|
||||||
fn validate_ws_endpoint(
|
fn validate_ws_endpoint(
|
||||||
&self,
|
&self,
|
||||||
endpoint: &KbWsEndpointConfig,
|
endpoint: &WsEndpointConfig,
|
||||||
endpoint_names: &mut std::vec::Vec<std::string::String>,
|
endpoint_names: &mut std::vec::Vec<std::string::String>,
|
||||||
) -> Result<(), crate::KbError> {
|
) -> Result<(), crate::Error> {
|
||||||
if endpoint.name.trim().is_empty() {
|
if endpoint.name.trim().is_empty() {
|
||||||
return Err(crate::KbError::Config("ws endpoint name must not be empty".to_string()));
|
return Err(crate::Error::Config("ws endpoint name must not be empty".to_string()));
|
||||||
}
|
}
|
||||||
if endpoint_names.iter().any(|name| return name == &endpoint.name) {
|
if endpoint_names.iter().any(|name| return name == &endpoint.name) {
|
||||||
return Err(crate::KbError::Config(format!(
|
return Err(crate::Error::Config(format!(
|
||||||
"duplicated endpoint name '{}'",
|
"duplicated endpoint name '{}'",
|
||||||
endpoint.name
|
endpoint.name
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
if !endpoint.url.starts_with("ws://") && !endpoint.url.starts_with("wss://") {
|
if !endpoint.url.starts_with("ws://") && !endpoint.url.starts_with("wss://") {
|
||||||
return Err(crate::KbError::Config(format!(
|
return Err(crate::Error::Config(format!(
|
||||||
"ws endpoint '{}' must start with ws:// or wss://",
|
"ws endpoint '{}' must start with ws:// or wss://",
|
||||||
endpoint.name
|
endpoint.name
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
if endpoint.max_subscriptions == 0 {
|
if endpoint.max_subscriptions == 0 {
|
||||||
return Err(crate::KbError::Config(format!(
|
return Err(crate::Error::Config(format!(
|
||||||
"ws endpoint '{}' max_subscriptions must be > 0",
|
"ws endpoint '{}' max_subscriptions must be > 0",
|
||||||
endpoint.name
|
endpoint.name
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
if endpoint.connect_timeout_ms == 0 {
|
if endpoint.connect_timeout_ms == 0 {
|
||||||
return Err(crate::KbError::Config(format!(
|
return Err(crate::Error::Config(format!(
|
||||||
"ws endpoint '{}' connect_timeout_ms must be > 0",
|
"ws endpoint '{}' connect_timeout_ms must be > 0",
|
||||||
endpoint.name
|
endpoint.name
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
if endpoint.request_timeout_ms == 0 {
|
if endpoint.request_timeout_ms == 0 {
|
||||||
return Err(crate::KbError::Config(format!(
|
return Err(crate::Error::Config(format!(
|
||||||
"ws endpoint '{}' request_timeout_ms must be > 0",
|
"ws endpoint '{}' request_timeout_ms must be > 0",
|
||||||
endpoint.name
|
endpoint.name
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
if endpoint.unsubscribe_timeout_ms == 0 {
|
if endpoint.unsubscribe_timeout_ms == 0 {
|
||||||
return Err(crate::KbError::Config(format!(
|
return Err(crate::Error::Config(format!(
|
||||||
"ws endpoint '{}' unsubscribe_timeout_ms must be > 0",
|
"ws endpoint '{}' unsubscribe_timeout_ms must be > 0",
|
||||||
endpoint.name
|
endpoint.name
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
if endpoint.write_channel_capacity == 0 {
|
if endpoint.write_channel_capacity == 0 {
|
||||||
return Err(crate::KbError::Config(format!(
|
return Err(crate::Error::Config(format!(
|
||||||
"ws endpoint '{}' write_channel_capacity must be > 0",
|
"ws endpoint '{}' write_channel_capacity must be > 0",
|
||||||
endpoint.name
|
endpoint.name
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
if endpoint.event_channel_capacity == 0 {
|
if endpoint.event_channel_capacity == 0 {
|
||||||
return Err(crate::KbError::Config(format!(
|
return Err(crate::Error::Config(format!(
|
||||||
"ws endpoint '{}' event_channel_capacity must be > 0",
|
"ws endpoint '{}' event_channel_capacity must be > 0",
|
||||||
endpoint.name
|
endpoint.name
|
||||||
)));
|
)));
|
||||||
@@ -293,7 +288,7 @@ impl KbConfig {
|
|||||||
|
|
||||||
/// Generic application settings.
|
/// Generic application settings.
|
||||||
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
|
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||||
pub struct KbAppConfig {
|
pub struct AppConfig {
|
||||||
/// Human-readable application name.
|
/// Human-readable application name.
|
||||||
pub name: std::string::String,
|
pub name: std::string::String,
|
||||||
/// Current environment name such as `development` or `production`.
|
/// Current environment name such as `development` or `production`.
|
||||||
@@ -312,7 +307,7 @@ pub struct KbAppConfig {
|
|||||||
/// configuration so that the format policy is stabilized early, even though
|
/// configuration so that the format policy is stabilized early, even though
|
||||||
/// their handling will be refined in later versions.
|
/// their handling will be refined in later versions.
|
||||||
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
|
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||||
pub struct KbLoggingConfig {
|
pub struct LoggingConfig {
|
||||||
/// Global default log level.
|
/// Global default log level.
|
||||||
pub level: std::string::String,
|
pub level: std::string::String,
|
||||||
/// Enables console logging.
|
/// Enables console logging.
|
||||||
@@ -335,47 +330,47 @@ pub struct KbLoggingConfig {
|
|||||||
pub target_filters: std::collections::BTreeMap<std::string::String, std::string::String>,
|
pub target_filters: std::collections::BTreeMap<std::string::String, std::string::String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl KbLoggingConfig {
|
impl LoggingConfig {
|
||||||
/// Returns the resolved logging directory path.
|
/// Returns the resolved logging directory path.
|
||||||
pub fn directory_path(&self) -> std::path::PathBuf {
|
pub fn directory_path(&self) -> std::path::PathBuf {
|
||||||
return kb_resolve_workspace_relative_path(&self.directory);
|
return resolve_workspace_relative_path(&self.directory);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Local data paths used by the application.
|
/// Local data paths used by the application.
|
||||||
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
|
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||||
pub struct KbDataConfig {
|
pub struct DataConfig {
|
||||||
/// SQLite database path.
|
/// SQLite database path.
|
||||||
pub sqlite_path: std::string::String,
|
pub sqlite_path: std::string::String,
|
||||||
/// Directory storing Solana wallets and related material in future versions.
|
/// Directory storing Solana wallets and related material in future versions.
|
||||||
pub wallets_directory: std::string::String,
|
pub wallets_directory: std::string::String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl KbDataConfig {
|
impl DataConfig {
|
||||||
/// Returns the resolved SQLite database path.
|
/// Returns the resolved SQLite database path.
|
||||||
pub fn sqlite_path_buf(&self) -> std::path::PathBuf {
|
pub fn sqlite_path_buf(&self) -> std::path::PathBuf {
|
||||||
return kb_resolve_workspace_relative_path(&self.sqlite_path);
|
return resolve_workspace_relative_path(&self.sqlite_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the resolved wallets directory path.
|
/// Returns the resolved wallets directory path.
|
||||||
pub fn wallets_directory_path(&self) -> std::path::PathBuf {
|
pub fn wallets_directory_path(&self) -> std::path::PathBuf {
|
||||||
return kb_resolve_workspace_relative_path(&self.wallets_directory);
|
return resolve_workspace_relative_path(&self.wallets_directory);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Solana transport configuration.
|
/// Solana transport configuration.
|
||||||
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
|
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||||
pub struct KbSolanaConfig {
|
pub struct SolanaConfig {
|
||||||
/// Named HTTP endpoints.
|
/// Named HTTP endpoints.
|
||||||
pub http_endpoints: std::vec::Vec<KbHttpEndpointConfig>,
|
pub http_endpoints: std::vec::Vec<HttpEndpointConfig>,
|
||||||
/// Named WebSocket endpoints.
|
/// Named WebSocket endpoints.
|
||||||
pub ws_endpoints: std::vec::Vec<KbWsEndpointConfig>,
|
pub ws_endpoints: std::vec::Vec<WsEndpointConfig>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// HTTP endpoint configuration.
|
/// HTTP endpoint configuration.
|
||||||
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
|
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct KbHttpEndpointConfig {
|
pub struct HttpEndpointConfig {
|
||||||
/// Logical endpoint name.
|
/// Logical endpoint name.
|
||||||
pub name: std::string::String,
|
pub name: std::string::String,
|
||||||
/// Whether this endpoint is enabled.
|
/// Whether this endpoint is enabled.
|
||||||
@@ -412,10 +407,10 @@ pub struct KbHttpEndpointConfig {
|
|||||||
pub max_concurrent_requests_per_endpoint: usize,
|
pub max_concurrent_requests_per_endpoint: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl KbHttpEndpointConfig {
|
impl HttpEndpointConfig {
|
||||||
/// Returns the resolved URL, replacing an `${ENV_VAR}` placeholder when
|
/// Returns the resolved URL, replacing an `${ENV_VAR}` placeholder when
|
||||||
/// `api_key_env_var` is configured.
|
/// `api_key_env_var` is configured.
|
||||||
pub fn resolved_url(&self) -> Result<std::string::String, crate::KbError> {
|
pub fn resolved_url(&self) -> Result<std::string::String, crate::Error> {
|
||||||
let env_var_name_option = self.api_key_env_var.as_ref();
|
let env_var_name_option = self.api_key_env_var.as_ref();
|
||||||
let env_var_name = match env_var_name_option {
|
let env_var_name = match env_var_name_option {
|
||||||
Some(env_var_name) => env_var_name,
|
Some(env_var_name) => env_var_name,
|
||||||
@@ -427,7 +422,7 @@ impl KbHttpEndpointConfig {
|
|||||||
let api_key = match api_key_result {
|
let api_key = match api_key_result {
|
||||||
Ok(api_key) => api_key,
|
Ok(api_key) => api_key,
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
return Err(crate::KbError::Config(format!(
|
return Err(crate::Error::Config(format!(
|
||||||
"cannot resolve api key env var '{}' for http endpoint '{}': {}",
|
"cannot resolve api key env var '{}' for http endpoint '{}': {}",
|
||||||
env_var_name, self.name, error
|
env_var_name, self.name, error
|
||||||
)));
|
)));
|
||||||
@@ -443,7 +438,7 @@ impl KbHttpEndpointConfig {
|
|||||||
|
|
||||||
/// WebSocket endpoint configuration.
|
/// WebSocket endpoint configuration.
|
||||||
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
|
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||||
pub struct KbWsEndpointConfig {
|
pub struct WsEndpointConfig {
|
||||||
/// Stable internal endpoint name used by the application.
|
/// Stable internal endpoint name used by the application.
|
||||||
pub name: std::string::String,
|
pub name: std::string::String,
|
||||||
/// Enables or disables the endpoint.
|
/// Enables or disables the endpoint.
|
||||||
@@ -472,17 +467,17 @@ pub struct KbWsEndpointConfig {
|
|||||||
pub auto_reconnect: bool,
|
pub auto_reconnect: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl KbWsEndpointConfig {
|
impl WsEndpointConfig {
|
||||||
/// Returns the resolved endpoint URL.
|
/// Returns the resolved endpoint URL.
|
||||||
pub fn resolved_url(&self) -> Result<std::string::String, crate::KbError> {
|
pub fn resolved_url(&self) -> Result<std::string::String, crate::Error> {
|
||||||
return kb_resolve_endpoint_url(&self.url, &self.api_key_env_var);
|
return resolve_endpoint_url(&self.url, &self.api_key_env_var);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// SQLite configuration.
|
/// SQLite configuration.
|
||||||
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
|
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct KbSqliteDatabaseConfig {
|
pub struct SqliteDatabaseConfig {
|
||||||
/// SQLite database path.
|
/// SQLite database path.
|
||||||
pub path: std::string::String,
|
pub path: std::string::String,
|
||||||
/// Whether the file should be created if missing.
|
/// Whether the file should be created if missing.
|
||||||
@@ -497,26 +492,26 @@ pub struct KbSqliteDatabaseConfig {
|
|||||||
pub use_wal: bool,
|
pub use_wal: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl KbSqliteDatabaseConfig {
|
impl SqliteDatabaseConfig {
|
||||||
/// Returns the resolved SQLite database path.
|
/// Returns the resolved SQLite database path.
|
||||||
pub fn path_buf(&self) -> std::path::PathBuf {
|
pub fn path_buf(&self) -> std::path::PathBuf {
|
||||||
return kb_resolve_workspace_relative_path(&self.path);
|
return resolve_workspace_relative_path(&self.path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Database configuration.
|
/// Database configuration.
|
||||||
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
|
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct KbDatabaseConfig {
|
pub struct DatabaseConfig {
|
||||||
/// Whether the database layer is enabled.
|
/// Whether the database layer is enabled.
|
||||||
pub enabled: bool,
|
pub enabled: bool,
|
||||||
/// Selected backend.
|
/// Selected backend.
|
||||||
pub backend: crate::KbDatabaseBackend,
|
pub backend: crate::DatabaseBackend,
|
||||||
/// SQLite-specific configuration.
|
/// SQLite-specific configuration.
|
||||||
pub sqlite: KbSqliteDatabaseConfig,
|
pub sqlite: SqliteDatabaseConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn kb_workspace_root_dir() -> std::path::PathBuf {
|
fn workspace_root_dir() -> std::path::PathBuf {
|
||||||
let manifest_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
let manifest_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
||||||
match manifest_dir.parent() {
|
match manifest_dir.parent() {
|
||||||
Some(parent) => return parent.to_path_buf(),
|
Some(parent) => return parent.to_path_buf(),
|
||||||
@@ -524,18 +519,18 @@ fn kb_workspace_root_dir() -> std::path::PathBuf {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn kb_resolve_workspace_relative_path<P: AsRef<std::path::Path>>(path: P) -> std::path::PathBuf {
|
fn resolve_workspace_relative_path<P: AsRef<std::path::Path>>(path: P) -> std::path::PathBuf {
|
||||||
let input_path = std::path::PathBuf::from(path.as_ref());
|
let input_path = std::path::PathBuf::from(path.as_ref());
|
||||||
if input_path.is_absolute() {
|
if input_path.is_absolute() {
|
||||||
return input_path;
|
return input_path;
|
||||||
}
|
}
|
||||||
return kb_workspace_root_dir().join(input_path);
|
return workspace_root_dir().join(input_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn kb_resolve_endpoint_url(
|
fn resolve_endpoint_url(
|
||||||
url: &str,
|
url: &str,
|
||||||
api_key_env_var: &std::option::Option<std::string::String>,
|
api_key_env_var: &std::option::Option<std::string::String>,
|
||||||
) -> Result<std::string::String, crate::KbError> {
|
) -> Result<std::string::String, crate::Error> {
|
||||||
let env_var_name_option = api_key_env_var.as_deref();
|
let env_var_name_option = api_key_env_var.as_deref();
|
||||||
let env_var_name = match env_var_name_option {
|
let env_var_name = match env_var_name_option {
|
||||||
Some(env_var_name) => env_var_name,
|
Some(env_var_name) => env_var_name,
|
||||||
@@ -551,7 +546,7 @@ fn kb_resolve_endpoint_url(
|
|||||||
let env_value = match env_value_result {
|
let env_value = match env_value_result {
|
||||||
Ok(env_value) => env_value,
|
Ok(env_value) => env_value,
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
return Err(crate::KbError::Config(format!(
|
return Err(crate::Error::Config(format!(
|
||||||
"environment variable '{}' is required to resolve endpoint url '{}': {error}",
|
"environment variable '{}' is required to resolve endpoint url '{}': {error}",
|
||||||
env_var_name, url
|
env_var_name, url
|
||||||
)));
|
)));
|
||||||
|
|||||||
@@ -2,22 +2,59 @@
|
|||||||
|
|
||||||
//! Solana program and mint constants reused by the project.
|
//! Solana program and mint constants reused by the project.
|
||||||
|
|
||||||
/// SPL Token program identifier.
|
/// SPL Token program identifier. ("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA").
|
||||||
pub const SPL_TOKEN_PROGRAM_ID: solana_sdk::pubkey::Pubkey = spl_token_interface::ID;
|
/// @see solana_sdk::pubkey::Pubkey = spl_token_interface::ID
|
||||||
|
pub const SPL_TOKEN_PROGRAM_ID: &str = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA";
|
||||||
|
|
||||||
/// SPL Token-2022 program identifier.
|
/// SPL Token-2022 program identifier. ("TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb").
|
||||||
pub const SPL_TOKEN_2022_PROGRAM_ID: solana_sdk::pubkey::Pubkey = spl_token_2022_interface::ID;
|
/// @see solana_sdk::pubkey::Pubkey = spl_token_2022_interface::ID
|
||||||
|
pub const SPL_TOKEN_2022_PROGRAM_ID: &str = "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb";
|
||||||
|
|
||||||
/// Associated Token Account program identifier.
|
/// Associated Token Account program identifier. ("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL").
|
||||||
pub const ASSOCIATED_TOKEN_PROGRAM_ID: solana_sdk::pubkey::Pubkey =
|
/// @see solana_sdk::pubkey::Pubkey = spl_associated_token_account_interface::program::ID
|
||||||
spl_associated_token_account_interface::program::ID;
|
pub const ASSOCIATED_TOKEN_PROGRAM_ID: &str = "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL";
|
||||||
|
|
||||||
/// Wrapped SOL mint identifier.
|
/// Wrapped SOL mint identifier. ("So11111111111111111111111111111111111111112").
|
||||||
pub const WSOL_MINT_ID: solana_sdk::pubkey::Pubkey = spl_token_interface::native_mint::ID;
|
/// @see solana_sdk::pubkey::Pubkey = spl_token_interface::native_mint::ID
|
||||||
|
pub const WSOL_MINT_ID: &str = "So11111111111111111111111111111111111111112";
|
||||||
|
|
||||||
/// System program identifier.
|
/// System program identifier. ("11111111111111111111111111111111").
|
||||||
pub const SYSTEM_PROGRAM_ID: solana_sdk::pubkey::Pubkey = solana_sdk_ids::system_program::ID;
|
/// @see solana_sdk::pubkey::Pubkey = solana_sdk_ids::system_program::ID
|
||||||
|
pub const SYSTEM_PROGRAM_ID: &str = "11111111111111111111111111111111";
|
||||||
|
|
||||||
/// Compute Budget program identifier.
|
/// Compute Budget program identifier. ("ComputeBudget111111111111111111111111111111").
|
||||||
pub const COMPUTE_BUDGET_PROGRAM_ID: solana_sdk::pubkey::Pubkey =
|
/// @see solana_sdk_ids::compute_budget::ID
|
||||||
solana_sdk_ids::compute_budget::ID;
|
pub const COMPUTE_BUDGET_PROGRAM_ID: &str = "ComputeBudget111111111111111111111111111111";
|
||||||
|
|
||||||
|
/// DexLab Swap/Pool program id. ("DSwpgjMvXhtGn6BsbqmacdBZyfLj6jSWf3HJpdJtmg6N").
|
||||||
|
pub const DEXLAB_PROGRAM_ID: &str = "DSwpgjMvXhtGn6BsbqmacdBZyfLj6jSWf3HJpdJtmg6N";
|
||||||
|
|
||||||
|
/// FluxBeam program id. ("FLUXubRmkEi2q6K3Y9kBPg9248ggaZVsoSFhtJHSrm1X").
|
||||||
|
pub const FLUXBEAM_PROGRAM_ID: &str = "FLUXubRmkEi2q6K3Y9kBPg9248ggaZVsoSFhtJHSrm1X";
|
||||||
|
|
||||||
|
/// Meteora DAMM v1 program id. ("Eo7WjKq67rjJQSZxS6z3YkapzY3eMj6Xy8X5EQVn5UaB").
|
||||||
|
pub const METEORA_DAMM_V1_PROGRAM_ID: &str = "Eo7WjKq67rjJQSZxS6z3YkapzY3eMj6Xy8X5EQVn5UaB";
|
||||||
|
|
||||||
|
/// Meteora DAMM v2 program id. ("cpamdpZCGKUy5JxQXB4dcpGPiikHawvSWAd6mEn1sGG").
|
||||||
|
pub const METEORA_DAMM_V2_PROGRAM_ID: &str = "cpamdpZCGKUy5JxQXB4dcpGPiikHawvSWAd6mEn1sGG";
|
||||||
|
|
||||||
|
/// Meteora DBC program id. ("dbcij3LWUppWqq96dh6gJWwBifmcGfLSB5D4DuSMaqN").
|
||||||
|
pub const METEORA_DBC_PROGRAM_ID: &str = "dbcij3LWUppWqq96dh6gJWwBifmcGfLSB5D4DuSMaqN";
|
||||||
|
|
||||||
|
/// Orca Whirlpools program id. ("whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc").
|
||||||
|
pub const ORCA_WHIRLPOOLS_PROGRAM_ID: &str = "whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc";
|
||||||
|
|
||||||
|
/// Pump.fun program id. ("6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P").
|
||||||
|
pub const PUMP_FUN_PROGRAM_ID: &str = "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P";
|
||||||
|
|
||||||
|
/// PumpSwap / PumpAMM program id. ("pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA").
|
||||||
|
pub const PUMP_SWAP_PROGRAM_ID: &str = "pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA";
|
||||||
|
|
||||||
|
/// Raydium AmmV4 program id. ("675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8").
|
||||||
|
pub const RAYDIUM_AMM_V4_PROGRAM_ID: &str = "675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8";
|
||||||
|
|
||||||
|
/// Raydium CLMM program id. ("CAMMCzo5YL8w4VFF8KVHrK22GGUsp5VTaW7grrKgrWqK").
|
||||||
|
pub const RAYDIUM_CLMM_PROGRAM_ID: &str = "CAMMCzo5YL8w4VFF8KVHrK22GGUsp5VTaW7grrKgrWqK";
|
||||||
|
|
||||||
|
/// Raydium CPMM mainnet program id. ("CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C").
|
||||||
|
pub const RAYDIUM_CPMM_PROGRAM_ID: &str = "CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C";
|
||||||
|
|||||||
392
kb_lib/src/db.rs
@@ -13,201 +13,197 @@ mod schema;
|
|||||||
mod sqlite;
|
mod sqlite;
|
||||||
mod types;
|
mod types;
|
||||||
|
|
||||||
pub use connection::KbDatabase;
|
pub use connection::Database;
|
||||||
pub use connection::KbDatabaseConnection;
|
pub use connection::DatabaseConnection;
|
||||||
pub use dtos::KbAnalysisSignalDto;
|
pub use dtos::AnalysisSignalDto;
|
||||||
pub use dtos::KbChainInstructionDto;
|
pub use dtos::ChainInstructionDto;
|
||||||
pub use dtos::KbChainSlotDto;
|
pub use dtos::ChainSlotDto;
|
||||||
pub use dtos::KbChainTransactionDto;
|
pub use dtos::ChainTransactionDto;
|
||||||
pub use dtos::KbDbMetadataDto;
|
pub use dtos::DbMetadataDto;
|
||||||
pub use dtos::KbDbRuntimeEventDto;
|
pub use dtos::DbRuntimeEventDto;
|
||||||
pub use dtos::KbDexDecodedEventDto;
|
pub use dtos::DexDecodedEventDto;
|
||||||
pub use dtos::KbDexDto;
|
pub use dtos::DexDto;
|
||||||
pub use dtos::KbKnownHttpEndpointDto;
|
pub use dtos::KnownHttpEndpointDto;
|
||||||
pub use dtos::KbKnownWsEndpointDto;
|
pub use dtos::KnownWsEndpointDto;
|
||||||
pub use dtos::KbLaunchAttributionDto;
|
pub use dtos::LaunchAttributionDto;
|
||||||
pub use dtos::KbLaunchSurfaceDto;
|
pub use dtos::LaunchSurfaceDto;
|
||||||
pub use dtos::KbLaunchSurfaceKeyDto;
|
pub use dtos::LaunchSurfaceKeyDto;
|
||||||
pub use dtos::KbLiquidityEventDto;
|
pub use dtos::LiquidityEventDto;
|
||||||
pub use dtos::KbLocalDecodedEventDiagnosticSummaryDto;
|
pub use dtos::LocalDecodedEventDiagnosticSummaryDto;
|
||||||
pub(crate) use dtos::KbLocalDecodedEventDiagnosticSummaryRow;
|
pub use dtos::LocalDexDiagnosticSummaryDto;
|
||||||
pub use dtos::KbLocalDexDiagnosticSummaryDto;
|
pub use dtos::LocalDuplicateDecodedEventTradeDiagnosticSampleDto;
|
||||||
pub(crate) use dtos::KbLocalDexDiagnosticSummaryRow;
|
pub use dtos::LocalMissingTradeEventDiagnosticSampleDto;
|
||||||
pub use dtos::KbLocalDuplicateDecodedEventTradeDiagnosticSampleDto;
|
pub use dtos::LocalMissingTradeEventReasonSummaryDto;
|
||||||
pub(crate) use dtos::KbLocalDuplicateDecodedEventTradeDiagnosticSampleRow;
|
pub use dtos::LocalMultiTradeSignaturePairDiagnosticSampleDto;
|
||||||
pub use dtos::KbLocalMissingTradeEventDiagnosticSampleDto;
|
pub use dtos::LocalNonActionablePairDiagnosticSummaryDto;
|
||||||
pub(crate) use dtos::KbLocalMissingTradeEventDiagnosticSampleRow;
|
pub use dtos::LocalPairDiagnosticSummaryDto;
|
||||||
pub use dtos::KbLocalMultiTradeSignaturePairDiagnosticSampleDto;
|
pub use dtos::LocalPairGapDiagnosticSampleDto;
|
||||||
pub(crate) use dtos::KbLocalMultiTradeSignaturePairDiagnosticSampleRow;
|
pub use dtos::LocalPipelineDiagnosticCountersDto;
|
||||||
pub use dtos::KbLocalPairDiagnosticSummaryDto;
|
pub use dtos::LocalPipelineDiagnosticSummaryDto;
|
||||||
pub(crate) use dtos::KbLocalPairDiagnosticSummaryRow;
|
pub use dtos::ObservedTokenDto;
|
||||||
pub use dtos::KbLocalPairGapDiagnosticSampleDto;
|
pub use dtos::OnchainObservationDto;
|
||||||
pub(crate) use dtos::KbLocalPairGapDiagnosticSampleRow;
|
pub use dtos::PairAnalyticSignalDto;
|
||||||
pub use dtos::KbLocalPipelineDiagnosticCountersDto;
|
pub use dtos::PairCandleDto;
|
||||||
pub(crate) use dtos::KbLocalPipelineDiagnosticCountersRow;
|
pub use dtos::PairDto;
|
||||||
pub use dtos::KbLocalPipelineDiagnosticSummaryDto;
|
pub use dtos::PairMetricDto;
|
||||||
pub use dtos::KbObservedTokenDto;
|
pub use dtos::PoolDto;
|
||||||
pub use dtos::KbOnchainObservationDto;
|
pub use dtos::PoolListingDto;
|
||||||
pub use dtos::KbPairAnalyticSignalDto;
|
pub use dtos::PoolOriginDto;
|
||||||
pub use dtos::KbPairCandleDto;
|
pub use dtos::PoolTokenDto;
|
||||||
pub use dtos::KbPairDto;
|
pub use dtos::SwapDto;
|
||||||
pub use dtos::KbPairMetricDto;
|
pub use dtos::TokenBurnEventDto;
|
||||||
pub use dtos::KbPoolDto;
|
pub use dtos::TokenDto;
|
||||||
pub use dtos::KbPoolListingDto;
|
pub use dtos::TokenMintEventDto;
|
||||||
pub use dtos::KbPoolOriginDto;
|
pub use dtos::TradeEventDto;
|
||||||
pub use dtos::KbPoolTokenDto;
|
pub use dtos::WalletDto;
|
||||||
pub use dtos::KbSwapDto;
|
pub use dtos::WalletHoldingDto;
|
||||||
pub use dtos::KbTokenBurnEventDto;
|
pub use dtos::WalletParticipationDto;
|
||||||
pub use dtos::KbTokenDto;
|
pub use entities::AnalysisSignalEntity;
|
||||||
pub use dtos::KbTokenMintEventDto;
|
pub use entities::ChainInstructionEntity;
|
||||||
pub use dtos::KbTradeEventDto;
|
pub use entities::ChainSlotEntity;
|
||||||
pub use dtos::KbWalletDto;
|
pub use entities::ChainTransactionEntity;
|
||||||
pub use dtos::KbWalletHoldingDto;
|
pub use entities::DbMetadataEntity;
|
||||||
pub use dtos::KbWalletParticipationDto;
|
pub use entities::DbRuntimeEventEntity;
|
||||||
pub use entities::KbAnalysisSignalEntity;
|
pub use entities::DexDecodedEventEntity;
|
||||||
pub use entities::KbChainInstructionEntity;
|
pub use entities::DexEntity;
|
||||||
pub use entities::KbChainSlotEntity;
|
pub use entities::KnownHttpEndpointEntity;
|
||||||
pub use entities::KbChainTransactionEntity;
|
pub use entities::KnownWsEndpointEntity;
|
||||||
pub use entities::KbDbMetadataEntity;
|
pub use entities::LaunchAttributionEntity;
|
||||||
pub use entities::KbDbRuntimeEventEntity;
|
pub use entities::LaunchSurfaceEntity;
|
||||||
pub use entities::KbDexDecodedEventEntity;
|
pub use entities::LaunchSurfaceKeyEntity;
|
||||||
pub use entities::KbDexEntity;
|
pub use entities::LiquidityEventEntity;
|
||||||
pub use entities::KbKnownHttpEndpointEntity;
|
pub use entities::ObservedTokenEntity;
|
||||||
pub use entities::KbKnownWsEndpointEntity;
|
pub use entities::OnchainObservationEntity;
|
||||||
pub use entities::KbLaunchAttributionEntity;
|
pub use entities::PairAnalyticSignalEntity;
|
||||||
pub use entities::KbLaunchSurfaceEntity;
|
pub use entities::PairCandleEntity;
|
||||||
pub use entities::KbLaunchSurfaceKeyEntity;
|
pub use entities::PairEntity;
|
||||||
pub use entities::KbLiquidityEventEntity;
|
pub use entities::PairMetricEntity;
|
||||||
pub use entities::KbObservedTokenEntity;
|
pub use entities::PoolEntity;
|
||||||
pub use entities::KbOnchainObservationEntity;
|
pub use entities::PoolListingEntity;
|
||||||
pub use entities::KbPairAnalyticSignalEntity;
|
pub use entities::PoolOriginEntity;
|
||||||
pub use entities::KbPairCandleEntity;
|
pub use entities::PoolTokenEntity;
|
||||||
pub use entities::KbPairEntity;
|
pub use entities::SwapEntity;
|
||||||
pub use entities::KbPairMetricEntity;
|
pub use entities::TokenBurnEventEntity;
|
||||||
pub use entities::KbPoolEntity;
|
pub use entities::TokenEntity;
|
||||||
pub use entities::KbPoolListingEntity;
|
pub use entities::TokenMintEventEntity;
|
||||||
pub use entities::KbPoolOriginEntity;
|
pub use entities::TradeEventEntity;
|
||||||
pub use entities::KbPoolTokenEntity;
|
pub use entities::WalletEntity;
|
||||||
pub use entities::KbSwapEntity;
|
pub use entities::WalletHoldingEntity;
|
||||||
pub use entities::KbTokenBurnEventEntity;
|
pub use entities::WalletParticipationEntity;
|
||||||
pub use entities::KbTokenEntity;
|
pub use queries::query_analysis_signals_insert;
|
||||||
pub use entities::KbTokenMintEventEntity;
|
pub use queries::query_analysis_signals_list;
|
||||||
pub use entities::KbTradeEventEntity;
|
pub use queries::query_chain_instructions_delete_by_transaction_id;
|
||||||
pub use entities::KbWalletEntity;
|
pub use queries::query_chain_instructions_get_by_id;
|
||||||
pub use entities::KbWalletHoldingEntity;
|
pub use queries::query_chain_instructions_insert;
|
||||||
pub use entities::KbWalletParticipationEntity;
|
pub use queries::query_chain_instructions_list_by_transaction_id;
|
||||||
pub use queries::delete_chain_instructions_by_transaction_id;
|
pub use queries::query_chain_slots_get;
|
||||||
pub use queries::get_chain_instruction_by_id;
|
pub use queries::query_chain_slots_list_recent;
|
||||||
pub use queries::get_chain_slot;
|
pub use queries::query_chain_slots_upsert;
|
||||||
pub use queries::get_chain_transaction_by_signature;
|
pub use queries::query_chain_transactions_get_by_signature;
|
||||||
pub use queries::get_db_metadata;
|
pub use queries::query_chain_transactions_list_recent;
|
||||||
pub use queries::get_dex_by_code;
|
pub use queries::query_chain_transactions_list_signatures_for_replay;
|
||||||
pub use queries::get_dex_decoded_event_by_key;
|
pub use queries::query_chain_transactions_upsert;
|
||||||
pub use queries::get_known_http_endpoint;
|
pub use queries::query_db_metadatas_get;
|
||||||
pub use queries::get_known_ws_endpoint;
|
pub use queries::query_db_metadatas_list;
|
||||||
pub use queries::get_latest_pump_fun_create_payload_by_mint;
|
pub use queries::query_db_metadatas_upsert;
|
||||||
pub use queries::get_launch_attribution_by_decoded_event_id;
|
pub use queries::query_db_runtime_events_insert;
|
||||||
pub use queries::get_launch_surface_by_code;
|
pub use queries::query_db_runtime_events_list_recent;
|
||||||
pub use queries::get_launch_surface_key_by_match;
|
pub use queries::query_dex_decoded_events_get_by_key;
|
||||||
pub use queries::get_local_pipeline_diagnostic_counters;
|
pub use queries::query_dex_decoded_events_get_latest_pump_fun_create_payload_by_mint;
|
||||||
pub use queries::get_observed_token_by_mint;
|
pub use queries::query_dex_decoded_events_list_by_transaction_id;
|
||||||
pub use queries::get_pair_analytic_signal_by_key;
|
pub use queries::query_dex_decoded_events_upsert;
|
||||||
pub use queries::get_pair_by_pool_id;
|
pub use queries::query_dexs_get_by_code;
|
||||||
pub use queries::get_pair_candle_by_key;
|
pub use queries::query_dexs_list;
|
||||||
pub use queries::get_pair_metric_by_pair_id;
|
pub use queries::query_dexs_upsert;
|
||||||
pub use queries::get_pool_by_address;
|
pub use queries::query_known_http_endpoints_get;
|
||||||
pub use queries::get_pool_listing_by_pool_id;
|
pub use queries::query_known_http_endpoints_list;
|
||||||
pub use queries::get_pool_origin_by_pool_id;
|
pub use queries::query_known_http_endpoints_upsert;
|
||||||
pub use queries::get_token_by_id;
|
pub use queries::query_known_ws_endpoints_get;
|
||||||
pub use queries::get_token_by_mint;
|
pub use queries::query_known_ws_endpoints_list;
|
||||||
pub use queries::get_trade_event_by_decoded_event_id;
|
pub use queries::query_known_ws_endpoints_upsert;
|
||||||
pub use queries::get_wallet_by_address;
|
pub use queries::query_launch_attributions_get_by_decoded_event_id;
|
||||||
pub use queries::get_wallet_holding_by_wallet_and_token;
|
pub use queries::query_launch_attributions_list_by_pool_id;
|
||||||
pub use queries::get_wallet_participation_by_unique_key;
|
pub use queries::query_launch_attributions_upsert;
|
||||||
pub use queries::insert_analysis_signal;
|
pub use queries::query_launch_surface_keys_get_by_match;
|
||||||
pub use queries::insert_chain_instruction;
|
pub use queries::query_launch_surface_keys_list_by_surface_id;
|
||||||
pub use queries::insert_db_runtime_event;
|
pub use queries::query_launch_surface_keys_upsert;
|
||||||
pub use queries::insert_onchain_observation;
|
pub use queries::query_launch_surfaces_get_by_code;
|
||||||
pub use queries::list_chain_instructions_by_transaction_id;
|
pub use queries::query_launch_surfaces_list;
|
||||||
pub use queries::list_chain_transaction_signatures_for_replay;
|
pub use queries::query_launch_surfaces_upsert;
|
||||||
pub use queries::list_db_metadata;
|
pub use queries::query_liquidity_events_list_recent;
|
||||||
pub use queries::list_dex_decoded_events_by_transaction_id;
|
pub use queries::query_liquidity_events_upsert;
|
||||||
pub use queries::list_dexes;
|
pub use queries::query_local_decoded_event_diagnostic_list_summaries;
|
||||||
pub use queries::list_known_http_endpoints;
|
pub use queries::query_local_duplicate_decoded_event_trade_diagnostic_list_samples;
|
||||||
pub use queries::list_known_ws_endpoints;
|
pub use queries::query_local_missing_trade_event_diagnostic_list_samples;
|
||||||
pub use queries::list_launch_attributions_by_pool_id;
|
pub use queries::query_local_missing_trade_event_reason_list_summaries;
|
||||||
pub use queries::list_launch_surface_keys_by_surface_id;
|
pub use queries::query_local_multi_trade_signature_pair_diagnostic_list_samples;
|
||||||
pub use queries::list_launch_surfaces;
|
pub use queries::query_local_non_actionable_pair_diagnostic_list_summaries;
|
||||||
pub use queries::list_local_decoded_event_diagnostic_summaries;
|
pub use queries::query_local_pair_diagnostic_list_summaries;
|
||||||
pub use queries::list_local_dex_diagnostic_summaries;
|
pub use queries::query_local_pair_without_candle_diagnostic_list_samples;
|
||||||
pub use queries::list_local_duplicate_decoded_event_trade_diagnostic_samples;
|
pub use queries::query_local_pair_without_trade_diagnostic_list_samples;
|
||||||
pub use queries::list_local_missing_trade_event_diagnostic_samples;
|
pub use queries::query_local_pipeline_diagnostic_get_counters;
|
||||||
pub use queries::list_local_multi_trade_signature_pair_diagnostic_samples;
|
pub use queries::query_local_pipeline_diagnostic_list_summaries;
|
||||||
pub use queries::list_local_pair_diagnostic_summaries;
|
pub use queries::query_observed_tokens_get_by_mint;
|
||||||
pub use queries::list_local_pair_without_candle_diagnostic_samples;
|
pub use queries::query_observed_tokens_list;
|
||||||
pub use queries::list_local_pair_without_trade_diagnostic_samples;
|
pub use queries::query_observed_tokens_upsert;
|
||||||
pub use queries::list_observed_tokens;
|
pub use queries::query_onchain_observations_insert;
|
||||||
pub use queries::list_pair_analytic_signals_by_pair_id;
|
pub use queries::query_onchain_observations_list_recent;
|
||||||
pub use queries::list_pair_candles_by_pair_and_timeframe;
|
pub use queries::query_pair_analytic_signals_get_by_key;
|
||||||
pub use queries::list_pair_metrics;
|
pub use queries::query_pair_analytic_signals_list_by_pair_id;
|
||||||
pub use queries::list_pairs;
|
pub use queries::query_pair_analytic_signals_upsert;
|
||||||
pub use queries::list_pool_listings;
|
pub use queries::query_pair_candles_get_by_key;
|
||||||
pub use queries::list_pool_origins;
|
pub use queries::query_pair_candles_list_by_pair_and_timeframe;
|
||||||
pub use queries::list_pool_tokens_by_pool_id;
|
pub use queries::query_pair_candles_upsert;
|
||||||
pub use queries::list_pools;
|
pub use queries::query_pair_metrics_get_by_pair_id;
|
||||||
pub use queries::list_recent_analysis_signals;
|
pub use queries::query_pair_metrics_list;
|
||||||
pub use queries::list_recent_chain_slots;
|
pub use queries::query_pair_metrics_upsert;
|
||||||
pub use queries::list_recent_chain_transactions;
|
pub use queries::query_pairs_get_by_pool_id;
|
||||||
pub use queries::list_recent_db_runtime_events;
|
pub use queries::query_pairs_list;
|
||||||
pub use queries::list_recent_liquidity_events;
|
pub use queries::query_pairs_update_symbol;
|
||||||
pub use queries::list_recent_onchain_observations;
|
pub use queries::query_pairs_upsert;
|
||||||
pub use queries::list_recent_swaps;
|
pub use queries::query_pool_listings_get_by_pool_id;
|
||||||
pub use queries::list_recent_token_burn_events;
|
pub use queries::query_pool_listings_list;
|
||||||
pub use queries::list_recent_token_mint_events;
|
pub use queries::query_pool_listings_upsert;
|
||||||
pub use queries::list_tokens;
|
pub use queries::query_pool_origins_get_by_pool_id;
|
||||||
pub use queries::list_tokens_missing_metadata;
|
pub use queries::query_pool_origins_list;
|
||||||
pub use queries::list_trade_events_by_pair_id;
|
pub use queries::query_pool_origins_upsert;
|
||||||
pub use queries::list_trade_events_by_transaction_id;
|
pub use queries::query_pool_tokens_list_by_pool_id;
|
||||||
pub use queries::list_wallet_holdings_by_wallet_id;
|
pub use queries::query_pool_tokens_upsert;
|
||||||
pub use queries::list_wallet_participations_by_pool_id;
|
pub use queries::query_pools_get_by_address;
|
||||||
pub use queries::list_wallet_participations_by_wallet_id;
|
pub use queries::query_pools_list;
|
||||||
pub use queries::list_wallets;
|
pub use queries::query_pools_upsert;
|
||||||
pub use queries::update_pair_symbol;
|
pub use queries::query_swaps_list_recent;
|
||||||
pub use queries::upsert_chain_slot;
|
pub use queries::query_swaps_upsert;
|
||||||
pub use queries::upsert_chain_transaction;
|
pub use queries::query_token_burn_events_list_recent;
|
||||||
pub use queries::upsert_db_metadata;
|
pub use queries::query_token_burn_events_upsert;
|
||||||
pub use queries::upsert_dex;
|
pub use queries::query_token_mint_events_list_recent;
|
||||||
pub use queries::upsert_dex_decoded_event;
|
pub use queries::query_token_mint_events_upsert;
|
||||||
pub use queries::upsert_known_http_endpoint;
|
pub use queries::query_tokens_get_by_id;
|
||||||
pub use queries::upsert_known_ws_endpoint;
|
pub use queries::query_tokens_get_by_mint;
|
||||||
pub use queries::upsert_launch_attribution;
|
pub use queries::query_tokens_list;
|
||||||
pub use queries::upsert_launch_surface;
|
pub use queries::query_tokens_list_missing_metadata;
|
||||||
pub use queries::upsert_launch_surface_key;
|
pub use queries::query_tokens_upsert;
|
||||||
pub use queries::upsert_liquidity_event;
|
pub use queries::query_trade_events_get_by_decoded_event_id;
|
||||||
pub use queries::upsert_observed_token;
|
pub use queries::query_trade_events_list_by_pair_id;
|
||||||
pub use queries::upsert_pair;
|
pub use queries::query_trade_events_list_by_transaction_id;
|
||||||
pub use queries::upsert_pair_analytic_signal;
|
pub use queries::query_trade_events_upsert;
|
||||||
pub use queries::upsert_pair_candle;
|
pub use queries::query_wallet_holdings_get_by_wallet_and_token;
|
||||||
pub use queries::upsert_pair_metric;
|
pub use queries::query_wallet_holdings_list_by_wallet_id;
|
||||||
pub use queries::upsert_pool;
|
pub use queries::query_wallet_holdings_upsert;
|
||||||
pub use queries::upsert_pool_listing;
|
pub use queries::query_wallet_participations_get_by_unique_key;
|
||||||
pub use queries::upsert_pool_origin;
|
pub use queries::query_wallet_participations_list_by_pool_id;
|
||||||
pub use queries::upsert_pool_token;
|
pub use queries::query_wallet_participations_list_by_wallet_id;
|
||||||
pub use queries::upsert_swap;
|
pub use queries::query_wallet_participations_upsert;
|
||||||
pub use queries::upsert_token;
|
pub use queries::query_wallets_get_by_address;
|
||||||
pub use queries::upsert_token_burn_event;
|
pub use queries::query_wallets_list;
|
||||||
pub use queries::upsert_token_mint_event;
|
pub use queries::query_wallets_upsert;
|
||||||
pub use queries::upsert_trade_event;
|
pub use types::AnalysisSignalSeverity;
|
||||||
pub use queries::upsert_wallet;
|
pub use types::DatabaseBackend;
|
||||||
pub use queries::upsert_wallet_holding;
|
pub use types::DbRuntimeEventLevel;
|
||||||
pub use queries::upsert_wallet_participation;
|
pub use types::LiquidityEventKind;
|
||||||
pub use types::KbAnalysisSignalSeverity;
|
pub use types::ObservationSourceKind;
|
||||||
pub use types::KbDatabaseBackend;
|
pub use types::ObservedTokenStatus;
|
||||||
pub use types::KbDbRuntimeEventLevel;
|
pub use types::PoolKind;
|
||||||
pub use types::KbLiquidityEventKind;
|
pub use types::PoolStatus;
|
||||||
pub use types::KbObservationSourceKind;
|
pub use types::PoolTokenRole;
|
||||||
pub use types::KbObservedTokenStatus;
|
pub use types::SwapTradeSide;
|
||||||
pub use types::KbPoolKind;
|
|
||||||
pub use types::KbPoolStatus;
|
|
||||||
pub use types::KbPoolTokenRole;
|
|
||||||
pub use types::KbSwapTradeSide;
|
|
||||||
|
|||||||
@@ -4,29 +4,27 @@
|
|||||||
|
|
||||||
/// Concrete database connection.
|
/// Concrete database connection.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum KbDatabaseConnection {
|
pub enum DatabaseConnection {
|
||||||
/// SQLite connection pool.
|
/// SQLite connection pool.
|
||||||
Sqlite(sqlx::SqlitePool),
|
Sqlite(sqlx::SqlitePool),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Database facade.
|
/// Database facade.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct KbDatabase {
|
pub struct Database {
|
||||||
backend: crate::KbDatabaseBackend,
|
backend: crate::DatabaseBackend,
|
||||||
database_url: std::string::String,
|
database_url: std::string::String,
|
||||||
connection: KbDatabaseConnection,
|
connection: DatabaseConnection,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl KbDatabase {
|
impl Database {
|
||||||
/// Opens a database connection without initializing the schema.
|
/// Opens a database connection without initializing the schema.
|
||||||
pub async fn connect(config: &crate::KbDatabaseConfig) -> Result<Self, crate::KbError> {
|
pub async fn connect(config: &crate::DatabaseConfig) -> Result<Self, crate::Error> {
|
||||||
if !config.enabled {
|
if !config.enabled {
|
||||||
return Err(crate::KbError::Config(
|
return Err(crate::Error::Config("database is disabled in configuration".to_string()));
|
||||||
"database is disabled in configuration".to_string(),
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
match config.backend {
|
match config.backend {
|
||||||
crate::KbDatabaseBackend::Sqlite => {
|
crate::DatabaseBackend::Sqlite => {
|
||||||
let database_url_result =
|
let database_url_result =
|
||||||
crate::db::sqlite::sqlite_database_url_from_config(config);
|
crate::db::sqlite::sqlite_database_url_from_config(config);
|
||||||
let database_url = match database_url_result {
|
let database_url = match database_url_result {
|
||||||
@@ -39,9 +37,9 @@ impl KbDatabase {
|
|||||||
Err(error) => return Err(error),
|
Err(error) => return Err(error),
|
||||||
};
|
};
|
||||||
return Ok(Self {
|
return Ok(Self {
|
||||||
backend: crate::KbDatabaseBackend::Sqlite,
|
backend: crate::DatabaseBackend::Sqlite,
|
||||||
database_url,
|
database_url,
|
||||||
connection: KbDatabaseConnection::Sqlite(pool),
|
connection: DatabaseConnection::Sqlite(pool),
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -49,8 +47,8 @@ impl KbDatabase {
|
|||||||
|
|
||||||
/// Opens a database connection and initializes the schema if configured.
|
/// Opens a database connection and initializes the schema if configured.
|
||||||
pub async fn connect_and_initialize(
|
pub async fn connect_and_initialize(
|
||||||
config: &crate::KbDatabaseConfig,
|
config: &crate::DatabaseConfig,
|
||||||
) -> Result<Self, crate::KbError> {
|
) -> Result<Self, crate::Error> {
|
||||||
let connect_result = Self::connect(config).await;
|
let connect_result = Self::connect(config).await;
|
||||||
let database = match connect_result {
|
let database = match connect_result {
|
||||||
Ok(database) => database,
|
Ok(database) => database,
|
||||||
@@ -66,7 +64,7 @@ impl KbDatabase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the configured backend.
|
/// Returns the configured backend.
|
||||||
pub fn backend(&self) -> crate::KbDatabaseBackend {
|
pub fn backend(&self) -> crate::DatabaseBackend {
|
||||||
return self.backend;
|
return self.backend;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,14 +74,14 @@ impl KbDatabase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Pings the database.
|
/// Pings the database.
|
||||||
pub async fn ping(&self) -> Result<(), crate::KbError> {
|
pub async fn ping(&self) -> Result<(), crate::Error> {
|
||||||
match &self.connection {
|
match &self.connection {
|
||||||
KbDatabaseConnection::Sqlite(pool) => {
|
DatabaseConnection::Sqlite(pool) => {
|
||||||
let ping_result = sqlx::query("SELECT 1").execute(pool).await;
|
let ping_result = sqlx::query("SELECT 1").execute(pool).await;
|
||||||
match ping_result {
|
match ping_result {
|
||||||
Ok(_) => return Ok(()),
|
Ok(_) => return Ok(()),
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
return Err(crate::KbError::Db(format!(
|
return Err(crate::Error::Db(format!(
|
||||||
"cannot ping sqlite database '{}': {}",
|
"cannot ping sqlite database '{}': {}",
|
||||||
self.database_url, error
|
self.database_url, error
|
||||||
)));
|
)));
|
||||||
@@ -94,7 +92,7 @@ impl KbDatabase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the underlying connection enum.
|
/// Returns the underlying connection enum.
|
||||||
pub(crate) fn connection(&self) -> &KbDatabaseConnection {
|
pub(crate) fn connection(&self) -> &DatabaseConnection {
|
||||||
return &self.connection;
|
return &self.connection;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -105,10 +103,10 @@ mod tests {
|
|||||||
async fn connect_and_ping_sqlite_database_works() {
|
async fn connect_and_ping_sqlite_database_works() {
|
||||||
let tempdir = tempfile::tempdir().expect("tempdir must succeed");
|
let tempdir = tempfile::tempdir().expect("tempdir must succeed");
|
||||||
let database_path = tempdir.path().join("connection.sqlite3");
|
let database_path = tempdir.path().join("connection.sqlite3");
|
||||||
let config = crate::KbDatabaseConfig {
|
let config = crate::DatabaseConfig {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
backend: crate::KbDatabaseBackend::Sqlite,
|
backend: crate::DatabaseBackend::Sqlite,
|
||||||
sqlite: crate::KbSqliteDatabaseConfig {
|
sqlite: crate::SqliteDatabaseConfig {
|
||||||
path: database_path.to_string_lossy().to_string(),
|
path: database_path.to_string_lossy().to_string(),
|
||||||
create_if_missing: true,
|
create_if_missing: true,
|
||||||
busy_timeout_ms: 5000,
|
busy_timeout_ms: 5000,
|
||||||
@@ -117,13 +115,13 @@ mod tests {
|
|||||||
use_wal: true,
|
use_wal: true,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
let database = crate::KbDatabase::connect_and_initialize(&config)
|
let database = crate::Database::connect_and_initialize(&config)
|
||||||
.await
|
.await
|
||||||
.expect("database init must succeed");
|
.expect("database init must succeed");
|
||||||
assert_eq!(database.backend(), crate::KbDatabaseBackend::Sqlite);
|
assert_eq!(database.backend(), crate::DatabaseBackend::Sqlite);
|
||||||
assert!(database.database_url().starts_with("sqlite://"));
|
assert!(database.database_url().starts_with("sqlite://"));
|
||||||
database.ping().await.expect("ping must succeed");
|
database.ping().await.expect("ping must succeed");
|
||||||
let metadata = crate::get_db_metadata(&database, "schema_version")
|
let metadata = crate::query_db_metadatas_get(&database, "schema_version")
|
||||||
.await
|
.await
|
||||||
.expect("metadata fetch must succeed");
|
.expect("metadata fetch must succeed");
|
||||||
assert!(metadata.is_some());
|
assert!(metadata.is_some());
|
||||||
|
|||||||
@@ -36,52 +36,57 @@ mod wallet;
|
|||||||
mod wallet_holding;
|
mod wallet_holding;
|
||||||
mod wallet_participation;
|
mod wallet_participation;
|
||||||
|
|
||||||
pub use analysis_signal::KbAnalysisSignalDto;
|
pub(crate) use local_pipeline_diagnostics::LocalDecodedEventDiagnosticSummaryRow;
|
||||||
pub use chain_instruction::KbChainInstructionDto;
|
pub(crate) use local_pipeline_diagnostics::LocalDexDiagnosticSummaryRow;
|
||||||
pub use chain_slot::KbChainSlotDto;
|
pub(crate) use local_pipeline_diagnostics::LocalDuplicateDecodedEventTradeDiagnosticSampleRow;
|
||||||
pub use chain_transaction::KbChainTransactionDto;
|
pub(crate) use local_pipeline_diagnostics::LocalMissingTradeEventDiagnosticSampleRow;
|
||||||
pub use db_metadata::KbDbMetadataDto;
|
pub(crate) use local_pipeline_diagnostics::LocalMissingTradeEventReasonSummaryRow;
|
||||||
pub use db_runtime_event::KbDbRuntimeEventDto;
|
pub(crate) use local_pipeline_diagnostics::LocalMultiTradeSignaturePairDiagnosticSampleRow;
|
||||||
pub use dex::KbDexDto;
|
pub(crate) use local_pipeline_diagnostics::LocalNonActionablePairDiagnosticSummaryRow;
|
||||||
pub use dex_decoded_event::KbDexDecodedEventDto;
|
pub(crate) use local_pipeline_diagnostics::LocalPairDiagnosticSummaryRow;
|
||||||
pub use known_http_endpoint::KbKnownHttpEndpointDto;
|
pub(crate) use local_pipeline_diagnostics::LocalPairGapDiagnosticSampleRow;
|
||||||
pub use known_ws_endpoint::KbKnownWsEndpointDto;
|
pub(crate) use local_pipeline_diagnostics::LocalPipelineDiagnosticCountersRow;
|
||||||
pub use launch_attribution::KbLaunchAttributionDto;
|
|
||||||
pub use launch_surface::KbLaunchSurfaceDto;
|
pub use analysis_signal::AnalysisSignalDto;
|
||||||
pub use launch_surface_key::KbLaunchSurfaceKeyDto;
|
pub use chain_instruction::ChainInstructionDto;
|
||||||
pub use liquidity_event::KbLiquidityEventDto;
|
pub use chain_slot::ChainSlotDto;
|
||||||
pub use local_pipeline_diagnostics::KbLocalDecodedEventDiagnosticSummaryDto;
|
pub use chain_transaction::ChainTransactionDto;
|
||||||
pub(crate) use local_pipeline_diagnostics::KbLocalDecodedEventDiagnosticSummaryRow;
|
pub use db_metadata::DbMetadataDto;
|
||||||
pub use local_pipeline_diagnostics::KbLocalDexDiagnosticSummaryDto;
|
pub use db_runtime_event::DbRuntimeEventDto;
|
||||||
pub(crate) use local_pipeline_diagnostics::KbLocalDexDiagnosticSummaryRow;
|
pub use dex::DexDto;
|
||||||
pub use local_pipeline_diagnostics::KbLocalDuplicateDecodedEventTradeDiagnosticSampleDto;
|
pub use dex_decoded_event::DexDecodedEventDto;
|
||||||
pub(crate) use local_pipeline_diagnostics::KbLocalDuplicateDecodedEventTradeDiagnosticSampleRow;
|
pub use known_http_endpoint::KnownHttpEndpointDto;
|
||||||
pub use local_pipeline_diagnostics::KbLocalMissingTradeEventDiagnosticSampleDto;
|
pub use known_ws_endpoint::KnownWsEndpointDto;
|
||||||
pub(crate) use local_pipeline_diagnostics::KbLocalMissingTradeEventDiagnosticSampleRow;
|
pub use launch_attribution::LaunchAttributionDto;
|
||||||
pub use local_pipeline_diagnostics::KbLocalMultiTradeSignaturePairDiagnosticSampleDto;
|
pub use launch_surface::LaunchSurfaceDto;
|
||||||
pub(crate) use local_pipeline_diagnostics::KbLocalMultiTradeSignaturePairDiagnosticSampleRow;
|
pub use launch_surface_key::LaunchSurfaceKeyDto;
|
||||||
pub use local_pipeline_diagnostics::KbLocalPairDiagnosticSummaryDto;
|
pub use liquidity_event::LiquidityEventDto;
|
||||||
pub(crate) use local_pipeline_diagnostics::KbLocalPairDiagnosticSummaryRow;
|
pub use local_pipeline_diagnostics::LocalDecodedEventDiagnosticSummaryDto;
|
||||||
pub use local_pipeline_diagnostics::KbLocalPairGapDiagnosticSampleDto;
|
pub use local_pipeline_diagnostics::LocalDexDiagnosticSummaryDto;
|
||||||
pub(crate) use local_pipeline_diagnostics::KbLocalPairGapDiagnosticSampleRow;
|
pub use local_pipeline_diagnostics::LocalDuplicateDecodedEventTradeDiagnosticSampleDto;
|
||||||
pub use local_pipeline_diagnostics::KbLocalPipelineDiagnosticCountersDto;
|
pub use local_pipeline_diagnostics::LocalMissingTradeEventDiagnosticSampleDto;
|
||||||
pub(crate) use local_pipeline_diagnostics::KbLocalPipelineDiagnosticCountersRow;
|
pub use local_pipeline_diagnostics::LocalMissingTradeEventReasonSummaryDto;
|
||||||
pub use local_pipeline_diagnostics::KbLocalPipelineDiagnosticSummaryDto;
|
pub use local_pipeline_diagnostics::LocalMultiTradeSignaturePairDiagnosticSampleDto;
|
||||||
pub use observed_token::KbObservedTokenDto;
|
pub use local_pipeline_diagnostics::LocalNonActionablePairDiagnosticSummaryDto;
|
||||||
pub use onchain_observation::KbOnchainObservationDto;
|
pub use local_pipeline_diagnostics::LocalPairDiagnosticSummaryDto;
|
||||||
pub use pair::KbPairDto;
|
pub use local_pipeline_diagnostics::LocalPairGapDiagnosticSampleDto;
|
||||||
pub use pair_analytic_signal::KbPairAnalyticSignalDto;
|
pub use local_pipeline_diagnostics::LocalPipelineDiagnosticCountersDto;
|
||||||
pub use pair_candle::KbPairCandleDto;
|
pub use local_pipeline_diagnostics::LocalPipelineDiagnosticSummaryDto;
|
||||||
pub use pair_metric::KbPairMetricDto;
|
pub use observed_token::ObservedTokenDto;
|
||||||
pub use pool::KbPoolDto;
|
pub use onchain_observation::OnchainObservationDto;
|
||||||
pub use pool_listing::KbPoolListingDto;
|
pub use pair::PairDto;
|
||||||
pub use pool_origin::KbPoolOriginDto;
|
pub use pair_analytic_signal::PairAnalyticSignalDto;
|
||||||
pub use pool_token::KbPoolTokenDto;
|
pub use pair_candle::PairCandleDto;
|
||||||
pub use swap::KbSwapDto;
|
pub use pair_metric::PairMetricDto;
|
||||||
pub use token::KbTokenDto;
|
pub use pool::PoolDto;
|
||||||
pub use token_burn_event::KbTokenBurnEventDto;
|
pub use pool_listing::PoolListingDto;
|
||||||
pub use token_mint_event::KbTokenMintEventDto;
|
pub use pool_origin::PoolOriginDto;
|
||||||
pub use trade_event::KbTradeEventDto;
|
pub use pool_token::PoolTokenDto;
|
||||||
pub use wallet::KbWalletDto;
|
pub use swap::SwapDto;
|
||||||
pub use wallet_holding::KbWalletHoldingDto;
|
pub use token::TokenDto;
|
||||||
pub use wallet_participation::KbWalletParticipationDto;
|
pub use token_burn_event::TokenBurnEventDto;
|
||||||
|
pub use token_mint_event::TokenMintEventDto;
|
||||||
|
pub use trade_event::TradeEventDto;
|
||||||
|
pub use wallet::WalletDto;
|
||||||
|
pub use wallet_holding::WalletHoldingDto;
|
||||||
|
pub use wallet_participation::WalletParticipationDto;
|
||||||
|
|||||||