v0.1.0-pre.059

This commit is contained in:
2026-07-27 10:02:26 +02:00
parent e828677201
commit 626385f9b6
10 changed files with 219 additions and 408 deletions

View File

@@ -1,5 +1,5 @@
<!-- file: KHADHROONY_BOT3_MIGRATION_CLOSURE_TODO.md -->
<!-- version: 7 -->
<!-- version: 8 -->
# Checklist détaillée de clôture de la migration `khadhroony-bot3`
@@ -109,18 +109,19 @@
### 2.4 Journaux généraux et viewers
- [ ] Vérifier Backfill HTTP.
- [ ] Vérifier Extraction core.
- [x] Vérifier Backfill HTTP en runtime.
- [ ] Vérifier Extraction core en runtime après correction du gabarit pleine largeur.
- [ ] Vérifier Exécution Solana Core.
- [ ] Vérifier Exécution SPL.
- [ ] Vérifier Decode replay.
- [ ] Vérifier HTTP.
- [ ] Vérifier WebSocket.
- [ ] Placer les paramètres dans les accordéons.
- [ ] Placer les journaux et résultats globaux sous les accordéons.
- [x] Vérifier HTTP en runtime.
- [x] Vérifier WebSocket en runtime.
- [x] Placer les paramètres dExtraction core dans les accordéons.
- [x] Placer les journaux et résultats globaux dExtraction core sous les accordéons.
- [ ] Réserver le viewer JSON au JSON structuré.
- [ ] Utiliser des textareas pour texte brut, logs et diagnostics concaténés.
- [ ] Fixer la hauteur de tous les viewers JSON.
- [x] Fixer la hauteur du viewer JSON dExtraction core.
- [ ] Étendre la vérification des journaux/viewers aux fenêtres Exécution Solana Core, Exécution SPL et Decode replay.
### 2.5 Fenêtres SQL
@@ -170,11 +171,27 @@
## 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 ladaptation des arguments et la délégation.
- [x] Inventorier toutes les fonctions `#[tauri::command]` de `tauri.rs` et mesurer les blocs de 20 lignes ou plus.
- [x] Déplacer options, exécution et annulation dExtraction core dans `demo_core_extraction.rs`.
- [x] Mutualiser les ouvertures de fenêtres via un helper privé strictement Tauri dans `tauri.rs`.
- [ ] Déplacer progressivement la logique Decode replay, Backfill, SQL replay et Exécution hors de `tauri.rs`.
- [x] Vérifier les wrappers Extraction core et Splash.
- [ ] Vérifier les autres wrappers Tauri après leur migration progressive.
- [ ] Ajouter ou conserver les tests unitaires dans les modules fonctionnels, pas dans les wrappers.
## 5.2 Câblage des scénarios Devnet
- [x] Confirmer que `kb-app-demo-desktop` appelle `kb-pipeline-demo-scenarios` pour System transfer.
- [x] Confirmer le câblage Memo.
- [x] Confirmer le câblage SPL Token classique.
- [x] Confirmer le câblage SPL ATA.
- [x] Confirmer le câblage SPL Token-2022.
- [x] Confirmer que ces workflows ne sont pas dupliqués dans `kb-pipeline`.
- [ ] Vérifier que chaque option HTML correspond à un scénario backend accessible.
- [ ] Vérifier simulation, envoi, progression, résumé et diagnostics en runtime Devnet.
- [ ] Vérifier le replay post-exécution et la matérialisation pour chaque famille.
- [ ] Raccorder ou documenter le workflow `execute_devnet_spl_token_lifecycle`, présent dans la crate mais non exposé par lapplication desktop.
## 6. Cycle de vie WebSocket
- [x] Vérifier quaucun pool WebSocket nest créé au démarrage.
@@ -324,10 +341,10 @@
- [ ] `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/113 passaient ; contrat dordre de décodeurs corrigé, relance requise).
- [x] `cargo test -p kb-app-demo-desktop` : 115 tests réussis.
- [x] `cargo check --workspace`.
- [ ] `cargo test --workspace`.
- [ ] `python3 scripts/audit_rust_workspace_rules.py`.
- [x] `python3 scripts/audit_rust_workspace_rules.py` après `pre.058-fix-002`.
- [ ] `cargo tauri dev -c kb-app-demo-desktop/tauri.conf.json`.
- [ ] `cargo clippy --all-targets` en dernier.

View File

@@ -29,6 +29,7 @@ Toute divergence avec une règle générale doit être explicitement documentée
- Corriger le type Rust/TS-rs source puis régénérer les bindings ; ne pas considérer une modification manuelle isolée dun fichier généré comme un correctif durable.
- `kb-app-demo-desktop/src/tauri.rs` contient les attributs `#[tauri::command]`, lenregistrement 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 ladaptation des handles/states/arguments, lappel 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`.
- Les helpers privés de `tauri.rs` sont autorisés lorsquils mutualisent exclusivement une adaptation Tauri, par exemple louverture, laffichage, le focus ou lémission vers une fenêtre ; ils ne doivent contenir aucune logique métier, requête SQL ou orchestration de pipeline.
## Règles d'architecture

View File

