v0.1.0-pre.058
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
<!-- file: KHADHROONY_BOT3_MIGRATION_CLOSURE_TODO.md -->
|
||||
<!-- version: 4 -->
|
||||
<!-- version: 7 -->
|
||||
|
||||
# Checklist détaillée de clôture de la migration `khadhroony-bot3`
|
||||
|
||||
@@ -86,16 +86,28 @@
|
||||
- [x] Garantir exactement un bouton Effacer pour la sortie principale.
|
||||
- [x] Vider le buffer TypeScript lors de l’effacement.
|
||||
- [ ] Tester JSON, scalaire, texte, erreur et résultat vide.
|
||||
- [x] Restaurer le gabarit desktop à deux colonnes : requête à gauche, endpoints et résultat à droite.
|
||||
|
||||
### 2.2 WebSocket
|
||||
|
||||
- [x] Afficher les messages dans un composant texte/log.
|
||||
- [x] Fixer la hauteur et le scroll interne.
|
||||
- [x] Garantir une seule paire Copier/Effacer pour la sortie Messages.
|
||||
- [ ] Conserver les messages pendant la session active.
|
||||
- [ ] Restaurer l’affichage après fermeture/réouverture de la fenêtre.
|
||||
- [x] Borner le débit et la taille des notifications envoyées à l’UI pour éviter les freezes.
|
||||
- [x] Conserver le payload complet sous forme JSON compacte dans les logs de debug avant formatage et troncage UI.
|
||||
- [x] Conserver les messages pendant la session active.
|
||||
- [x] Restaurer l’affichage après fermeture/réouverture de la fenêtre.
|
||||
- [x] Restaurer le gabarit desktop à deux colonnes : souscription à gauche, statut/endpoints/messages à droite.
|
||||
- [x] Appliquer `base64` par défaut aux souscriptions `accountSubscribe` et `programSubscribe` lorsque l’encodage est absent, sans écraser un encodage explicite.
|
||||
- [ ] Décider après test runtime si Statut et Endpoints WebSocket restent textuels ou deviennent des viewers JSON structurés ; supprimer alors tout bouton Copier redondant fourni par le viewer.
|
||||
|
||||
### 2.3 Journaux généraux et viewers
|
||||
### 2.3 Backfill et autocomplétion
|
||||
|
||||
- [x] Garder Program ID librement éditable.
|
||||
- [x] Ajouter un autocomplétion non bloquant depuis le registre public `kb-program-ids`.
|
||||
- [ ] Ajouter ultérieurement les autocomplétions Token et Pool lorsque leurs tables de référence existeront.
|
||||
|
||||
### 2.4 Journaux généraux et viewers
|
||||
|
||||
- [ ] Vérifier Backfill HTTP.
|
||||
- [ ] Vérifier Extraction core.
|
||||
@@ -110,7 +122,7 @@
|
||||
- [ ] Utiliser des textareas pour texte brut, logs et diagnostics concaténés.
|
||||
- [ ] Fixer la hauteur de tous les viewers JSON.
|
||||
|
||||
### 2.4 Fenêtres SQL
|
||||
### 2.5 Fenêtres SQL
|
||||
|
||||
- [ ] Mettre toutes les fenêtres SQL, sauf Replay Candidates, sur une seule colonne pleine largeur.
|
||||
- [ ] Mettre les tables sur toute la largeur.
|
||||
@@ -155,35 +167,44 @@
|
||||
- [ ] Marquer les variables limitées aux scénarios de démonstration.
|
||||
- [ ] Documenter les futurs remplacements par configuration ou structure typée.
|
||||
|
||||
## 5.1 Wrappers Tauri
|
||||
|
||||
- [x] Déplacer la logique complète de `splash_frontend_ready` dans `splash.rs` et conserver un wrapper privé mince dans `tauri.rs`.
|
||||
- [ ] Inventorier toutes les fonctions `#[tauri::command]` de `tauri.rs`.
|
||||
- [ ] Déplacer progressivement toute logique métier restante dans son module fonctionnel `pub(crate)`.
|
||||
- [ ] Vérifier que chaque wrapper Tauri ne fait que l’adaptation des arguments et la délégation.
|
||||
- [ ] Ajouter ou conserver les tests unitaires dans les modules fonctionnels, pas dans les wrappers.
|
||||
|
||||
## 6. Cycle de vie WebSocket
|
||||
|
||||
- [ ] Vérifier qu’aucun pool WebSocket n’est créé au démarrage.
|
||||
- [ ] Vérifier la création paresseuse au premier accès.
|
||||
- [ ] Vérifier le stockage du pool dans `AppState`.
|
||||
- [ ] Vérifier la survie de la session après fermeture de `demo_ws`.
|
||||
- [ ] Vérifier la récupération de l’état à la réouverture.
|
||||
- [x] Vérifier qu’aucun pool WebSocket n’est créé au démarrage.
|
||||
- [x] Vérifier la création paresseuse au premier accès.
|
||||
- [x] Vérifier le stockage du pool dans `AppState`.
|
||||
- [x] Vérifier la survie de la session après fermeture de `demo_ws`.
|
||||
- [x] Vérifier la récupération de l’état à la réouverture.
|
||||
- [ ] Vérifier la fermeture explicite.
|
||||
- [ ] Vérifier le timeout.
|
||||
- [ ] Vérifier l’arrêt global de l’application.
|
||||
- [ ] Vérifier que la fermeture de la fenêtre n’appelle jamais la déconnexion globale.
|
||||
- [x] Vérifier que la fermeture de la fenêtre n’appelle jamais la déconnexion globale.
|
||||
|
||||
## 7. Initialisation PostgreSQL et splash
|
||||
|
||||
- [ ] Charger l’environnement.
|
||||
- [ ] Charger et valider la configuration.
|
||||
- [ ] Initialiser le logging.
|
||||
- [ ] Initialiser le pool HTTP.
|
||||
- [x] Charger l’environnement.
|
||||
- [x] Charger et valider la configuration.
|
||||
- [x] Initialiser le logging.
|
||||
- [x] Initialiser le pool HTTP.
|
||||
- [ ] Se connecter à PostgreSQL.
|
||||
- [ ] Appeler `initialize_postgres_schema_for_startup`.
|
||||
- [ ] Appeler `emit_sql_startup_table_report`.
|
||||
- [ ] Relier `emit_sql_startup_error`.
|
||||
- [ ] Relier `emit_sql_startup_splash`.
|
||||
- [ ] Ouvrir `main` seulement après le rapport PostgreSQL.
|
||||
- [ ] Fermer le splash après le statut final.
|
||||
- [ ] Ne pas initialiser WebSocket dans cette séquence.
|
||||
- [ ] Rendre la zone de messages du splash scrollable.
|
||||
- [ ] Garder le dernier message visible.
|
||||
- [ ] Afficher connexion, schéma, nombre de tables, tables manquantes et statut final.
|
||||
- [x] Appeler `initialize_postgres_schema_for_startup` après disponibilité du frontend splash.
|
||||
- [x] Appeler `emit_sql_startup_table_report`.
|
||||
- [x] Relier `emit_sql_startup_error`.
|
||||
- [x] Relier `emit_sql_startup_splash`.
|
||||
- [x] Ouvrir `main` seulement après le rapport PostgreSQL et le fade-out.
|
||||
- [x] Fermer le splash après le statut final.
|
||||
- [x] Ne pas initialiser WebSocket dans cette séquence.
|
||||
- [x] Rendre les zones du splash scrollables sans afficher leurs barres de défilement.
|
||||
- [x] Garder automatiquement le dernier log et le dernier message visibles.
|
||||
- [x] Afficher connexion, schéma, nombre de tables, tables manquantes et statut final.
|
||||
- [ ] Valider visuellement le fade-in, les messages intermédiaires, le fade-out et l’ouverture de `main` sur le workspace réel.
|
||||
|
||||
## 8. Purge contrôlée des données dérivées
|
||||
|
||||
@@ -295,7 +316,7 @@
|
||||
|
||||
- [ ] `cargo fmt --all`.
|
||||
- [ ] `cargo check -p kb-config`.
|
||||
- [ ] `cargo test -p kb-config`.
|
||||
- [ ] `cargo test -p kb-config` (44/45 passaient ; contrat de route groupée `kb-pipeline` corrigé dans la prochaine prerelease).
|
||||
- [ ] `cargo check -p kb-pipeline-demo-scenarios`.
|
||||
- [ ] `cargo test -p kb-pipeline-demo-scenarios`.
|
||||
- [ ] `cargo check -p kb-store`.
|
||||
@@ -303,7 +324,7 @@
|
||||
- [ ] `cargo check -p kb-lib`.
|
||||
- [ ] `cargo test -p kb-lib`.
|
||||
- [x] `cargo check -p kb-app-demo-desktop`.
|
||||
- [ ] `cargo test -p kb-app-demo-desktop` (112 tests passaient, 1 contrat d’ordre de décodeurs corrigé dans `pre.057`; relance requise).
|
||||
- [ ] `cargo test -p kb-app-demo-desktop` (112/113 passaient ; contrat d’ordre de décodeurs corrigé, relance requise).
|
||||
- [x] `cargo check --workspace`.
|
||||
- [ ] `cargo test --workspace`.
|
||||
- [ ] `python3 scripts/audit_rust_workspace_rules.py`.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!-- file: RULES_GENERAL.md -->
|
||||
<!-- version: 3 -->
|
||||
<!-- version: 4 -->
|
||||
|
||||
# Règles générales du projet
|
||||
|
||||
@@ -85,3 +85,9 @@ python3 scripts/audit_rust_workspace_rules.py
|
||||
```
|
||||
|
||||
Les validations supplémentaires dépendent du périmètre du delta et sont consignées dans `delta.md`.
|
||||
|
||||
### Fin de fichier et formatters
|
||||
|
||||
- La contrainte de fin de fichier avec exactement un saut de ligne terminal s’applique aux sources Rust, TypeScript, JavaScript, Python, shell, TOML, YAML et Markdown lorsqu’elle est vérifiée par les outils du projet.
|
||||
- Elle ne s’applique pas aux fichiers HTML ni JSON : leur formatter peut supprimer automatiquement la ligne vide terminale.
|
||||
- Aucun audit ne doit réintroduire artificiellement une ligne vide dans un fichier HTML ou JSON après formatage.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!-- file: RULES_RUST.md -->
|
||||
<!-- version: 6 -->
|
||||
<!-- version: 7 -->
|
||||
|
||||
# Règles Rust générales
|
||||
|
||||
@@ -70,6 +70,12 @@ implicit_saturating_sub = "allow"
|
||||
- Le groupe `pub use` précède le groupe `pub(crate) use` dans une même façade.
|
||||
- L’ordre interne suit l’ordre naturel produit par `rustfmt` : les segments et mots séparés sont comparés avant leurs suffixes numériques, par exemple `U8` avant `U16` et `TOKEN` avant `TOKEN2022`.
|
||||
|
||||
## Assertions sur les collections
|
||||
|
||||
- Un test qui vérifie uniquement l’appartenance à un ensemble peut trier la valeur obtenue et la valeur attendue avant `assert_eq!`, afin de ne pas dépendre d’un ordre d’enregistrement non contractuel.
|
||||
- Le tri doit être explicite dans le test et appliqué aux deux collections comparées.
|
||||
- Ne jamais trier une collection lorsque l’ordre fait partie du contrat : comptes Solana, metas, instructions, signers, événements, étapes de pipeline, priorités, journaux ou toute autre séquence ordonnée.
|
||||
|
||||
## Imports et chemins
|
||||
|
||||
- `use` est interdit pour les constantes, fonctions, structures, énumérations, unions, alias de types, modules et macros.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!-- file: RULES_SPECIFIC_KHADHROONY.md -->
|
||||
<!-- version: 4 -->
|
||||
<!-- version: 5 -->
|
||||
|
||||
# Règles spécifiques à `khadhroony-bot3`
|
||||
|
||||
@@ -27,6 +27,8 @@ Toute divergence avec une règle générale doit être explicitement documentée
|
||||
- Les bindings générés doivent être produits dans un dossier dédié, généralement `../frontend/ts/bindings` ou `#[ts(export, export_to = "../frontend/ts/bindings/MyStruct.ts")]`.
|
||||
- Les types purement internes au backend ne doivent pas être exportés vers TypeScript par défaut.
|
||||
- Corriger le type Rust/TS-rs source puis régénérer les bindings ; ne pas considérer une modification manuelle isolée d’un fichier généré comme un correctif durable.
|
||||
- `kb-app-demo-desktop/src/tauri.rs` contient les attributs `#[tauri::command]`, l’enregistrement des commandes et des wrappers privés minces ; la logique complète doit vivre dans le module fonctionnel correspondant sous une fonction `pub(crate)` testable.
|
||||
- Un wrapper Tauri ne doit effectuer que l’adaptation des handles/states/arguments, l’appel de la fonction de module et le retour du résultat ; toute validation métier, orchestration ou construction de payload doit rester hors de `tauri.rs`.
|
||||
|
||||
## Règles d'architecture
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!-- file: kb-app-demo-desktop/frontend/demo_backfill.html -->
|
||||
<!-- version: 5 -->
|
||||
<!-- version: 6 -->
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
|
||||
@@ -97,7 +97,7 @@
|
||||
<div aria-labelledby="programBackfillHeading" class="accordion-collapse collapse" data-bs-parent="#backfillAccordion" id="programBackfillCollapse">
|
||||
<div class="accordion-body">
|
||||
<div class="row g-3 mb-3">
|
||||
<div class="col-12"><label class="form-label" for="programAddressInput">Program ID</label><input class="form-control font-monospace" id="programAddressInput" /></div>
|
||||
<div class="col-12"><label class="form-label" for="programAddressInput">Program ID</label><input class="form-control font-monospace" id="programAddressInput" list="programAddressOptions" autocomplete="off" /><datalist id="programAddressOptions"></datalist></div>
|
||||
<div class="col-12"><label class="form-label" for="programAnchorInput">Signature d'ancrage</label><input class="form-control font-monospace" id="programAnchorInput" />
|
||||
<div class="form-text">Optionnelle pour « Avant, plus anciennes » : vide, la recherche commence aux transactions les plus récentes. Obligatoire pour « Après, plus récentes ».</div>
|
||||
</div>
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
<div class="osb-scrollable pt-1 pb-4" data-simplebar>
|
||||
<div class="container-fluid px-4 py-4">
|
||||
<div class="row g-4 justify-content-center">
|
||||
<div class="col-12">
|
||||
<div class="col-12 col-xl-5">
|
||||
<div class="card shadow-sm border-0 mb-4">
|
||||
<div class="card-body">
|
||||
<h1 class="h3 card-title">Extraction transactionnelle canonical vers core</h1>
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
<div class="osb-scrollable pt-1 pb-4" data-simplebar>
|
||||
<div class="container-fluid px-4 py-4">
|
||||
<div class="row g-4 justify-content-center">
|
||||
<div class="col-12">
|
||||
<div class="col-12 col-xl-5">
|
||||
<div class="card shadow-sm border-0 mb-4">
|
||||
<div class="card-body">
|
||||
<h1 class="h4 mb-3">Sélection bornée</h1>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!-- file: kb-app-demo-desktop/frontend/demo_http.html -->
|
||||
<!-- version: 5 -->
|
||||
<!-- version: 6 -->
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
<div class="osb-scrollable pt-1 pb-4" data-simplebar>
|
||||
<div class="container-fluid px-4 py-4">
|
||||
<div class="row g-4 justify-content-center">
|
||||
<div class="col-12">
|
||||
<div class="col-12 col-xl-5">
|
||||
<div class="card shadow-sm border-0">
|
||||
<div class="card-body">
|
||||
<h1 class="h4 card-title">Requête HTTP Solana standard</h1>
|
||||
|
||||
@@ -202,6 +202,7 @@
|
||||
<div class="col-12 col-lg-4">
|
||||
<label for="programContainsInput" class="form-label">Program ID contient</label>
|
||||
<input id="programContainsInput" class="form-control font-monospace" type="text" list="knownProgramDatalist" autocomplete="off" />
|
||||
<datalist id="knownProgramDatalist"></datalist>
|
||||
</div>
|
||||
<div class="col-6 col-lg-1">
|
||||
<label for="programLimitInput" class="form-label">Limite</label>
|
||||
@@ -439,7 +440,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
<datalist id="knownProgramDatalist"></datalist>
|
||||
<script type="module" src="ts/demo_sql_replay_candidates.ts" defer></script>
|
||||
</body>
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!-- file: kb-app-demo-desktop/frontend/demo_ws.html -->
|
||||
<!-- version: 7 -->
|
||||
<!-- version: 8 -->
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
<div class="osb-scrollable pt-1 pb-4" data-simplebar>
|
||||
<div class="container-fluid px-4 py-4">
|
||||
<div class="row g-4 justify-content-center">
|
||||
<div class="col-12">
|
||||
<div class="col-12 col-xl-5">
|
||||
<div class="card shadow-sm border-0">
|
||||
<div class="card-body">
|
||||
<h1 class="h4 card-title">Souscription WebSocket Solana standard</h1>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: kb_app_demo/frontend/sass/splash.scss
|
||||
// version: 3
|
||||
// version: 4
|
||||
|
||||
@font-face {
|
||||
font-family: 'Dos Amazigh';
|
||||
@@ -66,6 +66,7 @@ body {
|
||||
width: 50%;
|
||||
max-height: 30%;
|
||||
overflow-y: auto;
|
||||
scrollbar-width: none;
|
||||
color: white;
|
||||
font-family: monospace;
|
||||
font-size: 12px;
|
||||
@@ -77,17 +78,25 @@ body {
|
||||
|
||||
#messages-container {
|
||||
position: absolute;
|
||||
font-family: Arial, sans-serif;
|
||||
font-size: 14px;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
max-height: 30%;
|
||||
overflow-y: auto;
|
||||
scrollbar-width: none;
|
||||
background-color: rgba(0, 0, 0, 0.1);
|
||||
padding: 10px 10px 40px;
|
||||
z-index: 2;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
#debug-info::-webkit-scrollbar,
|
||||
#messages-container::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.splash-message {
|
||||
margin-bottom: 8px;
|
||||
padding: 6px 10px;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: kb-app-demo-desktop/frontend/ts/demo_backfill.ts
|
||||
// version: 7
|
||||
// version: 8
|
||||
|
||||
import * as bootstrap from "bootstrap";
|
||||
import "simplebar";
|
||||
@@ -210,6 +210,17 @@ async function cancelBackfill(): Promise<void> {
|
||||
|
||||
async function loadOptions(): Promise<void> {
|
||||
const options = await invoke<DemoBackfillOptionsPayload>("demo_backfill_options");
|
||||
const optionsWithPrograms = options as DemoBackfillOptionsPayload & {
|
||||
programs: Array<{ code: string; programId: string }>;
|
||||
};
|
||||
const programOptions = element<HTMLDataListElement>("#programAddressOptions");
|
||||
programOptions.replaceChildren();
|
||||
for (const program of optionsWithPrograms.programs) {
|
||||
const option = document.createElement("option");
|
||||
option.value = program.programId;
|
||||
option.label = program.code;
|
||||
programOptions.append(option);
|
||||
}
|
||||
const roleSelect = element<HTMLSelectElement>("#backfillRoleSelect");
|
||||
roleSelect.replaceChildren();
|
||||
for (const role of options.roles) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// file: kb-app-demo-desktop/frontend/ts/splash.ts
|
||||
// version: 2
|
||||
// version: 3
|
||||
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
import { listen } from "@tauri-apps/api/event";
|
||||
import type { SplashOrder } from "./bindings/kb_app_demo_desktop/splash/SplashOrder.ts";
|
||||
|
||||
@@ -105,6 +106,7 @@ async function initializeSplash(): Promise<void> {
|
||||
await listen<SplashOrder>("splash", event => {
|
||||
void handleSplashOrder(event.payload);
|
||||
});
|
||||
await invoke("splash_frontend_ready");
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: kb-app-demo-desktop/src/app_state.rs
|
||||
// version: 9
|
||||
// version: 10
|
||||
|
||||
//! Shared Tauri application state and startup initialization.
|
||||
|
||||
@@ -23,6 +23,7 @@ pub(crate) struct AppState {
|
||||
demo_decode_replay_campaign_id: std::sync::Mutex<std::option::Option<std::string::String>>,
|
||||
demo_execution_solana_core_running: std::sync::atomic::AtomicBool,
|
||||
demo_execution_solana_core_cancel_requested: std::sync::atomic::AtomicBool,
|
||||
startup_sequence_started: std::sync::atomic::AtomicBool,
|
||||
}
|
||||
|
||||
impl crate::AppState {
|
||||
@@ -68,6 +69,7 @@ impl crate::AppState {
|
||||
demo_decode_replay_campaign_id: std::sync::Mutex::new(std::option::Option::None),
|
||||
demo_execution_solana_core_running: std::sync::atomic::AtomicBool::new(false),
|
||||
demo_execution_solana_core_cancel_requested: std::sync::atomic::AtomicBool::new(false),
|
||||
startup_sequence_started: std::sync::atomic::AtomicBool::new(false),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -172,6 +174,19 @@ impl crate::AppState {
|
||||
return &self.demo_execution_solana_core_cancel_requested;
|
||||
}
|
||||
|
||||
/// Starts the splash startup sequence at most once.
|
||||
pub(crate) fn begin_startup_sequence(&self) -> bool {
|
||||
return self
|
||||
.startup_sequence_started
|
||||
.compare_exchange(
|
||||
false,
|
||||
true,
|
||||
std::sync::atomic::Ordering::AcqRel,
|
||||
std::sync::atomic::Ordering::Acquire,
|
||||
)
|
||||
.is_ok();
|
||||
}
|
||||
|
||||
/// Returns the number of logging routes held by the logging guard.
|
||||
pub(crate) fn logging_route_count(&self) -> usize {
|
||||
let lock_result = self.logging_guard.lock();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: kb-app-demo-desktop/src/demo_backfill.rs
|
||||
// version: 11
|
||||
// version: 12
|
||||
|
||||
//! Tauri commands and UI payloads for bounded HTTP transaction backfills.
|
||||
|
||||
@@ -20,6 +20,20 @@ pub(crate) struct DemoBackfillRoleOption {
|
||||
pub(crate) providers: std::vec::Vec<std::string::String>,
|
||||
}
|
||||
|
||||
/// One known program suggested by the free-form Program ID field.
|
||||
#[derive(Clone, Debug, serde::Serialize, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(
|
||||
export,
|
||||
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_backfill/DemoBackfillProgramOption.ts"
|
||||
)]
|
||||
pub(crate) struct DemoBackfillProgramOption {
|
||||
/// Stable program code.
|
||||
pub(crate) code: std::string::String,
|
||||
/// Canonical program identifier.
|
||||
pub(crate) program_id: std::string::String,
|
||||
}
|
||||
|
||||
/// Initial options shown by the backfill demo.
|
||||
#[derive(Clone, Debug, serde::Serialize, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
@@ -30,6 +44,8 @@ pub(crate) struct DemoBackfillRoleOption {
|
||||
pub(crate) struct DemoBackfillOptionsPayload {
|
||||
/// Selectable endpoint roles.
|
||||
pub(crate) roles: std::vec::Vec<crate::DemoBackfillRoleOption>,
|
||||
/// Known programs offered as non-blocking autocomplete suggestions.
|
||||
pub(crate) programs: std::vec::Vec<crate::DemoBackfillProgramOption>,
|
||||
/// Preferred role when configured.
|
||||
pub(crate) default_role: std::option::Option<std::string::String>,
|
||||
/// Default transaction commitment.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: kb-app-demo-desktop/src/demo_decode_replay.rs
|
||||
// version: 28
|
||||
// version: 29
|
||||
|
||||
//! Tauri commands and UI payloads for contextual instruction decode replay.
|
||||
|
||||
@@ -720,22 +720,22 @@ mod tests {
|
||||
fn native_memo_token2022_elgamal_classic_token_and_ata_decoders_are_registered() {
|
||||
let decoders = crate::available_decoders();
|
||||
assert_eq!(decoders.len(), 7);
|
||||
let names = decoders
|
||||
let mut names = decoders
|
||||
.iter()
|
||||
.map(|decoder| return decoder.identity().name)
|
||||
.collect::<std::vec::Vec<_>>();
|
||||
assert_eq!(
|
||||
names,
|
||||
std::vec![
|
||||
"solana_native_classifier".to_string(),
|
||||
"spl_associated_token_account".to_string(),
|
||||
"spl_elgamal_registry".to_string(),
|
||||
"spl_memo".to_string(),
|
||||
"spl_token".to_string(),
|
||||
"spl_token2022".to_string(),
|
||||
"metadata_metaplex_token_metadata".to_string(),
|
||||
]
|
||||
);
|
||||
names.sort();
|
||||
let mut expected_names = std::vec![
|
||||
"metadata_metaplex_token_metadata".to_string(),
|
||||
"solana_native_classifier".to_string(),
|
||||
"spl_associated_token_account".to_string(),
|
||||
"spl_elgamal_registry".to_string(),
|
||||
"spl_memo".to_string(),
|
||||
"spl_token".to_string(),
|
||||
"spl_token2022".to_string(),
|
||||
];
|
||||
expected_names.sort();
|
||||
assert_eq!(names, expected_names);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: kb-app-demo-desktop/src/demo_sql_common.rs
|
||||
// version: 8
|
||||
// version: 9
|
||||
|
||||
//! Shared SQL demo helpers and serializable payloads.
|
||||
|
||||
@@ -201,25 +201,23 @@ pub(crate) async fn initialize_postgres_schema_for_startup(
|
||||
return;
|
||||
},
|
||||
};
|
||||
if !options.auto_initialize_schema {
|
||||
emit_sql_startup_splash(
|
||||
splash_window,
|
||||
"PostgreSQL schema auto-initialization disabled.",
|
||||
"info",
|
||||
false,
|
||||
);
|
||||
let masked_dsn = options.masked_dsn();
|
||||
tracing::debug!(target: crate::TRACING_TARGET, dsn = masked_dsn.as_str(), "PostgreSQL schema auto-initialization disabled");
|
||||
return;
|
||||
}
|
||||
let initialize_schema = options.auto_initialize_schema;
|
||||
let masked_dsn = options.masked_dsn();
|
||||
options.auto_initialize_schema = false;
|
||||
emit_sql_startup_splash(
|
||||
splash_window,
|
||||
"Checking PostgreSQL raw/core/decode tables...",
|
||||
"Connexion PostgreSQL et vérification des tables raw/core/decode...",
|
||||
"info",
|
||||
true,
|
||||
false,
|
||||
);
|
||||
if !initialize_schema {
|
||||
emit_sql_startup_splash(
|
||||
splash_window,
|
||||
"Auto-initialisation désactivée : vérification sans création de table.",
|
||||
"info",
|
||||
true,
|
||||
);
|
||||
}
|
||||
tracing::debug!(target: crate::TRACING_TARGET, dsn = masked_dsn.as_str(), "start PostgreSQL schema initialization");
|
||||
let store_result = kb_store::PostgresStore::connect(options).await;
|
||||
let store = match store_result {
|
||||
@@ -241,12 +239,14 @@ pub(crate) async fn initialize_postgres_schema_for_startup(
|
||||
return;
|
||||
},
|
||||
};
|
||||
let initialize_result = store.initialize_store_schema().await;
|
||||
if let std::result::Result::Err(error) = initialize_result {
|
||||
let error_message = error.to_string();
|
||||
emit_sql_startup_error(splash_window, error_message.as_str());
|
||||
tracing::error!(target: crate::TRACING_TARGET, error = error_message.as_str(), "PostgreSQL store schema initialization failed");
|
||||
return;
|
||||
if initialize_schema {
|
||||
let initialize_result = store.initialize_store_schema().await;
|
||||
if let std::result::Result::Err(error) = initialize_result {
|
||||
let error_message = error.to_string();
|
||||
emit_sql_startup_error(splash_window, error_message.as_str());
|
||||
tracing::error!(target: crate::TRACING_TARGET, error = error_message.as_str(), "PostgreSQL store schema initialization failed");
|
||||
return;
|
||||
}
|
||||
}
|
||||
let after_result = store.known_table_diagnostics().await;
|
||||
let after_tables = match after_result {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: kb-app-demo-desktop/src/demo_ws.rs
|
||||
// version: 14
|
||||
// version: 16
|
||||
|
||||
//! Standard Solana WebSocket demo commands backed by `kb_onchain_transport::WsSession`.
|
||||
|
||||
@@ -431,17 +431,26 @@ fn spawn_demo_ws_event_bridge(
|
||||
};
|
||||
match event {
|
||||
kb_onchain_transport::WsSessionEvent::Notification { notification, .. } => {
|
||||
let compact_payload = match serde_json::to_string(¬ification) {
|
||||
std::result::Result::Ok(payload) => payload,
|
||||
std::result::Result::Err(error) => {
|
||||
format!("cannot serialize typed WebSocket notification: {error}")
|
||||
},
|
||||
};
|
||||
tracing::debug!(
|
||||
target: crate::TRACING_TARGET,
|
||||
payload = %compact_payload,
|
||||
"demo WebSocket notification received"
|
||||
);
|
||||
if rate_limiter.should_emit(&app_handle) {
|
||||
let payload = match serde_json::to_string_pretty(¬ification) {
|
||||
let ui_payload = match serde_json::to_string_pretty(¬ification) {
|
||||
std::result::Result::Ok(payload) => payload,
|
||||
std::result::Result::Err(error) => {
|
||||
format!("cannot serialize typed WebSocket notification: {error}")
|
||||
},
|
||||
std::result::Result::Err(_) => compact_payload,
|
||||
};
|
||||
emit_demo_ws_message(
|
||||
&app_handle,
|
||||
"notification",
|
||||
truncate_payload(payload),
|
||||
truncate_payload(ui_payload),
|
||||
);
|
||||
}
|
||||
},
|
||||
@@ -640,20 +649,27 @@ fn build_standard_ws_request(
|
||||
std::result::Result::Ok(target) => target,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let config = match parse_optional_json_as::<
|
||||
let mut config = match parse_optional_json_as::<
|
||||
kb_onchain_transport::WsAccountSubscribeConfig,
|
||||
>(config_json, "configJson")
|
||||
{
|
||||
std::result::Result::Ok(config) => config,
|
||||
std::result::Result::Ok(config) => config.unwrap_or_default(),
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
if config.encoding.is_none() {
|
||||
config.encoding =
|
||||
std::option::Option::Some(kb_onchain_transport::RpcAccountEncoding::Base64);
|
||||
}
|
||||
if let std::result::Result::Err(error) =
|
||||
reject_present(filter_json, "filterJson", method)
|
||||
{
|
||||
return std::result::Result::Err(error);
|
||||
}
|
||||
std::result::Result::Ok(kb_onchain_transport::StandardWsRequest::Account(
|
||||
kb_onchain_transport::AccountSubscribeRequest { pubkey: target, config },
|
||||
kb_onchain_transport::AccountSubscribeRequest {
|
||||
pubkey: target,
|
||||
config: std::option::Option::Some(config),
|
||||
},
|
||||
))
|
||||
},
|
||||
"blockSubscribe" => {
|
||||
@@ -710,15 +726,22 @@ fn build_standard_ws_request(
|
||||
{
|
||||
return std::result::Result::Err(error);
|
||||
}
|
||||
let config = match parse_optional_json_as::<
|
||||
let mut config = match parse_optional_json_as::<
|
||||
kb_onchain_transport::WsProgramSubscribeConfig,
|
||||
>(config_json, "configJson")
|
||||
{
|
||||
std::result::Result::Ok(config) => config,
|
||||
std::result::Result::Ok(config) => config.unwrap_or_default(),
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
if config.encoding.is_none() {
|
||||
config.encoding =
|
||||
std::option::Option::Some(kb_onchain_transport::RpcAccountEncoding::Base64);
|
||||
}
|
||||
std::result::Result::Ok(kb_onchain_transport::StandardWsRequest::Program(
|
||||
kb_onchain_transport::ProgramSubscribeRequest { program_id: target, config },
|
||||
kb_onchain_transport::ProgramSubscribeRequest {
|
||||
program_id: target,
|
||||
config: std::option::Option::Some(config),
|
||||
},
|
||||
))
|
||||
},
|
||||
"signatureSubscribe" => {
|
||||
@@ -879,6 +902,42 @@ fn truncate_payload(payload: std::string::String) -> std::string::String {
|
||||
mod tests {
|
||||
use ts_rs::TS; // rust-rules: derive-import
|
||||
|
||||
#[test]
|
||||
fn account_and_program_subscriptions_default_to_base64_without_overriding_explicit_encoding() {
|
||||
let account = super::build_standard_ws_request(
|
||||
"accountSubscribe",
|
||||
std::option::Option::Some("11111111111111111111111111111111".to_string()),
|
||||
std::option::Option::None,
|
||||
std::option::Option::None,
|
||||
);
|
||||
let account = match account {
|
||||
std::result::Result::Ok(kb_onchain_transport::StandardWsRequest::Account(value)) => {
|
||||
value
|
||||
},
|
||||
_ => panic!("accountSubscribe request was not built"),
|
||||
};
|
||||
assert_eq!(
|
||||
account.config.and_then(|value| return value.encoding),
|
||||
std::option::Option::Some(kb_onchain_transport::RpcAccountEncoding::Base64)
|
||||
);
|
||||
let program = super::build_standard_ws_request(
|
||||
"programSubscribe",
|
||||
std::option::Option::Some(kb_program_ids::SPL_TOKEN2022_PROGRAM_ID.to_string()),
|
||||
std::option::Option::None,
|
||||
std::option::Option::Some(r#"{"encoding":"jsonParsed"}"#.to_string()),
|
||||
);
|
||||
let program = match program {
|
||||
std::result::Result::Ok(kb_onchain_transport::StandardWsRequest::Program(value)) => {
|
||||
value
|
||||
},
|
||||
_ => panic!("programSubscribe request was not built"),
|
||||
};
|
||||
assert_eq!(
|
||||
program.config.and_then(|value| return value.encoding),
|
||||
std::option::Option::Some(kb_onchain_transport::RpcAccountEncoding::JsonParsed)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tauri_numeric_bindings_use_json_compatible_numbers() {
|
||||
let config = ts_rs::Config::default();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: kb-app-demo-desktop/src/lib.rs
|
||||
// version: 9
|
||||
// version: 10
|
||||
|
||||
//! Tauri desktop demo application for `khadhroony-bot3`.
|
||||
|
||||
@@ -39,6 +39,8 @@ pub(crate) use self::app_state::AppState;
|
||||
pub(crate) use self::demo_backfill::DemoBackfillObserver;
|
||||
/// Initial options shown by the backfill window.
|
||||
pub(crate) use self::demo_backfill::DemoBackfillOptionsPayload;
|
||||
/// One known program suggested by the backfill Program ID autocomplete.
|
||||
pub(crate) use self::demo_backfill::DemoBackfillProgramOption;
|
||||
/// One progress event emitted to the backfill window.
|
||||
pub(crate) use self::demo_backfill::DemoBackfillProgressPayload;
|
||||
/// UI request for one bounded backfill campaign.
|
||||
@@ -327,6 +329,8 @@ pub(crate) use self::splash::SPLASH_MINIMUM_MS;
|
||||
pub(crate) use self::splash::SplashOrder;
|
||||
/// Emits one order to the splash frontend.
|
||||
pub(crate) use self::splash::emit_splash_order;
|
||||
/// Runs the startup sequence after the splash frontend is ready.
|
||||
pub(crate) use self::splash::splash_frontend_ready_inner;
|
||||
/// Waits for the minimum splash duration.
|
||||
pub(crate) use self::splash::wait_until_minimum;
|
||||
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
// file: kb-app-demo-desktop/src/splash.rs
|
||||
// version: 3
|
||||
// version: 4
|
||||
|
||||
//! Splash-window payloads and startup sequencing helpers.
|
||||
|
||||
use tauri::Emitter; // rust-rules: trait-import
|
||||
use tauri::Manager; // rust-rules: trait-import
|
||||
use ts_rs::TS; // rust-rules: derive-import
|
||||
|
||||
/// Minimum splash duration before the main window is shown.
|
||||
pub(crate) const SPLASH_MINIMUM_MS: u64 = 3100;
|
||||
pub(crate) const SPLASH_MINIMUM_MS: u64 = 12100;
|
||||
/// Splash fade duration.
|
||||
pub(crate) const SPLASH_FADE_MS: u32 = 3000;
|
||||
pub(crate) const SPLASH_FADE_MS: u32 = 12000;
|
||||
/// Delay after fade-out before destroying the splash window.
|
||||
pub(crate) const SPLASH_CLOSE_WAIT_MS: u64 = 3100;
|
||||
pub(crate) const SPLASH_CLOSE_WAIT_MS: u64 = 12100;
|
||||
|
||||
/// Command sent from Rust to the splash frontend.
|
||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize, TS)]
|
||||
@@ -50,6 +51,96 @@ pub(crate) fn emit_splash_order(
|
||||
}
|
||||
}
|
||||
|
||||
/// Runs the startup sequence after the splash frontend installed its listener.
|
||||
pub(crate) async fn splash_frontend_ready_inner(
|
||||
app: tauri::AppHandle,
|
||||
window: tauri::WebviewWindow,
|
||||
state: &crate::AppState,
|
||||
) -> std::result::Result<(), std::string::String> {
|
||||
if window.label() != "splash" {
|
||||
return std::result::Result::Err(
|
||||
"splash readiness may only originate from splash".to_string(),
|
||||
);
|
||||
}
|
||||
if !state.begin_startup_sequence() {
|
||||
return std::result::Result::Ok(());
|
||||
}
|
||||
let main_window = match app.get_webview_window("main") {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => {
|
||||
return std::result::Result::Err("main window is missing".to_string());
|
||||
},
|
||||
};
|
||||
let started_at = tokio::time::Instant::now();
|
||||
crate::emit_splash_order(
|
||||
&window,
|
||||
"fadein",
|
||||
std::option::Option::None,
|
||||
std::option::Option::None,
|
||||
std::option::Option::Some(crate::SPLASH_FADE_MS),
|
||||
);
|
||||
crate::emit_splash_order(
|
||||
&window,
|
||||
"add_msg",
|
||||
std::option::Option::Some("Chargement de la configuration terminé."),
|
||||
std::option::Option::Some("success"),
|
||||
std::option::Option::None,
|
||||
);
|
||||
crate::emit_splash_order(
|
||||
&window,
|
||||
"add_log",
|
||||
std::option::Option::Some(
|
||||
format!("Profil actif : {}", state.active_profile().name).as_str(),
|
||||
),
|
||||
std::option::Option::None,
|
||||
std::option::Option::None,
|
||||
);
|
||||
crate::emit_splash_order(
|
||||
&window,
|
||||
"add_msg",
|
||||
std::option::Option::Some(
|
||||
format!("Logging initialisé : {} routes actives.", state.logging_route_count())
|
||||
.as_str(),
|
||||
),
|
||||
std::option::Option::Some("success"),
|
||||
std::option::Option::None,
|
||||
);
|
||||
crate::emit_splash_order(
|
||||
&window,
|
||||
"add_msg",
|
||||
std::option::Option::Some("Pool HTTP initialisé."),
|
||||
std::option::Option::Some("success"),
|
||||
std::option::Option::None,
|
||||
);
|
||||
crate::initialize_postgres_schema_for_startup(state, &window).await;
|
||||
crate::emit_splash_order(
|
||||
&window,
|
||||
"add_msg",
|
||||
std::option::Option::Some("Initialisation terminée."),
|
||||
std::option::Option::Some("success"),
|
||||
std::option::Option::None,
|
||||
);
|
||||
crate::wait_until_minimum(started_at, crate::SPLASH_MINIMUM_MS).await;
|
||||
crate::emit_splash_order(
|
||||
&window,
|
||||
"fadeout",
|
||||
std::option::Option::None,
|
||||
std::option::Option::None,
|
||||
std::option::Option::Some(crate::SPLASH_FADE_MS),
|
||||
);
|
||||
tokio::time::sleep(std::time::Duration::from_millis(crate::SPLASH_CLOSE_WAIT_MS)).await;
|
||||
if let std::result::Result::Err(error) = main_window.show() {
|
||||
return std::result::Result::Err(format!("cannot show main window: {error:?}"));
|
||||
}
|
||||
if let std::result::Result::Err(error) = main_window.set_focus() {
|
||||
return std::result::Result::Err(format!("cannot focus main window: {error:?}"));
|
||||
}
|
||||
if let std::result::Result::Err(error) = window.destroy() {
|
||||
return std::result::Result::Err(format!("cannot destroy splash window: {error:?}"));
|
||||
}
|
||||
return std::result::Result::Ok(());
|
||||
}
|
||||
|
||||
/// Waits until the configured minimum splash duration has elapsed.
|
||||
pub(crate) async fn wait_until_minimum(start: tokio::time::Instant, minimum_ms: u64) {
|
||||
let minimum = std::time::Duration::from_millis(minimum_ms);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: kb-app-demo-desktop/src/tauri.rs
|
||||
// version: 10
|
||||
// version: 12
|
||||
|
||||
//! Tauri runtime assembly and private command wrappers.
|
||||
|
||||
@@ -36,6 +36,7 @@ pub fn run() -> kb_core::Result<()> {
|
||||
builder = builder.manage(app_state);
|
||||
builder = builder.invoke_handler(tauri::generate_handler![
|
||||
emit_frontend_log,
|
||||
splash_frontend_ready,
|
||||
load_project_readme,
|
||||
open_demo_backfill_window,
|
||||
demo_backfill_options,
|
||||
@@ -104,99 +105,18 @@ pub fn run() -> kb_core::Result<()> {
|
||||
});
|
||||
builder = builder.plugin(tracing_builder.build::<tauri::Wry>());
|
||||
builder = builder.setup(|app| {
|
||||
let splash_window = match app.get_webview_window("splash") {
|
||||
std::option::Option::Some(window) => window,
|
||||
std::option::Option::None => {
|
||||
return std::result::Result::Err(std::boxed::Box::new(std::io::Error::new(
|
||||
std::io::ErrorKind::NotFound,
|
||||
"splash window is missing",
|
||||
)));
|
||||
},
|
||||
};
|
||||
let main_window = match app.get_webview_window("main") {
|
||||
std::option::Option::Some(window) => window,
|
||||
std::option::Option::None => {
|
||||
return std::result::Result::Err(std::boxed::Box::new(std::io::Error::new(
|
||||
std::io::ErrorKind::NotFound,
|
||||
"main window is missing",
|
||||
)));
|
||||
},
|
||||
};
|
||||
let app_handle = app.handle().clone();
|
||||
tauri::async_runtime::spawn(async move {
|
||||
let started_at = tokio::time::Instant::now();
|
||||
crate::emit_splash_order(
|
||||
&splash_window,
|
||||
"add_log",
|
||||
std::option::Option::Some("Configuration chargée"),
|
||||
std::option::Option::None,
|
||||
std::option::Option::None,
|
||||
);
|
||||
crate::emit_splash_order(
|
||||
&splash_window,
|
||||
"add_log",
|
||||
std::option::Option::Some("Sous-système de logs initialisé"),
|
||||
std::option::Option::None,
|
||||
std::option::Option::None,
|
||||
);
|
||||
crate::emit_splash_order(
|
||||
&splash_window,
|
||||
"add_log",
|
||||
std::option::Option::Some("Pool HTTP initialisé"),
|
||||
std::option::Option::None,
|
||||
std::option::Option::None,
|
||||
);
|
||||
let state = app_handle.state::<crate::AppState>();
|
||||
crate::initialize_postgres_schema_for_startup(&state, &splash_window).await;
|
||||
crate::emit_splash_order(
|
||||
&splash_window,
|
||||
"fadein",
|
||||
std::option::Option::None,
|
||||
std::option::Option::None,
|
||||
std::option::Option::Some(crate::SPLASH_FADE_MS),
|
||||
);
|
||||
crate::emit_splash_order(
|
||||
&splash_window,
|
||||
"add_msg",
|
||||
std::option::Option::Some("Initialisation..."),
|
||||
std::option::Option::Some("info"),
|
||||
std::option::Option::None,
|
||||
);
|
||||
crate::emit_splash_order(
|
||||
&splash_window,
|
||||
"add_msg",
|
||||
std::option::Option::Some("Loading complete..."),
|
||||
std::option::Option::Some("success"),
|
||||
std::option::Option::None,
|
||||
);
|
||||
crate::wait_until_minimum(started_at, crate::SPLASH_MINIMUM_MS).await;
|
||||
if cfg!(debug_assertions) {
|
||||
crate::emit_splash_order(
|
||||
&splash_window,
|
||||
"add_log",
|
||||
std::option::Option::Some("Start Fade-out"),
|
||||
std::option::Option::None,
|
||||
std::option::Option::None,
|
||||
);
|
||||
}
|
||||
crate::emit_splash_order(
|
||||
&splash_window,
|
||||
"fadeout",
|
||||
std::option::Option::None,
|
||||
std::option::Option::None,
|
||||
std::option::Option::Some(crate::SPLASH_FADE_MS),
|
||||
);
|
||||
tokio::time::sleep(std::time::Duration::from_millis(crate::SPLASH_CLOSE_WAIT_MS)).await;
|
||||
if let std::result::Result::Err(error) = splash_window.destroy() {
|
||||
tracing::error!(target: crate::TRACING_TARGET, "cannot destroy splash window: {error:?}");
|
||||
}
|
||||
if let std::result::Result::Err(error) = main_window.show() {
|
||||
tracing::error!(target: crate::TRACING_TARGET, "cannot show main window: {error:?}");
|
||||
}
|
||||
if let std::result::Result::Err(error) = main_window.set_focus() {
|
||||
tracing::error!(target: crate::TRACING_TARGET, "cannot focus main window: {error:?}");
|
||||
}
|
||||
});
|
||||
if app.get_webview_window("splash").is_none() {
|
||||
return std::result::Result::Err(std::boxed::Box::new(std::io::Error::new(
|
||||
std::io::ErrorKind::NotFound,
|
||||
"splash window is missing",
|
||||
)));
|
||||
}
|
||||
if app.get_webview_window("main").is_none() {
|
||||
return std::result::Result::Err(std::boxed::Box::new(std::io::Error::new(
|
||||
std::io::ErrorKind::NotFound,
|
||||
"main window is missing",
|
||||
)));
|
||||
}
|
||||
return std::result::Result::Ok(());
|
||||
});
|
||||
let run_result = builder.run(tauri::generate_context!());
|
||||
@@ -208,6 +128,15 @@ pub fn run() -> kb_core::Result<()> {
|
||||
};
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn splash_frontend_ready(
|
||||
app: tauri::AppHandle,
|
||||
window: tauri::WebviewWindow,
|
||||
state: tauri::State<'_, crate::AppState>,
|
||||
) -> std::result::Result<(), std::string::String> {
|
||||
return crate::splash_frontend_ready_inner(app, window, &state).await;
|
||||
}
|
||||
|
||||
fn install_default_rustls_provider() -> kb_core::Result<()> {
|
||||
if rustls::crypto::CryptoProvider::get_default().is_some() {
|
||||
return std::result::Result::Ok(());
|
||||
@@ -759,8 +688,18 @@ fn demo_backfill_options(
|
||||
} else {
|
||||
roles.first().map(|item| return item.role.clone())
|
||||
};
|
||||
let programs = kb_program_ids::registered_program_ids()
|
||||
.iter()
|
||||
.map(|entry| {
|
||||
return crate::DemoBackfillProgramOption {
|
||||
code: entry.code().to_string(),
|
||||
program_id: entry.program_id().to_string(),
|
||||
};
|
||||
})
|
||||
.collect();
|
||||
return crate::DemoBackfillOptionsPayload {
|
||||
roles,
|
||||
programs,
|
||||
default_role,
|
||||
default_commitment: "confirmed".to_string(),
|
||||
default_page_size: 100,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: kb-config/src/settings.rs
|
||||
// version: 15
|
||||
// version: 16
|
||||
|
||||
//! Typed configuration models shared by applications and workers.
|
||||
|
||||
@@ -1349,13 +1349,18 @@ mod tests {
|
||||
("info.log", "info", "human"),
|
||||
("error.jsonl", "error", "json"),
|
||||
] {
|
||||
let expected_path = format!("logs/{directory}/{crate_name}/{suffix}");
|
||||
let route_directory = if crate_name == "kb-pipeline-demo-scenarios" {
|
||||
"kb-pipeline"
|
||||
} else {
|
||||
crate_name.as_str()
|
||||
};
|
||||
let expected_path = format!("logs/{directory}/{route_directory}/{suffix}");
|
||||
let route_exists = profile.logging.targets.iter().any(|target| {
|
||||
return target.enabled
|
||||
&& target.path == expected_path
|
||||
&& target.level == level
|
||||
&& target.format == format
|
||||
&& target.targets == std::vec![crate_name.clone()];
|
||||
&& target.targets.iter().any(|value| return value == crate_name);
|
||||
});
|
||||
assert!(
|
||||
route_exists,
|
||||
|
||||
Reference in New Issue
Block a user