v0.1.4-pre.014
This commit is contained in:
@@ -1,12 +1,12 @@
|
||||
# file: Cargo.toml
|
||||
# version: 82
|
||||
# version: 83
|
||||
|
||||
[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.13"
|
||||
version = "0.1.4-pre.14"
|
||||
edition = "2024"
|
||||
license = "MIT"
|
||||
repository = "https://git.sasedev.com/Sasedev/khadhroony-solana-project"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!-- file: crates/ksp-app-config-desk/README.md -->
|
||||
<!-- version: 14 -->
|
||||
<!-- version: 15 -->
|
||||
|
||||
# `ksp-app-config-desk`
|
||||
|
||||
@@ -29,6 +29,7 @@ Le gabarit desktop actif fournit maintenant :
|
||||
- le panneau Documents générique alimenté par le registre `ksp-config-lib`, avec diagnostics backend et réparation validée ;
|
||||
- le panneau Profils générique pour `default_profile`, sélection explicite, vues global/profil/effective sûre et provenance ;
|
||||
- le panneau Environnement alimenté uniquement par `ConfigManagement`, avec rapport desired/effective sûr, source/shadowing, management atomique `.env` et reveal Secret privilégié/transitoire ;
|
||||
- le panneau Logging en lecture typée, alimenté par `ConfigManagement::load_logging_document()` et projetant profils, console, fichiers, filtres, targets et domains sans parsing frontend ;
|
||||
- `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;
|
||||
@@ -53,6 +54,7 @@ frontend/
|
||||
│ ├── environment.ts
|
||||
│ ├── frontend_log.ts
|
||||
│ ├── invoke.ts
|
||||
│ ├── logging.ts
|
||||
│ ├── main.ts
|
||||
│ ├── profiles.ts
|
||||
│ ├── secret_reveal.ts
|
||||
@@ -130,7 +132,7 @@ KSP_DESK_SPLASH_FADE_MS=300
|
||||
|
||||
Après la readiness frontend, Rust émet `fade_in`, attend la durée minimale, émet `fade_out`, attend la durée de fade puis `tw_main.rs` affiche/focalise `main` avant destruction du splash. Le backend journalise en `debug` les valeurs résolues, leur provenance (`process`, `dotenv` ou `fallback`), chaque attente réellement observée et la durée totale. Avec `12000/3000`, la durée backend attendue entre readiness et activation de `main` est donc d'environ `15000 ms`. Une valeur splash invalide ne bloque pas le manager : des timings de fallback sûrs restent en mémoire afin que la future surface `.env` puisse permettre la réparation.
|
||||
|
||||
Le shell principal expose les cinq routes de référence `Vue d'ensemble`, `Documents`, `Profils`, `Environnement / .env` et `Logging`. Le logo porte déjà l'identité KSP ; le texte du header suit donc la forme `Config Desk — <vue active>` au lieu de répéter `KSP`. Les quelques commandes principales restent des pills/tabs à droite ; un dropdown sera préféré lorsqu'une application possède trop de commandes pour conserver ce format lisible. Les routes non encore fonctionnelles affichent un placeholder sans réimplémenter les services prévus par les prereleases suivantes.
|
||||
Le shell principal expose les cinq routes de référence `Vue d'ensemble`, `Documents`, `Profils`, `Environnement / .env` et `Logging`. Le logo porte déjà l'identité KSP ; le texte du header suit donc la forme `Config Desk — <vue active>` au lieu de répéter `KSP`. Les quelques commandes principales restent des pills/tabs à droite ; un dropdown sera préféré lorsqu'une application possède trop de commandes pour conserver ce format lisible. La route Logging expose maintenant la lecture typée du document et de ses profils ; les mutations/persistence et le hot reload restent réservés aux prereleases suivantes.
|
||||
|
||||
|
||||
## Panneau Profils
|
||||
@@ -139,6 +141,12 @@ La vue **Profils** inspecte les documents validés qui exposent le contrat stand
|
||||
|
||||
L'inspection expose le profil par défaut ou une sélection explicite, les vues source `globals` et `profile`, ainsi que l'effective après résolution environnementale. Cette dernière est toujours sérialisée depuis `ResolvedConfigJson::safe_value()` afin qu'une substitution Secret soit redacted. La provenance top-level distingue `global` / `profile`; la provenance environnement n'expose que JSON Pointer, nom de variable, source `process`/`.env`/fallback et sensibilité, jamais la valeur résolue.
|
||||
|
||||
## Panneau Logging — lecture typée
|
||||
|
||||
La vue **Logging** charge le document standard exclusivement avec `ConfigManagement::load_logging_document()`. Rust mappe ensuite les types publics `LoggingConfigDocument`, `LoggingProfileConfig`, `LoggingConsoleConfig`, `LoggingFileConfig`, `LoggingOutputFilterConfig` et `LoggingTargetFilterConfig` vers des DTO TS-RS dédiés. Le frontend ne parse donc ni le JSON source ni son schema.
|
||||
|
||||
La tranche de lecture expose `format_version`, `logs_directory`, `default_profile`, tous les profils, la console, les fichiers persistants, les filtres locaux, les target overrides et les listes de targets/domains. La sélection d'un profil ne modifie rien : `pre.014` est strictement read-only. Les mutations typées et la persistence via `save_logging_document()` appartiennent à `pre.015`.
|
||||
|
||||
## Traçabilité frontend
|
||||
|
||||
Les actions utilisateur significatives et changements d'état sont journalisés en `debug`; les opérations plus fines (rendu/remplacement DOM, étapes IPC, animations et événements fréquents) en `trace`. Le wrapper `invoke.ts` trace début/fin des commandes sans journaliser leurs arguments, afin de ne pas créer ultérieurement de fuite de valeurs sensibles.
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<!-- file: crates/ksp-app-config-desk/TODO.md -->
|
||||
<!-- version: 12 -->
|
||||
<!-- version: 13 -->
|
||||
|
||||
# TODO `ksp-app-config-desk`
|
||||
|
||||
Le gabarit frontend, le bootstrap Config/Logging, `AppState`, le `LoggingGuard` durable, les premiers DTO TS-RS, le bridge frontend logging KSP, le lifecycle `splash -> main`, les panneaux Documents, Profils/provenance et Environnement avec management `.env` et reveal Secret privilégié sont maintenant présents.
|
||||
Le gabarit frontend, le bootstrap Config/Logging, `AppState`, le `LoggingGuard` durable, les premiers DTO TS-RS, le bridge frontend logging KSP, le lifecycle `splash -> main`, les panneaux Documents, Profils/provenance et Environnement avec management `.env`/reveal Secret privilégié, ainsi que la lecture typée du document Logging sont maintenant présents.
|
||||
|
||||
Les prochaines tranches doivent encore :
|
||||
|
||||
- développer les surfaces Logging ;
|
||||
- ajouter les mutations/persistence du Logging editor puis son hot reload et son panneau de test ;
|
||||
- faire évoluer le runtime Logging vers des fichiers applicatifs uniques par lancement (`app-name.<start-timestamp>.<ext>`) ;
|
||||
- ramener le target applicatif de `debug` à `info` ou `warn` avant clôture de la release ;
|
||||
- démontrer le hot reload Logging et le panneau de test de routage ;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<!-- file: crates/ksp-app-config-desk/USAGE.md -->
|
||||
<!-- version: 14 -->
|
||||
<!-- version: 15 -->
|
||||
|
||||
# Utilisation de `ksp-app-config-desk`
|
||||
|
||||
## État actuel
|
||||
|
||||
Le gabarit Rust/Tauri et le frontend Vite/TypeScript/SCSS sont présents. Le backend initialise Config, le runtime Logging et `AppState`. Les panneaux Documents, Profils et Environnement/`.env` sont fonctionnels, y compris les mutations `.env` create/update/remove ; les reveals Secrets et Logging restent à compléter.
|
||||
Le gabarit Rust/Tauri et le frontend Vite/TypeScript/SCSS sont présents. Le backend initialise Config, le runtime Logging et `AppState`. Les panneaux Documents, Profils et Environnement/`.env` sont fonctionnels, y compris les mutations `.env` create/update/remove et les reveals Secrets privilégiés. Logging expose maintenant son document/profils/sinks/filtres en lecture typée ; les mutations et le hot reload restent à compléter.
|
||||
|
||||
La fenêtre `splash` est visible au démarrage. Après readiness du frontend et temporisation résolue par Config, l'application effectue le fade-out, affiche/focalise `main` puis détruit `splash`. La fenêtre principale expose immédiatement la navigation monofenêtre de référence.
|
||||
|
||||
@@ -131,7 +131,7 @@ Environnement / .env
|
||||
Logging
|
||||
```
|
||||
|
||||
Les vues d'ensemble, Documents, Profils et Environnement / `.env` sont fonctionnelles ; Logging reste un placeholder jusqu'à sa tranche fonctionnelle. Le header affiche `Config Desk — <vue active>` : le logo fournit déjà l'identité KSP. Les commandes principales peu nombreuses utilisent des pills/tabs alignées à droite ; une application plus chargée devra préférer un dropdown. Chaque clic de tab est tracé en `trace`, l'activation utilisateur significative reste tracée en `debug`, et chaque remplacement/rendu de section en `trace`. Les appels Tauri partagés utilisent `frontend/ts/invoke.ts`, qui journalise le début et la fin d'une commande sans journaliser ses arguments.
|
||||
Les cinq vues sont maintenant fonctionnelles en lecture. Logging charge le contrat typé `std.logging.json`; ses mutations et son hot reload restent désactivés jusqu'aux tranches dédiées. Le header affiche `Config Desk — <vue active>` : le logo fournit déjà l'identité KSP. Les commandes principales peu nombreuses utilisent des pills/tabs alignées à droite ; une application plus chargée devra préférer un dropdown. Chaque clic de tab est tracé en `trace`, l'activation utilisateur significative reste tracée en `debug`, et chaque remplacement/rendu de section en `trace`. Les appels Tauri partagés utilisent `frontend/ts/invoke.ts`, qui journalise le début et la fin d'une commande sans journaliser ses arguments.
|
||||
|
||||
## Asset font du splash
|
||||
|
||||
@@ -199,3 +199,16 @@ Le reveal utilise un DTO et une commande séparés des rapports ordinaires. Apr
|
||||
Le bouton **Supprimer** ouvre un modal Bootstrap indiquant la variable ciblée. **Annuler**, la fermeture du modal ou `Esc` abandonnent l'opération ; **Supprimer** confirme ensuite l'appel à `ConfigManagement::remove_dotenv_value()`. Aucun `window.confirm()` natif n'est utilisé.
|
||||
|
||||
Pour valider le shadowing, lancer ponctuellement l'application avec `KSP_LOGS_DIRECTORY` fourni par le process puis modifier la même variable dans `.env` depuis le panneau. Le résultat attendu est `source_changed=true`, `effective_changed=false`, `shadowed_by_process_environment=true`, `reload_required=false`. Le process hérité reste prioritaire et n'est jamais modifié par Config Desk.
|
||||
|
||||
## Panneau Logging — lecture typée
|
||||
|
||||
La vue **Logging** appelle `get_logging_document`. Le backend charge le document avec `ConfigManagement::load_logging_document()` et expose :
|
||||
|
||||
- `file_id`, path résolu, `format_version`, `logs_directory`, `default_profile` ;
|
||||
- chaque profil avec `default_filter` et `span_events` ;
|
||||
- la console (`enabled`, output, ANSI, format, level, targets, domains) ;
|
||||
- tous les fichiers (`output_id`, path, rotation, format, ANSI et filtre) ;
|
||||
- tous les `target_filters` globaux.
|
||||
|
||||
Le sélecteur **Profil inspecté** ne modifie pas le document. **Rafraîchir** relit la source typée. Aucun bouton de sauvegarde n'est présent en `pre.014`; les opérations create/clone/rename/delete, `default_profile` et persistence sont volontairement reportées à `pre.015`.
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!-- file: crates/ksp-app-config-desk/frontend/main.html -->
|
||||
<!-- version: 9 -->
|
||||
<!-- version: 10 -->
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
|
||||
@@ -297,10 +297,114 @@
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<section id="placeholderPanel" hidden aria-live="polite">
|
||||
<div class="alert alert-secondary mb-4" role="status">
|
||||
Cette route du shell est prête ; son contenu fonctionnel sera ajouté dans la prerelease prévue par le plan 0.1.4.
|
||||
<section id="loggingPanel" hidden aria-label="Logging Config">
|
||||
<div class="d-flex flex-wrap gap-2 align-items-center justify-content-between mb-3">
|
||||
<div>
|
||||
<h2 class="h5 mb-1">Logging editor — lecture typée</h2>
|
||||
<p class="text-body-secondary small mb-0">Projection read-only de std.logging.json via les types publics de ksp-config-lib ; aucune mutation runtime dans cette tranche.</p>
|
||||
</div>
|
||||
<button id="refreshLoggingDocument" class="btn btn-outline-primary btn-sm" type="button">
|
||||
<i class="fa-solid fa-rotate me-1" aria-hidden="true"></i>
|
||||
Rafraîchir
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<dl class="row small mb-4 app-profile-metadata">
|
||||
<dt class="col-lg-3">file_id</dt>
|
||||
<dd id="loggingFileId" class="col-lg-9 font-monospace">—</dd>
|
||||
<dt class="col-lg-3">Path résolu</dt>
|
||||
<dd id="loggingPath" class="col-lg-9 font-monospace text-break">—</dd>
|
||||
<dt class="col-lg-3">format_version</dt>
|
||||
<dd id="loggingFormatVersion" class="col-lg-9 font-monospace">—</dd>
|
||||
<dt class="col-lg-3">logs_directory</dt>
|
||||
<dd id="loggingLogsDirectory" class="col-lg-9 font-monospace text-break">—</dd>
|
||||
<dt class="col-lg-3 mb-0">default_profile</dt>
|
||||
<dd id="loggingDefaultProfile" class="col-lg-9 mb-0 font-monospace">—</dd>
|
||||
</dl>
|
||||
|
||||
<div class="row g-3 align-items-end mb-4">
|
||||
<div class="col-xl-5">
|
||||
<label class="form-label" for="loggingProfileSelect">Profil inspecté</label>
|
||||
<select id="loggingProfileSelect" class="form-select" disabled></select>
|
||||
</div>
|
||||
<div class="col-xl-3">
|
||||
<span class="form-label d-block">default_filter</span>
|
||||
<code id="loggingProfileDefaultFilter">—</code>
|
||||
</div>
|
||||
<div class="col-xl-4">
|
||||
<span class="form-label d-block">span_events</span>
|
||||
<code id="loggingProfileSpanEvents">—</code>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<section class="border rounded p-3 mb-4 bg-body-tertiary" aria-label="Console Logging">
|
||||
<h3 class="h6 mb-3">Console</h3>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-sm align-middle mb-0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Enabled</th>
|
||||
<th scope="col">Output</th>
|
||||
<th scope="col">ANSI</th>
|
||||
<th scope="col">Format</th>
|
||||
<th scope="col">Level</th>
|
||||
<th scope="col">Targets</th>
|
||||
<th scope="col">Domains</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td id="loggingConsoleEnabled">—</td>
|
||||
<td id="loggingConsoleOutput">—</td>
|
||||
<td id="loggingConsoleAnsi">—</td>
|
||||
<td id="loggingConsoleFormat">—</td>
|
||||
<td id="loggingConsoleLevel">—</td>
|
||||
<td id="loggingConsoleTargets" class="font-monospace text-break">—</td>
|
||||
<td id="loggingConsoleDomains" class="font-monospace text-break">—</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="mb-4" aria-label="Fichiers Logging">
|
||||
<h3 class="h6 mb-3">Fichiers</h3>
|
||||
<div class="table-responsive">
|
||||
<table id="loggingFilesTable" class="table table-striped table-hover table-sm align-middle w-100">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">output_id</th>
|
||||
<th scope="col">Enabled</th>
|
||||
<th scope="col">Path</th>
|
||||
<th scope="col">Rotation</th>
|
||||
<th scope="col">Format</th>
|
||||
<th scope="col">ANSI</th>
|
||||
<th scope="col">Level</th>
|
||||
<th scope="col">Targets</th>
|
||||
<th scope="col">Domains</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="mb-4" aria-label="Target filters Logging">
|
||||
<h3 class="h6 mb-3">Target filters globaux</h3>
|
||||
<div class="table-responsive">
|
||||
<table id="loggingTargetFiltersTable" class="table table-striped table-hover table-sm align-middle w-100">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">target_prefix</th>
|
||||
<th scope="col">Level</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div id="loggingStatus" class="alert alert-primary mb-0" role="status" aria-live="polite">Chargement du document Logging typé...</div>
|
||||
</section>
|
||||
|
||||
<div class="alert alert-primary mb-0" role="status">
|
||||
|
||||
190
crates/ksp-app-config-desk/frontend/ts/logging.ts
Normal file
190
crates/ksp-app-config-desk/frontend/ts/logging.ts
Normal file
@@ -0,0 +1,190 @@
|
||||
// file: crates/ksp-app-config-desk/frontend/ts/logging.ts
|
||||
// version: 1
|
||||
|
||||
//! Read-only typed Logging editor backed exclusively by ConfigManagement.
|
||||
|
||||
import type { LoggingDocumentDto } from "./bindings/ksp_app_config_desk/logging/LoggingDocumentDto";
|
||||
import type { LoggingOutputFilterDto } from "./bindings/ksp_app_config_desk/logging/LoggingOutputFilterDto";
|
||||
import type { LoggingProfileDto } from "./bindings/ksp_app_config_desk/logging/LoggingProfileDto";
|
||||
import { frontendDebug, frontendTrace } from "./frontend_log";
|
||||
import { invokeKsp } from "./invoke";
|
||||
|
||||
let loggingDocument: LoggingDocumentDto | null = null;
|
||||
|
||||
function setLoggingStatus(message: string, tone: "primary" | "success" | "danger" = "primary"): void {
|
||||
const status = document.querySelector<HTMLElement>("#loggingStatus");
|
||||
if (!status) {
|
||||
return;
|
||||
}
|
||||
status.className = `alert alert-${tone} mb-0`;
|
||||
status.textContent = message;
|
||||
frontendTrace("main", "Logging editor status replaced", { tone });
|
||||
}
|
||||
|
||||
function setText(selector: string, value: string): void {
|
||||
const element = document.querySelector<HTMLElement>(selector);
|
||||
if (element) {
|
||||
element.textContent = value;
|
||||
}
|
||||
}
|
||||
|
||||
function boolLabel(value: boolean): string {
|
||||
return value ? "oui" : "non";
|
||||
}
|
||||
|
||||
function selectorsLabel(values: string[]): string {
|
||||
return values.length > 0 ? values.join(", ") : "—";
|
||||
}
|
||||
|
||||
function filterSummary(filter: LoggingOutputFilterDto): { level: string; targets: string; domains: string } {
|
||||
return { level: filter.level, targets: selectorsLabel(filter.targets), domains: selectorsLabel(filter.domains) };
|
||||
}
|
||||
|
||||
function appendCell(row: HTMLTableRowElement, value: string, className?: string): void {
|
||||
const cell = document.createElement("td");
|
||||
cell.textContent = value;
|
||||
if (className) {
|
||||
cell.className = className;
|
||||
}
|
||||
row.append(cell);
|
||||
}
|
||||
|
||||
function renderProfileSelect(documentDto: LoggingDocumentDto): void {
|
||||
const select = document.querySelector<HTMLSelectElement>("#loggingProfileSelect");
|
||||
if (!select) {
|
||||
return;
|
||||
}
|
||||
const previous = select.value;
|
||||
select.replaceChildren();
|
||||
for (const profile of documentDto.profiles) {
|
||||
const option = document.createElement("option");
|
||||
option.value = profile.profileId;
|
||||
option.textContent = profile.profileId === documentDto.defaultProfile ? `${profile.profileId} (default)` : profile.profileId;
|
||||
select.append(option);
|
||||
}
|
||||
const previousExists = documentDto.profiles.some(profile => profile.profileId === previous);
|
||||
select.value = previousExists ? previous : documentDto.defaultProfile;
|
||||
if (!documentDto.profiles.some(profile => profile.profileId === select.value) && documentDto.profiles.length > 0) {
|
||||
select.value = documentDto.profiles[0].profileId;
|
||||
}
|
||||
select.disabled = documentDto.profiles.length === 0;
|
||||
frontendTrace("main", "Logging editor profile select replaced", { profileCount: documentDto.profiles.length });
|
||||
}
|
||||
|
||||
function renderConsole(profile: LoggingProfileDto): void {
|
||||
const summary = filterSummary(profile.console.filter);
|
||||
setText("#loggingConsoleEnabled", boolLabel(profile.console.enabled));
|
||||
setText("#loggingConsoleOutput", profile.console.output);
|
||||
setText("#loggingConsoleAnsi", boolLabel(profile.console.ansi));
|
||||
setText("#loggingConsoleFormat", profile.console.format);
|
||||
setText("#loggingConsoleLevel", summary.level);
|
||||
setText("#loggingConsoleTargets", summary.targets);
|
||||
setText("#loggingConsoleDomains", summary.domains);
|
||||
}
|
||||
|
||||
function renderFiles(profile: LoggingProfileDto): void {
|
||||
const body = document.querySelector<HTMLTableSectionElement>("#loggingFilesTable tbody");
|
||||
if (!body) {
|
||||
return;
|
||||
}
|
||||
body.replaceChildren();
|
||||
for (const file of profile.files) {
|
||||
const summary = filterSummary(file.filter);
|
||||
const row = document.createElement("tr");
|
||||
appendCell(row, file.outputId, "font-monospace");
|
||||
appendCell(row, boolLabel(file.enabled));
|
||||
appendCell(row, file.path, "font-monospace text-break");
|
||||
appendCell(row, file.rotation);
|
||||
appendCell(row, file.format);
|
||||
appendCell(row, boolLabel(file.ansi));
|
||||
appendCell(row, summary.level);
|
||||
appendCell(row, summary.targets, "font-monospace text-break");
|
||||
appendCell(row, summary.domains, "font-monospace text-break");
|
||||
body.append(row);
|
||||
}
|
||||
frontendTrace("main", "Logging file sink table DOM replaced", { fileCount: profile.files.length });
|
||||
}
|
||||
|
||||
function renderTargetFilters(profile: LoggingProfileDto): void {
|
||||
const body = document.querySelector<HTMLTableSectionElement>("#loggingTargetFiltersTable tbody");
|
||||
if (!body) {
|
||||
return;
|
||||
}
|
||||
body.replaceChildren();
|
||||
for (const filter of profile.targetFilters) {
|
||||
const row = document.createElement("tr");
|
||||
appendCell(row, filter.targetPrefix, "font-monospace");
|
||||
appendCell(row, filter.level);
|
||||
body.append(row);
|
||||
}
|
||||
frontendTrace("main", "Logging target override table DOM replaced", { targetFilterCount: profile.targetFilters.length });
|
||||
}
|
||||
|
||||
function renderSelectedProfile(source: "load" | "selection"): void {
|
||||
const documentDto = loggingDocument;
|
||||
const select = document.querySelector<HTMLSelectElement>("#loggingProfileSelect");
|
||||
if (!documentDto || !select) {
|
||||
return;
|
||||
}
|
||||
const profile = documentDto.profiles.find(candidate => candidate.profileId === select.value);
|
||||
if (!profile) {
|
||||
setLoggingStatus("Le profil Logging sélectionné n'existe plus dans le document chargé.", "danger");
|
||||
return;
|
||||
}
|
||||
setText("#loggingProfileDefaultFilter", profile.defaultFilter);
|
||||
setText("#loggingProfileSpanEvents", profile.spanEvents);
|
||||
renderConsole(profile);
|
||||
renderFiles(profile);
|
||||
renderTargetFilters(profile);
|
||||
frontendTrace("main", "Logging profile read-only view rendered", {
|
||||
profileId: profile.profileId,
|
||||
source,
|
||||
fileCount: profile.files.length,
|
||||
targetFilterCount: profile.targetFilters.length,
|
||||
});
|
||||
}
|
||||
|
||||
function renderDocument(documentDto: LoggingDocumentDto): void {
|
||||
loggingDocument = documentDto;
|
||||
setText("#loggingFileId", documentDto.fileId);
|
||||
setText("#loggingPath", documentDto.path);
|
||||
setText("#loggingFormatVersion", documentDto.formatVersion.toString());
|
||||
setText("#loggingLogsDirectory", documentDto.logsDirectory);
|
||||
setText("#loggingDefaultProfile", documentDto.defaultProfile);
|
||||
renderProfileSelect(documentDto);
|
||||
renderSelectedProfile("load");
|
||||
frontendTrace("main", "Logging document read-only view rendered", { profileCount: documentDto.profiles.length });
|
||||
}
|
||||
|
||||
async function refreshLoggingDocument(source: "startup" | "user"): Promise<void> {
|
||||
if (source === "user") {
|
||||
frontendDebug("main", "Logging document refresh requested");
|
||||
}
|
||||
setLoggingStatus("Chargement du document Logging typé...");
|
||||
try {
|
||||
const documentDto = await invokeKsp<LoggingDocumentDto>("main", "get_logging_document");
|
||||
renderDocument(documentDto);
|
||||
setLoggingStatus(`${documentDto.profiles.length} profil(s) Logging chargé(s) en lecture seule.`, "success");
|
||||
frontendDebug("main", "Logging document refresh completed", {
|
||||
profileCount: documentDto.profiles.length,
|
||||
defaultProfile: documentDto.defaultProfile,
|
||||
});
|
||||
} catch {
|
||||
loggingDocument = null;
|
||||
setLoggingStatus("Le document Logging typé n'a pas pu être chargé. Consulte les diagnostics backend.", "danger");
|
||||
}
|
||||
}
|
||||
|
||||
/// Initializes the read-only Logging editor surface.
|
||||
export function initializeLoggingPanel(): void {
|
||||
document.querySelector<HTMLButtonElement>("#refreshLoggingDocument")?.addEventListener("click", () => {
|
||||
void refreshLoggingDocument("user");
|
||||
});
|
||||
document.querySelector<HTMLSelectElement>("#loggingProfileSelect")?.addEventListener("change", event => {
|
||||
const profileId = (event.currentTarget as HTMLSelectElement).value;
|
||||
frontendDebug("main", "Logging read-only profile selection changed", { profileId });
|
||||
renderSelectedProfile("selection");
|
||||
});
|
||||
frontendTrace("main", "Logging editor handlers installed");
|
||||
void refreshLoggingDocument("startup");
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-app-config-desk/frontend/ts/main.ts
|
||||
// version: 8
|
||||
// version: 9
|
||||
|
||||
import "bootstrap";
|
||||
import ResizeObserver from "resize-observer-polyfill";
|
||||
@@ -9,6 +9,7 @@ import type { AppSnapshotDto } from "./bindings/ksp_app_config_desk/dto_common/A
|
||||
import { frontendDebug, frontendInfo, frontendTrace, installFrontendConsoleBridge } from "./frontend_log";
|
||||
import { initializeDocumentsPanel } from "./documents";
|
||||
import { initializeEnvironmentPanel } from "./environment";
|
||||
import { initializeLoggingPanel } from "./logging";
|
||||
import { initializeProfilesPanel } from "./profiles";
|
||||
import { invokeKsp } from "./invoke";
|
||||
import { clearTransientSecretReveal } from "./secret_reveal";
|
||||
@@ -37,7 +38,7 @@ const viewCopy: Record<ViewId, { title: string; description: string }> = {
|
||||
},
|
||||
logging: {
|
||||
title: "Logging",
|
||||
description: "Édition des profils et hot reload du runtime Logging — surfaces fonctionnelles prévues en pre.014 à pre.017.",
|
||||
description: "Lecture typée des profils, sinks et filtres Logging ; mutations et hot reload restent dans les tranches suivantes.",
|
||||
},
|
||||
};
|
||||
|
||||
@@ -88,9 +89,9 @@ function activateView(viewId: ViewId, source: "startup" | "user"): void {
|
||||
if (environment) {
|
||||
environment.hidden = viewId !== "environment";
|
||||
}
|
||||
const placeholder = document.querySelector<HTMLElement>("#placeholderPanel");
|
||||
if (placeholder) {
|
||||
placeholder.hidden = viewId !== "logging";
|
||||
const logging = document.querySelector<HTMLElement>("#loggingPanel");
|
||||
if (logging) {
|
||||
logging.hidden = viewId !== "logging";
|
||||
}
|
||||
frontendTrace("main", "Main view DOM updated", { viewId, source });
|
||||
}
|
||||
@@ -152,6 +153,7 @@ async function initializeMain(): Promise<void> {
|
||||
initializeDocumentsPanel();
|
||||
initializeProfilesPanel();
|
||||
initializeEnvironmentPanel();
|
||||
initializeLoggingPanel();
|
||||
activateView("overview", "startup");
|
||||
try {
|
||||
await loadSnapshot();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-app-config-desk/src/constants.rs
|
||||
// version: 6
|
||||
// version: 7
|
||||
|
||||
//! Application-owned tracing targets and domains.
|
||||
|
||||
@@ -25,3 +25,5 @@ pub(crate) const TRACING_DOMAIN_PROFILES: &str = "config.profiles";
|
||||
pub(crate) const TRACING_DOMAIN_ENVIRONMENT: &str = "config.environment";
|
||||
/// Structured domain for explicit privileged Secret reveal operations.
|
||||
pub(crate) const TRACING_DOMAIN_SECRETS: &str = "config.secrets";
|
||||
/// Structured domain for typed Logging editor inspection.
|
||||
pub(crate) const TRACING_DOMAIN_LOGGING_EDITOR: &str = "config.logging_editor";
|
||||
|
||||
@@ -15,6 +15,7 @@ mod dto_common;
|
||||
mod environment;
|
||||
mod errors;
|
||||
mod frontend_logging;
|
||||
mod logging_editor;
|
||||
mod profiles;
|
||||
mod secrets;
|
||||
mod splash;
|
||||
@@ -32,6 +33,7 @@ pub(crate) use self::constants::TRACING_DOMAIN_BOOTSTRAP;
|
||||
pub(crate) use self::constants::TRACING_DOMAIN_DOCUMENTS;
|
||||
pub(crate) use self::constants::TRACING_DOMAIN_ENVIRONMENT;
|
||||
pub(crate) use self::constants::TRACING_DOMAIN_FRONTEND;
|
||||
pub(crate) use self::constants::TRACING_DOMAIN_LOGGING_EDITOR;
|
||||
pub(crate) use self::constants::TRACING_DOMAIN_PROFILES;
|
||||
pub(crate) use self::constants::TRACING_DOMAIN_SECRETS;
|
||||
pub(crate) use self::constants::TRACING_DOMAIN_WINDOWS;
|
||||
@@ -64,6 +66,7 @@ pub(crate) use self::errors::ERROR_CODE_TAURI_WINDOW_MISSING;
|
||||
pub(crate) use self::errors::ERROR_CODE_TAURI_WINDOW_OPERATION_FAILED;
|
||||
pub(crate) use self::frontend_logging::FrontendLogPayloadDto;
|
||||
pub(crate) use self::frontend_logging::emit_frontend_log_event;
|
||||
pub(crate) use self::logging_editor::LoggingDocumentDto;
|
||||
pub(crate) use self::profiles::ConfigProfileDetailDto;
|
||||
pub(crate) use self::profiles::ConfigProfileDocumentDto;
|
||||
pub(crate) use self::secrets::SecretRevealRequestDto;
|
||||
|
||||
200
crates/ksp-app-config-desk/src/logging_editor.rs
Normal file
200
crates/ksp-app-config-desk/src/logging_editor.rs
Normal file
@@ -0,0 +1,200 @@
|
||||
// file: crates/ksp-app-config-desk/src/logging_editor.rs
|
||||
// version: 1
|
||||
|
||||
//! Read-only typed Logging document projection for the Config Desk Logging editor.
|
||||
|
||||
use ts_rs::TS; // rust-rules: derive-import
|
||||
|
||||
/// One Logging sink selector/filter exposed to the read-only editor.
|
||||
#[derive(Clone, Debug, serde::Serialize, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "../frontend/ts/bindings/ksp_app_config_desk/logging/LoggingOutputFilterDto.ts")]
|
||||
pub(crate) struct LoggingOutputFilterDto {
|
||||
/// Filter level source text.
|
||||
pub(crate) level: String,
|
||||
/// Target selectors in source order.
|
||||
pub(crate) targets: std::vec::Vec<String>,
|
||||
/// Structured domain selectors in source order.
|
||||
pub(crate) domains: std::vec::Vec<String>,
|
||||
}
|
||||
|
||||
/// Console sink configuration exposed to the read-only editor.
|
||||
#[derive(Clone, Debug, serde::Serialize, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "../frontend/ts/bindings/ksp_app_config_desk/logging/LoggingConsoleDto.ts")]
|
||||
pub(crate) struct LoggingConsoleDto {
|
||||
/// Whether the console sink is enabled.
|
||||
pub(crate) enabled: bool,
|
||||
/// `stdout` or `stderr` source text.
|
||||
pub(crate) output: String,
|
||||
/// Whether ANSI output is requested.
|
||||
pub(crate) ansi: bool,
|
||||
/// Console format source text.
|
||||
pub(crate) format: String,
|
||||
/// Sink-local selectors.
|
||||
pub(crate) filter: LoggingOutputFilterDto,
|
||||
}
|
||||
|
||||
/// One persistent file sink exposed to the read-only editor.
|
||||
#[derive(Clone, Debug, serde::Serialize, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "../frontend/ts/bindings/ksp_app_config_desk/logging/LoggingFileDto.ts")]
|
||||
pub(crate) struct LoggingFileDto {
|
||||
/// Stable sink identifier.
|
||||
pub(crate) output_id: String,
|
||||
/// Whether the file sink is enabled.
|
||||
pub(crate) enabled: bool,
|
||||
/// Source path relative to the Logging root.
|
||||
pub(crate) path: String,
|
||||
/// Rotation source text.
|
||||
pub(crate) rotation: String,
|
||||
/// File format source text.
|
||||
pub(crate) format: String,
|
||||
/// Whether ANSI output is requested.
|
||||
pub(crate) ansi: bool,
|
||||
/// Sink-local selectors.
|
||||
pub(crate) filter: LoggingOutputFilterDto,
|
||||
}
|
||||
|
||||
/// One global Logging target override exposed to the read-only editor.
|
||||
#[derive(Clone, Debug, serde::Serialize, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "../frontend/ts/bindings/ksp_app_config_desk/logging/LoggingTargetFilterDto.ts")]
|
||||
pub(crate) struct LoggingTargetFilterDto {
|
||||
/// KSP target prefix.
|
||||
pub(crate) target_prefix: String,
|
||||
/// Override level source text.
|
||||
pub(crate) level: String,
|
||||
}
|
||||
|
||||
/// One typed Logging profile exposed to the read-only editor.
|
||||
#[derive(Clone, Debug, serde::Serialize, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "../frontend/ts/bindings/ksp_app_config_desk/logging/LoggingProfileDto.ts")]
|
||||
pub(crate) struct LoggingProfileDto {
|
||||
/// Stable profile identifier.
|
||||
pub(crate) profile_id: String,
|
||||
/// Profile-level default filter source text.
|
||||
pub(crate) default_filter: String,
|
||||
/// Span lifecycle source text.
|
||||
pub(crate) span_events: String,
|
||||
/// Console sink configuration.
|
||||
pub(crate) console: LoggingConsoleDto,
|
||||
/// Persistent file sinks in source order.
|
||||
pub(crate) files: std::vec::Vec<LoggingFileDto>,
|
||||
/// Global target overrides in source order.
|
||||
pub(crate) target_filters: std::vec::Vec<LoggingTargetFilterDto>,
|
||||
}
|
||||
|
||||
/// Complete typed Logging document exposed to the read-only editor.
|
||||
#[derive(Clone, Debug, serde::Serialize, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "../frontend/ts/bindings/ksp_app_config_desk/logging/LoggingDocumentDto.ts")]
|
||||
pub(crate) struct LoggingDocumentDto {
|
||||
/// Stable Config file identifier.
|
||||
pub(crate) file_id: String,
|
||||
/// Physical path resolved by Config.
|
||||
pub(crate) path: String,
|
||||
/// Source format version.
|
||||
pub(crate) format_version: u32,
|
||||
/// Source Logging root expression/path.
|
||||
pub(crate) logs_directory: String,
|
||||
/// Autonomous default profile identifier.
|
||||
pub(crate) default_profile: String,
|
||||
/// Typed profiles in source order.
|
||||
pub(crate) profiles: std::vec::Vec<LoggingProfileDto>,
|
||||
}
|
||||
|
||||
/// Loads the typed Logging source through ConfigManagement and projects it for the frontend.
|
||||
pub(crate) fn document(state: &crate::AppState) -> ksp_core_lib::Result<LoggingDocumentDto> {
|
||||
return document_from_management(state.config_management());
|
||||
}
|
||||
|
||||
fn document_from_management(management: &ksp_config_lib::ConfigManagement) -> ksp_core_lib::Result<LoggingDocumentDto> {
|
||||
let source = management.load_logging_document();
|
||||
let source = match source {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let file_id = ksp_config_lib::ConfigFileId::new(ksp_config_lib::FILE_ID_STD_LOGGING);
|
||||
let file_id = match file_id {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let path = management.engine().registry().resolve_path(management.engine().bootstrap(), &file_id);
|
||||
let path = match path {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let mut profiles = std::vec::Vec::<LoggingProfileDto>::with_capacity(source.profiles().len());
|
||||
let mut file_count = 0usize;
|
||||
let mut target_filter_count = 0usize;
|
||||
for profile in source.profiles() {
|
||||
file_count += profile.files().len();
|
||||
target_filter_count += profile.target_filters().len();
|
||||
profiles.push(project_profile(profile));
|
||||
}
|
||||
let result = LoggingDocumentDto {
|
||||
file_id: file_id.as_str().to_owned(),
|
||||
path: path.display().to_string(),
|
||||
format_version: source.format_version(),
|
||||
logs_directory: source.logs_directory().to_owned(),
|
||||
default_profile: source.default_profile().to_owned(),
|
||||
profiles,
|
||||
};
|
||||
ksp_logging_lib::debug!(
|
||||
target: crate::TRACING_TARGET,
|
||||
domain = crate::TRACING_DOMAIN_LOGGING_EDITOR,
|
||||
file_id = result.file_id.as_str(),
|
||||
profile_count = result.profiles.len(),
|
||||
file_count,
|
||||
target_filter_count,
|
||||
"typed Logging document projected for read-only editor"
|
||||
);
|
||||
return std::result::Result::Ok(result);
|
||||
}
|
||||
|
||||
fn project_profile(profile: &ksp_config_lib::LoggingProfileConfig) -> LoggingProfileDto {
|
||||
let mut files = std::vec::Vec::<LoggingFileDto>::with_capacity(profile.files().len());
|
||||
for file in profile.files() {
|
||||
files.push(LoggingFileDto {
|
||||
output_id: file.output_id().to_owned(),
|
||||
enabled: file.enabled(),
|
||||
path: file.path().to_owned(),
|
||||
rotation: file.rotation().to_owned(),
|
||||
format: file.format().to_owned(),
|
||||
ansi: file.ansi(),
|
||||
filter: project_output_filter(file.filter()),
|
||||
});
|
||||
}
|
||||
let mut target_filters = std::vec::Vec::<LoggingTargetFilterDto>::with_capacity(profile.target_filters().len());
|
||||
for target_filter in profile.target_filters() {
|
||||
target_filters.push(LoggingTargetFilterDto { target_prefix: target_filter.target_prefix().to_owned(), level: target_filter.level().to_owned() });
|
||||
}
|
||||
return LoggingProfileDto {
|
||||
profile_id: profile.profile_id().to_owned(),
|
||||
default_filter: profile.default_filter().to_owned(),
|
||||
span_events: profile.span_events().to_owned(),
|
||||
console: LoggingConsoleDto {
|
||||
enabled: profile.console().enabled(),
|
||||
output: profile.console().output().to_owned(),
|
||||
ansi: profile.console().ansi(),
|
||||
format: profile.console().format().to_owned(),
|
||||
filter: project_output_filter(profile.console().filter()),
|
||||
},
|
||||
files,
|
||||
target_filters,
|
||||
};
|
||||
}
|
||||
|
||||
fn project_output_filter(filter: &ksp_config_lib::LoggingOutputFilterConfig) -> LoggingOutputFilterDto {
|
||||
return LoggingOutputFilterDto {
|
||||
level: filter.level().to_owned(),
|
||||
targets: filter.targets().to_vec(),
|
||||
domains: filter.domains().to_vec(),
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "../unit_tests/logging_editor.rs"]
|
||||
mod tests;
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-app-config-desk/src/tauri.rs
|
||||
// version: 11
|
||||
// version: 12
|
||||
|
||||
//! Tauri runtime assembly for the KSP configuration desktop application.
|
||||
|
||||
@@ -48,6 +48,7 @@ fn configure_commands(builder: tauri::Builder<tauri::Wry>) -> tauri::Builder<tau
|
||||
set_environment_value,
|
||||
remove_environment_value,
|
||||
reveal_environment_value,
|
||||
get_logging_document,
|
||||
emit_frontend_log,
|
||||
splash_frontend_ready
|
||||
]);
|
||||
@@ -170,6 +171,15 @@ fn reveal_environment_value(
|
||||
};
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn get_logging_document(state: tauri::State<'_, crate::AppState>) -> std::result::Result<crate::LoggingDocumentDto, crate::CommandErrorDto> {
|
||||
let result = crate::logging_editor::document(&state);
|
||||
return match result {
|
||||
std::result::Result::Ok(value) => std::result::Result::Ok(value),
|
||||
std::result::Result::Err(error) => std::result::Result::Err(crate::CommandErrorDto::from_error(&error)),
|
||||
};
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn emit_frontend_log(payload: crate::FrontendLogPayloadDto) -> std::result::Result<(), crate::CommandErrorDto> {
|
||||
let result = crate::emit_frontend_log_event(payload);
|
||||
|
||||
64
crates/ksp-app-config-desk/unit_tests/logging_editor.rs
Normal file
64
crates/ksp-app-config-desk/unit_tests/logging_editor.rs
Normal file
@@ -0,0 +1,64 @@
|
||||
// file: crates/ksp-app-config-desk/unit_tests/logging_editor.rs
|
||||
// version: 1
|
||||
|
||||
#[test]
|
||||
fn committed_logging_document_maps_complete_read_only_editor_contract() {
|
||||
let management = committed_management();
|
||||
assert!(management.is_ok(), "committed management should construct: {management:?}");
|
||||
if let std::result::Result::Ok(management) = management {
|
||||
let document = super::document_from_management(&management);
|
||||
assert!(document.is_ok(), "Logging editor document should load: {document:?}");
|
||||
if let std::result::Result::Ok(document) = document {
|
||||
assert_eq!(document.file_id, ksp_config_lib::FILE_ID_STD_LOGGING);
|
||||
assert_eq!(document.format_version, 1);
|
||||
assert_eq!(document.logs_directory, "${KSP_LOGS_DIRECTORY:-logs}");
|
||||
assert_eq!(document.default_profile, "local_dev");
|
||||
assert_eq!(document.profiles.len(), 1);
|
||||
let profile = &document.profiles[0];
|
||||
assert_eq!(profile.profile_id, "local_dev");
|
||||
assert_eq!(profile.default_filter, "warn");
|
||||
assert_eq!(profile.span_events, "new_and_close");
|
||||
assert!(profile.console.enabled);
|
||||
assert_eq!(profile.console.output, "stderr");
|
||||
assert_eq!(profile.console.format, "compact");
|
||||
assert_eq!(profile.console.filter.level, "debug");
|
||||
assert_eq!(profile.console.filter.targets, std::vec!["*".to_owned()]);
|
||||
assert_eq!(profile.console.filter.domains, std::vec!["*".to_owned()]);
|
||||
assert_eq!(profile.files.len(), 2);
|
||||
assert_eq!(profile.files[0].output_id, "file.all.debug");
|
||||
assert_eq!(profile.files[0].filter.targets, std::vec!["*".to_owned()]);
|
||||
assert_eq!(profile.files[0].filter.domains, std::vec!["*".to_owned()]);
|
||||
assert_eq!(profile.target_filters.len(), 3);
|
||||
assert_eq!(profile.target_filters[2].target_prefix, "ksp-app-config-desk");
|
||||
assert_eq!(profile.target_filters[2].level, "debug");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn logging_editor_mapping_preserves_multiple_targets_and_domains() {
|
||||
let filter = ksp_config_lib::LoggingOutputFilterConfig::new(
|
||||
"trace",
|
||||
std::vec!["ksp-config-lib".to_owned(), "ksp-app-config-desk".to_owned()],
|
||||
std::vec!["config".to_owned(), "frontend".to_owned()],
|
||||
);
|
||||
let mapped = super::project_output_filter(&filter);
|
||||
assert_eq!(mapped.level, "trace");
|
||||
assert_eq!(mapped.targets, std::vec!["ksp-config-lib".to_owned(), "ksp-app-config-desk".to_owned()]);
|
||||
assert_eq!(mapped.domains, std::vec!["config".to_owned(), "frontend".to_owned()]);
|
||||
}
|
||||
|
||||
fn committed_management() -> ksp_core_lib::Result<ksp_config_lib::ConfigManagement> {
|
||||
let workspace = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../..");
|
||||
let bootstrap = ksp_config_lib::ConfigBootstrapOptions::from_paths(workspace.join("config"), workspace.join("config/schemas"));
|
||||
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::defaults();
|
||||
let registry = match registry {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
return std::result::Result::Ok(ksp_config_lib::ConfigManagement::new(ksp_config_lib::ConfigDocumentEngine::new(bootstrap, registry)));
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-config-lib/src/management.rs
|
||||
// version: 2
|
||||
// version: 3
|
||||
|
||||
/// Raw source of one registered Config document read for explicit management/correction.
|
||||
#[derive(Clone, Eq, PartialEq)]
|
||||
@@ -476,6 +476,12 @@ impl LoggingFileConfig {
|
||||
self.format = value.into();
|
||||
}
|
||||
|
||||
/// Returns whether ANSI output is requested.
|
||||
#[must_use]
|
||||
pub const fn ansi(&self) -> bool {
|
||||
return self.ansi;
|
||||
}
|
||||
|
||||
/// Returns the output filter.
|
||||
#[must_use]
|
||||
pub const fn filter(&self) -> &LoggingOutputFilterConfig {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-config-lib/tests/public_api.rs
|
||||
// version: 12
|
||||
// version: 13
|
||||
|
||||
//! Integration tests for the public `ksp-config-lib` bootstrap, registry, JSON/profile/composite, environment-resolution, sensitivity, Logging-adapter and
|
||||
//! management contracts.
|
||||
@@ -217,6 +217,8 @@ fn management_contracts_are_available_from_crate_root() {
|
||||
assert_eq!(logging.logs_directory(), "public-api-management-test");
|
||||
assert_eq!(logging.profiles().len(), 1);
|
||||
assert_eq!(logging.profiles()[0].profile_id(), "local_dev");
|
||||
assert_eq!(logging.profiles()[0].files().len(), 2);
|
||||
assert!(!logging.profiles()[0].files()[0].ansi());
|
||||
}
|
||||
}
|
||||
let save_source_candidate: fn(
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-config-lib/unit_tests/management.rs
|
||||
// version: 2
|
||||
// version: 3
|
||||
|
||||
static NEXT_FIXTURE_ID: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(1);
|
||||
|
||||
@@ -230,6 +230,8 @@ fn typed_logging_document_can_be_mutated_validated_and_persisted_atomically() {
|
||||
assert_eq!(document.format_version(), 1);
|
||||
assert_eq!(document.default_profile(), "local_dev");
|
||||
assert_eq!(document.profiles().len(), 1);
|
||||
assert_eq!(document.profiles()[0].files().len(), 2);
|
||||
assert!(!document.profiles()[0].files()[0].ansi());
|
||||
document.set_logs_directory("managed-logs");
|
||||
if let std::option::Option::Some(profile) = document.profiles_mut().first_mut() {
|
||||
profile.set_default_filter("info");
|
||||
|
||||
107
deltas/0.1.4/pre.014.md
Normal file
107
deltas/0.1.4/pre.014.md
Normal file
@@ -0,0 +1,107 @@
|
||||
<!-- file: deltas/0.1.4/pre.014.md -->
|
||||
<!-- version: 1 -->
|
||||
|
||||
# Delta `0.1.4-pre.014` — Logging editor, lecture/DTO
|
||||
|
||||
## Statut
|
||||
|
||||
Tranche livrée pour validation locale.
|
||||
|
||||
`0.1.4-pre.013` est considérée validée : `fmt`, `check`, `clippy`, les 38 tests Config Desk, les 87 tests Config, les cinq audits ownership et les 12 tests d'API publique passent. Les reveals privilégiés `effective` et `.env` ont été exercés avec confirmation explicite, affichage/masquage transitoire et nettoyage à la fermeture, sans valeur Secret visible dans les logs.
|
||||
|
||||
## Objectif
|
||||
|
||||
Ouvrir la surface Logging fonctionnelle en **lecture typée uniquement**, en utilisant le contrat public de `ksp-config-lib` comme unique autorité et sans commencer prématurément les mutations, la persistence ou le hot reload.
|
||||
|
||||
## Modifications
|
||||
|
||||
### Backend
|
||||
|
||||
- nouveau module `logging_editor.rs` ;
|
||||
- chargement exclusif via `ConfigManagement::load_logging_document()` ;
|
||||
- extension publique minimale de Config : `LoggingFileConfig::ansi()` permet de projeter le champ déjà possédé par le contrat typé sans sérialisation JSON de contournement ;
|
||||
- résolution du path via le registre Config, sans chemin physique codé en dur ;
|
||||
- mapping des types publics Config vers des DTO TS-RS dédiés :
|
||||
- `LoggingDocumentDto` ;
|
||||
- `LoggingProfileDto` ;
|
||||
- `LoggingConsoleDto` ;
|
||||
- `LoggingFileDto` ;
|
||||
- `LoggingOutputFilterDto` ;
|
||||
- `LoggingTargetFilterDto` ;
|
||||
- nouvelle commande Tauri centralisée `get_logging_document` ;
|
||||
- nouveau domaine technique `config.logging_editor` ;
|
||||
- logs bornés aux compteurs/identifiants de structure, sans contenu sensible.
|
||||
|
||||
### Frontend
|
||||
|
||||
- la route **Logging** remplace son placeholder par une vue réelle ;
|
||||
- nouveau module `frontend/ts/logging.ts` ;
|
||||
- affichage read-only de :
|
||||
- `file_id` ;
|
||||
- path résolu ;
|
||||
- `format_version` ;
|
||||
- `logs_directory` ;
|
||||
- `default_profile` ;
|
||||
- sélection locale du profil inspecté ;
|
||||
- affichage de `default_filter` et `span_events` ;
|
||||
- projection de la console avec son filtre `level/targets/domains` ;
|
||||
- projection de tous les fichiers et de leurs filtres ;
|
||||
- projection des `target_filters` globaux ;
|
||||
- bouton **Rafraîchir** pour relire le contrat typé ;
|
||||
- aucune sauvegarde ni mutation dans cette tranche.
|
||||
|
||||
### Tests
|
||||
|
||||
- le document Logging commité doit mapper l'intégralité de la surface de lecture attendue ;
|
||||
- le getter `LoggingFileConfig::ansi()` est verrouillé dans les tests unitaires et dans le test d'intégration de l'API publique ;
|
||||
- les listes multiples `targets` et `domains` doivent être conservées sans perte ni réordonnancement ;
|
||||
- les dérivations TS-RS ajoutent les tests d'export des six nouveaux DTO.
|
||||
|
||||
### Documentation
|
||||
|
||||
- `pre.013` marquée réalisée ;
|
||||
- `pre.014` marquée en cours ;
|
||||
- README/USAGE/TODO alignés sur la nouvelle surface Logging read-only.
|
||||
|
||||
## Hors scope maintenu
|
||||
|
||||
Cette tranche ne modifie pas :
|
||||
|
||||
- les profils Logging ;
|
||||
- `default_profile` ;
|
||||
- les sinks ;
|
||||
- les filtres ;
|
||||
- le runtime Logging actif ;
|
||||
- `LoggingGuard` ;
|
||||
- les fichiers de log persistés.
|
||||
|
||||
Les mutations/persistence appartiennent à `pre.015`; le runtime/hot reload et les fichiers uniques par lancement appartiennent à `pre.016`.
|
||||
|
||||
## Scénarios de validation
|
||||
|
||||
1. ouvrir **Logging** : `cfg.std.logging` et son path résolu doivent apparaître ;
|
||||
2. vérifier `format_version=1`, `logs_directory=${KSP_LOGS_DIRECTORY:-logs}` et `default_profile=local_dev` ;
|
||||
3. le sélecteur doit proposer `local_dev (default)` ;
|
||||
4. le profil doit afficher `default_filter=warn` et `span_events=new_and_close` ;
|
||||
5. la console doit afficher stderr / ANSI oui / compact / level debug / targets `*` / domains `*` ;
|
||||
6. les deux fichiers `file.all.debug` et `file.config.error` doivent être présents avec leurs chemins, formats, rotations et filtres ;
|
||||
7. les trois target filters `ksp-config-lib`, `ksp-logging-lib`, `ksp-app-config-desk` doivent apparaître ;
|
||||
8. **Rafraîchir** doit relire le document sans modifier le runtime ni la génération Logging.
|
||||
|
||||
## Version technique
|
||||
|
||||
```text
|
||||
workspace.package.version = "0.1.4-pre.14"
|
||||
```
|
||||
|
||||
## Validation locale demandée
|
||||
|
||||
```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
|
||||
```
|
||||
@@ -1,5 +1,5 @@
|
||||
<!-- file: docs/plans/006-V0_1_4_CONFIG_DESKTOP_PLAN.md -->
|
||||
<!-- version: 15 -->
|
||||
<!-- version: 16 -->
|
||||
|
||||
# Plan `0.1.4` — `ksp-app-config-desk`
|
||||
|
||||
@@ -238,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.
|
||||
|
||||
@@ -249,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 :
|
||||
|
||||
@@ -694,23 +694,23 @@ Le shell applique également une instrumentation frontend systématique : clics
|
||||
|
||||
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` :
|
||||
|
||||
@@ -1226,14 +1226,14 @@ pre.012 Environnement — management/test .env [réalisé]
|
||||
- démonstration process > .env
|
||||
- démonstration placeholder KSP_LOGS_DIRECTORY
|
||||
|
||||
pre.013 Secrets privilégiés [en cours]
|
||||
pre.013 Secrets privilégiés [réalisé]
|
||||
- DTO séparés
|
||||
- confirmation explicite
|
||||
- reveal transitoire
|
||||
- édition Secret sans préchargement réel
|
||||
- audits non-divulgation
|
||||
|
||||
pre.014 Logging editor — lecture/DTO
|
||||
pre.014 Logging editor — lecture/DTO [en cours]
|
||||
- document/profils/console/files/filters
|
||||
- targets/domains
|
||||
- mapping types Config -> DTO
|
||||
|
||||
Reference in New Issue
Block a user