@@ -1,5 +1,5 @@
<!-- file: kb-app-demo-desktop/frontend/demo_core_extraction.html -->
<!-- version: 7 -->
<!-- version: 8 -->
<!DOCTYPE html>
<html lang="fr">
@@ -34,14 +34,16 @@
<main class="app-main">
<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 col-xl-5">
<div class="card shadow-sm border-0 mb-4">
<div class="row g-4">
<div class="col-12">
<div class="card shadow-sm border-0">
<div class="card-body">
<h1 class="h3 card-title">Extraction transactionnelle canonical vers core</h1>
<p class="text-body-secondary mb-0">Chaque transaction canonique sélectionnée est validée, normalisée dans les tables core et enregistrée dans le ledger de traitement au sein d'une seule transaction PostgreSQL.</p>
</div>
</div>
</div>
<div class="col-12">
<div class="accordion shadow-sm" id="coreExtractionAccordion">
<div class="accordion-item">
<h2 class="accordion-header" id="coreExtractionSettingsHeading">
@@ -127,15 +129,22 @@
</div>
</div>
<div class="card shadow-sm border-0 mt-4">
</div>
<div class="col-12">
<div class="card shadow-sm border-0">
<div class="card-body">
<h2 class="h5 mb-3">Journal et résultat</h2>
<div class="d-flex justify-content-between align-items-center mb-2">
<h3 class="h6 mb-0">Journal</h3>
<div class="row g-4">
<div class="col-12 col-xl-7">
<h3 class="h6">Journal</h3>
<textarea class="form-control font-monospace" id="coreExtractionLogOutput" readonly="" style="height: 28rem; overflow-y: auto;">Aucune campagne exécutée.</textarea>
</div>
<textarea class="form-control font-monospace mb-3" id="coreExtractionLogOutput" readonly="" rows="15">Aucune campagne exécutée.</textarea>
<div class="col-12 col-xl-5">
<h3 class="h6">Résumé JSON</h3>
<textarea class="form-control font-monospace" data-json-viewer="true" id="coreExtractionSummaryOutput" readonly="" rows="16">Aucun résultat.</textarea>
<textarea class="form-control font-monospace" data-json-viewer="true" id="coreExtractionSummaryOutput" readonly="" style="height: 28rem; overflow-y: auto;">Aucun résultat.</textarea>
</div>
</div>
</div>
</div>
</div>
</div>

View File

@@ -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 col-xl-5">
<div class="col-12 col-xl-7">
<div class="card shadow-sm border-0 mb-4">
<div class="card-body">
<h1 class="h4 mb-3">Sélection bornée</h1>

View File

