v0.1.4-pre.006

This commit is contained in:
2026-08-16 11:38:07 +02:00
parent abe3f8daa6
commit 34f6f448fb
18 changed files with 669 additions and 50 deletions

View File

@@ -1,12 +1,12 @@
# file: Cargo.toml
# version: 68
# version: 69
[workspace]
resolver = "3"
members = ["crates/ksp-app-config-desk", "crates/ksp-config-lib", "crates/ksp-core-lib", "crates/ksp-logging-lib"]
[workspace.package]
version = "0.1.4-pre.5.fix.1"
version = "0.1.4-pre.6"
edition = "2024"
license = "MIT"
repository = "https://git.sasedev.com/Sasedev/khadhroony-solana-project"
@@ -26,6 +26,7 @@ tokio = { version = "^1.53", default-features = false, features = ["rt", "rt-mul
tauri = { version = "^2.11" }
tauri-build = { version = "^2.6" }
tauri-plugin-tracing = { version = "^0.3" }
ts-rs = { version = "^12.0" }
[workspace.lints.rust]
missing_docs = "warn"

View File

@@ -1,5 +1,5 @@
# file: crates/ksp-app-config-desk/Cargo.toml
# version: 2
# version: 3
[package]
name = "ksp-app-config-desk"
@@ -23,9 +23,13 @@ tauri-build.workspace = true
[dependencies]
fs2.workspace = true
ksp-config-lib = { path = "../ksp-config-lib" }
ksp-core-lib = { path = "../ksp-core-lib" }
ksp-logging-lib = { path = "../ksp-logging-lib" }
serde.workspace = true
tauri.workspace = true
tauri-plugin-tracing.workspace = true
ts-rs.workspace = true
[lints]
workspace = true

View File

@@ -1,5 +1,5 @@
<!-- file: crates/ksp-app-config-desk/README.md -->
<!-- version: 3 -->
<!-- version: 4 -->
# `ksp-app-config-desk`
@@ -27,7 +27,8 @@ Le gabarit desktop actif fournit maintenant :
- SimpleBar + `resize-observer-polyfill` pour le scrolling du shell ;
- `tauri-plugin-tracing` côté Rust et `@fltsci/tauri-plugin-tracing` côté frontend ;
- les ports dédiés `1430` pour Vite HTTP et `1431` pour le WebSocket de développement ;
- la destination frontend externe commune à Tauri et Vite.
- la destination frontend externe commune à Tauri et Vite;
- le bootstrap Config/Logging réel, un `AppState` durable et le premier contrat Tauri/TS-RS `get_app_snapshot`.
Le bridge de logging frontend KSP à target explicite n'est pas encore introduit : il appartient à une tranche dédiée afin de passer par `ksp-logging-lib` et de ne pas figer le target vide du bridge JS générique comme contrat KSP.
@@ -51,13 +52,13 @@ frontend/
└── splash.html
```
Les futurs bindings TS-RS seront générés sous :
Les premiers bindings TS-RS sont générés par les tests `ts-rs` sous :
```text
frontend/ts/bindings/ksp_app_config_desk/...
```
Le répertoire de bindings n'est pas créé artificiellement avant le premier DTO réel.
Les DTO Rust applicatifs restent la source de vérité et les bindings générés ne sont pas versionnés.
## Artefacts frontend
@@ -85,3 +86,16 @@ Le port HTTP est strict. Les futures applications desk utilisent les couples sui
Les bibliothèques consommées par le bundle applicatif sont déclarées sous `dependencies` : `@fltsci/tauri-plugin-tracing`, `@fortawesome/fontawesome-free`, `@tauri-apps/api`, `bootstrap`, `simplebar` et `resize-observer-polyfill`.
Les outils de compilation/développement et déclarations de types appartiennent à `devDependencies`, notamment `@tauri-apps/cli`, `@types/bootstrap`, `@types/node`, `sass-embedded`, `typescript` et `vite`. Les commandes npm directes sont réservées à l'installation ou à la mise à jour de ces dépendances ; le cycle normal de développement et de build est lancé depuis la racine du workspace par Tauri avec la configuration explicite de l'application.
## Bootstrap Config et Logging
Au démarrage, l'application construit `ConfigBootstrapOptions`, `ConfigFileRegistry`, `ConfigDocumentEngine` et `ConfigManagement` à partir des arguments complets du processus. Elle tente ensuite de résoudre le profil Logging par défaut avec un snapshot `ConfigEnvironment` frais.
Si le document Logging, sa résolution environnementale ou l'initialisation du runtime configuré échoue avant installation du subscriber, Config Desk installe un fallback **transitoire** en mémoire : console stderr, niveau `Info`, aucun fichier, aucun span lifecycle. Ce fallback n'est jamais persisté et son diagnostic frontend est limité à `domain`, `code` et `message`.
Le `LoggingGuard` est conservé dans `AppState` pour préparer le hot reload ultérieur.
## Police du splash
Le splash bot3 de référence utilise `DOS_Amazigh.ttf` (`font-family: Dos Amazigh`). Le répertoire `frontend/fonts/` documente la provenance, la destination attendue et l'empreinte de l'asset de référence. Le style/lifecycle final du splash est traité dans la tranche dédiée `pre.008`.

View File

@@ -1,16 +1,15 @@
<!-- file: crates/ksp-app-config-desk/TODO.md -->
<!-- version: 2 -->
<!-- version: 3 -->
# TODO `ksp-app-config-desk`
Le gabarit frontend de base est maintenant en place : Vite/TypeScript/SCSS, Bootstrap, Font Awesome, SimpleBar, resize observer, plugin tracing et ports dédiés.
Le gabarit frontend, le bootstrap Config/Logging, `AppState`, le `LoggingGuard` durable et les premiers DTO TS-RS sont maintenant présents.
Les prochaines tranches doivent encore :
- introduire les DTO TS-RS au premier contrat Tauri réel, avec bindings générés sous `frontend/ts/bindings/` ;
- introduire `AppState`, `ksp-config-lib`, `ksp-logging-lib` et l'ownership durable de `LoggingGuard` ;
- reprendre/refondre `frontend_log.ts` depuis bot3 avec targets KSP whitelistés et émission via `ksp-logging-lib` ;
- finaliser le lifecycle splash configurable sans temporisation environnementale codée en dur ;
- appliquer la police `Dos Amazigh` du gabarit bot3 lorsque l'asset local `DOS_Amazigh.ttf` est présent sous `frontend/fonts/` ;
- développer les panneaux Documents, Profils, Environnement/`.env`, Secrets et Logging ;
- introduire DataTables au premier tableau qui exige tri, filtrage, sélection ou checkbox ;
- démontrer le hot reload Logging et le panneau de test de routage.

View File

@@ -1,11 +1,11 @@
<!-- file: crates/ksp-app-config-desk/USAGE.md -->
<!-- version: 3 -->
<!-- version: 4 -->
# Utilisation de `ksp-app-config-desk`
## État actuel
Le gabarit Rust/Tauri et le frontend Vite/TypeScript/SCSS sont maintenant présents. Les panneaux métier Config restent volontairement absents à ce stade.
Le gabarit Rust/Tauri et le frontend Vite/TypeScript/SCSS sont présents. Le backend initialise désormais Config, le runtime Logging et `AppState`; les panneaux métier Config restent volontairement absents à ce stade.
La fenêtre `splash` est la fenêtre visible au démarrage. La fenêtre `main` est déjà construite et bundlée, mais son lifecycle d'affichage est finalisé dans la tranche splash/shell dédiée afin de ne pas introduire une temporisation codée en dur.
@@ -86,3 +86,16 @@ frontend/ts/bindings/ksp_app_config_desk/...
```
Les bindings sont générés au premier DTO Tauri réel ; aucune structure factice n'est ajoutée uniquement pour créer le répertoire.
## Bootstrap backend
Les arguments `--cfgpath`, `--schemapath` et `--filemap=...` sont transmis tels quels à `ksp-config-lib`. Le profil Logging initial est le `default_profile` de `std.logging.json`.
Si cette configuration ne peut pas être utilisée, l'application doit rester démarrable pour permettre sa réparation : elle utilise alors un fallback Logging console/stderr en mémoire. Le fallback n'écrit aucun fichier de configuration et n'écrase aucune valeur utilisateur.
La commande Tauri `get_app_snapshot` expose un état sûr du bootstrap. Ses types TypeScript sont générés par `cargo test -p ksp-app-config-desk` sous `frontend/ts/bindings/`.
## Asset font du splash
La police bot3 de référence est `DOS_Amazigh.ttf`. Vérifier l'asset local contre l'empreinte documentée dans `frontend/fonts/README.md` avant la tranche de finition du splash.

View File

@@ -0,0 +1,31 @@
<!-- file: crates/ksp-app-config-desk/frontend/fonts/README.md -->
<!-- version: 1 -->
# Fonts du splash
Le gabarit de splash repris de `khadhroony-bot3` utilise la police locale suivante :
```text
DOS_Amazigh.ttf
font-family: Dos Amazigh
```
Source de référence auditée :
```text
kb-app-demo-desktop/frontend/fonts/DOS_Amazigh.ttf
```
Destination KSP attendue :
```text
crates/ksp-app-config-desk/frontend/fonts/DOS_Amazigh.ttf
```
Empreinte SHA-256 de l'asset bot3 audité :
```text
a01ff4b5a0d699db7cefcf5336bf0e379ff6fbf0d096087571a13c8782f9aa02
```
Le fichier est un asset tiers/historique du gabarit et doit être repris depuis la copie bot3 possédée par le projet. `splash.scss` basculera explicitement sur `Dos Amazigh` lorsque l'asset est présent dans le workspace ; le lifecycle visuel complet reste traité dans la tranche splash dédiée.

View File

@@ -0,0 +1,78 @@
// file: crates/ksp-app-config-desk/src/app_state.rs
// version: 1
//! Shared backend state owned by the Tauri application.
struct LoggingRuntimeState {
guard: ksp_logging_lib::LoggingGuard,
active_profile_id: std::option::Option<String>,
generation: u32,
fallback_active: bool,
startup_diagnostic: std::option::Option<crate::CommandErrorDto>,
}
/// Shared Config Desk application state managed by Tauri.
pub(crate) struct AppState {
config_management: ksp_config_lib::ConfigManagement,
logging_runtime: std::sync::Mutex<LoggingRuntimeState>,
}
impl AppState {
/// Initializes Config ownership, the Logging runtime and durable application state.
pub(crate) fn initialize(arguments: &[std::ffi::OsString]) -> ksp_core_lib::Result<Self> {
let config_management = crate::config_management(arguments);
let config_management = match config_management {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let logging_startup = crate::initialize_logging(&config_management);
let logging_startup = match logging_startup {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
return std::result::Result::Ok(Self {
config_management,
logging_runtime: std::sync::Mutex::new(LoggingRuntimeState {
guard: logging_startup.guard,
active_profile_id: logging_startup.active_profile_id,
generation: 1,
fallback_active: logging_startup.fallback_active,
startup_diagnostic: logging_startup.startup_diagnostic,
}),
});
}
/// Builds a safe initial frontend snapshot without exposing resolved secret values or arbitrary error context.
pub(crate) fn snapshot(&self) -> ksp_core_lib::Result<crate::AppSnapshotDto> {
let document_count = self.config_management.engine().registry().descriptors().count();
let document_count = u32::try_from(document_count);
let document_count = match document_count {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
return std::result::Result::Err(
ksp_core_lib::Error::new(crate::ERROR_CODE_APP_STATE_INVALID, "Config registry contains too many descriptors for the desktop DTO")
.with_source(error),
);
},
};
let runtime = self.logging_runtime.lock();
let runtime = match runtime {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => {
return std::result::Result::Err(ksp_core_lib::Error::new(
crate::ERROR_CODE_APP_STATE_LOCK_FAILED,
"Config Desk Logging runtime state lock is poisoned",
));
},
};
let _keep_guard_alive = &runtime.guard;
return std::result::Result::Ok(crate::AppSnapshotDto {
application_version: env!("CARGO_PKG_VERSION").to_owned(),
config_document_count: document_count,
active_logging_profile: runtime.active_profile_id.clone(),
logging_generation: runtime.generation,
fallback_logging_active: runtime.fallback_active,
startup_diagnostic: runtime.startup_diagnostic.clone(),
});
}
}

View File

@@ -0,0 +1,102 @@
// file: crates/ksp-app-config-desk/src/bootstrap.rs
// version: 1
//! Config and Logging bootstrap for the desktop application.
pub(crate) struct LoggingStartup {
pub(crate) guard: ksp_logging_lib::LoggingGuard,
pub(crate) active_profile_id: std::option::Option<String>,
pub(crate) fallback_active: bool,
pub(crate) startup_diagnostic: std::option::Option<crate::CommandErrorDto>,
}
pub(crate) fn config_management(arguments: &[std::ffi::OsString]) -> ksp_core_lib::Result<ksp_config_lib::ConfigManagement> {
let bootstrap = ksp_config_lib::ConfigBootstrapOptions::from_args(arguments);
let bootstrap = match bootstrap {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let registry = ksp_config_lib::ConfigFileRegistry::from_args(arguments);
let registry = match registry {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let engine = ksp_config_lib::ConfigDocumentEngine::new(bootstrap, registry);
return std::result::Result::Ok(ksp_config_lib::ConfigManagement::new(engine));
}
pub(crate) fn initialize_logging(management: &ksp_config_lib::ConfigManagement) -> ksp_core_lib::Result<LoggingStartup> {
let environment = ksp_config_lib::ConfigEnvironment::load();
let environment = match environment {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return initialize_fallback_logging(error),
};
let resolved = management.engine().load_resolved_logging_config(std::option::Option::None, &environment);
let resolved = match resolved {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return initialize_fallback_logging(error),
};
let active_profile_id = resolved.profile_id().to_owned();
let settings = resolved.into_settings();
let guard = ksp_logging_lib::initialize(&settings);
return match guard {
std::result::Result::Ok(guard) => {
ksp_logging_lib::info!(
target: crate::TRACING_TARGET,
domain = crate::TRACING_DOMAIN_BOOTSTRAP,
active_profile = active_profile_id.as_str(),
"initialized Config Desk logging from managed configuration"
);
std::result::Result::Ok(LoggingStartup {
guard,
active_profile_id: std::option::Option::Some(active_profile_id),
fallback_active: false,
startup_diagnostic: std::option::Option::None,
})
},
std::result::Result::Err(error) => initialize_fallback_logging(error),
};
}
fn initialize_fallback_logging(initial_error: ksp_core_lib::Error) -> ksp_core_lib::Result<LoggingStartup> {
let diagnostic = crate::CommandErrorDto::from_error(&initial_error);
let settings = fallback_logging_settings();
let guard = ksp_logging_lib::initialize(&settings);
let guard = match guard {
std::result::Result::Ok(value) => value,
std::result::Result::Err(fallback_error) => {
return std::result::Result::Err(
ksp_core_lib::Error::new(crate::ERROR_CODE_LOGGING_BOOTSTRAP_FAILED, "Cannot initialize Config Desk fallback Logging runtime")
.with_context("initial_error_domain", initial_error.code().domain())
.with_context("initial_error_code", initial_error.code().code())
.with_source(fallback_error),
);
},
};
ksp_logging_lib::warn!(
target: crate::TRACING_TARGET,
domain = crate::TRACING_DOMAIN_BOOTSTRAP,
error_domain = diagnostic.domain.as_str(),
error_code = diagnostic.code.as_str(),
"managed Logging configuration is unavailable; using transient in-memory fallback"
);
return std::result::Result::Ok(LoggingStartup {
guard,
active_profile_id: std::option::Option::None,
fallback_active: true,
startup_diagnostic: std::option::Option::Some(diagnostic),
});
}
pub(crate) fn fallback_logging_settings() -> ksp_logging_lib::LoggingSettings {
return ksp_logging_lib::LoggingSettings::new(
ksp_logging_lib::LogFilterLevel::Info,
ksp_logging_lib::SpanEvents::Off,
std::option::Option::Some(ksp_logging_lib::ConsoleSettings::stderr()),
std::vec::Vec::new(),
);
}
#[cfg(test)]
#[path = "../unit_tests/bootstrap.rs"]
mod tests;

View File

@@ -0,0 +1,9 @@
// file: crates/ksp-app-config-desk/src/constants.rs
// version: 1
//! Application-owned tracing targets and domains.
/// Owning target for backend events emitted by Config Desk.
pub(crate) const TRACING_TARGET: &str = "ksp-app-config-desk";
/// Structured domain used while bootstrapping Config and Logging.
pub(crate) const TRACING_DOMAIN_BOOTSTRAP: &str = "config.bootstrap";

View File

@@ -0,0 +1,54 @@
// file: crates/ksp-app-config-desk/src/dto_common.rs
// version: 1
//! Common Tauri DTOs shared by Config Desk commands.
use ts_rs::TS; // rust-rules: derive-import
/// Safe command error projection that never serializes arbitrary KSP error context or source values.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export, export_to = "../frontend/ts/bindings/ksp_app_config_desk/dto_common/CommandErrorDto.ts")]
pub(crate) struct CommandErrorDto {
/// Stable KSP error domain.
pub(crate) domain: String,
/// Stable KSP error code within the domain.
pub(crate) code: String,
/// Human-readable error message without arbitrary context fields.
pub(crate) message: String,
}
impl CommandErrorDto {
/// Builds a bounded safe projection from a KSP error.
#[must_use]
pub(crate) fn from_error(error: &ksp_core_lib::Error) -> Self {
return Self {
domain: error.code().domain().to_owned(),
code: error.code().code().to_owned(),
message: error.message().to_owned(),
};
}
}
/// Initial application/runtime snapshot exposed to the frontend.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export, export_to = "../frontend/ts/bindings/ksp_app_config_desk/dto_common/AppSnapshotDto.ts")]
pub(crate) struct AppSnapshotDto {
/// Cargo application version.
pub(crate) application_version: String,
/// Number of files registered by the Config registry.
pub(crate) config_document_count: u32,
/// Active configured Logging profile, or `None` while the transient fallback runtime is active.
pub(crate) active_logging_profile: std::option::Option<String>,
/// Monotonic runtime generation, initialized to one after the first Logging runtime install.
pub(crate) logging_generation: u32,
/// Whether Config Desk had to install its transient in-memory Logging fallback.
pub(crate) fallback_logging_active: bool,
/// Safe startup diagnostic that caused fallback Logging, when applicable.
pub(crate) startup_diagnostic: std::option::Option<CommandErrorDto>,
}
#[cfg(test)]
#[path = "../unit_tests/dto_common.rs"]
mod tests;

View File

@@ -1,7 +1,13 @@
// file: crates/ksp-app-config-desk/src/errors.rs
// version: 1
// version: 2
//! Application-local error codes for the configuration desktop shell.
/// Tauri runtime assembly or execution failed.
pub(crate) const ERROR_CODE_TAURI_RUNTIME_FAILED: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config_desk", "tauri_runtime_failed");
/// Config Desk could not install the managed Logging runtime or its safe fallback.
pub(crate) const ERROR_CODE_LOGGING_BOOTSTRAP_FAILED: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config_desk", "logging_bootstrap_failed");
/// Shared Config Desk application state is internally inconsistent.
pub(crate) const ERROR_CODE_APP_STATE_INVALID: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config_desk", "app_state_invalid");
/// Shared Config Desk runtime state cannot be locked safely.
pub(crate) const ERROR_CODE_APP_STATE_LOCK_FAILED: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config_desk", "app_state_lock_failed");

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-app-config-desk/src/lib.rs
// version: 1
// version: 2
//! Tauri desktop application for managing and validating KSP configuration.
@@ -7,10 +7,24 @@
#![deny(unreachable_pub)]
#![warn(missing_docs)]
mod app_state;
mod bootstrap;
mod constants;
mod dto_common;
mod errors;
mod tauri;
/// Runs the KSP configuration desktop application.
pub use self::tauri::run;
pub(crate) use self::app_state::AppState;
pub(crate) use self::bootstrap::config_management;
pub(crate) use self::bootstrap::initialize_logging;
pub(crate) use self::constants::TRACING_DOMAIN_BOOTSTRAP;
pub(crate) use self::constants::TRACING_TARGET;
pub(crate) use self::dto_common::AppSnapshotDto;
pub(crate) use self::dto_common::CommandErrorDto;
pub(crate) use self::errors::ERROR_CODE_APP_STATE_INVALID;
pub(crate) use self::errors::ERROR_CODE_APP_STATE_LOCK_FAILED;
pub(crate) use self::errors::ERROR_CODE_LOGGING_BOOTSTRAP_FAILED;
pub(crate) use self::errors::ERROR_CODE_TAURI_RUNTIME_FAILED;

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-app-config-desk/src/tauri.rs
// version: 2
// version: 3
//! Tauri runtime assembly for the KSP configuration desktop application.
@@ -7,9 +7,16 @@ use tauri::Manager; // rust-rules: trait-import
/// Runs the configuration desktop application.
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run(_arguments: &[std::ffi::OsString]) -> ksp_core_lib::Result<()> {
pub fn run(arguments: &[std::ffi::OsString]) -> ksp_core_lib::Result<()> {
let app_state = crate::AppState::initialize(arguments);
let app_state = match app_state {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let mut builder = tauri::Builder::default();
builder = configure_state(builder, app_state);
builder = configure_plugins(builder);
builder = configure_commands(builder);
builder = configure_setup(builder);
let run_result = builder.run(tauri::generate_context!());
return match run_result {
@@ -21,11 +28,19 @@ pub fn run(_arguments: &[std::ffi::OsString]) -> ksp_core_lib::Result<()> {
};
}
fn configure_state(builder: tauri::Builder<tauri::Wry>, app_state: crate::AppState) -> tauri::Builder<tauri::Wry> {
return builder.manage(app_state);
}
fn configure_plugins(builder: tauri::Builder<tauri::Wry>) -> tauri::Builder<tauri::Wry> {
let tracing_plugin = tauri_plugin_tracing::Builder::new().build::<tauri::Wry>();
return builder.plugin(tracing_plugin);
}
fn configure_commands(builder: tauri::Builder<tauri::Wry>) -> tauri::Builder<tauri::Wry> {
return builder.invoke_handler(tauri::generate_handler![get_app_snapshot]);
}
fn configure_setup(builder: tauri::Builder<tauri::Wry>) -> tauri::Builder<tauri::Wry> {
return builder.setup(|app| {
if app.get_webview_window("splash").is_none() {
@@ -37,3 +52,12 @@ fn configure_setup(builder: tauri::Builder<tauri::Wry>) -> tauri::Builder<tauri:
return std::result::Result::Ok(());
});
}
#[tauri::command]
fn get_app_snapshot(state: tauri::State<'_, crate::AppState>) -> std::result::Result<crate::AppSnapshotDto, crate::CommandErrorDto> {
let snapshot = state.snapshot();
return match snapshot {
std::result::Result::Ok(value) => std::result::Result::Ok(value),
std::result::Result::Err(error) => std::result::Result::Err(crate::CommandErrorDto::from_error(&error)),
};
}

View File

@@ -0,0 +1,17 @@
// file: crates/ksp-app-config-desk/unit_tests/bootstrap.rs
// version: 1
#[test]
fn fallback_logging_is_console_only_and_bounded_to_info() {
let settings = super::fallback_logging_settings();
assert_eq!(settings.default_filter(), ksp_logging_lib::LogFilterLevel::Info);
assert_eq!(settings.span_events(), ksp_logging_lib::SpanEvents::Off);
assert!(settings.files().is_empty());
let console = settings.console();
assert!(console.is_some());
if let std::option::Option::Some(console) = console {
assert!(console.enabled());
assert_eq!(console.output(), ksp_logging_lib::ConsoleOutput::Stderr);
assert_eq!(console.format(), ksp_logging_lib::LogFormat::Human);
}
}

View File

@@ -0,0 +1,11 @@
// file: crates/ksp-app-config-desk/unit_tests/dto_common.rs
// version: 1
#[test]
fn command_error_projection_excludes_context_values() {
let error = ksp_core_lib::Error::new(ksp_core_lib::ErrorCode::new("test", "failure"), "safe message").with_context("secret_canary", "must-not-be-exported");
let dto = super::CommandErrorDto::from_error(&error);
assert_eq!(dto.domain, "test");
assert_eq!(dto.code, "failure");
assert_eq!(dto.message, "safe message");
}

240
deltas/0.1.4/pre.006.md Normal file
View File

@@ -0,0 +1,240 @@
<!-- file: deltas/0.1.4/pre.006.md -->
<!-- version: 1 -->
# `0.1.4-pre.006` — Bootstrap backend, `AppState` et runtime Logging initial
## 1. Objectif
Cette tranche raccorde pour la première fois `ksp-app-config-desk` aux fondations Config et Logging qu'elle doit valider. Elle reste volontairement bornée au bootstrap backend et au premier contrat Tauri/TS-RS ; les services Documents/Environment/Logging éditable et le bridge frontend logging restent dans les tranches suivantes.
La base `0.1.4-pre.5.fix.1` a été validée localement avec installation npm, `cargo fmt`, check workspace, Clippy, tests de l'application, tests Config, `cargo tree` et lancement réel :
```bash
cargo tauri dev -c crates/ksp-app-config-desk/tauri.conf.json
```
Vite a démarré sur `1430` et le binaire Tauri a été lancé depuis le target externe du workspace.
## 2. Version technique
La tranche modifie le runtime Rust et les dépendances de la crate :
```text
workspace.package.version = "0.1.4-pre.6"
```
Les versions frontend/Tauri applicatives restent `0.1.4` selon la politique courante.
## 3. Bootstrap Config
`src/bootstrap.rs` construit exclusivement à travers `ksp-config-lib` :
```text
argv complet
-> ConfigBootstrapOptions::from_args(...)
-> ConfigFileRegistry::from_args(...)
-> ConfigDocumentEngine
-> ConfigManagement
```
L'application ne parse donc pas elle-même `--cfgpath`, `--schemapath` ou `--filemap`, ne lit aucun `KSP_*` directement et ne résout aucun chemin physique Config en dehors de la façade Config.
`ConfigManagement` est conservé durablement dans `AppState` pour les services des tranches suivantes.
## 4. Bootstrap Logging et fallback de réparation
Le chemin nominal est :
```text
ConfigEnvironment::load()
-> load_resolved_logging_config(None, env)
-> ResolvedLoggingConfig::into_settings()
-> ksp_logging_lib::initialize(...)
-> LoggingGuard
```
`None` sélectionne le `default_profile` du document `std.logging.json`.
Pour respecter l'exigence de réparation de Config Desk, une erreur lors du chargement/résolution Logging ou de la préparation du runtime configuré ne condamne pas automatiquement l'application. Tant qu'aucun subscriber global n'a pu être installé, Config Desk tente un fallback transitoire en mémoire :
```text
default_filter : Info
span_events : Off
console : stderr / Human
files : aucun
persistence : aucune
```
Le fallback :
- n'est jamais écrit dans `std.logging.json` ;
- ne modifie aucun profil utilisateur ;
- conserve un diagnostic frontend borné ;
- permet à l'application de rester utilisable pour réparer la configuration ;
- installe toujours le subscriber via `ksp-logging-lib`, jamais via l'application ou `tauri-plugin-tracing`.
Si le fallback lui-même ne peut pas installer le runtime global, le bootstrap échoue explicitement avec `config_desk.logging_bootstrap_failed`.
## 5. Ownership `AppState`
L'état backend devient :
```text
AppState
├── ConfigManagement
└── Mutex<LoggingRuntimeState>
├── LoggingGuard
├── active_profile_id
├── generation = 1
├── fallback_active
└── startup_diagnostic
```
Le `LoggingGuard` vit donc avec l'application et prépare le futur `reinitialize()` transactionnel. Le guard et les métadonnées runtime sont regroupés sous le même mutex afin que les futures mutations restent atomiques au niveau applicatif.
## 6. Premier contrat Tauri + TS-RS
La crate introduit `ts-rs = "^12.0"` au niveau workspace et les premiers DTO applicatifs :
```text
CommandErrorDto
AppSnapshotDto
```
Les exports sont générés sous :
```text
frontend/ts/bindings/ksp_app_config_desk/dto_common/
```
`CommandErrorDto` projette uniquement :
```text
domain
code
message
```
Il ne sérialise jamais automatiquement `Error::context()` ni la chaîne des sources.
La première commande réelle est :
```text
get_app_snapshot
```
Elle retourne notamment la version applicative, le nombre de descripteurs Config, le profil Logging actif éventuel, la génération runtime, l'état fallback et le diagnostic de bootstrap sûr.
Toutes les annotations `#[tauri::command]` restent centralisées dans `tauri.rs`.
## 7. Builder Tauri
Le builder reste volontairement découpé :
```text
configure_state
configure_plugins
configure_commands
configure_setup
```
Le plugin tracing conserve `Builder::new()` sans `with_default_subscriber()`. `ksp-logging-lib::initialize()` reste le seul propriétaire de l'installation du subscriber global KSP.
## 8. Police du splash bot3
L'audit du gabarit bot3 confirme une seule police locale spécifique au splash :
```text
DOS_Amazigh.ttf
font-family: Dos Amazigh
```
Source auditée :
```text
kb-app-demo-desktop/frontend/fonts/DOS_Amazigh.ttf
```
Destination KSP :
```text
crates/ksp-app-config-desk/frontend/fonts/DOS_Amazigh.ttf
```
Empreinte de la copie bot3 auditée :
```text
SHA-256 a01ff4b5a0d699db7cefcf5336bf0e379ff6fbf0d096087571a13c8782f9aa02
```
Le répertoire `frontend/fonts/` et son manifeste sont ajoutés dès cette tranche. Le style `@font-face` et l'animation/lifecycle final restent associés à `pre.008`, afin de ne pas coupler le bootstrap backend à la finition du splash.
## 9. Tests ajoutés
Tests unitaires hors `src/` :
- le fallback Logging est console-only, stderr, `Info`, sans fichier et sans span lifecycle ;
- le DTO d'erreur commun ne reprend pas les champs de contexte KSP arbitraires.
Les dérives `#[ts(export)]` ajoutent également les tests d'export TS-RS au package.
## 10. Dépendances
Nouvelles dépendances directes de `ksp-app-config-desk` :
```text
ksp-config-lib
ksp-logging-lib
serde
ts-rs
```
`ts-rs` est centralisé dans `[workspace.dependencies]` avec une contrainte `^12.0`. Aucune dépendance Solana/protocole supplémentaire n'est introduite.
## 11. Fichiers
| Fichier | Action |
| ----------------------------------------------------------- | ------: |
| `Cargo.toml` | modifié |
| `crates/ksp-app-config-desk/Cargo.toml` | modifié |
| `crates/ksp-app-config-desk/src/app_state.rs` | ajouté |
| `crates/ksp-app-config-desk/src/bootstrap.rs` | ajouté |
| `crates/ksp-app-config-desk/src/constants.rs` | ajouté |
| `crates/ksp-app-config-desk/src/dto_common.rs` | ajouté |
| `crates/ksp-app-config-desk/src/errors.rs` | modifié |
| `crates/ksp-app-config-desk/src/lib.rs` | modifié |
| `crates/ksp-app-config-desk/src/tauri.rs` | modifié |
| `crates/ksp-app-config-desk/unit_tests/bootstrap.rs` | ajouté |
| `crates/ksp-app-config-desk/unit_tests/dto_common.rs` | ajouté |
| `crates/ksp-app-config-desk/frontend/fonts/README.md` | ajouté |
| `crates/ksp-app-config-desk/README.md` | modifié |
| `crates/ksp-app-config-desk/USAGE.md` | modifié |
| `crates/ksp-app-config-desk/TODO.md` | modifié |
| `docs/plans/006-V0_1_4_CONFIG_DESKTOP_PLAN.md` | modifié |
| `deltas/0.1.4/pre.006.md` | ajouté |
## 12. Validations à exécuter
Depuis la racine du workspace :
```bash
cargo fmt --all
cargo check --workspace
cargo clippy --workspace --all-targets
cargo test -p ksp-app-config-desk
cargo test -p ksp-config-lib
cargo tree -p ksp-app-config-desk
cargo tauri dev -c crates/ksp-app-config-desk/tauri.conf.json
```
Après `cargo test -p ksp-app-config-desk`, vérifier également que les bindings ont été générés sous :
```text
crates/ksp-app-config-desk/frontend/ts/bindings/ksp_app_config_desk/dto_common/
```
Le lancement desktop nominal doit utiliser le profil Logging par défaut de `config/std.logging.json`. Le diagnostic de fallback sera validé plus profondément avec les écrans Documents/Logging des tranches suivantes.
## 13. Suite
Après validation, `pre.007` reprend/refond le bridge `frontend_log.ts` de bot3 : targets KSP whitelistés, commande `emit_frontend_log` centralisée dans `tauri.rs` et émission finale exclusivement via `ksp-logging-lib`.

View File

@@ -1,5 +1,5 @@
<!-- file: docs/plans/006-V0_1_4_CONFIG_DESKTOP_PLAN.md -->
<!-- version: 6 -->
<!-- version: 7 -->
# Plan `0.1.4` — `ksp-app-config-desk`
@@ -166,6 +166,7 @@ La référence est réutilisée par **principes**, pas copiée mécaniquement.
- SCSS/SASS et Bootstrap ;
- gabarit visuel initial de bot3 comme base à simplifier/affiner, sans recopier les écrans métier ;
- même icône de base bot3 (`favicon.png` / `favicon.ico`) tant qu'aucune identité KSP plus spécifique n'est décidée ;
- police locale du splash bot3 `DOS_Amazigh.ttf` (`Dos Amazigh`) reprise dans le répertoire `frontend/fonts/` lors de la tranche bootstrap/splash ;
- Font Awesome pour l'iconographie ;
- SimpleBar pour le scrolling applicatif cohérent et visuellement plus confortable que les scrollbars WebKitGTK par défaut ;
- `resize-observer-polyfill` comme dépendance du gabarit pour les composants/layouts qui doivent réagir proprement aux redimensionnements ;
@@ -237,8 +238,8 @@ La gestion npm distingue le runtime frontend du tooling. Les bibliothèques cons
Chaque application Tauri desk KSP reçoit un couple de ports Vite/HMR propre. La première application réserve :
| Application | Port Vite HTTP | Port HMR |
|-----------------------|---------------:|---------:|
| `ksp-app-config-desk` | `1430` | `1431` |
| --------------------- | -------------: | -------: |
| `ksp-app-config-desk` | `1430` | `1431` |
Les applications suivantes incrémentent le couple de deux ports (`1432/1433`, puis `1434/1435`, etc.). Vite doit utiliser un port strict afin qu'une collision soit signalée au lieu de provoquer un basculement silencieux vers un autre port. Cette allocation permet de faire fonctionner simultanément plusieurs applications desk en mode développement.
@@ -248,20 +249,20 @@ Aucune dépendance n'est ajoutée par `pre.001`. Les versions suivantes ont ét
| Dépendance | Version actuelle auditée | Usage envisagé |
| ------------------------------- | -----------------------: | -------------------------------------------- |
| `tauri` | `2.11.5` | runtime desktop |
| `tauri-build` | `2.6.3` | build Tauri |
| `tauri-plugin-tracing` | `0.3.4` | intégration tracing à la frontière Tauri |
| `ts-rs` | `12.0.1` | bindings DTO applicatifs |
| `fs2` | `0.4.3` | verrou single-instance |
| `@fltsci/tauri-plugin-tracing` | `0.3.4` | package frontend compagnon du plugin tracing |
| `@tauri-apps/api` | `2.11.1` | frontend Tauri |
| `@tauri-apps/cli` | `2.11.4` | dev/build desktop |
| `@types/bootstrap` | `5.2.11` | typings Bootstrap |
| `vite` | `8.2.1` | build frontend |
| `typescript` | `7.0.2` | frontend TypeScript |
| `sass-embedded` | `1.102.0` | compilation SCSS |
| `bootstrap` | `5.3.8` | composants/layout |
| `@fortawesome/fontawesome-free` | `7.3.1` | iconographie |
| `tauri` | `2.11.5` | runtime desktop |
| `tauri-build` | `2.6.3` | build Tauri |
| `tauri-plugin-tracing` | `0.3.4` | intégration tracing à la frontière Tauri |
| `ts-rs` | `12.0.1` | bindings DTO applicatifs |
| `fs2` | `0.4.3` | verrou single-instance |
| `@fltsci/tauri-plugin-tracing` | `0.3.4` | package frontend compagnon du plugin tracing |
| `@tauri-apps/api` | `2.11.1` | frontend Tauri |
| `@tauri-apps/cli` | `2.11.4` | dev/build desktop |
| `@types/bootstrap` | `5.2.11` | typings Bootstrap |
| `vite` | `8.2.1` | build frontend |
| `typescript` | `7.0.2` | frontend TypeScript |
| `sass-embedded` | `1.102.0` | compilation SCSS |
| `bootstrap` | `5.3.8` | composants/layout |
| `@fortawesome/fontawesome-free` | `7.3.1` | iconographie |
Sources d'audit :
@@ -690,23 +691,23 @@ En parallèle, le `frontend_log.ts` commun reprend/refond le mécanisme bot3 pou
Les noms ci-dessous sont les noms fonctionnels cibles ; ils pourront être normalisés avant implémentation, mais leurs responsabilités sont fixées.
| Commande Tauri | Service interne | API KSP principale | Secret réel ? |
| Commande Tauri | Service interne | API KSP principale | Secret réel ? |
| ------------------------------ | ------------------------------------ | --------------------------------------------------------------------------- | -------------------------------------------: |
| `get_app_snapshot` | `config_service` + `logging_service` | registry/management + runtime state | non |
| `list_config_documents` | `config_service` | future vue publique du `ConfigFileRegistry` | non |
| `inspect_config_document` | `config_service` | `load_validated_document` + `read_source` si erreur | non |
| `save_config_source_candidate` | `config_service` | future API Config de réparation validée | non |
| `inspect_profile` | `config_service` | `load_resolved_profile` + résolution détaillée | non |
| `get_environment_report` | `environment_service` | `ConfigManagement::environment_report` | non |
| `get_app_snapshot` | `config_service` + `logging_service` | registry/management + runtime state | non |
| `list_config_documents` | `config_service` | future vue publique du `ConfigFileRegistry` | non |
| `inspect_config_document` | `config_service` | `load_validated_document` + `read_source` si erreur | non |
| `save_config_source_candidate` | `config_service` | future API Config de réparation validée | non |
| `inspect_profile` | `config_service` | `load_resolved_profile` + résolution détaillée | non |
| `get_environment_report` | `environment_service` | `ConfigManagement::environment_report` | non |
| `set_dotenv_value` | `environment_service` | `ConfigManagement::set_dotenv_value` + rapport frais | entrée potentiellement Secret, jamais loggée |
| `remove_dotenv_value` | `environment_service` | `ConfigManagement::remove_dotenv_value` + rapport frais | non |
| `reveal_environment_value` | `secret_service` | `reveal_effective_environment_value` ou `reveal_dotenv_value` | **oui** |
| `get_logging_editor` | `logging_service` | `load_logging_document` | non |
| `save_logging_document` | `logging_service` | `save_logging_document` | non par design Logging |
| `apply_logging_profile` | `logging_service` | `ConfigEnvironment::load` + `load_resolved_logging_config` + `reinitialize` | valeurs resolved possibles, jamais DTO/log |
| `emit_frontend_log` | `frontend_logging_service` | façade `ksp-logging-lib` | non, message technique frontend seulement |
| `emit_logging_test` | `logging_service` | façade/macros `ksp-logging-lib` | non, message explicitement saisi pour test |
| `splash_frontend_ready` | `splash` | lifecycle Tauri + settings splash déjà résolus | non |
| `remove_dotenv_value` | `environment_service` | `ConfigManagement::remove_dotenv_value` + rapport frais | non |
| `reveal_environment_value` | `secret_service` | `reveal_effective_environment_value` ou `reveal_dotenv_value` | **oui** |
| `get_logging_editor` | `logging_service` | `load_logging_document` | non |
| `save_logging_document` | `logging_service` | `save_logging_document` | non par design Logging |
| `apply_logging_profile` | `logging_service` | `ConfigEnvironment::load` + `load_resolved_logging_config` + `reinitialize` | valeurs resolved possibles, jamais DTO/log |
| `emit_frontend_log` | `frontend_logging_service` | façade `ksp-logging-lib` | non, message technique frontend seulement |
| `emit_logging_test` | `logging_service` | façade/macros `ksp-logging-lib` | non, message explicitement saisi pour test |
| `splash_frontend_ready` | `splash` | lifecycle Tauri + settings splash déjà résolus | non |
Tous les wrappers dans `tauri.rs` :
@@ -1162,7 +1163,8 @@ pre.006 bootstrap backend + AppState
- initialisation Logging
- fallback sûr si std.logging invalide
- LoggingGuard durable
- erreurs/DTO communs
- erreurs/DTO communs + premier get_app_snapshot TS-RS
- inventaire/provenance de la police DOS_Amazigh du splash bot3
pre.007 frontend logging commun
- frontend_log.ts repris/refondu depuis bot3