@@ -1,5 +1,5 @@
// file: kb-app-demo-desktop/src/demo_core_extraction.rs
// version: 9
// version: 10
//! Tauri commands and UI payloads for canonical transaction to core extraction.
@@ -158,6 +158,70 @@ impl std::ops::Drop for crate::DemoCoreExtractionRunGuard<'_> {
}
}
pub(crate) fn demo_core_extraction_options_payload(
state: &crate::AppState,
) -> crate::DemoCoreExtractionOptionsPayload {
return crate::DemoCoreExtractionOptionsPayload {
processor_version: kb_pipeline::CORE_EXTRACTION_PROCESSOR_VERSION.to_string(),
default_limit: 100,
default_max_concurrent_extractions: 4,
running: state.demo_core_extraction_running().load(std::sync::atomic::Ordering::Acquire),
};
}
pub(crate) async fn execute_demo_core_extraction(
app_handle: tauri::AppHandle,
state: &crate::AppState,
request: crate::DemoCoreExtractionRequest,
) -> std::result::Result<crate::DemoCoreExtractionSummaryPayload, std::string::String> {
let acquire_result = state.demo_core_extraction_running().compare_exchange(
false,
true,
std::sync::atomic::Ordering::AcqRel,
std::sync::atomic::Ordering::Acquire,
);
if acquire_result.is_err() {
return std::result::Result::Err(
"a core extraction campaign is already running".to_string(),
);
}
let _run_guard = crate::DemoCoreExtractionRunGuard {
running: state.demo_core_extraction_running(),
};
state
.demo_core_extraction_cancel_requested()
.store(false, std::sync::atomic::Ordering::Release);
let pipeline_request_result = crate::build_demo_core_extraction_pipeline_request(request);
let pipeline_request = match pipeline_request_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let store_result = crate::connect_postgres_store(state.active_profile()).await;
let store = match store_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let observer = crate::DemoCoreExtractionObserver {
app_handle,
cancel_requested: state.demo_core_extraction_cancel_requested(),
};
let summary_result =
kb_pipeline::execute_core_extraction(&store, &pipeline_request, &observer).await;
let summary = match summary_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
return std::result::Result::Ok(crate::demo_core_extraction_summary_payload(summary));
}
pub(crate) fn cancel_demo_core_extraction(state: &crate::AppState) -> bool {
let running = state.demo_core_extraction_running().load(std::sync::atomic::Ordering::Acquire);
state
.demo_core_extraction_cancel_requested()
.store(true, std::sync::atomic::Ordering::Release);
return running;
}
pub(crate) fn build_demo_core_extraction_pipeline_request(
request: crate::DemoCoreExtractionRequest,
) -> std::result::Result<kb_pipeline::CoreExtractionRequest, std::string::String> {

View File

@@ -1,9 +1,8 @@
// file: kb-app-demo-desktop/src/demo_sql_common.rs
// version: 9
// version: 10
//! Shared SQL demo helpers and serializable payloads.
use tauri::Manager; // rust-rules: trait-import
use ts_rs::TS; // rust-rules: derive-import
/// UI-safe table diagnostics shown by SQL demo windows.
@@ -35,58 +34,6 @@ pub(crate) struct DemoSqlTableSnapshot {
pub(crate) latest_created_at: std::option::Option<std::string::String>,
}
/// Opens or focuses a SQL demo window.
pub(crate) fn open_sql_demo_window(
app_handle: tauri::AppHandle,
label: &str,
html_path: &str,
title: &str,
) -> std::result::Result<(), std::string::String> {
tracing::info!(target: crate::TRACING_TARGET, window = label, "open SQL demo window");
let existing_window = app_handle.get_webview_window(label);
if let std::option::Option::Some(window) = existing_window {
let show_result = window.show();
if let std::result::Result::Err(error) = show_result {
return std::result::Result::Err(
kb_core::Error::tauri(format!("cannot show {label} window: {error}")).to_string(),
);
}
let focus_result = window.set_focus();
if let std::result::Result::Err(error) = focus_result {
return std::result::Result::Err(
kb_core::Error::tauri(format!("cannot focus {label} window: {error}")).to_string(),
);
}
return std::result::Result::Ok(());
}
let builder = tauri::WebviewWindowBuilder::new(
&app_handle,
label,
tauri::WebviewUrl::App(html_path.into()),
)
.title(title)
.inner_size(1500.0, 960.0)
.min_inner_size(1024.0, 768.0)
.resizable(true)
.visible(true);
let build_result = builder.build();
return match build_result {
std::result::Result::Ok(window) => {
let focus_result = window.set_focus();
if let std::result::Result::Err(error) = focus_result {
return std::result::Result::Err(
kb_core::Error::tauri(format!("cannot focus created {label} window: {error}"))
.to_string(),
);
}
std::result::Result::Ok(())
},
std::result::Result::Err(error) => std::result::Result::Err(
kb_core::Error::tauri(format!("cannot create {label} window: {error}")).to_string(),
),
};
}
/// Builds PostgreSQL options from the active profile without coupling kb-store to kb-config.
pub(crate) fn postgres_store_options_from_profile(
profile: &kb_config::ProfileConfig,

View File

@@ -76,7 +76,13 @@ pub(crate) use self::demo_core_extraction::DemoCoreExtractionSummaryPayload;
/// Internal demo core extraction item.
pub(crate) use self::demo_core_extraction::build_demo_core_extraction_pipeline_request;
/// Internal demo core extraction item.
pub(crate) use self::demo_core_extraction::cancel_demo_core_extraction;
/// Internal demo core extraction item.
pub(crate) use self::demo_core_extraction::demo_core_extraction_options_payload;
/// Internal demo core extraction item.
pub(crate) use self::demo_core_extraction::demo_core_extraction_summary_payload;
/// Internal demo core extraction item.
pub(crate) use self::demo_core_extraction::execute_demo_core_extraction;
/// Internal demo decode replay item.
pub(crate) use self::demo_decode_replay::DemoDecodeCoverageSummaryPayload;
/// Internal demo decode replay item.
@@ -231,8 +237,6 @@ pub(crate) use self::demo_sql_common::connect_postgres_store;
pub(crate) use self::demo_sql_common::debug_status;
/// Initializes the PostgreSQL schema during startup.
pub(crate) use self::demo_sql_common::initialize_postgres_schema_for_startup;
/// Opens or focuses one SQL demo window.
pub(crate) use self::demo_sql_common::open_sql_demo_window;
/// Builds PostgreSQL options from the active profile.
pub(crate) use self::demo_sql_common::postgres_store_options_from_profile;
/// Converts one PostgreSQL table diagnostic.

View File

@@ -171,8 +171,8 @@ mod tests {
#[test]
fn splash_timing_is_ordered() {
assert_eq!(crate::SPLASH_MINIMUM_MS, 3100);
assert_eq!(crate::SPLASH_FADE_MS, 3000);
assert_eq!(crate::SPLASH_MINIMUM_MS, 12100);
assert_eq!(crate::SPLASH_FADE_MS, 12000);
assert!(crate::SPLASH_CLOSE_WAIT_MS >= u64::from(crate::SPLASH_FADE_MS));
}
}

View File

@@ -1,5 +1,5 @@
// file: kb-app-demo-desktop/src/tauri.rs
// version: 12
// version: 14
//! Tauri runtime assembly and private command wrappers.
@@ -7,6 +7,7 @@ use tauri::Manager; // rust-rules: trait-import
/// Runs the desktop demo application.
#[cfg_attr(mobile, tauri::mobile_entry_point)]
#[allow(clippy::question_mark_used)]
pub fn run() -> kb_core::Result<()> {
let rustls_result = install_default_rustls_provider();
if let std::result::Result::Err(error) = rustls_result {
@@ -149,156 +150,95 @@ fn install_default_rustls_provider() -> kb_core::Result<()> {
)),
};
}
/// Opens or focuses the canonical to core extraction demo window.
#[tauri::command]
fn open_demo_core_extraction_window(
fn open_or_focus_demo_window(
app_handle: tauri::AppHandle,
label: &str,
html_path: &str,
title: &str,
) -> std::result::Result<(), std::string::String> {
tracing::info!(target: crate::TRACING_TARGET, "open core extraction demo window");
let existing_window = app_handle.get_webview_window("demo_core_extraction");
if let std::option::Option::Some(window) = existing_window {
let show_result = window.show();
if let std::result::Result::Err(error) = show_result {
return std::result::Result::Err(error.to_string());
tracing::info!(target: crate::TRACING_TARGET, action = "open_window", window = label, "open demo window");
if let std::option::Option::Some(window) = app_handle.get_webview_window(label) {
if let std::result::Result::Err(error) = window.show() {
return std::result::Result::Err(
kb_core::Error::tauri(format!("cannot show {label} window: {error}")).to_string(),
);
}
let focus_result = window.set_focus();
if let std::result::Result::Err(error) = focus_result {
return std::result::Result::Err(error.to_string());
if let std::result::Result::Err(error) = window.set_focus() {
return std::result::Result::Err(
kb_core::Error::tauri(format!("cannot focus {label} window: {error}")).to_string(),
);
}
return std::result::Result::Ok(());
}
let build_result = tauri::WebviewWindowBuilder::new(
&app_handle,
"demo_core_extraction",
tauri::WebviewUrl::App("demo_core_extraction.html".into()),
label,
tauri::WebviewUrl::App(html_path.into()),
)
.title("Extraction canonical vers core")
.title(title)
.inner_size(1500.0, 960.0)
.min_inner_size(1024.0, 768.0)
.resizable(true)
.visible(true)
.build();
return match build_result {
std::result::Result::Ok(window) => {
let focus_result = window.set_focus();
if let std::result::Result::Err(error) = focus_result {
return std::result::Result::Err(error.to_string());
}
std::result::Result::Ok(())
std::result::Result::Ok(window) => match window.set_focus() {
std::result::Result::Ok(()) => std::result::Result::Ok(()),
std::result::Result::Err(error) => std::result::Result::Err(
kb_core::Error::tauri(format!("cannot focus created {label} window: {error}"))
.to_string(),
),
},
std::result::Result::Err(error) => std::result::Result::Err(error.to_string()),
std::result::Result::Err(error) => std::result::Result::Err(
kb_core::Error::tauri(format!("cannot create {label} window: {error}")).to_string(),
),
};
}
/// Returns the initial core extraction demo options.
#[tauri::command]
fn open_demo_core_extraction_window(
app_handle: tauri::AppHandle,
) -> std::result::Result<(), std::string::String> {
return open_or_focus_demo_window(
app_handle,
"demo_core_extraction",
"demo_core_extraction.html",
"Extraction canonical vers core",
);
}
#[tauri::command]
fn demo_core_extraction_options(
state: tauri::State<'_, crate::AppState>,
) -> crate::DemoCoreExtractionOptionsPayload {
return crate::DemoCoreExtractionOptionsPayload {
processor_version: kb_pipeline::CORE_EXTRACTION_PROCESSOR_VERSION.to_string(),
default_limit: 100,
default_max_concurrent_extractions: 4,
running: state.demo_core_extraction_running().load(std::sync::atomic::Ordering::Acquire),
};
return crate::demo_core_extraction_options_payload(&state);
}
/// Executes one bounded canonical transaction to core extraction campaign.
#[tauri::command]
async fn demo_core_extraction_execute(
app_handle: tauri::AppHandle,
state: tauri::State<'_, crate::AppState>,
request: crate::DemoCoreExtractionRequest,
) -> std::result::Result<crate::DemoCoreExtractionSummaryPayload, std::string::String> {
let acquire_result = state.demo_core_extraction_running().compare_exchange(
false,
true,
std::sync::atomic::Ordering::AcqRel,
std::sync::atomic::Ordering::Acquire,
);
if acquire_result.is_err() {
return std::result::Result::Err(
"a core extraction campaign is already running".to_string(),
);
}
let _run_guard = crate::DemoCoreExtractionRunGuard {
running: state.demo_core_extraction_running(),
};
state
.demo_core_extraction_cancel_requested()
.store(false, std::sync::atomic::Ordering::Release);
let pipeline_request_result = crate::build_demo_core_extraction_pipeline_request(request);
let pipeline_request = match pipeline_request_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let store_result = crate::connect_postgres_store(state.active_profile()).await;
let store = match store_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let observer = crate::DemoCoreExtractionObserver {
app_handle,
cancel_requested: state.demo_core_extraction_cancel_requested(),
};
let summary_result =
kb_pipeline::execute_core_extraction(&store, &pipeline_request, &observer).await;
let summary = match summary_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
return std::result::Result::Ok(crate::demo_core_extraction_summary_payload(summary));
return crate::execute_demo_core_extraction(app_handle, &state, request).await;
}
/// Requests cooperative cancellation of the current core extraction campaign.
#[tauri::command]
fn demo_core_extraction_cancel(state: tauri::State<'_, crate::AppState>) -> bool {
let running = state.demo_core_extraction_running().load(std::sync::atomic::Ordering::Acquire);
state
.demo_core_extraction_cancel_requested()
.store(true, std::sync::atomic::Ordering::Release);
return running;
return crate::cancel_demo_core_extraction(&state);
}
/// Opens or focuses the contextual decode replay window.
#[tauri::command]
fn open_demo_decode_replay_window(
app_handle: tauri::AppHandle,
) -> std::result::Result<(), std::string::String> {
tracing::info!(target: crate::TRACING_TARGET, action = "open_window", window = "demo_decode_replay", "open contextual decode replay window");
let existing_window = app_handle.get_webview_window("demo_decode_replay");
if let std::option::Option::Some(window) = existing_window {
let show_result = window.show();
if let std::result::Result::Err(error) = show_result {
return std::result::Result::Err(error.to_string());
}
let focus_result = window.set_focus();
if let std::result::Result::Err(error) = focus_result {
return std::result::Result::Err(error.to_string());
}
return std::result::Result::Ok(());
}
let build_result = tauri::WebviewWindowBuilder::new(
&app_handle,
return open_or_focus_demo_window(
app_handle,
"demo_decode_replay",
tauri::WebviewUrl::App("demo_decode_replay.html".into()),
)
.title("Décodage et matérialisation")
.inner_size(1500.0, 960.0)
.min_inner_size(1024.0, 768.0)
.resizable(true)
.visible(true)
.build();
return match build_result {
std::result::Result::Ok(window) => {
let focus_result = window.set_focus();
if let std::result::Result::Err(error) = focus_result {
return std::result::Result::Err(error.to_string());
}
std::result::Result::Ok(())
},
std::result::Result::Err(error) => std::result::Result::Err(error.to_string()),
};
"demo_decode_replay.html",
"Décodage et matérialisation",
);
}
/// Returns available decoders and default replay bounds.
@@ -584,33 +524,12 @@ async fn demo_decode_replay_annotations(
fn open_demo_config_window(
app_handle: tauri::AppHandle,
) -> std::result::Result<(), std::string::String> {
if let std::option::Option::Some(window) = app_handle.get_webview_window("demo_config") {
if let std::result::Result::Err(error) = window.show() {
return std::result::Result::Err(error.to_string());
}
if let std::result::Result::Err(error) = window.set_focus() {
return std::result::Result::Err(error.to_string());
}
return std::result::Result::Ok(());
}
let builder = tauri::WebviewWindowBuilder::new(
&app_handle,
return open_or_focus_demo_window(
app_handle,
"demo_config",
tauri::WebviewUrl::App("demo_config.html".into()),
)
.title("Configuration")
.inner_size(1500.0, 960.0)
.min_inner_size(1024.0, 768.0);
let window = match builder.build() {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
return std::result::Result::Err(error.to_string());
},
};
if let std::result::Result::Err(error) = window.set_focus() {
return std::result::Result::Err(error.to_string());
}
return std::result::Result::Ok(());
"demo_config.html",
"Configuration",
);
}
#[tauri::command]
@@ -648,34 +567,12 @@ fn demo_backfill_cancel(state: tauri::State<'_, crate::AppState>) -> bool {
fn open_demo_backfill_window(
app_handle: tauri::AppHandle,
) -> std::result::Result<(), std::string::String> {
let existing_window = app_handle.get_webview_window("demo_backfill");
if let std::option::Option::Some(window) = existing_window {
if let std::result::Result::Err(error) = window.show() {
return std::result::Result::Err(error.to_string());
}
if let std::result::Result::Err(error) = window.set_focus() {
return std::result::Result::Err(error.to_string());
}
return std::result::Result::Ok(());
}
let build_result = tauri::WebviewWindowBuilder::new(
&app_handle,
return open_or_focus_demo_window(
app_handle,
"demo_backfill",
tauri::WebviewUrl::App("demo_backfill.html".into()),
)
.title("Backfill HTTP")
.inner_size(1500.0, 960.0)
.min_inner_size(1024.0, 768.0)
.resizable(true)
.visible(true)
.build();
return match build_result {
std::result::Result::Ok(window) => match window.set_focus() {
std::result::Result::Ok(()) => std::result::Result::Ok(()),
std::result::Result::Err(error) => std::result::Result::Err(error.to_string()),
},
std::result::Result::Err(error) => std::result::Result::Err(error.to_string()),
};
"demo_backfill.html",
"Backfill HTTP",
);
}
#[tauri::command]
@@ -711,7 +608,6 @@ fn demo_backfill_options(
}
#[tauri::command]
#[allow(clippy::question_mark_used)]
async fn demo_backfill_execute(
app_handle: tauri::AppHandle,
state: tauri::State<'_, crate::AppState>,
@@ -773,34 +669,7 @@ async fn demo_backfill_execute(
fn open_demo_http_window(
app_handle: tauri::AppHandle,
) -> std::result::Result<(), std::string::String> {
let existing_window = app_handle.get_webview_window("demo_http");
if let std::option::Option::Some(window) = existing_window {
if let std::result::Result::Err(error) = window.show() {
return std::result::Result::Err(error.to_string());
}
if let std::result::Result::Err(error) = window.set_focus() {
return std::result::Result::Err(error.to_string());
}
return std::result::Result::Ok(());
}
let build_result = tauri::WebviewWindowBuilder::new(
&app_handle,
"demo_http",
tauri::WebviewUrl::App("demo_http.html".into()),
)
.title("HTTP JSON-RPC")
.inner_size(1500.0, 960.0)
.min_inner_size(1024.0, 768.0)
.resizable(true)
.visible(true)
.build();
return match build_result {
std::result::Result::Ok(window) => match window.set_focus() {
std::result::Result::Ok(()) => std::result::Result::Ok(()),
std::result::Result::Err(error) => std::result::Result::Err(error.to_string()),
},
std::result::Result::Err(error) => std::result::Result::Err(error.to_string()),
};
return open_or_focus_demo_window(app_handle, "demo_http", "demo_http.html", "HTTP JSON-RPC");
}
#[tauri::command]
@@ -819,7 +688,6 @@ fn demo_http_options(state: tauri::State<'_, crate::AppState>) -> crate::DemoHtt
}
#[tauri::command]
#[allow(clippy::question_mark_used)]
async fn demo_http_execute_request(
state: tauri::State<'_, crate::AppState>,
request: crate::DemoHttpRequest,
@@ -827,48 +695,11 @@ async fn demo_http_execute_request(
return crate::demo_http_execute_request_inner(state, request).await;
}
/// Opens or focuses the WebSocket demo window.
#[tauri::command]
fn open_demo_ws_window(
app_handle: tauri::AppHandle,
) -> std::result::Result<(), std::string::String> {
tracing::info!(target: crate::TRACING_TARGET, "open_demo_ws_window");
if let std::option::Option::Some(window) = app_handle.get_webview_window("demo_ws") {
if let std::result::Result::Err(error) = window.show() {
return std::result::Result::Err(
kb_core::Error::tauri(format!("cannot show demo_ws window: {error}")).to_string(),
);
}
if let std::result::Result::Err(error) = window.set_focus() {
return std::result::Result::Err(
kb_core::Error::tauri(format!("cannot focus demo_ws window: {error}")).to_string(),
);
}
return std::result::Result::Ok(());
}
let build_result = tauri::WebviewWindowBuilder::new(
&app_handle,
"demo_ws",
tauri::WebviewUrl::App("demo_ws.html".into()),
)
.title("WebSocket standard")
.inner_size(1500.0, 960.0)
.min_inner_size(1024.0, 768.0)
.resizable(true)
.visible(true)
.build();
return match build_result {
std::result::Result::Ok(window) => match window.set_focus() {
std::result::Result::Ok(()) => std::result::Result::Ok(()),
std::result::Result::Err(error) => std::result::Result::Err(
kb_core::Error::tauri(format!("cannot focus created demo_ws window: {error}"))
.to_string(),
),
},
std::result::Result::Err(error) => std::result::Result::Err(
kb_core::Error::tauri(format!("cannot create demo_ws window: {error}")).to_string(),
),
};
return open_or_focus_demo_window(app_handle, "demo_ws", "demo_ws.html", "WebSocket standard");
}
/// Lists WebSocket endpoints available through the configured pool.
@@ -902,7 +733,6 @@ fn demo_ws_options(
/// Returns the current WebSocket demo session status.
#[tauri::command]
#[allow(clippy::question_mark_used)]
async fn demo_ws_status(
state: tauri::State<'_, crate::AppState>,
) -> std::result::Result<crate::DemoWsStatusPayload, std::string::String> {
@@ -911,7 +741,6 @@ async fn demo_ws_status(
/// Connects if needed, then subscribes through `kb_onchain_transport::WsSession`.
#[tauri::command]
#[allow(clippy::question_mark_used)]
async fn demo_ws_connect(
app_handle: tauri::AppHandle,
state: tauri::State<'_, crate::AppState>,
@@ -922,7 +751,6 @@ async fn demo_ws_connect(
/// Unsubscribes one subscription while keeping the WebSocket connection open.
#[tauri::command]
#[allow(clippy::question_mark_used)]
async fn demo_ws_unsubscribe(
app_handle: tauri::AppHandle,
state: tauri::State<'_, crate::AppState>,
@@ -934,19 +762,17 @@ async fn demo_ws_unsubscribe(
/// Disconnects the current persistent WebSocket demo session.
#[tauri::command]
#[allow(clippy::question_mark_used)]
async fn demo_ws_disconnect(
state: tauri::State<'_, crate::AppState>,
) -> std::result::Result<crate::DemoWsStatusPayload, std::string::String> {
return crate::demo_ws_disconnect_inner(state.inner()).await;
}
/// Opens or focuses the SQL diagnostic demo window.
#[tauri::command]
fn open_demo_sql_diag_window(
app_handle: tauri::AppHandle,
) -> std::result::Result<(), std::string::String> {
return crate::open_sql_demo_window(
return open_or_focus_demo_window(
app_handle,
"demo_sql_diag",
"demo_sql_diag.html",
@@ -956,7 +782,6 @@ fn open_demo_sql_diag_window(
/// Loads read-only SQL diagnostics from the active profile.
#[tauri::command]
#[allow(clippy::question_mark_used)]
async fn load_demo_sql_diag(
state: tauri::State<'_, crate::AppState>,
) -> std::result::Result<crate::DemoSqlDiagPayload, std::string::String> {
@@ -991,12 +816,11 @@ async fn load_demo_sql_diag(
});
}
/// Opens or focuses the PostgreSQL canonical acquisition demo window.
#[tauri::command]
fn open_demo_sql_pg_raw_window(
app_handle: tauri::AppHandle,
) -> std::result::Result<(), std::string::String> {
return crate::open_sql_demo_window(
return open_or_focus_demo_window(
app_handle,
"demo_sql_pg_raw",
"demo_sql_pg_raw.html",
@@ -1006,7 +830,6 @@ fn open_demo_sql_pg_raw_window(
/// Loads read-only raw table diagnostics from PostgreSQL.
#[tauri::command]
#[allow(clippy::question_mark_used)]
async fn load_demo_sql_pg_raw(
state: tauri::State<'_, crate::AppState>,
) -> std::result::Result<crate::DemoSqlPgRawPayload, std::string::String> {
@@ -1028,12 +851,11 @@ async fn load_demo_sql_pg_raw(
});
}
/// Opens or focuses the PostgreSQL core store demo window.
#[tauri::command]
fn open_demo_sql_pg_core_window(
app_handle: tauri::AppHandle,
) -> std::result::Result<(), std::string::String> {
return crate::open_sql_demo_window(
return open_or_focus_demo_window(
app_handle,
"demo_sql_pg_core",
"demo_sql_pg_core.html",
@@ -1043,7 +865,6 @@ fn open_demo_sql_pg_core_window(
/// Loads read-only core table diagnostics from PostgreSQL.
#[tauri::command]
#[allow(clippy::question_mark_used)]
async fn load_demo_sql_pg_core(
state: tauri::State<'_, crate::AppState>,
) -> std::result::Result<crate::DemoSqlPgCorePayload, std::string::String> {
@@ -1065,12 +886,11 @@ async fn load_demo_sql_pg_core(
});
}
/// Opens or focuses the SQL replay candidate browser.
#[tauri::command]
fn open_demo_sql_replay_candidates_window(
app_handle: tauri::AppHandle,
) -> std::result::Result<(), std::string::String> {
return crate::open_sql_demo_window(
return open_or_focus_demo_window(
app_handle,
"demo_sql_replay_candidates",
"demo_sql_replay_candidates.html",
@@ -1080,7 +900,6 @@ fn open_demo_sql_replay_candidates_window(
/// Writes a bounded replay-candidate CSV file into `./data/exports_csv/`.
#[tauri::command]
#[allow(clippy::question_mark_used)]
async fn export_demo_sql_replay_csv(
file_name: std::string::String,
content: std::string::String,
@@ -1127,7 +946,6 @@ async fn export_demo_sql_replay_csv(
/// Loads static browser options from the active PostgreSQL profile.
#[tauri::command]
#[allow(clippy::question_mark_used)]
async fn demo_sql_replay_options(
state: tauri::State<'_, crate::AppState>,
) -> std::result::Result<crate::DemoSqlReplayOptionsPayload, std::string::String> {
@@ -1155,7 +973,6 @@ async fn demo_sql_replay_options(
/// Loads bounded transaction replay candidates from PostgreSQL.
#[tauri::command]
#[allow(clippy::question_mark_used)]
async fn load_demo_sql_replay_transactions(
state: tauri::State<'_, crate::AppState>,
request: crate::DemoSqlReplayTransactionRequest,
@@ -1207,7 +1024,6 @@ async fn load_demo_sql_replay_transactions(
/// Loads bounded program summaries from PostgreSQL.
#[tauri::command]
#[allow(clippy::question_mark_used)]
async fn load_demo_sql_replay_programs(
state: tauri::State<'_, crate::AppState>,
request: crate::DemoSqlReplayProgramRequest,
@@ -1238,7 +1054,6 @@ async fn load_demo_sql_replay_programs(
/// Loads bounded mint, owner or account-key summaries from PostgreSQL core tables.
#[tauri::command]
#[allow(clippy::question_mark_used)]
async fn load_demo_sql_replay_entities(
state: tauri::State<'_, crate::AppState>,
request: crate::DemoSqlReplayEntityRequest,
@@ -1275,44 +1090,16 @@ async fn load_demo_sql_replay_entities(
return std::result::Result::Ok(output);
}
/// Opens or focuses the Solana Core execution demo window.
#[tauri::command]
fn open_demo_execution_solana_core_window(
app_handle: tauri::AppHandle,
) -> std::result::Result<(), std::string::String> {
let existing_window = app_handle.get_webview_window("demo_execution_solana_core");
if let std::option::Option::Some(window) = existing_window {
let show_result = window.show();
if let std::result::Result::Err(error) = show_result {
return std::result::Result::Err(error.to_string());
}
let focus_result = window.set_focus();
if let std::result::Result::Err(error) = focus_result {
return std::result::Result::Err(error.to_string());
}
return std::result::Result::Ok(());
}
let build_result = tauri::WebviewWindowBuilder::new(
&app_handle,
return open_or_focus_demo_window(
app_handle,
"demo_execution_solana_core",
tauri::WebviewUrl::App("demo_execution_solana_core.html".into()),
)
.title("Exécution Solana Devnet")
.inner_size(1500.0, 960.0)
.min_inner_size(1024.0, 768.0)
.resizable(true)
.visible(true)
.build();
return match build_result {
std::result::Result::Ok(window) => {
let focus_result = window.set_focus();
if let std::result::Result::Err(error) = focus_result {
return std::result::Result::Err(error.to_string());
}
std::result::Result::Ok(())
},
std::result::Result::Err(error) => std::result::Result::Err(error.to_string()),
};
"demo_execution_solana_core.html",
"Exécution Solana Devnet",
);
}
/// Returns compatible Devnet profiles and conservative defaults.
@@ -1349,7 +1136,6 @@ fn demo_execution_solana_core_generate_recipient()
/// Executes one bounded System transfer simulation or Devnet submission.
#[tauri::command]
#[allow(clippy::question_mark_used)]
async fn demo_execution_solana_core_execute(
app_handle: tauri::AppHandle,
state: tauri::State<'_, crate::AppState>,
@@ -1436,7 +1222,6 @@ async fn demo_execution_solana_core_execute(
/// Executes one Memo v4 simulation or explicitly authorized Devnet submission.
#[tauri::command]
#[allow(clippy::question_mark_used)]
async fn demo_execution_spl_memo_execute(
app_handle: tauri::AppHandle,
state: tauri::State<'_, crate::AppState>,
@@ -1566,50 +1351,20 @@ fn demo_execution_spl_validation_scenarios()
.collect();
}
/// Opens or focuses the dedicated SPL execution window.
#[tauri::command]
fn open_demo_execution_spl_window(
app_handle: tauri::AppHandle,
) -> std::result::Result<(), std::string::String> {
tracing::info!(target: crate::TRACING_TARGET, "open SPL Devnet execution window");
let existing_window = app_handle.get_webview_window("demo_execution_spl");
if let std::option::Option::Some(window) = existing_window {
let show_result = window.show();
if let std::result::Result::Err(error) = show_result {
return std::result::Result::Err(error.to_string());
}
let focus_result = window.set_focus();
if let std::result::Result::Err(error) = focus_result {
return std::result::Result::Err(error.to_string());
}
return std::result::Result::Ok(());
}
let build_result = tauri::WebviewWindowBuilder::new(
&app_handle,
return open_or_focus_demo_window(
app_handle,
"demo_execution_spl",
tauri::WebviewUrl::App("demo_execution_spl.html".into()),
)
.title("Exécution SPL Devnet")
.inner_size(1500.0, 960.0)
.min_inner_size(1024.0, 768.0)
.resizable(true)
.visible(true)
.build();
return match build_result {
std::result::Result::Ok(window) => {
let focus_result = window.set_focus();
if let std::result::Result::Err(error) = focus_result {
return std::result::Result::Err(error.to_string());
}
std::result::Result::Ok(())
},
std::result::Result::Err(error) => std::result::Result::Err(error.to_string()),
};
"demo_execution_spl.html",
"Exécution SPL Devnet",
);
}
/// Executes one checked SPL Token simulation or explicitly authorized Devnet submission.
#[tauri::command]
#[allow(clippy::question_mark_used)]
async fn demo_execution_spl_token_execute(
app_handle: tauri::AppHandle,
state: tauri::State<'_, crate::AppState>,
@@ -1704,7 +1459,6 @@ async fn demo_execution_spl_token_execute(
/// Loads a bounded, typed journal of committed classic SPL Token projections.
#[tauri::command]
#[allow(clippy::question_mark_used)]
async fn demo_spl_token_journal(
state: tauri::State<'_, crate::AppState>,
request: crate::DemoSplTokenJournalRequest,
@@ -1763,7 +1517,6 @@ async fn demo_spl_token_journal(
/// Derives the representative profile wallet ATA without exposing private key material.
#[tauri::command]
#[allow(clippy::question_mark_used)]
async fn demo_spl_ata_derive(
state: tauri::State<'_, crate::AppState>,
request: crate::DemoSplAtaDerivationRequest,
@@ -1800,7 +1553,6 @@ async fn demo_spl_ata_derive(
/// Executes one representative Create or CreateIdempotent ATA flow.
#[tauri::command]
#[allow(clippy::question_mark_used)]
async fn demo_execution_spl_ata_execute(
app_handle: tauri::AppHandle,
state: tauri::State<'_, crate::AppState>,
@@ -1910,7 +1662,6 @@ async fn demo_execution_spl_ata_execute(
/// Loads a bounded journal of ATA-owned lifecycle facts.
#[tauri::command]
#[allow(clippy::question_mark_used)]
async fn demo_spl_ata_journal(
state: tauri::State<'_, crate::AppState>,
request: crate::DemoSplAtaJournalRequest,
@@ -1958,7 +1709,6 @@ async fn demo_spl_ata_journal(
/// Executes one public Token-2022 simulation or explicitly authorized Devnet submission.
#[tauri::command]
#[allow(clippy::question_mark_used)]
async fn demo_execution_spl_token2022_execute(
app_handle: tauri::AppHandle,
state: tauri::State<'_, crate::AppState>,

View File

@@ -359,6 +359,25 @@ Documenter la décision.
---
### État validé après `0.1.0-pre.058`
- `cargo test -p kb-app-demo-desktop` : 115 tests réussis ;
- `cargo check --workspace` : réussi ;
- audit Rust : propre ;
- fenêtres Configuration, HTTP, WebSocket, Backfill et SQL validées visuellement ;
- persistance WebSocket, limitation UI et encodage Base64 automatique validés ;
- `autoInitializeSchema` reste volontairement à `false` pour `mainnet_research` ;
- le câblage statique de System transfer, Memo, SPL Token, ATA et Token-2022 vers `kb-pipeline-demo-scenarios` est confirmé ;
- `execute_devnet_spl_token_lifecycle` reste à raccorder ou à documenter comme scénario non exposé.
### Tranche Tauri en cours
- les ouvertures de fenêtres doivent utiliser un helper privé commun strictement Tauri ;
- les commandes Extraction core doivent déléguer à `demo_core_extraction.rs` ;
- poursuivre ensuite avec Decode replay, Backfill, SQL replay et Exécution ;
- les helpers de `tauri.rs` sont autorisés uniquement pour ladaptation Tauri mutualisée et ne portent aucune logique métier.
## 10. WebSocket
Comportement obligatoire :