v0.5.3-pre.002
This commit is contained in:
@@ -1,8 +1,33 @@
|
||||
<!-- file: kb-app-demo-desktop/CHANGELOG.md -->
|
||||
<!-- version: 60 -->
|
||||
<!-- version: 64 -->
|
||||
|
||||
# CHANGELOG — kb-app-demo-desktop
|
||||
|
||||
## `0.5.3-pre.002-delta-fix-004`
|
||||
|
||||
- aligne les versions Tauri/Vite de l'application sur `0.5.3` dans `package.json` et `tauri.conf.json` ;
|
||||
- remplace le groupe visible `SQL` par `Store` dans le menu principal et renomme les entrées backend-spécifiques en `Raw store` / `Core store` ;
|
||||
- retire les titres de fenêtres `PostgreSQL ...` des surfaces `demo_store_*` et renomme le titre de la page de replay pour rester backend-agnostique ;
|
||||
- réconcilie les rustdocs, les bindings TS-RS et la documentation d'usage des surfaces desktop afin qu'ils décrivent la façade `Store` et non PostgreSQL/SQL ;
|
||||
- aligne les messages de préparation Devnet et le texte d'extraction Core sur les ressources/store génériques au lieu de tables ou transactions PostgreSQL.
|
||||
|
||||
## `0.5.3-pre.002-delta-fix-003`
|
||||
|
||||
- corrige `demo_core_extraction_execute`, qui reçoit déjà `&AppState` et ne doit donc pas appeler `state.inner()` avant de réutiliser le `Store` partagé ;
|
||||
- rend `store_open_options_from_profile` strictement privé à `demo_store_common` et supprime son réexport crate-root devenu inutilisé.
|
||||
|
||||
## `0.5.3-pre.002-delta-fix-002`
|
||||
|
||||
- renomme les modules, commandes Tauri, fenêtres, pages HTML, TypeScript et DTO de `demo_sql_*` vers `demo_store_*` afin que la nomenclature ne dépende plus d’un backend SQL ;
|
||||
- conserve la refonte fonctionnelle finale des tableaux/compteurs pour `pre.005`.
|
||||
|
||||
## `0.5.3-pre.002`
|
||||
|
||||
- conserve un `ks_store::Store` backend-agnostique dans `AppState` via `OnceCell` et réutilise ce handle dans les commandes de backfill, extraction Core, replay et scénarios d'exécution ;
|
||||
- remplace les payloads SQL/configuration dépendants de PostgreSQL par des descripteurs de connexion sanitisés et des diagnostics de ressources/modèles génériques ;
|
||||
- adapte le splash à la vérification du store par modèle logique et compteurs de ressources, sans afficher les noms physiques des tables/index ;
|
||||
- remplace le vocabulaire replay `outer` par `top-level` dans les nouveaux contrats UI, tout en conservant temporairement les noms techniques des fenêtres `demo_store_*` jusqu'à leur refonte finale.
|
||||
|
||||
## `0.5.2-pre.006-delta-fix-009`
|
||||
|
||||
- corrige les adaptateurs Tauri Devnet qui transmettaient directement `tauri::State<AppState>` aux helpers wallet/projection attendant `&AppState`; les commandes empruntent désormais explicitement `&state` pour System, SPL et les trois familles Metadata ;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!-- file: kb-app-demo-desktop/README.md -->
|
||||
<!-- version: 21 -->
|
||||
<!-- version: 23 -->
|
||||
|
||||
# kb-app-demo-desktop
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
- configuration et diagnostics de logging ;
|
||||
- fenêtre Wallets dédiée : inventaire sûr des identités `.kswallet`, création d’un conteneur persistant via un secret backend-only, sélection session-only du wallet d'exécution Devnet, inspection/import/export des formats keypair supportés sous `data/wallets/`, extraction sûre de pubkey depuis un keypair legacy, inspection explicite d’un `.kswallet` externe et exploration publique du solde SOL, des comptes SPL Token / Token-2022 et des transactions récentes sur un profil RPC explicitement sélectionnable ;
|
||||
- transport HTTP JSON-RPC et WebSocket ;
|
||||
- diagnostics PostgreSQL ;
|
||||
- diagnostics de stockage backend-agnostiques ;
|
||||
- backfill, extraction Core et replay, avec enregistrement générique de Solana Program Metadata ;
|
||||
- démonstrations Solana Core, SPL Memo, SPL Token, ATA et Token-2022 ;
|
||||
- panneau Metadata avec parcours séparés Metaplex Token Metadata, Token-2022 Token Metadata et Solana Program Metadata, préparation du profil et de la base, simulation RPC réelle, soumission explicitement confirmée et affichage des postconditions ;
|
||||
@@ -20,6 +20,8 @@
|
||||
|
||||
Les commandes Tauri sont des adaptateurs minces. La logique réutilisable appartient à `ks-pipeline`, `ks-pipeline-demo-scenarios`, `ks-lib`, `ks-store` et `ks-onchain-transport`.
|
||||
|
||||
Depuis `0.5.3-pre.002`, l'application conserve une façade `ks_store::Store` unique dans `AppState`. Les commandes ne construisent plus de `PostgresStore` et les payloads de configuration/diagnostic ne transportent plus de champs PostgreSQL/SQLite concrets. Les anciennes surfaces `demo_sql_*` sont renommées en `demo_store_*` dès `pre.002`; la refonte fonctionnelle finale de leurs tableaux et compteurs reste planifiée en `pre.005`.
|
||||
|
||||
Les campagnes Devnet/Testnet réutilisables appartiennent à `ks-pipeline-demo-scenarios`. Le desktop conserve leurs adaptateurs UI/Tauri, la sélection opérateur, la progression, les payloads TS-RS et la présentation ; `0.5.4` doit retirer les orchestrations réutilisables qui subsistent encore localement.
|
||||
|
||||
Depuis `0.5.1-pre.008`, TS-RS appartient à cette frontière applicative : `ks-config` et `ks-lib` ne génèrent plus de bindings TypeScript. Lorsqu'un type généraliste doit être présenté au frontend, le desktop construit un DTO/wrapper dédié.
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
<!-- file: kb-app-demo-desktop/TODO.md -->
|
||||
<!-- version: 20 -->
|
||||
<!-- version: 22 -->
|
||||
|
||||
# TODO — kb-app-demo-desktop
|
||||
|
||||
## Évolutions générales
|
||||
|
||||
- [ ] `0.5.3` — finaliser en `pre.005` la présentation des surfaces `demo_store_*`, puis aligner `demo_config` et le splash sur les modèles/compteurs du nouveau baseline sans exposer de noms physiques ; le renommage technique est terminé en `pre.002`.
|
||||
- [ ] `0.5.4` — réduire les panneaux d'exécution à l'adaptation UI/Tauri en déplaçant toute orchestration de campagne encore réutilisable vers `ks-pipeline-demo-scenarios`.
|
||||
- [ ] UX — empêcher les doubles déclenchements pendant une préparation, simulation ou soumission active.
|
||||
- [ ] Observabilité — afficher explicitement les comptes relus et les projections matérialisées.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!-- file: kb-app-demo-desktop/USAGE.md -->
|
||||
<!-- version: 21 -->
|
||||
<!-- version: 24 -->
|
||||
|
||||
# Utilisation de kb-app-demo-desktop
|
||||
|
||||
@@ -116,7 +116,7 @@ Les autres fenêtres sont ouvertes par les commandes Tauri dédiées :
|
||||
- backfill ;
|
||||
- HTTP ;
|
||||
- WebSocket ;
|
||||
- diagnostics SQL ;
|
||||
- diagnostics store ;
|
||||
- candidats replay ;
|
||||
- configuration ;
|
||||
- wallets ;
|
||||
@@ -311,22 +311,24 @@ L’inventaire contient cinq variantes `Create -> Mint`, puis collection `Verify
|
||||
|
||||
Dans la colonne de résultats, `Préflight et opérations`, `Simulation et confirmation` et `Postconditions et preuves` sont présentés dans un accordéon commun. La carte `Exigences` reste sous cet accordéon et le journal d’exécution reste sous la grille, sur toute la largeur.
|
||||
|
||||
### Diagnostics PostgreSQL
|
||||
### Diagnostics du store
|
||||
|
||||
```typescript
|
||||
const diagnostics = await invoke(
|
||||
"load_demo_sql_diag"
|
||||
"load_demo_store_diag"
|
||||
);
|
||||
const rawTables = await invoke(
|
||||
"load_demo_sql_pg_raw"
|
||||
"load_demo_store_raw"
|
||||
);
|
||||
const coreTables = await invoke(
|
||||
"load_demo_sql_pg_core"
|
||||
"load_demo_store_core"
|
||||
);
|
||||
|
||||
console.log(diagnostics, rawTables, coreTables);
|
||||
```
|
||||
|
||||
Les commandes/fenêtres historiques `demo_sql_*` sont renommées en `demo_store_*` dès `pre.002`, et leurs payloads sont backend-agnostiques : `connectionDescriptor`, `resources`, `backend`, `namespace` et compteurs logiques. Le frontend n'accède plus à un DSN PostgreSQL ni à des noms physiques de tables. La tranche consommateurs de `0.5.3` doit finaliser la présentation tabulaire de `demo_store_*`, aligner `demo_config` et exploiter les compteurs tables/index du nouveau baseline dans le splash.
|
||||
|
||||
## Capabilities Tauri
|
||||
|
||||
Les fenêtres autorisées sont listées dans `capabilities/default.json`. La capability active doit inclure au minimum les permissions Tauri Core et tracing nécessaires aux fenêtres déclarées.
|
||||
@@ -350,7 +352,7 @@ Toute nouvelle fenêtre ou commande sensible doit être auditée avant ajout de
|
||||
- tests de System Transfer et Memo ;
|
||||
- tests ATA, SPL Token et Token-2022 ;
|
||||
- tests de backfill, extraction Core et decode replay ;
|
||||
- tests de diagnostics SQL et exports CSV ;
|
||||
- tests de diagnostics store et exports CSV ;
|
||||
- tests de journalisation frontend ;
|
||||
- test du verrou d’état applicatif et de la séquence splash.
|
||||
|
||||
@@ -368,7 +370,7 @@ Depuis la fenêtre principale, choisir **Exécution Devnet → Exécution Metada
|
||||
|
||||
- de choisir un scénario synthétique ou un contrat de campagne Devnet ;
|
||||
- de sélectionner le profil Devnet ;
|
||||
- de préparer et vérifier la base PostgreSQL du profil ;
|
||||
- de préparer et vérifier le store persistant du profil ;
|
||||
- d’afficher les contrats, contrôles de préflight et preuves attendues dans le JSON viewer commun ;
|
||||
- de restaurer le scénario sélectionné lors de la réouverture.
|
||||
|
||||
@@ -378,7 +380,7 @@ Les scénarios synthétiques restent disponibles pour inspecter les contrats san
|
||||
|
||||
### Campagnes Metaplex qualifiées
|
||||
|
||||
Ouvrir **Metaplex Token Metadata -> Campagnes Devnet qualifiées**, sélectionner un profil persistant et préparer PostgreSQL. Le sélecteur présente 11 campagnes : cinq familles `Create -> Mint`, collection, Print/Burn, lifecycle pNFT, escrow, maintenance et Use. Après confirmation opérateur, le desktop appelle le runner correspondant et affiche :
|
||||
Ouvrir **Metaplex Token Metadata -> Campagnes Devnet qualifiées**, sélectionner un profil persistant et préparer le store du profil. Le sélecteur présente 11 campagnes : cinq familles `Create -> Mint`, collection, Print/Burn, lifecycle pNFT, escrow, maintenance et Use. Après confirmation opérateur, le desktop appelle le runner correspondant et affiche :
|
||||
|
||||
- la fixture et son setup ;
|
||||
- les exécutions confirmées et les probes indisponibles dans leur ordre réel ;
|
||||
@@ -389,7 +391,7 @@ La surface générique par opération reste une API avancée et ne doit pas êtr
|
||||
|
||||
### Campagne Token-2022 Token Metadata
|
||||
|
||||
Ouvrir l’accordéon **Token-2022 Token Metadata**, sélectionner un profil Devnet persistant et préparer sa base PostgreSQL. Le panneau affiche le contrat fixe de cinq opérations :
|
||||
Ouvrir l’accordéon **Token-2022 Token Metadata**, sélectionner un profil Devnet persistant et préparer son store persistant. Le panneau affiche le contrat fixe de cinq opérations :
|
||||
|
||||
```text
|
||||
Initialize -> UpdateField -> Emit -> RemoveKey -> UpdateAuthority
|
||||
|
||||
@@ -12,10 +12,10 @@
|
||||
"demo_backfill",
|
||||
"demo_core_extraction",
|
||||
"demo_decode_replay",
|
||||
"demo_sql_diag",
|
||||
"demo_sql_pg_raw",
|
||||
"demo_sql_pg_core",
|
||||
"demo_sql_replay_candidates",
|
||||
"demo_store_diag",
|
||||
"demo_store_raw",
|
||||
"demo_store_core",
|
||||
"demo_store_replay_candidates",
|
||||
"demo_execution_solana_core",
|
||||
"demo_execution_spl",
|
||||
"demo_execution_metadata"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!-- file: kb-app-demo-desktop/frontend/demo_core_extraction.html -->
|
||||
<!-- version: 10 -->
|
||||
<!-- version: 11 -->
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
<div class="card shadow-sm border-0">
|
||||
<div class="card-body">
|
||||
<h1 class="h3 card-title">Extraction transactionnelle canonical vers core</h1>
|
||||
<p class="text-body-secondary mb-0">Chaque transaction canonique sélectionnée est validée, normalisée dans les tables core et enregistrée dans le ledger de traitement au sein d'une seule transaction PostgreSQL.</p>
|
||||
<p class="text-body-secondary mb-0">Chaque transaction canonique sélectionnée est validée, normalisée dans les tables core et enregistrée dans le ledger de traitement au sein d'une seule transaction de stockage atomique.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -163,4 +163,4 @@
|
||||
<script defer="" src="ts/demo_core_extraction.ts" type="module"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!-- file: kb-app-demo-desktop/frontend/demo_decode_replay.html -->
|
||||
<!-- version: 8 -->
|
||||
<!-- version: 9 -->
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
</div>
|
||||
<div class="col-12 col-lg-6">
|
||||
<label class="form-label" for="decodeReplaySignaturesTextarea">Signatures explicites, une par ligne</label>
|
||||
<textarea id="decodeReplaySignaturesTextarea" class="form-control font-monospace" rows="7" placeholder="Copier depuis demo_sql_replay_candidates"></textarea>
|
||||
<textarea id="decodeReplaySignaturesTextarea" class="form-control font-monospace" rows="7" placeholder="Copier depuis demo_store_replay_candidates"></textarea>
|
||||
</div>
|
||||
<div class="col-12 col-lg-6">
|
||||
<label class="form-label" for="decodeReplayPathsTextarea">Instruction paths exacts, un par ligne</label>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!-- file: kb-app-demo-desktop/frontend/demo_sql_pg_core.html -->
|
||||
<!-- version: 6 -->
|
||||
<!-- file: kb-app-demo-desktop/frontend/demo_store_core.html -->
|
||||
<!-- version: 8 -->
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<meta content="width=device-width, initial-scale=1.0" name="viewport">
|
||||
<link rel="icon" href="imgs/logo.png">
|
||||
<link rel="stylesheet" href="sass/main.scss">
|
||||
<title>Khadhroony Bot3 — PostgreSQL core store</title>
|
||||
<title>Khadhroony Bot3 — Core store diagnostics</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@@ -17,7 +17,7 @@
|
||||
<div class="container-fluid px-4 my-0">
|
||||
<div class="navbar-brand d-flex align-items-center">
|
||||
<img alt="Logo" src="imgs/logo.png" class="app-logo">
|
||||
<span class="ps-2 fs-4 fw-bold text-primary font-logo">Bot3 — <span class="badge text-bg-primary">PostgreSQL core store</span></span>
|
||||
<span class="ps-2 fs-4 fw-bold text-primary font-logo">Bot3 — <span class="badge text-bg-primary">Core store diagnostics</span></span>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
@@ -36,11 +36,11 @@
|
||||
</div>
|
||||
<dl class="row mb-0">
|
||||
<dt class="col-sm-3">Statut</dt>
|
||||
<dd class="col-sm-9"><code id="sqlCoreStatus">Chargement...</code></dd>
|
||||
<dd class="col-sm-9"><code id="storeCoreStatus">Chargement...</code></dd>
|
||||
<dt class="col-sm-3">Profil</dt>
|
||||
<dd class="col-sm-9"><code id="sqlCoreProfile">—</code></dd>
|
||||
<dt class="col-sm-3">DSN</dt>
|
||||
<dd class="col-sm-9"><code id="sqlCoreDsn">—</code></dd>
|
||||
<dd class="col-sm-9"><code id="storeCoreProfile">—</code></dd>
|
||||
<dt class="col-sm-3">Connexion</dt>
|
||||
<dd class="col-sm-9"><code id="storeCoreConnection">—</code></dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
@@ -49,13 +49,13 @@
|
||||
<div class="col-12">
|
||||
<div class="card shadow-sm border-0">
|
||||
<div class="card-body">
|
||||
<h2 class="h5 card-title mb-3">Tables core</h2>
|
||||
<h2 class="h5 card-title mb-3">Ressources Core</h2>
|
||||
<div class="table-responsive app-table-full-width">
|
||||
<table class="table table-sm align-middle mb-0" id="sqlCoreTable">
|
||||
<table class="table table-sm align-middle mb-0" id="storeCoreTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Table</th>
|
||||
<th>Domaine</th>
|
||||
<th>Ressource</th>
|
||||
<th>Modèle</th>
|
||||
<th>Rôle</th>
|
||||
<th>Existe</th>
|
||||
<th>Lignes</th>
|
||||
@@ -64,7 +64,7 @@
|
||||
<th>Dernière insertion</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="sqlCoreTableBody"></tbody>
|
||||
<tbody id="storeCoreTableBody"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
@@ -75,7 +75,7 @@
|
||||
<div class="card shadow-sm border-0">
|
||||
<div class="card-body">
|
||||
<h2 class="h5 card-title mb-3">Payload JSON</h2>
|
||||
<div id="sqlCoreJson" class="app-config-box app-json-viewer mb-0">{"status":"loading","message":"Chargement des données SQL."}</div>
|
||||
<div id="storeCoreJson" class="app-config-box app-json-viewer mb-0">{"status":"loading","message":"Chargement des données du store."}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -91,7 +91,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
<script type="module" src="ts/demo_sql_pg_core.ts" defer></script>
|
||||
<script type="module" src="ts/demo_store_core.ts" defer></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
||||
@@ -1,5 +1,5 @@
|
||||
<!-- file: kb-app-demo-desktop/frontend/demo_sql_diag.html -->
|
||||
<!-- version: 5 -->
|
||||
<!-- file: kb-app-demo-desktop/frontend/demo_store_diag.html -->
|
||||
<!-- version: 7 -->
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<meta content="width=device-width, initial-scale=1.0" name="viewport">
|
||||
<link rel="icon" href="imgs/logo.png">
|
||||
<link rel="stylesheet" href="sass/main.scss">
|
||||
<title>Khadhroony Bot3 — SQL diagnostics</title>
|
||||
<title>Khadhroony Bot3 — Store diagnostics</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@@ -17,7 +17,7 @@
|
||||
<div class="container-fluid px-4 my-0">
|
||||
<div class="navbar-brand d-flex align-items-center">
|
||||
<img alt="Logo" src="imgs/logo.png" class="app-logo">
|
||||
<span class="ps-2 fs-4 fw-bold text-primary font-logo">Bot3 — <span class="badge text-bg-primary">SQL diagnostics</span></span>
|
||||
<span class="ps-2 fs-4 fw-bold text-primary font-logo">Bot3 — <span class="badge text-bg-primary">Store diagnostics</span></span>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
@@ -31,7 +31,7 @@
|
||||
<div class="card shadow-sm border-0">
|
||||
<div class="card-body">
|
||||
<div class="d-flex flex-wrap gap-2 justify-content-between align-items-center mb-3">
|
||||
<h1 class="h3 card-title mb-0">Diagnostic PostgreSQL</h1>
|
||||
<h1 class="h3 card-title mb-0">Diagnostic du store</h1>
|
||||
<div class="d-flex gap-2">
|
||||
<button id="refreshSqlDiagButton" class="btn btn-primary" type="button">Rafraîchir</button>
|
||||
<button id="copySqlDiagButton" class="btn btn-outline-secondary" type="button">Copier JSON</button>
|
||||
@@ -39,21 +39,21 @@
|
||||
</div>
|
||||
<dl class="row mb-0">
|
||||
<dt class="col-sm-3">Statut</dt>
|
||||
<dd class="col-sm-9"><code id="sqlDiagStatus">Chargement...</code></dd>
|
||||
<dd class="col-sm-9"><code id="storeDiagStatus">Chargement...</code></dd>
|
||||
<dt class="col-sm-3">Fichier config</dt>
|
||||
<dd class="col-sm-9"><code id="sqlDiagConfigPath">—</code></dd>
|
||||
<dd class="col-sm-9"><code id="storeDiagConfigPath">—</code></dd>
|
||||
<dt class="col-sm-3">Profil</dt>
|
||||
<dd class="col-sm-9"><code id="sqlDiagProfile">—</code></dd>
|
||||
<dd class="col-sm-9"><code id="storeDiagProfile">—</code></dd>
|
||||
<dt class="col-sm-3">Backend</dt>
|
||||
<dd class="col-sm-9"><code id="sqlDiagBackend">—</code></dd>
|
||||
<dt class="col-sm-3">DSN</dt>
|
||||
<dd class="col-sm-9"><code id="sqlDiagDsn">—</code></dd>
|
||||
<dd class="col-sm-9"><code id="storeDiagBackend">—</code></dd>
|
||||
<dt class="col-sm-3">Connexion</dt>
|
||||
<dd class="col-sm-9"><code id="storeDiagConnection">—</code></dd>
|
||||
<dt class="col-sm-3">Schema courant</dt>
|
||||
<dd class="col-sm-9"><code id="sqlDiagSchema">—</code></dd>
|
||||
<dd class="col-sm-9"><code id="storeDiagSchema">—</code></dd>
|
||||
<dt class="col-sm-3">Healthcheck</dt>
|
||||
<dd class="col-sm-9"><code id="sqlDiagHealth">—</code></dd>
|
||||
<dd class="col-sm-9"><code id="storeDiagHealth">—</code></dd>
|
||||
<dt class="col-sm-3">Migrations</dt>
|
||||
<dd class="col-sm-9"><code id="sqlDiagMigration">—</code></dd>
|
||||
<dd class="col-sm-9"><code id="storeDiagMigration">—</code></dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
@@ -62,13 +62,13 @@
|
||||
<div class="col-12">
|
||||
<div class="card shadow-sm border-0">
|
||||
<div class="card-body">
|
||||
<h2 class="h5 card-title mb-3">Tables raw/core connues</h2>
|
||||
<h2 class="h5 card-title mb-3">Ressources store connues</h2>
|
||||
<div class="table-responsive app-table-full-width">
|
||||
<table class="table table-sm align-middle mb-0" id="sqlDiagTable">
|
||||
<table class="table table-sm align-middle mb-0" id="storeDiagTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Table</th>
|
||||
<th>Domaine</th>
|
||||
<th>Ressource</th>
|
||||
<th>Modèle</th>
|
||||
<th>Rôle</th>
|
||||
<th>Existe</th>
|
||||
<th>Lignes</th>
|
||||
@@ -77,7 +77,7 @@
|
||||
<th>Dernière insertion</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="sqlDiagTableBody"></tbody>
|
||||
<tbody id="storeDiagTableBody"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
@@ -88,7 +88,7 @@
|
||||
<div class="card shadow-sm border-0">
|
||||
<div class="card-body">
|
||||
<h2 class="h5 card-title mb-3">Payload JSON</h2>
|
||||
<div id="sqlDiagJson" class="app-config-box app-json-viewer mb-0">{"status":"loading","message":"Chargement des données SQL."}</div>
|
||||
<div id="storeDiagJson" class="app-config-box app-json-viewer mb-0">{"status":"loading","message":"Chargement des données du store."}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -104,7 +104,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
<script type="module" src="ts/demo_sql_diag.ts" defer></script>
|
||||
<script type="module" src="ts/demo_store_diag.ts" defer></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
||||
@@ -1,5 +1,5 @@
|
||||
<!-- file: kb-app-demo-desktop/frontend/demo_sql_pg_raw.html -->
|
||||
<!-- version: 7 -->
|
||||
<!-- file: kb-app-demo-desktop/frontend/demo_store_raw.html -->
|
||||
<!-- version: 9 -->
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<meta content="width=device-width, initial-scale=1.0" name="viewport">
|
||||
<link rel="icon" href="imgs/logo.png">
|
||||
<link rel="stylesheet" href="sass/main.scss">
|
||||
<title>Khadhroony Bot3 — PostgreSQL canonical acquisition</title>
|
||||
<title>Khadhroony Bot3 — Raw store diagnostics</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@@ -17,7 +17,7 @@
|
||||
<div class="container-fluid px-4 my-0">
|
||||
<div class="navbar-brand d-flex align-items-center">
|
||||
<img alt="Logo" src="imgs/logo.png" class="app-logo">
|
||||
<span class="ps-2 fs-4 fw-bold text-primary font-logo">Bot3 — <span class="badge text-bg-primary">PostgreSQL canonical acquisition</span></span>
|
||||
<span class="ps-2 fs-4 fw-bold text-primary font-logo">Bot3 — <span class="badge text-bg-primary">Raw store diagnostics</span></span>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
@@ -36,11 +36,11 @@
|
||||
</div>
|
||||
<dl class="row mb-0">
|
||||
<dt class="col-sm-3">Statut</dt>
|
||||
<dd class="col-sm-9"><code id="sqlRawStatus">Chargement...</code></dd>
|
||||
<dd class="col-sm-9"><code id="storeRawStatus">Chargement...</code></dd>
|
||||
<dt class="col-sm-3">Profil</dt>
|
||||
<dd class="col-sm-9"><code id="sqlRawProfile">—</code></dd>
|
||||
<dt class="col-sm-3">DSN</dt>
|
||||
<dd class="col-sm-9"><code id="sqlRawDsn">—</code></dd>
|
||||
<dd class="col-sm-9"><code id="storeRawProfile">—</code></dd>
|
||||
<dt class="col-sm-3">Connexion</dt>
|
||||
<dd class="col-sm-9"><code id="storeRawConnection">—</code></dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
@@ -51,11 +51,11 @@
|
||||
<div class="card-body">
|
||||
<h2 class="h5 card-title mb-3">Transactions canoniques et observations</h2>
|
||||
<div class="table-responsive app-table-full-width">
|
||||
<table class="table table-sm align-middle mb-0" id="sqlRawTable">
|
||||
<table class="table table-sm align-middle mb-0" id="storeRawTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Table</th>
|
||||
<th>Domaine</th>
|
||||
<th>Ressource</th>
|
||||
<th>Modèle</th>
|
||||
<th>Rôle</th>
|
||||
<th>Existe</th>
|
||||
<th>Lignes</th>
|
||||
@@ -64,7 +64,7 @@
|
||||
<th>Dernière insertion</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="sqlRawTableBody"></tbody>
|
||||
<tbody id="storeRawTableBody"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
@@ -75,7 +75,7 @@
|
||||
<div class="card shadow-sm border-0">
|
||||
<div class="card-body">
|
||||
<h2 class="h5 card-title mb-3">Payload JSON</h2>
|
||||
<div id="sqlRawJson" class="app-config-box app-json-viewer mb-0">{"status":"loading","message":"Chargement des données SQL."}</div>
|
||||
<div id="storeRawJson" class="app-config-box app-json-viewer mb-0">{"status":"loading","message":"Chargement des données du store."}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -91,7 +91,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
<script type="module" src="ts/demo_sql_pg_raw.ts" defer></script>
|
||||
<script type="module" src="ts/demo_store_raw.ts" defer></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
||||
@@ -1,5 +1,5 @@
|
||||
<!-- file: kb-app-demo-desktop/frontend/demo_sql_replay_candidates.html -->
|
||||
<!-- version: 11 -->
|
||||
<!-- file: kb-app-demo-desktop/frontend/demo_store_replay_candidates.html -->
|
||||
<!-- version: 15 -->
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<meta content="width=device-width, initial-scale=1.0" name="viewport">
|
||||
<link rel="icon" href="imgs/logo.png">
|
||||
<link rel="stylesheet" href="sass/main.scss">
|
||||
<title>Khadhroony Bot3 — Candidats de replay SQL</title>
|
||||
<title>Khadhroony Bot3 — Candidats de replay du store</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@@ -17,7 +17,7 @@
|
||||
<div class="container-fluid px-4 my-0">
|
||||
<div class="navbar-brand d-flex align-items-center">
|
||||
<img alt="Logo" src="imgs/logo.png" class="app-logo">
|
||||
<span class="ps-2 fs-4 fw-bold text-primary font-logo">Bot3 — <span class="badge text-bg-primary">SQL replay candidates</span></span>
|
||||
<span class="ps-2 fs-4 fw-bold text-primary font-logo">Bot3 — <span class="badge text-bg-primary">Store replay candidates</span></span>
|
||||
</div>
|
||||
<div class="ms-auto d-flex align-items-center gap-2">
|
||||
<span id="replayCandidateProfileBadge" class="badge text-bg-info">Profil —</span>
|
||||
@@ -36,9 +36,9 @@
|
||||
<div class="card shadow-sm border-0 mb-4">
|
||||
<div class="card-body">
|
||||
<h1 class="h3 card-title">Exploration read-only des candidats</h1>
|
||||
<p class="text-body-secondary mb-2">Les requêtes sont paramétrées, bornées et sans SQL libre. Utilisez les cases à cocher pour sélectionner les lignes. La case du header sélectionne ou désélectionne toutes les lignes correspondant au filtre local. Sans sélection explicite, la copie et l’export CSV utilisent toutes les lignes filtrées. Les CSV UTF-8 avec BOM et séparateur point-virgule sont écrits par le backend Tauri dans <code>./data/exports_csv/</code>. LibreOffice Calc peut toujours afficher son dialogue d’import, car le format CSV ne décrit pas formellement son encodage ni son séparateur.</p>
|
||||
<p class="text-body-secondary mb-2">Les requêtes sont paramétrées, bornées et indépendantes du backend concret. Utilisez les cases à cocher pour sélectionner les lignes. La case du header sélectionne ou désélectionne toutes les lignes correspondant au filtre local. Sans sélection explicite, la copie et l’export CSV utilisent toutes les lignes filtrées. Les CSV UTF-8 avec BOM et séparateur point-virgule sont écrits par le backend Tauri dans <code>./data/exports_csv/</code>. LibreOffice Calc peut toujours afficher son dialogue d’import, car le format CSV ne décrit pas formellement son encodage ni son séparateur.</p>
|
||||
<div class="small text-body-secondary">
|
||||
<span class="fw-semibold">DSN :</span>
|
||||
<span class="fw-semibold">Connexion :</span>
|
||||
<code id="replayCandidateDsn">—</code>
|
||||
<span class="ms-3 fw-semibold">Limite serveur maximale :</span>
|
||||
<code id="replayCandidateMaximumLimit">—</code>
|
||||
@@ -68,7 +68,7 @@
|
||||
<section class="tab-pane fade show active" id="transactionsTabPane" role="tabpanel" aria-labelledby="transactionsTabButton" tabindex="0">
|
||||
<div class="card shadow-sm border-0 mb-4">
|
||||
<div class="card-body">
|
||||
<h2 class="h5 card-title">Filtres PostgreSQL bornés</h2>
|
||||
<h2 class="h5 card-title">Filtres store bornés</h2>
|
||||
<div class="row g-3 align-items-end">
|
||||
<div class="col-12 col-xl-4">
|
||||
<label for="transactionSignatureContainsInput" class="form-label">Signature contient</label>
|
||||
@@ -110,8 +110,8 @@
|
||||
<div class="col-6 col-md-3 col-xl-2">
|
||||
<label for="transactionProgramScopeSelect" class="form-label">Portée programme</label>
|
||||
<select id="transactionProgramScopeSelect" class="form-select">
|
||||
<option value="any">outer + inner + logs</option>
|
||||
<option value="outer">outer uniquement</option>
|
||||
<option value="any">top-level + inner + logs</option>
|
||||
<option value="top_level">top-level uniquement</option>
|
||||
<option value="inner">inner uniquement</option>
|
||||
<option value="logs">logs liés uniquement</option>
|
||||
</select>
|
||||
@@ -441,7 +441,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
<script type="module" src="ts/demo_sql_replay_candidates.ts" defer></script>
|
||||
<script type="module" src="ts/demo_store_replay_candidates.ts" defer></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
||||
@@ -1,5 +1,5 @@
|
||||
<!-- file: kb-app-demo-desktop/frontend/main.html -->
|
||||
<!-- version: 12 -->
|
||||
<!-- version: 14 -->
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
|
||||
@@ -48,12 +48,12 @@
|
||||
<hr class="dropdown-divider">
|
||||
</li>
|
||||
<li>
|
||||
<h6 class="dropdown-header">SQL</h6>
|
||||
<h6 class="dropdown-header">Store</h6>
|
||||
</li>
|
||||
<li><a id="openDemoSqlDiagLink" class="dropdown-item" href="#">SQL diagnostics</a></li>
|
||||
<li><a id="openDemoSqlPgRawLink" class="dropdown-item" href="#">SQL PostgreSQL raw</a></li>
|
||||
<li><a id="openDemoSqlPgCoreLink" class="dropdown-item" href="#">SQL PostgreSQL core</a></li>
|
||||
<li><a id="openDemoSqlReplayCandidatesLink" class="dropdown-item" href="#">SQL replay candidates</a></li>
|
||||
<li><a id="openDemoStoreDiagLink" class="dropdown-item" href="#">Store diagnostics</a></li>
|
||||
<li><a id="openDemoStoreRawLink" class="dropdown-item" href="#">Raw store</a></li>
|
||||
<li><a id="openDemoStoreCoreLink" class="dropdown-item" href="#">Core store</a></li>
|
||||
<li><a id="openDemoStoreReplayCandidatesLink" class="dropdown-item" href="#">Store replay candidates</a></li>
|
||||
<li>
|
||||
<hr class="dropdown-divider">
|
||||
</li>
|
||||
@@ -100,4 +100,4 @@
|
||||
<script type="module" src="ts/main.ts" defer></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: kb-app-demo-desktop/frontend/ts/demo_core_extraction.ts
|
||||
// version: 6
|
||||
// version: 7
|
||||
|
||||
import * as bootstrap from "bootstrap";
|
||||
import "simplebar";
|
||||
@@ -127,7 +127,7 @@ async function cancelCoreExtraction(): Promise<void> {
|
||||
}
|
||||
|
||||
|
||||
async function openDiagnostics(command: "open_demo_sql_pg_raw_window" | "open_demo_sql_pg_core_window" | "open_demo_sql_replay_candidates_window", label: string): Promise<void> {
|
||||
async function openDiagnostics(command: "open_demo_store_raw_window" | "open_demo_store_core_window" | "open_demo_store_replay_candidates_window", label: string): Promise<void> {
|
||||
try {
|
||||
await invoke(command);
|
||||
appendLog({ timestamp: new Date().toISOString(), level: "info", message: `${label} ouvert.` });
|
||||
@@ -166,13 +166,13 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
});
|
||||
});
|
||||
element<HTMLButtonElement>("#openRawDiagnosticsButton").addEventListener("click", () => {
|
||||
void openDiagnostics("open_demo_sql_pg_raw_window", "Diagnostic raw");
|
||||
void openDiagnostics("open_demo_store_raw_window", "Diagnostic raw");
|
||||
});
|
||||
element<HTMLButtonElement>("#openCoreDiagnosticsButton").addEventListener("click", () => {
|
||||
void openDiagnostics("open_demo_sql_pg_core_window", "Diagnostic core");
|
||||
void openDiagnostics("open_demo_store_core_window", "Diagnostic core");
|
||||
});
|
||||
element<HTMLButtonElement>("#openReplayCandidatesButton").addEventListener("click", () => {
|
||||
void openDiagnostics("open_demo_sql_replay_candidates_window", "Sélecteur de candidats replay");
|
||||
void openDiagnostics("open_demo_store_replay_candidates_window", "Sélecteur de candidats replay");
|
||||
});
|
||||
element<HTMLButtonElement>("#cancelCoreExtractionButton").addEventListener("click", () => {
|
||||
void cancelCoreExtraction();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: kb-app-demo-desktop/frontend/ts/demo_decode_replay.ts
|
||||
// version: 7
|
||||
// version: 8
|
||||
|
||||
import * as bootstrap from "bootstrap";
|
||||
import "simplebar";
|
||||
@@ -204,7 +204,7 @@ async function loadDiagnostics(): Promise<void> {
|
||||
try {
|
||||
frontendDebug("kb_app_demo_desktop.frontend.demo_decode_replay", "Invoke demo_decode_replay_diagnostics");
|
||||
const diagnostics = await invoke<DemoDecodeDiagnosticsPayload>("demo_decode_replay_diagnostics");
|
||||
frontendDebug("kb_app_demo_desktop.frontend.demo_decode_replay", `Invoke demo_decode_replay_diagnostics completed tables=${diagnostics.tables.length} coverage=${diagnostics.coverage.length}`);
|
||||
frontendDebug("kb_app_demo_desktop.frontend.demo_decode_replay", `Invoke demo_decode_replay_diagnostics completed resources=${diagnostics.resources.length} coverage=${diagnostics.coverage.length}`);
|
||||
element<HTMLTextAreaElement>("#decodeReplayDiagnosticsOutput").value = JSON.stringify(diagnostics, null, 2);
|
||||
} catch (caughtError) {
|
||||
const message = caughtError instanceof Error ? caughtError.message : String(caughtError);
|
||||
@@ -282,7 +282,7 @@ async function loadAnnotations(): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
async function openWindow(command: "open_demo_sql_pg_core_window" | "open_demo_sql_replay_candidates_window"): Promise<void> {
|
||||
async function openWindow(command: "open_demo_store_core_window" | "open_demo_store_replay_candidates_window"): Promise<void> {
|
||||
try {
|
||||
frontendDebug("kb_app_demo_desktop.frontend.demo_decode_replay", `Invoke ${command}`);
|
||||
await invoke(command);
|
||||
@@ -358,8 +358,8 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
element<HTMLButtonElement>("#cancelDecodeReplayButton").addEventListener("click", () => void cancelDecodeReplay());
|
||||
element<HTMLButtonElement>("#loadDecodeDiagnosticsButton").addEventListener("click", () => void loadDiagnostics());
|
||||
element<HTMLButtonElement>("#loadAnnotationsButton").addEventListener("click", () => void loadAnnotations());
|
||||
element<HTMLButtonElement>("#openReplayCandidatesFromDecodeButton").addEventListener("click", () => void openWindow("open_demo_sql_replay_candidates_window"));
|
||||
element<HTMLButtonElement>("#openCoreDiagnosticsFromDecodeButton").addEventListener("click", () => void openWindow("open_demo_sql_pg_core_window"));
|
||||
element<HTMLButtonElement>("#openReplayCandidatesFromDecodeButton").addEventListener("click", () => void openWindow("open_demo_store_replay_candidates_window"));
|
||||
element<HTMLButtonElement>("#openCoreDiagnosticsFromDecodeButton").addEventListener("click", () => void openWindow("open_demo_store_core_window"));
|
||||
void listen<DemoDecodeReplayProgressPayload>("demo-decode-replay-progress", event => appendLog(event.payload));
|
||||
void loadOptions().catch(caughtError => {
|
||||
const message = caughtError instanceof Error ? caughtError.message : String(caughtError);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: kb-app-demo-desktop/frontend/ts/demo_execution_solana_core.ts
|
||||
// version: 10
|
||||
// version: 11
|
||||
|
||||
import * as bootstrap from "bootstrap";
|
||||
import "simplebar";
|
||||
@@ -20,18 +20,18 @@ const logLines: string[] = [];
|
||||
let running = false;
|
||||
let profileStoreReady = false;
|
||||
let profileOptions: DemoExecutionSolanaCoreProfileOption[] = [];
|
||||
function element<T extends HTMLElement>(selector: string): T { const value=document.querySelector<T>(selector); if(!value){throw new Error(`Missing UI element: ${selector}`);} return value; }
|
||||
function integerValue(selector: string): number { const value=Number.parseInt(element<HTMLInputElement>(selector).value,10); if(!Number.isFinite(value)||value<0){throw new Error(`Valeur numérique invalide pour ${selector}`);} return value; }
|
||||
function selectedProfile(): DemoExecutionSolanaCoreProfileOption|null { const name=element<HTMLSelectElement>("#executionProfileSelect").value; return profileOptions.find(profile=>profile.name===name)??null; }
|
||||
function appendLog(payload: DemoExecutionSolanaCoreProgressPayload|{timestamp:string;level:string;stage:string;message:string;signature?:string|null}):void { const signature=payload.signature?` signature=${payload.signature}`:""; logLines.push(`${payload.timestamp} ${payload.level.toUpperCase()} [${payload.stage}] ${payload.message}${signature}`); const output=element<HTMLTextAreaElement>("#executionLogOutput"); output.value=logLines.join("\n"); output.scrollTop=output.scrollHeight; }
|
||||
function setRunning(value:boolean):void { running=value; const profile=selectedProfile(); element<HTMLButtonElement>("#simulateExecutionButton").disabled=value||!profile||!profileStoreReady; element<HTMLButtonElement>("#submitExecutionButton").disabled=value||!profile||!profileStoreReady||!profile.sendEnabled; element<HTMLButtonElement>("#generateRecipientButton").disabled=value; element<HTMLButtonElement>("#cancelExecutionButton").disabled=!value; const badge=element<HTMLElement>("#executionStatusBadge"); badge.textContent=value?"Exécution en cours":"Prêt"; badge.className=value?"badge text-bg-warning":"badge text-bg-success"; }
|
||||
function updateProfileHelp():void { const profile=selectedProfile(); const help=element<HTMLElement>("#executionProfileHelp"); if(!profile){help.textContent="Aucun profil Devnet compatible.";return;} help.textContent=`wallet=${profile.walletAlias}, spendMax=${profile.maxSpendLamports}, airdropMax=${profile.maxAirdropLamports}, send=${profile.sendEnabled?"enabled":"disabled"}`; element<HTMLInputElement>("#executionAirdropInput").max=String(profile.maxAirdropLamports); element<HTMLInputElement>("#executionLamportsInput").max=String(profile.maxSpendLamports); setRunning(running); }
|
||||
function buildRequest(submit:boolean):DemoExecutionSolanaCoreRequest { const recipient=element<HTMLInputElement>("#executionRecipientInput").value.trim(); if(recipient.length===0){throw new Error("Le destinataire est obligatoire.");} return {profileName:element<HTMLSelectElement>("#executionProfileSelect").value,recipient,lamports:integerValue("#executionLamportsInput"),airdropLamports:integerValue("#executionAirdropInput"),submit,operatorConfirmed:element<HTMLInputElement>("#executionOperatorConfirmedInput").checked,forcePostValidationReplay:element<HTMLInputElement>("#executionForceReplayInput").checked,materializeAfterDecode:element<HTMLInputElement>("#executionMaterializeInput").checked}; }
|
||||
function titledJson(title:string,value:string):string { try { const parsed:unknown=JSON.parse(value); if(parsed!==null&&typeof parsed==="object"&&!Array.isArray(parsed)){return JSON.stringify({title,...parsed},null,2);} return JSON.stringify({title,value:parsed},null,2); } catch { return JSON.stringify({title,status:"invalid_json",raw:value},null,2); } }
|
||||
function displaySummary(summary:DemoExecutionSolanaCoreSummaryPayload):void { element<HTMLTextAreaElement>("#executionSummaryOutput").value=JSON.stringify({title:"Résumé",...summary},null,2); element<HTMLTextAreaElement>("#executionPlanOutput").value=titledJson("Plan exact",summary.planJson); element<HTMLTextAreaElement>("#executionSimulationOutput").value=titledJson("Simulation exacte",summary.simulationJson); element<HTMLTextAreaElement>("#executionDiagnosticsOutput").value=titledJson("Confirmation et replay",summary.diagnosticsJson); }
|
||||
function displayExecutionFailure(message:string,submit:boolean):void { element<HTMLTextAreaElement>("#executionSummaryOutput").value=JSON.stringify({title:"Résumé",status:"error",message},null,2); element<HTMLTextAreaElement>("#executionPlanOutput").value=JSON.stringify({title:"Plan exact",status:"unavailable",reason:"preflight_failed",message:"Aucun plan exécutable n’a été produit.",error:message},null,2); element<HTMLTextAreaElement>("#executionSimulationOutput").value=JSON.stringify({title:"Simulation exacte",status:"not_started",reason:"preflight_failed",message:"La simulation n’a pas été lancée.",error:message},null,2); element<HTMLTextAreaElement>("#executionDiagnosticsOutput").value=JSON.stringify({title:"Confirmation et replay",status:"not_started",reason:"execution_failed_before_submission",message:submit?"La signature, l’envoi, la confirmation et le replay n’ont pas été exécutés.":"La confirmation et le replay ne s’appliquent pas à cette simulation refusée.",error:message},null,2); appendLog({timestamp:new Date().toISOString(),level:"error",stage:"failed",message}); }
|
||||
async function execute(submit:boolean):Promise<void>{if(running){return;}try{const request=buildRequest(submit);if(submit&&!request.operatorConfirmed){const message="La confirmation opérateur explicite est obligatoire avant signature et envoi.";displayExecutionFailure(message,submit);frontendWarn("kb-app-demo-desktop.frontend.demo_execution_solana_core","Submission blocked: operator confirmation missing");return;}setRunning(true);const summary=await invoke<DemoExecutionSolanaCoreSummaryPayload>("demo_execution_solana_core_execute",{request});displaySummary(summary);}catch(caughtError){const message=caughtError instanceof Error?caughtError.message:String(caughtError);displayExecutionFailure(message,submit);frontendError("kb-app-demo-desktop.frontend.demo_execution_solana_core",`Execution failed: ${message}`);}finally{setRunning(false);}}
|
||||
function element<T extends HTMLElement>(selector: string): T { const value = document.querySelector<T>(selector); if (!value) { throw new Error(`Missing UI element: ${selector}`); } return value; }
|
||||
function integerValue(selector: string): number { const value = Number.parseInt(element<HTMLInputElement>(selector).value, 10); if (!Number.isFinite(value) || value < 0) { throw new Error(`Valeur numérique invalide pour ${selector}`); } return value; }
|
||||
function selectedProfile(): DemoExecutionSolanaCoreProfileOption | null { const name = element<HTMLSelectElement>("#executionProfileSelect").value; return profileOptions.find(profile => profile.name === name) ?? null; }
|
||||
function appendLog(payload: DemoExecutionSolanaCoreProgressPayload | { timestamp: string; level: string; stage: string; message: string; signature?: string | null }): void { const signature = payload.signature ? ` signature=${payload.signature}` : ""; logLines.push(`${payload.timestamp} ${payload.level.toUpperCase()} [${payload.stage}] ${payload.message}${signature}`); const output = element<HTMLTextAreaElement>("#executionLogOutput"); output.value = logLines.join("\n"); output.scrollTop = output.scrollHeight; }
|
||||
function setRunning(value: boolean): void { running = value; const profile = selectedProfile(); element<HTMLButtonElement>("#simulateExecutionButton").disabled = value || !profile || !profileStoreReady; element<HTMLButtonElement>("#submitExecutionButton").disabled = value || !profile || !profileStoreReady || !profile.sendEnabled; element<HTMLButtonElement>("#generateRecipientButton").disabled = value; element<HTMLButtonElement>("#cancelExecutionButton").disabled = !value; const badge = element<HTMLElement>("#executionStatusBadge"); badge.textContent = value ? "Exécution en cours" : "Prêt"; badge.className = value ? "badge text-bg-warning" : "badge text-bg-success"; }
|
||||
function updateProfileHelp(): void { const profile = selectedProfile(); const help = element<HTMLElement>("#executionProfileHelp"); if (!profile) { help.textContent = "Aucun profil Devnet compatible."; return; } help.textContent = `wallet=${profile.walletAlias}, spendMax=${profile.maxSpendLamports}, airdropMax=${profile.maxAirdropLamports}, send=${profile.sendEnabled ? "enabled" : "disabled"}`; element<HTMLInputElement>("#executionAirdropInput").max = String(profile.maxAirdropLamports); element<HTMLInputElement>("#executionLamportsInput").max = String(profile.maxSpendLamports); setRunning(running); }
|
||||
function buildRequest(submit: boolean): DemoExecutionSolanaCoreRequest { const recipient = element<HTMLInputElement>("#executionRecipientInput").value.trim(); if (recipient.length === 0) { throw new Error("Le destinataire est obligatoire."); } return { profileName: element<HTMLSelectElement>("#executionProfileSelect").value, recipient, lamports: integerValue("#executionLamportsInput"), airdropLamports: integerValue("#executionAirdropInput"), submit, operatorConfirmed: element<HTMLInputElement>("#executionOperatorConfirmedInput").checked, forcePostValidationReplay: element<HTMLInputElement>("#executionForceReplayInput").checked, materializeAfterDecode: element<HTMLInputElement>("#executionMaterializeInput").checked }; }
|
||||
function titledJson(title: string, value: string): string { try { const parsed: unknown = JSON.parse(value); if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)) { return JSON.stringify({ title, ...parsed }, null, 2); } return JSON.stringify({ title, value: parsed }, null, 2); } catch { return JSON.stringify({ title, status: "invalid_json", raw: value }, null, 2); } }
|
||||
function displaySummary(summary: DemoExecutionSolanaCoreSummaryPayload): void { element<HTMLTextAreaElement>("#executionSummaryOutput").value = JSON.stringify({ title: "Résumé", ...summary }, null, 2); element<HTMLTextAreaElement>("#executionPlanOutput").value = titledJson("Plan exact", summary.planJson); element<HTMLTextAreaElement>("#executionSimulationOutput").value = titledJson("Simulation exacte", summary.simulationJson); element<HTMLTextAreaElement>("#executionDiagnosticsOutput").value = titledJson("Confirmation et replay", summary.diagnosticsJson); }
|
||||
function displayExecutionFailure(message: string, submit: boolean): void { element<HTMLTextAreaElement>("#executionSummaryOutput").value = JSON.stringify({ title: "Résumé", status: "error", message }, null, 2); element<HTMLTextAreaElement>("#executionPlanOutput").value = JSON.stringify({ title: "Plan exact", status: "unavailable", reason: "preflight_failed", message: "Aucun plan exécutable n’a été produit.", error: message }, null, 2); element<HTMLTextAreaElement>("#executionSimulationOutput").value = JSON.stringify({ title: "Simulation exacte", status: "not_started", reason: "preflight_failed", message: "La simulation n’a pas été lancée.", error: message }, null, 2); element<HTMLTextAreaElement>("#executionDiagnosticsOutput").value = JSON.stringify({ title: "Confirmation et replay", status: "not_started", reason: "execution_failed_before_submission", message: submit ? "La signature, l’envoi, la confirmation et le replay n’ont pas été exécutés." : "La confirmation et le replay ne s’appliquent pas à cette simulation refusée.", error: message }, null, 2); appendLog({ timestamp: new Date().toISOString(), level: "error", stage: "failed", message }); }
|
||||
async function execute(submit: boolean): Promise<void> { if (running) { return; } try { const request = buildRequest(submit); if (submit && !request.operatorConfirmed) { const message = "La confirmation opérateur explicite est obligatoire avant signature et envoi."; displayExecutionFailure(message, submit); frontendWarn("kb-app-demo-desktop.frontend.demo_execution_solana_core", "Submission blocked: operator confirmation missing"); return; } setRunning(true); const summary = await invoke<DemoExecutionSolanaCoreSummaryPayload>("demo_execution_solana_core_execute", { request }); displaySummary(summary); } catch (caughtError) { const message = caughtError instanceof Error ? caughtError.message : String(caughtError); displayExecutionFailure(message, submit); frontendError("kb-app-demo-desktop.frontend.demo_execution_solana_core", `Execution failed: ${message}`); } finally { setRunning(false); } }
|
||||
|
||||
async function prepareSelectedProfile():Promise<void>{const profile=selectedProfile();profileStoreReady=false;setRunning(running);if(!profile){return;}appendLog({timestamp:new Date().toISOString(),level:"info",stage:"store_prepare",message:`Vérification PostgreSQL du profil ${profile.name}.`});try{const readiness=await invoke<DemoExecutionDevnetStoreReadinessPayload>("demo_execution_devnet_prepare_profile",{profileName:profile.name});profileStoreReady=true;appendLog({timestamp:new Date().toISOString(),level:"info",stage:"store_ready",message:`PostgreSQL prêt: ${readiness.existingTablesAfter}/${readiness.expectedTables} tables, créées=${readiness.createdTables}, autoInit=${readiness.autoInitializeSchema}.`});}catch(caughtError){const message=caughtError instanceof Error?caughtError.message:String(caughtError);frontendError("kb-app-demo-desktop.frontend.demo_execution_solana_core",`Devnet store preparation failed: ${message}`);}finally{setRunning(running);}}
|
||||
async function loadOptions():Promise<void>{const options=await invoke<DemoExecutionSolanaCoreOptionsPayload>("demo_execution_solana_core_options");profileOptions=options.profiles;const select=element<HTMLSelectElement>("#executionProfileSelect");select.replaceChildren();for(const profile of profileOptions){const option=document.createElement("option");option.value=profile.name;option.textContent=profile.name;option.selected=profile.name===options.defaultProfileName;select.append(option);}element<HTMLInputElement>("#executionLamportsInput").value=String(options.defaultTransferLamports);updateProfileHelp();setRunning(options.running);await prepareSelectedProfile();}
|
||||
document.addEventListener("DOMContentLoaded",async()=>{installFrontendConsoleBridge("kb-app-demo-desktop.frontend.demo_execution_solana_core");frontendDebug("kb-app-demo-desktop.frontend.demo_execution_solana_core","Solana Core execution window loaded");await listen<DemoExecutionSolanaCoreProgressPayload>("demo-execution-solana-core-progress",event=>appendLog(event.payload));element<HTMLSelectElement>("#executionProfileSelect").addEventListener("change",()=>{updateProfileHelp();void prepareSelectedProfile();});element<HTMLButtonElement>("#generateRecipientButton").addEventListener("click",async()=>{const payload=await invoke<DemoExecutionSolanaCoreGeneratedRecipientPayload>("demo_execution_solana_core_generate_recipient");element<HTMLInputElement>("#executionRecipientInput").value=payload.publicKey;});element<HTMLButtonElement>("#simulateExecutionButton").addEventListener("click",()=>void execute(false));element<HTMLButtonElement>("#submitExecutionButton").addEventListener("click",()=>void execute(true));element<HTMLButtonElement>("#cancelExecutionButton").addEventListener("click",()=>void invoke("demo_execution_solana_core_cancel"));element<HTMLTextAreaElement>("#executionLogOutput").addEventListener("app-log-clear",()=>{logLines.length=0;});try{await loadOptions();}catch(caughtError){const message=caughtError instanceof Error?caughtError.message:String(caughtError);frontendError("kb-app-demo-desktop.frontend.demo_execution_solana_core",`Options loading failed: ${message}`);}});
|
||||
async function prepareSelectedProfile(): Promise<void> { const profile = selectedProfile(); profileStoreReady = false; setRunning(running); if (!profile) { return; } appendLog({ timestamp: new Date().toISOString(), level: "info", stage: "store_prepare", message: `Vérification du store du profil ${profile.name}.` }); try { const readiness = await invoke<DemoExecutionDevnetStoreReadinessPayload>("demo_execution_devnet_prepare_profile", { profileName: profile.name }); profileStoreReady = true; appendLog({ timestamp: new Date().toISOString(), level: "info", stage: "store_ready", message: `Store ${readiness.backend} prêt: ${readiness.availableResourcesAfter}/${readiness.expectedResources} ressources, créées=${readiness.createdResources}, autoInit=${readiness.autoInitializeSchema}.` }); } catch (caughtError) { const message = caughtError instanceof Error ? caughtError.message : String(caughtError); frontendError("kb-app-demo-desktop.frontend.demo_execution_solana_core", `Devnet store preparation failed: ${message}`); } finally { setRunning(running); } }
|
||||
async function loadOptions(): Promise<void> { const options = await invoke<DemoExecutionSolanaCoreOptionsPayload>("demo_execution_solana_core_options"); profileOptions = options.profiles; const select = element<HTMLSelectElement>("#executionProfileSelect"); select.replaceChildren(); for (const profile of profileOptions) { const option = document.createElement("option"); option.value = profile.name; option.textContent = profile.name; option.selected = profile.name === options.defaultProfileName; select.append(option); } element<HTMLInputElement>("#executionLamportsInput").value = String(options.defaultTransferLamports); updateProfileHelp(); setRunning(options.running); await prepareSelectedProfile(); }
|
||||
document.addEventListener("DOMContentLoaded", async () => { installFrontendConsoleBridge("kb-app-demo-desktop.frontend.demo_execution_solana_core"); frontendDebug("kb-app-demo-desktop.frontend.demo_execution_solana_core", "Solana Core execution window loaded"); await listen<DemoExecutionSolanaCoreProgressPayload>("demo-execution-solana-core-progress", event => appendLog(event.payload)); element<HTMLSelectElement>("#executionProfileSelect").addEventListener("change", () => { updateProfileHelp(); void prepareSelectedProfile(); }); element<HTMLButtonElement>("#generateRecipientButton").addEventListener("click", async () => { const payload = await invoke<DemoExecutionSolanaCoreGeneratedRecipientPayload>("demo_execution_solana_core_generate_recipient"); element<HTMLInputElement>("#executionRecipientInput").value = payload.publicKey; }); element<HTMLButtonElement>("#simulateExecutionButton").addEventListener("click", () => void execute(false)); element<HTMLButtonElement>("#submitExecutionButton").addEventListener("click", () => void execute(true)); element<HTMLButtonElement>("#cancelExecutionButton").addEventListener("click", () => void invoke("demo_execution_solana_core_cancel")); element<HTMLTextAreaElement>("#executionLogOutput").addEventListener("app-log-clear", () => { logLines.length = 0; }); try { await loadOptions(); } catch (caughtError) { const message = caughtError instanceof Error ? caughtError.message : String(caughtError); frontendError("kb-app-demo-desktop.frontend.demo_execution_solana_core", `Options loading failed: ${message}`); } });
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: kb-app-demo-desktop/frontend/ts/demo_execution_spl.ts
|
||||
// version: 10
|
||||
// version: 11
|
||||
|
||||
import * as bootstrap from "bootstrap";
|
||||
import "simplebar";
|
||||
@@ -896,11 +896,11 @@ async function prepareSelectedProfile(): Promise<void> {
|
||||
if (!profile) {
|
||||
return;
|
||||
}
|
||||
appendLog({ timestamp: new Date().toISOString(), level: "info", stage: "store_prepare", message: `Vérification PostgreSQL du profil ${profile.name}.` });
|
||||
appendLog({ timestamp: new Date().toISOString(), level: "info", stage: "store_prepare", message: `Vérification du store du profil ${profile.name}.` });
|
||||
try {
|
||||
const readiness = await invoke<DemoExecutionDevnetStoreReadinessPayload>("demo_execution_devnet_prepare_profile", { profileName: profile.name });
|
||||
profileStoreReady = true;
|
||||
appendLog({ timestamp: new Date().toISOString(), level: "info", stage: "store_ready", message: `PostgreSQL prêt: ${readiness.existingTablesAfter}/${readiness.expectedTables} tables, créées=${readiness.createdTables}, autoInit=${readiness.autoInitializeSchema}.` });
|
||||
appendLog({ timestamp: new Date().toISOString(), level: "info", stage: "store_ready", message: `Store ${readiness.backend} prêt: ${readiness.availableResourcesAfter}/${readiness.expectedResources} ressources, créées=${readiness.createdResources}, autoInit=${readiness.autoInitializeSchema}.` });
|
||||
} catch (caughtError) {
|
||||
const message = caughtError instanceof Error ? caughtError.message : String(caughtError);
|
||||
frontendError("kb-app-demo-desktop.frontend.demo_execution_spl", `Devnet store preparation failed: ${message}`);
|
||||
|
||||
@@ -1,38 +1,38 @@
|
||||
// file: kb-app-demo-desktop/frontend/ts/demo_sql_pg_core.ts
|
||||
// version: 4
|
||||
// file: kb-app-demo-desktop/frontend/ts/demo_store_core.ts
|
||||
// version: 6
|
||||
|
||||
import * as bootstrap from "bootstrap";
|
||||
import "simplebar";
|
||||
import ResizeObserver from "resize-observer-polyfill";
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
import { frontendDebug, frontendError, installFrontendConsoleBridge } from "./frontend_log";
|
||||
import { renderJsonViewer, renderTables, setText } from "./demo_sql_tables";
|
||||
import type { DemoSqlPgCorePayload } from "./bindings/kb_app_demo_desktop/demo_sql_pg_core/DemoSqlPgCorePayload";
|
||||
import { renderJsonViewer, renderTables, setText } from "./demo_store_tables";
|
||||
import type { DemoStoreCorePayload } from "./bindings/kb_app_demo_desktop/demo_store_core/DemoStoreCorePayload";
|
||||
|
||||
(window as Window & typeof globalThis & { bootstrap?: typeof bootstrap }).bootstrap = bootstrap;
|
||||
(window as Window & typeof globalThis & { ResizeObserver?: typeof ResizeObserver }).ResizeObserver = ResizeObserver;
|
||||
|
||||
async function refreshCore(): Promise<void> {
|
||||
setText("#sqlCoreStatus", "Chargement...");
|
||||
setText("#storeCoreStatus", "Chargement...");
|
||||
try {
|
||||
const payload = await invoke<DemoSqlPgCorePayload>("load_demo_sql_pg_core");
|
||||
setText("#sqlCoreProfile", payload.activeProfileName);
|
||||
setText("#sqlCoreDsn", payload.maskedDsn);
|
||||
setText("#sqlCoreStatus", "OK");
|
||||
renderTables("#sqlCoreTable", "#sqlCoreTableBody", payload.tables);
|
||||
renderJsonViewer("#sqlCoreJson", payload);
|
||||
frontendDebug("kb_app_demo_desktop.frontend.demo_sql_pg_core", "PostgreSQL core diagnostics refreshed");
|
||||
const payload = await invoke<DemoStoreCorePayload>("load_demo_store_core");
|
||||
setText("#storeCoreProfile", payload.activeProfileName);
|
||||
setText("#storeCoreConnection", payload.connectionDescriptor);
|
||||
setText("#storeCoreStatus", "OK");
|
||||
renderTables("#storeCoreTable", "#storeCoreTableBody", payload.resources);
|
||||
renderJsonViewer("#storeCoreJson", payload);
|
||||
frontendDebug("kb_app_demo_desktop.frontend.demo_store_core", "Core store diagnostics refreshed");
|
||||
} catch (caughtError) {
|
||||
const message = caughtError instanceof Error ? caughtError.message : String(caughtError);
|
||||
setText("#sqlCoreStatus", `Erreur : ${message}`);
|
||||
renderJsonViewer("#sqlCoreJson", { error: message });
|
||||
frontendError("kb_app_demo_desktop.frontend.demo_sql_pg_core", `PostgreSQL core diagnostics loading failed: ${message}`);
|
||||
setText("#storeCoreStatus", `Erreur : ${message}`);
|
||||
renderJsonViewer("#storeCoreJson", { error: message });
|
||||
frontendError("kb_app_demo_desktop.frontend.demo_store_core", `Core store diagnostics loading failed: ${message}`);
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
installFrontendConsoleBridge("kb_app_demo_desktop.frontend.demo_sql_pg_core");
|
||||
frontendDebug("kb_app_demo_desktop.frontend.demo_sql_pg_core", "PostgreSQL core demo window loaded");
|
||||
installFrontendConsoleBridge("kb_app_demo_desktop.frontend.demo_store_core");
|
||||
frontendDebug("kb_app_demo_desktop.frontend.demo_store_core", "Core store demo window loaded");
|
||||
const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]');
|
||||
Array.from(tooltipTriggerList).map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl));
|
||||
document.querySelector<HTMLButtonElement>("#refreshSqlCoreButton")?.addEventListener("click", () => {
|
||||
@@ -1,41 +1,41 @@
|
||||
// file: kb-app-demo-desktop/frontend/ts/demo_sql_diag.ts
|
||||
// version: 3
|
||||
// file: kb-app-demo-desktop/frontend/ts/demo_store_diag.ts
|
||||
// version: 5
|
||||
|
||||
import * as bootstrap from "bootstrap";
|
||||
import "simplebar";
|
||||
import ResizeObserver from "resize-observer-polyfill";
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
import { frontendDebug, frontendError, installFrontendConsoleBridge } from "./frontend_log";
|
||||
import { jsonText, renderJsonViewer, renderTables, setText } from "./demo_sql_tables";
|
||||
import type { DemoSqlDiagPayload } from "./bindings/kb_app_demo_desktop/demo_sql_diag/DemoSqlDiagPayload";
|
||||
import { jsonText, renderJsonViewer, renderTables, setText } from "./demo_store_tables";
|
||||
import type { DemoStoreDiagPayload } from "./bindings/kb_app_demo_desktop/demo_store_diag/DemoStoreDiagPayload";
|
||||
|
||||
(window as Window & typeof globalThis & { bootstrap?: typeof bootstrap }).bootstrap = bootstrap;
|
||||
(window as Window & typeof globalThis & { ResizeObserver?: typeof ResizeObserver }).ResizeObserver = ResizeObserver;
|
||||
|
||||
let latestPayload: DemoSqlDiagPayload | null = null;
|
||||
let latestPayload: DemoStoreDiagPayload | null = null;
|
||||
|
||||
async function refreshDiagnostics(): Promise<void> {
|
||||
setText("#sqlDiagStatus", "Chargement...");
|
||||
setText("#storeDiagStatus", "Chargement...");
|
||||
try {
|
||||
const payload = await invoke<DemoSqlDiagPayload>("load_demo_sql_diag");
|
||||
setText("#sqlDiagConfigPath", payload.configPath);
|
||||
setText("#sqlDiagProfile", payload.activeProfileName);
|
||||
setText("#sqlDiagBackend", payload.backend);
|
||||
setText("#sqlDiagDsn", payload.maskedDsn);
|
||||
setText("#sqlDiagSchema", payload.currentSchema ?? "—");
|
||||
setText("#sqlDiagHealth", payload.healthStatus);
|
||||
setText("#sqlDiagMigration", payload.migrationStatus);
|
||||
setText("#sqlDiagStatus", "OK");
|
||||
renderTables("#sqlDiagTable", "#sqlDiagTableBody", payload.tables);
|
||||
const payload = await invoke<DemoStoreDiagPayload>("load_demo_store_diag");
|
||||
setText("#storeDiagConfigPath", payload.configPath);
|
||||
setText("#storeDiagProfile", payload.activeProfileName);
|
||||
setText("#storeDiagBackend", payload.backend);
|
||||
setText("#storeDiagConnection", payload.connectionDescriptor);
|
||||
setText("#storeDiagSchema", payload.namespace ?? "—");
|
||||
setText("#storeDiagHealth", payload.healthStatus);
|
||||
setText("#storeDiagMigration", payload.migrationStatus);
|
||||
setText("#storeDiagStatus", "OK");
|
||||
renderTables("#storeDiagTable", "#storeDiagTableBody", payload.resources);
|
||||
latestPayload = payload;
|
||||
renderJsonViewer("#sqlDiagJson", payload);
|
||||
frontendDebug("kb_app_demo_desktop.frontend.demo_sql_diag", "SQL diagnostics refreshed");
|
||||
renderJsonViewer("#storeDiagJson", payload);
|
||||
frontendDebug("kb_app_demo_desktop.frontend.demo_store_diag", "Store diagnostics refreshed");
|
||||
} catch (caughtError) {
|
||||
const message = caughtError instanceof Error ? caughtError.message : String(caughtError);
|
||||
setText("#sqlDiagStatus", `Erreur : ${message}`);
|
||||
setText("#storeDiagStatus", `Erreur : ${message}`);
|
||||
latestPayload = null;
|
||||
renderJsonViewer("#sqlDiagJson", { error: message });
|
||||
frontendError("kb_app_demo_desktop.frontend.demo_sql_diag", `SQL diagnostics loading failed: ${message}`);
|
||||
renderJsonViewer("#storeDiagJson", { error: message });
|
||||
frontendError("kb_app_demo_desktop.frontend.demo_store_diag", `Store diagnostics loading failed: ${message}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,8 +46,8 @@ async function copyDiagnostics(): Promise<void> {
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
installFrontendConsoleBridge("kb_app_demo_desktop.frontend.demo_sql_diag");
|
||||
frontendDebug("kb_app_demo_desktop.frontend.demo_sql_diag", "SQL diagnostics demo window loaded");
|
||||
installFrontendConsoleBridge("kb_app_demo_desktop.frontend.demo_store_diag");
|
||||
frontendDebug("kb_app_demo_desktop.frontend.demo_store_diag", "Store diagnostics demo window loaded");
|
||||
const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]');
|
||||
Array.from(tooltipTriggerList).map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl));
|
||||
document.querySelector<HTMLButtonElement>("#refreshSqlDiagButton")?.addEventListener("click", () => {
|
||||
@@ -1,38 +1,38 @@
|
||||
// file: kb-app-demo-desktop/frontend/ts/demo_sql_pg_raw.ts
|
||||
// version: 4
|
||||
// file: kb-app-demo-desktop/frontend/ts/demo_store_raw.ts
|
||||
// version: 6
|
||||
|
||||
import * as bootstrap from "bootstrap";
|
||||
import "simplebar";
|
||||
import ResizeObserver from "resize-observer-polyfill";
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
import { frontendDebug, frontendError, installFrontendConsoleBridge } from "./frontend_log";
|
||||
import { renderJsonViewer, renderTables, setText } from "./demo_sql_tables";
|
||||
import type { DemoSqlPgRawPayload } from "./bindings/kb_app_demo_desktop/demo_sql_pg_raw/DemoSqlPgRawPayload";
|
||||
import { renderJsonViewer, renderTables, setText } from "./demo_store_tables";
|
||||
import type { DemoStoreRawPayload } from "./bindings/kb_app_demo_desktop/demo_store_raw/DemoStoreRawPayload";
|
||||
|
||||
(window as Window & typeof globalThis & { bootstrap?: typeof bootstrap }).bootstrap = bootstrap;
|
||||
(window as Window & typeof globalThis & { ResizeObserver?: typeof ResizeObserver }).ResizeObserver = ResizeObserver;
|
||||
|
||||
async function refreshRaw(): Promise<void> {
|
||||
setText("#sqlRawStatus", "Chargement...");
|
||||
setText("#storeRawStatus", "Chargement...");
|
||||
try {
|
||||
const payload = await invoke<DemoSqlPgRawPayload>("load_demo_sql_pg_raw");
|
||||
setText("#sqlRawProfile", payload.activeProfileName);
|
||||
setText("#sqlRawDsn", payload.maskedDsn);
|
||||
setText("#sqlRawStatus", "OK");
|
||||
renderTables("#sqlRawTable", "#sqlRawTableBody", payload.tables);
|
||||
renderJsonViewer("#sqlRawJson", payload);
|
||||
frontendDebug("kb_app_demo_desktop.frontend.demo_sql_pg_raw", "PostgreSQL raw diagnostics refreshed");
|
||||
const payload = await invoke<DemoStoreRawPayload>("load_demo_store_raw");
|
||||
setText("#storeRawProfile", payload.activeProfileName);
|
||||
setText("#storeRawConnection", payload.connectionDescriptor);
|
||||
setText("#storeRawStatus", "OK");
|
||||
renderTables("#storeRawTable", "#storeRawTableBody", payload.resources);
|
||||
renderJsonViewer("#storeRawJson", payload);
|
||||
frontendDebug("kb_app_demo_desktop.frontend.demo_store_raw", "Raw store diagnostics refreshed");
|
||||
} catch (caughtError) {
|
||||
const message = caughtError instanceof Error ? caughtError.message : String(caughtError);
|
||||
setText("#sqlRawStatus", `Erreur : ${message}`);
|
||||
renderJsonViewer("#sqlRawJson", { error: message });
|
||||
frontendError("kb_app_demo_desktop.frontend.demo_sql_pg_raw", `PostgreSQL raw diagnostics loading failed: ${message}`);
|
||||
setText("#storeRawStatus", `Erreur : ${message}`);
|
||||
renderJsonViewer("#storeRawJson", { error: message });
|
||||
frontendError("kb_app_demo_desktop.frontend.demo_store_raw", `Raw store diagnostics loading failed: ${message}`);
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
installFrontendConsoleBridge("kb_app_demo_desktop.frontend.demo_sql_pg_raw");
|
||||
frontendDebug("kb_app_demo_desktop.frontend.demo_sql_pg_raw", "PostgreSQL raw demo window loaded");
|
||||
installFrontendConsoleBridge("kb_app_demo_desktop.frontend.demo_store_raw");
|
||||
frontendDebug("kb_app_demo_desktop.frontend.demo_store_raw", "Raw store demo window loaded");
|
||||
const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]');
|
||||
Array.from(tooltipTriggerList).map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl));
|
||||
document.querySelector<HTMLButtonElement>("#refreshSqlRawButton")?.addEventListener("click", () => {
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: kb-app-demo-desktop/frontend/ts/demo_sql_replay_candidates.ts
|
||||
// version: 9
|
||||
// file: kb-app-demo-desktop/frontend/ts/demo_store_replay_candidates.ts
|
||||
// version: 11
|
||||
|
||||
import * as bootstrap from "bootstrap";
|
||||
import "simplebar";
|
||||
@@ -10,14 +10,14 @@ import "datatables.net-select-bs5";
|
||||
import "datatables.net-select-bs5/css/select.bootstrap5.css";
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
import { frontendDebug, frontendError, installFrontendConsoleBridge } from "./frontend_log";
|
||||
import type { DemoSqlReplayEntityRequest } from "./bindings/kb_app_demo_desktop/demo_sql_replay_candidates/DemoSqlReplayEntityRequest";
|
||||
import type { DemoSqlReplayEntityRow } from "./bindings/kb_app_demo_desktop/demo_sql_replay_candidates/DemoSqlReplayEntityRow";
|
||||
import type { DemoSqlReplayKnownProgramOption } from "./bindings/kb_app_demo_desktop/demo_sql_replay_candidates/DemoSqlReplayKnownProgramOption";
|
||||
import type { DemoSqlReplayOptionsPayload } from "./bindings/kb_app_demo_desktop/demo_sql_replay_candidates/DemoSqlReplayOptionsPayload";
|
||||
import type { DemoSqlReplayProgramRequest } from "./bindings/kb_app_demo_desktop/demo_sql_replay_candidates/DemoSqlReplayProgramRequest";
|
||||
import type { DemoSqlReplayProgramRow } from "./bindings/kb_app_demo_desktop/demo_sql_replay_candidates/DemoSqlReplayProgramRow";
|
||||
import type { DemoSqlReplayTransactionRequest } from "./bindings/kb_app_demo_desktop/demo_sql_replay_candidates/DemoSqlReplayTransactionRequest";
|
||||
import type { DemoSqlReplayTransactionRow } from "./bindings/kb_app_demo_desktop/demo_sql_replay_candidates/DemoSqlReplayTransactionRow";
|
||||
import type { DemoStoreReplayEntityRequest } from "./bindings/kb_app_demo_desktop/demo_store_replay_candidates/DemoStoreReplayEntityRequest";
|
||||
import type { DemoStoreReplayEntityRow } from "./bindings/kb_app_demo_desktop/demo_store_replay_candidates/DemoStoreReplayEntityRow";
|
||||
import type { DemoStoreReplayKnownProgramOption } from "./bindings/kb_app_demo_desktop/demo_store_replay_candidates/DemoStoreReplayKnownProgramOption";
|
||||
import type { DemoStoreReplayOptionsPayload } from "./bindings/kb_app_demo_desktop/demo_store_replay_candidates/DemoStoreReplayOptionsPayload";
|
||||
import type { DemoStoreReplayProgramRequest } from "./bindings/kb_app_demo_desktop/demo_store_replay_candidates/DemoStoreReplayProgramRequest";
|
||||
import type { DemoStoreReplayProgramRow } from "./bindings/kb_app_demo_desktop/demo_store_replay_candidates/DemoStoreReplayProgramRow";
|
||||
import type { DemoStoreReplayTransactionRequest } from "./bindings/kb_app_demo_desktop/demo_store_replay_candidates/DemoStoreReplayTransactionRequest";
|
||||
import type { DemoStoreReplayTransactionRow } from "./bindings/kb_app_demo_desktop/demo_store_replay_candidates/DemoStoreReplayTransactionRow";
|
||||
|
||||
(window as Window & typeof globalThis & { bootstrap?: typeof bootstrap }).bootstrap = bootstrap;
|
||||
(window as Window & typeof globalThis & { ResizeObserver?: typeof ResizeObserver }).ResizeObserver = ResizeObserver;
|
||||
@@ -40,7 +40,7 @@ type EntityViewConfig = {
|
||||
fileName: string;
|
||||
};
|
||||
|
||||
const tracingTarget = "kb_app_demo_desktop.frontend.demo_sql_replay_candidates";
|
||||
const tracingTarget = "kb_app_demo_desktop.frontend.demo_store_replay_candidates";
|
||||
const entityViewConfigs: EntityViewConfig[] = [
|
||||
{
|
||||
kind: "mint",
|
||||
@@ -88,11 +88,11 @@ const entityViewConfigs: EntityViewConfig[] = [
|
||||
fileName: "replay_account_keys.csv",
|
||||
},
|
||||
];
|
||||
let transactionTable: Api<DemoSqlReplayTransactionRow> | null = null;
|
||||
let programTable: Api<DemoSqlReplayProgramRow> | null = null;
|
||||
let mintTable: Api<DemoSqlReplayEntityRow> | null = null;
|
||||
let ownerTable: Api<DemoSqlReplayEntityRow> | null = null;
|
||||
let accountTable: Api<DemoSqlReplayEntityRow> | null = null;
|
||||
let transactionTable: Api<DemoStoreReplayTransactionRow> | null = null;
|
||||
let programTable: Api<DemoStoreReplayProgramRow> | null = null;
|
||||
let mintTable: Api<DemoStoreReplayEntityRow> | null = null;
|
||||
let ownerTable: Api<DemoStoreReplayEntityRow> | null = null;
|
||||
let accountTable: Api<DemoStoreReplayEntityRow> | null = null;
|
||||
let busyOperationCount = 0;
|
||||
let maximumReplayCandidateLimit = 100_000;
|
||||
|
||||
@@ -199,7 +199,7 @@ function badgeDisplay(value: unknown, type: string): string {
|
||||
return `<span class="badge ${className}">${escapeHtml(text)}</span>`;
|
||||
}
|
||||
|
||||
function coreStatusText(row: DemoSqlReplayTransactionRow): string {
|
||||
function coreStatusText(row: DemoStoreReplayTransactionRow): string {
|
||||
if (!row.hasCoreTransaction) {
|
||||
return "absent";
|
||||
}
|
||||
@@ -209,7 +209,7 @@ function coreStatusText(row: DemoSqlReplayTransactionRow): string {
|
||||
return "présent";
|
||||
}
|
||||
|
||||
function coreDisplay(_value: unknown, type: string, row: DemoSqlReplayTransactionRow): string | number {
|
||||
function coreDisplay(_value: unknown, type: string, row: DemoStoreReplayTransactionRow): string | number {
|
||||
if (type === "display") {
|
||||
if (!row.hasCoreTransaction) {
|
||||
return '<span class="badge text-bg-secondary">absent</span>';
|
||||
@@ -228,7 +228,7 @@ function coreDisplay(_value: unknown, type: string, row: DemoSqlReplayTransactio
|
||||
return row.hasCoreTransaction ? 1 : 2;
|
||||
}
|
||||
|
||||
function occurrenceRatioDisplay(_value: unknown, type: string, row: DemoSqlReplayEntityRow): string | number {
|
||||
function occurrenceRatioDisplay(_value: unknown, type: string, row: DemoStoreReplayEntityRow): string | number {
|
||||
const ratio = row.transactionCount > 0 ? row.occurrenceCount / row.transactionCount : 0;
|
||||
if (type === "display" || type === "filter") {
|
||||
return ratio.toFixed(2);
|
||||
@@ -236,7 +236,7 @@ function occurrenceRatioDisplay(_value: unknown, type: string, row: DemoSqlRepla
|
||||
return ratio;
|
||||
}
|
||||
|
||||
function slotSpanDisplay(_value: unknown, type: string, row: DemoSqlReplayEntityRow): string | number {
|
||||
function slotSpanDisplay(_value: unknown, type: string, row: DemoStoreReplayEntityRow): string | number {
|
||||
const span = Math.max(0, row.maxSlot - row.minSlot);
|
||||
if (type === "display" || type === "filter") {
|
||||
return String(span);
|
||||
@@ -258,8 +258,8 @@ function commonLanguage(emptyTable: string, infoLabel: string, zeroRecords: stri
|
||||
};
|
||||
}
|
||||
|
||||
function createTransactionTable(): Api<DemoSqlReplayTransactionRow> {
|
||||
return new DataTable<DemoSqlReplayTransactionRow>("#transactionCandidateTable", {
|
||||
function createTransactionTable(): Api<DemoStoreReplayTransactionRow> {
|
||||
return new DataTable<DemoStoreReplayTransactionRow>("#transactionCandidateTable", {
|
||||
data: [],
|
||||
columns: [
|
||||
{
|
||||
@@ -277,9 +277,9 @@ function createTransactionTable(): Api<DemoSqlReplayTransactionRow> {
|
||||
{ data: "ledgerStatus", render: badgeDisplay },
|
||||
{ data: "processorVersion", defaultContent: "—", render: codeDisplay },
|
||||
{ data: "attemptCount" },
|
||||
{ data: "outerInstructionCount" },
|
||||
{ data: "topLevelInstructionCount" },
|
||||
{ data: "innerInstructionCount" },
|
||||
{ data: "outerProgramCount" },
|
||||
{ data: "topLevelProgramCount" },
|
||||
{ data: "innerProgramCount" },
|
||||
{ data: "updatedAt" },
|
||||
],
|
||||
@@ -296,8 +296,8 @@ function createTransactionTable(): Api<DemoSqlReplayTransactionRow> {
|
||||
});
|
||||
}
|
||||
|
||||
function createProgramTable(): Api<DemoSqlReplayProgramRow> {
|
||||
return new DataTable<DemoSqlReplayProgramRow>("#programCandidateTable", {
|
||||
function createProgramTable(): Api<DemoStoreReplayProgramRow> {
|
||||
return new DataTable<DemoStoreReplayProgramRow>("#programCandidateTable", {
|
||||
data: [],
|
||||
columns: [
|
||||
{
|
||||
@@ -311,7 +311,7 @@ function createProgramTable(): Api<DemoSqlReplayProgramRow> {
|
||||
{ data: "programCode", defaultContent: "—", render: codeDisplay },
|
||||
{ data: "programId", render: copyableCodeDisplay("le program ID") },
|
||||
{ data: "transactionCount" },
|
||||
{ data: "outerInstructionCount" },
|
||||
{ data: "topLevelInstructionCount" },
|
||||
{ data: "innerInstructionCount" },
|
||||
{ data: "logCount" },
|
||||
{ data: "minSlot" },
|
||||
@@ -330,8 +330,8 @@ function createProgramTable(): Api<DemoSqlReplayProgramRow> {
|
||||
});
|
||||
}
|
||||
|
||||
function createEntityTable(config: EntityViewConfig): Api<DemoSqlReplayEntityRow> {
|
||||
return new DataTable<DemoSqlReplayEntityRow>(config.tableSelector, {
|
||||
function createEntityTable(config: EntityViewConfig): Api<DemoStoreReplayEntityRow> {
|
||||
return new DataTable<DemoStoreReplayEntityRow>(config.tableSelector, {
|
||||
data: [],
|
||||
columns: [
|
||||
{
|
||||
@@ -363,7 +363,7 @@ function createEntityTable(config: EntityViewConfig): Api<DemoSqlReplayEntityRow
|
||||
});
|
||||
}
|
||||
|
||||
function entityTable(kind: EntityKindCode): Api<DemoSqlReplayEntityRow> | null {
|
||||
function entityTable(kind: EntityKindCode): Api<DemoStoreReplayEntityRow> | null {
|
||||
if (kind === "mint") {
|
||||
return mintTable;
|
||||
}
|
||||
@@ -425,7 +425,7 @@ function setBusy(busy: boolean, message: string): void {
|
||||
});
|
||||
}
|
||||
|
||||
function buildTransactionRequest(): DemoSqlReplayTransactionRequest {
|
||||
function buildTransactionRequest(): DemoStoreReplayTransactionRequest {
|
||||
const entityKind = optionalInputValue("#transactionEntityKindSelect");
|
||||
const entityValue = optionalInputValue("#transactionEntityValueInput");
|
||||
if ((entityKind === null) !== (entityValue === null)) {
|
||||
@@ -484,9 +484,9 @@ async function loadTransactions(): Promise<void> {
|
||||
try {
|
||||
setBusy(true, "Chargement transactions");
|
||||
const request = buildTransactionRequest();
|
||||
const rows = await invoke<DemoSqlReplayTransactionRow[]>("load_demo_sql_replay_transactions", { request });
|
||||
const rows = await invoke<DemoStoreReplayTransactionRow[]>("load_demo_store_replay_transactions", { request });
|
||||
replaceRows(transactionTable, rows);
|
||||
element<HTMLElement>("#transactionResultSummary").textContent = `${rows.length} transaction(s) chargée(s) depuis PostgreSQL.`;
|
||||
element<HTMLElement>("#transactionResultSummary").textContent = `${rows.length} transaction(s) chargée(s) depuis le store actif.`;
|
||||
frontendDebug(tracingTarget, `loaded ${rows.length} replay transaction candidates`);
|
||||
} catch (caughtError) {
|
||||
const message = caughtError instanceof Error ? caughtError.message : String(caughtError);
|
||||
@@ -503,13 +503,13 @@ async function loadPrograms(): Promise<void> {
|
||||
}
|
||||
try {
|
||||
setBusy(true, "Chargement programmes");
|
||||
const request: DemoSqlReplayProgramRequest = {
|
||||
const request: DemoStoreReplayProgramRequest = {
|
||||
programIdContains: optionalInputValue("#programContainsInput"),
|
||||
limit: boundedPositiveIntegerValue("#programLimitInput"),
|
||||
};
|
||||
const rows = await invoke<DemoSqlReplayProgramRow[]>("load_demo_sql_replay_programs", { request });
|
||||
const rows = await invoke<DemoStoreReplayProgramRow[]>("load_demo_store_replay_programs", { request });
|
||||
replaceRows(programTable, rows);
|
||||
element<HTMLElement>("#programResultSummary").textContent = `${rows.length} programme(s) chargé(s), avec occurrences outer, inner et logs liés.`;
|
||||
element<HTMLElement>("#programResultSummary").textContent = `${rows.length} programme(s) chargé(s), avec occurrences top-level, inner et logs liés.`;
|
||||
frontendDebug(tracingTarget, `loaded ${rows.length} replay program summaries`);
|
||||
} catch (caughtError) {
|
||||
const message = caughtError instanceof Error ? caughtError.message : String(caughtError);
|
||||
@@ -527,14 +527,14 @@ async function loadEntity(config: EntityViewConfig): Promise<void> {
|
||||
}
|
||||
try {
|
||||
setBusy(true, `Chargement ${config.pluralLabel}`);
|
||||
const request: DemoSqlReplayEntityRequest = {
|
||||
const request: DemoStoreReplayEntityRequest = {
|
||||
entityKind: config.kind,
|
||||
entityValueContains: optionalInputValue(config.containsSelector),
|
||||
limit: boundedPositiveIntegerValue(config.limitSelector),
|
||||
};
|
||||
const rows = await invoke<DemoSqlReplayEntityRow[]>("load_demo_sql_replay_entities", { request });
|
||||
const rows = await invoke<DemoStoreReplayEntityRow[]>("load_demo_store_replay_entities", { request });
|
||||
replaceRows(table, rows);
|
||||
element<HTMLElement>(config.summarySelector).textContent = `${rows.length} ${config.pluralLabel} chargé(s) depuis PostgreSQL.`;
|
||||
element<HTMLElement>(config.summarySelector).textContent = `${rows.length} ${config.pluralLabel} chargé(s) depuis le store actif.`;
|
||||
frontendDebug(tracingTarget, `loaded ${rows.length} replay ${config.kind} summaries`);
|
||||
} catch (caughtError) {
|
||||
const message = caughtError instanceof Error ? caughtError.message : String(caughtError);
|
||||
@@ -567,7 +567,7 @@ async function saveCsv(fileName: string, content: string, rowCount: number): Pro
|
||||
if (rowCount === 0) {
|
||||
throw new Error("Aucune ligne à exporter.");
|
||||
}
|
||||
const path = await invoke<string>("export_demo_sql_replay_csv", { fileName, content });
|
||||
const path = await invoke<string>("export_demo_store_replay_csv", { fileName, content });
|
||||
const badge = element<HTMLElement>("#replayCandidateStatusBadge");
|
||||
badge.textContent = `${rowCount} ligne(s) exportée(s) : ${path}`;
|
||||
badge.className = "badge text-bg-success";
|
||||
@@ -577,8 +577,8 @@ async function saveCsv(fileName: string, content: string, rowCount: number): Pro
|
||||
async function exportTransactionsCsv(): Promise<void> {
|
||||
const rows = transactionRowsForExport();
|
||||
const content = csvContent(
|
||||
["signature", "slot", "raw_processing_state", "retention_state", "core_status", "ledger_status", "processor_version", "attempt_count", "outer_instruction_count", "inner_instruction_count", "outer_program_count", "inner_program_count", "updated_at"],
|
||||
rows.map(row => [row.signature, row.slot, row.rawProcessingState, row.retentionState, coreStatusText(row), row.ledgerStatus, row.processorVersion, row.attemptCount, row.outerInstructionCount, row.innerInstructionCount, row.outerProgramCount, row.innerProgramCount, row.updatedAt]),
|
||||
["signature", "slot", "raw_processing_state", "retention_state", "core_status", "ledger_status", "processor_version", "attempt_count", "top_level_instruction_count", "inner_instruction_count", "top_level_program_count", "inner_program_count", "updated_at"],
|
||||
rows.map(row => [row.signature, row.slot, row.rawProcessingState, row.retentionState, coreStatusText(row), row.ledgerStatus, row.processorVersion, row.attemptCount, row.topLevelInstructionCount, row.innerInstructionCount, row.topLevelProgramCount, row.innerProgramCount, row.updatedAt]),
|
||||
);
|
||||
await saveCsv("replay_transactions.csv", content, rows.length);
|
||||
}
|
||||
@@ -586,8 +586,8 @@ async function exportTransactionsCsv(): Promise<void> {
|
||||
async function exportProgramsCsv(): Promise<void> {
|
||||
const rows = programRowsForExport();
|
||||
const content = csvContent(
|
||||
["program_code", "program_id", "transaction_count", "outer_instruction_count", "inner_instruction_count", "log_count", "min_slot", "max_slot"],
|
||||
rows.map(row => [row.programCode, row.programId, row.transactionCount, row.outerInstructionCount, row.innerInstructionCount, row.logCount, row.minSlot, row.maxSlot]),
|
||||
["program_code", "program_id", "transaction_count", "top_level_instruction_count", "inner_instruction_count", "log_count", "min_slot", "max_slot"],
|
||||
rows.map(row => [row.programCode, row.programId, row.transactionCount, row.topLevelInstructionCount, row.innerInstructionCount, row.logCount, row.minSlot, row.maxSlot]),
|
||||
);
|
||||
await saveCsv("replay_programs.csv", content, rows.length);
|
||||
}
|
||||
@@ -601,21 +601,21 @@ async function exportEntityCsv(config: EntityViewConfig): Promise<void> {
|
||||
await saveCsv(config.fileName, content, rows.length);
|
||||
}
|
||||
|
||||
function transactionRowsForExport(): DemoSqlReplayTransactionRow[] {
|
||||
function transactionRowsForExport(): DemoStoreReplayTransactionRow[] {
|
||||
if (transactionTable === null) {
|
||||
return [];
|
||||
}
|
||||
return selectedOrFilteredRows(transactionTable);
|
||||
}
|
||||
|
||||
function programRowsForExport(): DemoSqlReplayProgramRow[] {
|
||||
function programRowsForExport(): DemoStoreReplayProgramRow[] {
|
||||
if (programTable === null) {
|
||||
return [];
|
||||
}
|
||||
return selectedOrFilteredRows(programTable);
|
||||
}
|
||||
|
||||
function entityRowsForExport(kind: EntityKindCode): DemoSqlReplayEntityRow[] {
|
||||
function entityRowsForExport(kind: EntityKindCode): DemoStoreReplayEntityRow[] {
|
||||
const table = entityTable(kind);
|
||||
if (table === null) {
|
||||
return [];
|
||||
@@ -660,13 +660,13 @@ async function openCoreExtraction(): Promise<void> {
|
||||
}
|
||||
|
||||
async function loadOptions(): Promise<void> {
|
||||
const options = await invoke<DemoSqlReplayOptionsPayload>("demo_sql_replay_options");
|
||||
const options = await invoke<DemoStoreReplayOptionsPayload>("demo_store_replay_options");
|
||||
if (!Number.isSafeInteger(options.maximumLimit) || options.maximumLimit <= 0) {
|
||||
throw new Error("La limite maximale des candidats replay retournée par le backend est invalide.");
|
||||
}
|
||||
maximumReplayCandidateLimit = options.maximumLimit;
|
||||
element<HTMLElement>("#replayCandidateProfileBadge").textContent = `Profil ${options.activeProfileName}`;
|
||||
element<HTMLElement>("#replayCandidateDsn").textContent = options.maskedDsn;
|
||||
element<HTMLElement>("#replayCandidateDsn").textContent = options.connectionDescriptor;
|
||||
element<HTMLElement>("#replayCandidateMaximumLimit").textContent = String(options.maximumLimit);
|
||||
document.querySelectorAll<HTMLInputElement>("input[type=number][max]").forEach(input => {
|
||||
input.max = String(options.maximumLimit);
|
||||
@@ -674,7 +674,7 @@ async function loadOptions(): Promise<void> {
|
||||
populateKnownPrograms(options.knownPrograms);
|
||||
}
|
||||
|
||||
function populateKnownPrograms(programs: DemoSqlReplayKnownProgramOption[]): void {
|
||||
function populateKnownPrograms(programs: DemoStoreReplayKnownProgramOption[]): void {
|
||||
const select = element<HTMLSelectElement>("#knownProgramSelect");
|
||||
const datalist = element<HTMLDataListElement>("#knownProgramDatalist");
|
||||
select.replaceChildren(new Option("Tous les programmes connus", ""));
|
||||
@@ -779,7 +779,7 @@ function installHandlers(): void {
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
installFrontendConsoleBridge(tracingTarget);
|
||||
frontendDebug(tracingTarget, "SQL replay candidate browser loaded");
|
||||
frontendDebug(tracingTarget, "Store replay candidate browser loaded");
|
||||
transactionTable = createTransactionTable();
|
||||
programTable = createProgramTable();
|
||||
mintTable = createEntityTable(entityViewConfigs[0]);
|
||||
@@ -1,10 +1,10 @@
|
||||
// file: kb-app-demo-desktop/frontend/ts/demo_sql_tables.ts
|
||||
// version: 4
|
||||
// file: kb-app-demo-desktop/frontend/ts/demo_store_tables.ts
|
||||
// version: 6
|
||||
|
||||
import DataTable, { type Api } from "datatables.net-bs5";
|
||||
import "datatables.net-bs5/css/dataTables.bootstrap5.css";
|
||||
import { jsonText, renderJsonViewer } from "./json_viewer.ts";
|
||||
import type { DemoSqlTableSnapshot } from "./bindings/kb_app_demo_desktop/demo_sql/DemoSqlTableSnapshot";
|
||||
import type { DemoStoreResourceSnapshot } from "./bindings/kb_app_demo_desktop/demo_store/DemoStoreResourceSnapshot";
|
||||
|
||||
const tableInstances = new Map<string, Api>();
|
||||
|
||||
@@ -15,7 +15,7 @@ export function setText(selector: string, value: string): void {
|
||||
}
|
||||
}
|
||||
|
||||
export function renderTables(tableSelector: string, bodySelector: string, tables: DemoSqlTableSnapshot[]): void {
|
||||
export function renderTables(tableSelector: string, bodySelector: string, resources: DemoStoreResourceSnapshot[]): void {
|
||||
const existing = tableInstances.get(tableSelector);
|
||||
if (existing) {
|
||||
existing.destroy();
|
||||
@@ -26,16 +26,16 @@ export function renderTables(tableSelector: string, bodySelector: string, tables
|
||||
return;
|
||||
}
|
||||
tbody.textContent = "";
|
||||
for (const table of tables) {
|
||||
for (const resource of resources) {
|
||||
const row = document.createElement("tr");
|
||||
row.appendChild(cell(table.tableName, true));
|
||||
row.appendChild(cell(table.domain, false));
|
||||
row.appendChild(cell(table.role, false));
|
||||
row.appendChild(cell(table.exists ? "oui" : "non", false));
|
||||
row.appendChild(cell(table.rowCount === null ? "—" : String(table.rowCount), false));
|
||||
row.appendChild(cell(table.minSlot === null ? "—" : String(table.minSlot), false));
|
||||
row.appendChild(cell(table.maxSlot === null ? "—" : String(table.maxSlot), false));
|
||||
row.appendChild(cell(table.latestCreatedAt ?? "—", false));
|
||||
row.appendChild(cell(resource.resourceCode, true));
|
||||
row.appendChild(cell(resource.modelCode, false));
|
||||
row.appendChild(cell(resource.role, false));
|
||||
row.appendChild(cell(resource.available ? "oui" : "non", false));
|
||||
row.appendChild(cell(resource.recordCount === null ? "—" : String(resource.recordCount), false));
|
||||
row.appendChild(cell(resource.minSlot === null ? "—" : String(resource.minSlot), false));
|
||||
row.appendChild(cell(resource.maxSlot === null ? "—" : String(resource.maxSlot), false));
|
||||
row.appendChild(cell(resource.latestCreatedAt ?? "—", false));
|
||||
tbody.appendChild(row);
|
||||
}
|
||||
const tableElement = document.querySelector<HTMLTableElement>(tableSelector);
|
||||
@@ -48,12 +48,12 @@ export function renderTables(tableSelector: string, bodySelector: string, tables
|
||||
lengthMenu: [10, 25, 50, 100],
|
||||
order: [[0, "asc"]],
|
||||
language: {
|
||||
emptyTable: "Aucune table disponible",
|
||||
info: "Tables _START_ à _END_ sur _TOTAL_",
|
||||
infoEmpty: "Aucune table",
|
||||
lengthMenu: "Afficher _MENU_ tables",
|
||||
emptyTable: "Aucune ressource disponible",
|
||||
info: "Ressources _START_ à _END_ sur _TOTAL_",
|
||||
infoEmpty: "Aucune ressource",
|
||||
lengthMenu: "Afficher _MENU_ ressources",
|
||||
search: "Rechercher :",
|
||||
zeroRecords: "Aucune table correspondante",
|
||||
zeroRecords: "Aucune ressource correspondante",
|
||||
},
|
||||
}));
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: kb-app-demo-desktop/frontend/ts/main.ts
|
||||
// version: 7
|
||||
// version: 8
|
||||
|
||||
import * as bootstrap from "bootstrap";
|
||||
import MarkdownIt from "markdown-it";
|
||||
@@ -97,10 +97,10 @@ for (const [elementId, command] of pipelineDemoLinks) {
|
||||
}
|
||||
|
||||
const sqlDemoLinks: ReadonlyArray<readonly [string, string]> = [
|
||||
["openDemoSqlDiagLink", "open_demo_sql_diag_window"],
|
||||
["openDemoSqlPgRawLink", "open_demo_sql_pg_raw_window"],
|
||||
["openDemoSqlPgCoreLink", "open_demo_sql_pg_core_window"],
|
||||
["openDemoSqlReplayCandidatesLink", "open_demo_sql_replay_candidates_window"],
|
||||
["openDemoStoreDiagLink", "open_demo_store_diag_window"],
|
||||
["openDemoStoreRawLink", "open_demo_store_raw_window"],
|
||||
["openDemoStoreCoreLink", "open_demo_store_core_window"],
|
||||
["openDemoStoreReplayCandidatesLink", "open_demo_store_replay_candidates_window"],
|
||||
];
|
||||
for (const [elementId, command] of sqlDemoLinks) {
|
||||
const link = document.querySelector<HTMLAnchorElement>(`#${elementId}`);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "kb-app-demo-desktop",
|
||||
"private": true,
|
||||
"version": "0.5.2",
|
||||
"version": "0.5.3",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: kb-app-demo-desktop/src/app_state.rs
|
||||
// version: 17
|
||||
// version: 20
|
||||
|
||||
//! Shared Tauri application state and startup initialization.
|
||||
|
||||
@@ -17,6 +17,7 @@ pub(crate) struct AppState {
|
||||
active_profile: ks_config::ProfileConfig,
|
||||
logging_guard: std::sync::Mutex<ks_logging::LoggingGuard>,
|
||||
http_pool: ks_onchain_transport::HttpEndpointPool,
|
||||
store: tokio::sync::OnceCell<ks_store::Store>,
|
||||
ws_pool:
|
||||
std::sync::Mutex<std::option::Option<std::sync::Arc<ks_onchain_transport::WsEndpointPool>>>,
|
||||
demo_ws_session:
|
||||
@@ -106,6 +107,7 @@ impl crate::AppState {
|
||||
active_profile,
|
||||
logging_guard: std::sync::Mutex::new(logging_guard),
|
||||
http_pool,
|
||||
store: tokio::sync::OnceCell::new(),
|
||||
ws_pool: std::sync::Mutex::new(std::option::Option::None),
|
||||
demo_ws_session: tokio::sync::Mutex::new(std::option::Option::None),
|
||||
demo_backfill_running: std::sync::atomic::AtomicBool::new(false),
|
||||
@@ -174,6 +176,25 @@ impl crate::AppState {
|
||||
return &self.active_profile;
|
||||
}
|
||||
|
||||
/// Returns the persistent backend-agnostic store opened from the active profile.
|
||||
pub(crate) async fn store(&self) -> ks_core::Result<&ks_store::Store> {
|
||||
if let std::option::Option::Some(store) = self.store.get() {
|
||||
return std::result::Result::Ok(store);
|
||||
}
|
||||
let options = match ks_store::StoreOpenOptions::new(
|
||||
self.active_profile.database.enabled,
|
||||
self.active_profile.database.backend.clone(),
|
||||
self.active_profile.database.backend_options.clone(),
|
||||
) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
return self
|
||||
.store
|
||||
.get_or_try_init(|| return async move { return ks_store::Store::open(options).await })
|
||||
.await;
|
||||
}
|
||||
|
||||
/// Returns the session-only execution-wallet alias override for one profile.
|
||||
pub(crate) fn demo_execution_wallet_alias_override(
|
||||
&self,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: kb-app-demo-desktop/src/demo_backfill.rs
|
||||
// version: 16
|
||||
// version: 17
|
||||
|
||||
//! Tauri commands and UI payloads for bounded HTTP transaction backfills.
|
||||
|
||||
@@ -288,23 +288,10 @@ pub(crate) async fn demo_backfill_execute(
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let profile = state.active_profile();
|
||||
let store_options = match ks_store::PostgresStoreOptions::new(
|
||||
profile.database.postgres.url.clone(),
|
||||
profile.database.postgres.max_connections,
|
||||
profile.database.postgres.connect_timeout_ms,
|
||||
false,
|
||||
) {
|
||||
let store = match crate::shared_store(state.inner()).await {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let store = match ks_store::PostgresStore::connect(store_options).await {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
|
||||
};
|
||||
if let std::result::Result::Err(error) = store.initialize_store_schema().await {
|
||||
return std::result::Result::Err(error.to_string());
|
||||
}
|
||||
let observer = crate::DemoBackfillObserver {
|
||||
app_handle,
|
||||
cancel_requested: state.demo_backfill_cancel_requested(),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: kb-app-demo-desktop/src/demo_config.rs
|
||||
// version: 6
|
||||
// version: 8
|
||||
|
||||
//! Configuration demo payloads with explicit public and bounded diagnostic projections.
|
||||
|
||||
@@ -164,7 +164,7 @@ pub(crate) struct DemoConfigEndpointDiagnosticPayload {
|
||||
pub(crate) role_count: u32,
|
||||
}
|
||||
|
||||
/// Store diagnostic projection without DSN or SQLite path content.
|
||||
/// Backend-agnostic store diagnostic projection without backend option values.
|
||||
#[derive(Clone, Debug, serde::Serialize, TS)]
|
||||
#[ts(
|
||||
export,
|
||||
@@ -173,23 +173,16 @@ pub(crate) struct DemoConfigEndpointDiagnosticPayload {
|
||||
pub(crate) struct DemoConfigStoreDiagnosticPayload {
|
||||
/// Whether persistence is enabled.
|
||||
pub(crate) enabled: bool,
|
||||
/// Selected store backend.
|
||||
/// Selected store backend code.
|
||||
pub(crate) backend: std::string::String,
|
||||
/// Whether a PostgreSQL URL is configured.
|
||||
pub(crate) postgres_url_state: std::string::String,
|
||||
/// PostgreSQL connection pool ceiling.
|
||||
pub(crate) postgres_max_connections: u32,
|
||||
/// PostgreSQL connection timeout in milliseconds.
|
||||
#[ts(type = "number")]
|
||||
pub(crate) postgres_connect_timeout_ms: u64,
|
||||
/// Whether PostgreSQL schema initialization is enabled.
|
||||
pub(crate) postgres_auto_initialize_schema: bool,
|
||||
/// Whether a SQLite path is configured.
|
||||
pub(crate) sqlite_path_state: std::string::String,
|
||||
/// SQLite connection pool ceiling.
|
||||
pub(crate) sqlite_max_connections: u32,
|
||||
/// Whether SQLite schema initialization is enabled.
|
||||
pub(crate) sqlite_auto_initialize_schema: bool,
|
||||
/// Whether backend-specific configuration validated successfully.
|
||||
pub(crate) configuration_status: std::string::String,
|
||||
/// Whether a backend connection or location value is configured.
|
||||
pub(crate) connection_state: std::string::String,
|
||||
/// Whether automatic store initialization is enabled.
|
||||
pub(crate) auto_initialize_schema: bool,
|
||||
/// Number of opaque backend options supplied after composition.
|
||||
pub(crate) backend_option_count: u32,
|
||||
}
|
||||
|
||||
/// Wallet diagnostic projection without paths or secret key material.
|
||||
@@ -375,17 +368,7 @@ fn diagnostic_config_payload(state: &crate::AppState) -> DemoConfigDiagnosticPay
|
||||
source_diagnostic("execution", state.execution_config_path()),
|
||||
],
|
||||
endpoints,
|
||||
store: DemoConfigStoreDiagnosticPayload {
|
||||
enabled: profile.database.enabled,
|
||||
backend: profile.database.backend.clone(),
|
||||
postgres_url_state: configured_state(profile.database.postgres.url.as_str()),
|
||||
postgres_max_connections: profile.database.postgres.max_connections,
|
||||
postgres_connect_timeout_ms: profile.database.postgres.connect_timeout_ms,
|
||||
postgres_auto_initialize_schema: profile.database.postgres.auto_initialize_schema,
|
||||
sqlite_path_state: configured_state(profile.database.sqlite.path.as_str()),
|
||||
sqlite_max_connections: profile.database.sqlite.max_connections,
|
||||
sqlite_auto_initialize_schema: profile.database.sqlite.auto_initialize_schema,
|
||||
},
|
||||
store: store_diagnostic(profile),
|
||||
wallet: DemoConfigWalletDiagnosticPayload {
|
||||
wallet_directory_state: configured_state(profile.wallet.wallet_dir.as_str()),
|
||||
cluster: profile.wallet.cluster.clone(),
|
||||
@@ -398,6 +381,55 @@ fn diagnostic_config_payload(state: &crate::AppState) -> DemoConfigDiagnosticPay
|
||||
};
|
||||
}
|
||||
|
||||
fn store_diagnostic(profile: &ks_config::ProfileConfig) -> DemoConfigStoreDiagnosticPayload {
|
||||
let backend_option_count = match profile.database.backend_options.as_object() {
|
||||
std::option::Option::Some(value) => bounded_len(value.len()),
|
||||
std::option::Option::None => 0,
|
||||
};
|
||||
let options = match ks_store::StoreOpenOptions::new(
|
||||
profile.database.enabled,
|
||||
profile.database.backend.clone(),
|
||||
profile.database.backend_options.clone(),
|
||||
) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => {
|
||||
return DemoConfigStoreDiagnosticPayload {
|
||||
enabled: profile.database.enabled,
|
||||
backend: profile.database.backend.clone(),
|
||||
configuration_status: "invalid".to_string(),
|
||||
connection_state: "unknown".to_string(),
|
||||
auto_initialize_schema: false,
|
||||
backend_option_count,
|
||||
};
|
||||
},
|
||||
};
|
||||
let summary = match options.configuration_summary() {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => {
|
||||
return DemoConfigStoreDiagnosticPayload {
|
||||
enabled: profile.database.enabled,
|
||||
backend: profile.database.backend.clone(),
|
||||
configuration_status: "invalid".to_string(),
|
||||
connection_state: "unknown".to_string(),
|
||||
auto_initialize_schema: false,
|
||||
backend_option_count,
|
||||
};
|
||||
},
|
||||
};
|
||||
return DemoConfigStoreDiagnosticPayload {
|
||||
enabled: summary.enabled,
|
||||
backend: summary.backend_code,
|
||||
configuration_status: "ready".to_string(),
|
||||
connection_state: if summary.connection_configured {
|
||||
"configured".to_string()
|
||||
} else {
|
||||
"missing".to_string()
|
||||
},
|
||||
auto_initialize_schema: summary.auto_initialize_schema,
|
||||
backend_option_count: summary.backend_option_count,
|
||||
};
|
||||
}
|
||||
|
||||
fn source_diagnostic(category: &str, path: &str) -> DemoConfigSourceDiagnosticPayload {
|
||||
return DemoConfigSourceDiagnosticPayload {
|
||||
category: category.to_string(),
|
||||
@@ -503,9 +535,19 @@ mod tests {
|
||||
#[test]
|
||||
fn public_and_diagnostic_projections_never_serialize_secret_canaries() {
|
||||
let mut profile = active_fixture_profile();
|
||||
profile.database.postgres.url =
|
||||
"postgres://operator:POSTGRES-SECRET-CANARY@localhost/solana".to_string();
|
||||
profile.database.sqlite.path = "/private/SQLITE-PATH-CANARY.sqlite".to_string();
|
||||
if let std::option::Option::Some(options) = profile.database.backend_options.as_object_mut()
|
||||
{
|
||||
options.insert(
|
||||
"url".to_string(),
|
||||
serde_json::Value::String(
|
||||
"postgres://operator:POSTGRES-SECRET-CANARY@localhost/solana".to_string(),
|
||||
),
|
||||
);
|
||||
options.insert(
|
||||
"private_path".to_string(),
|
||||
serde_json::Value::String("/private/STORE-PATH-CANARY".to_string()),
|
||||
);
|
||||
}
|
||||
profile.wallet.wallet_dir = "/private/WALLET-PATH-CANARY".to_string();
|
||||
if let std::option::Option::Some(endpoint) = profile.solana.http_endpoints.first_mut() {
|
||||
endpoint.url = "https://provider.invalid/?api-key=HELIUS-SECRET-CANARY".to_string();
|
||||
@@ -536,17 +578,7 @@ mod tests {
|
||||
};
|
||||
})
|
||||
.collect::<std::vec::Vec<super::DemoConfigEndpointDiagnosticPayload>>(),
|
||||
store: super::DemoConfigStoreDiagnosticPayload {
|
||||
enabled: profile.database.enabled,
|
||||
backend: profile.database.backend.clone(),
|
||||
postgres_url_state: super::configured_state(profile.database.postgres.url.as_str()),
|
||||
postgres_max_connections: profile.database.postgres.max_connections,
|
||||
postgres_connect_timeout_ms: profile.database.postgres.connect_timeout_ms,
|
||||
postgres_auto_initialize_schema: profile.database.postgres.auto_initialize_schema,
|
||||
sqlite_path_state: super::configured_state(profile.database.sqlite.path.as_str()),
|
||||
sqlite_max_connections: profile.database.sqlite.max_connections,
|
||||
sqlite_auto_initialize_schema: profile.database.sqlite.auto_initialize_schema,
|
||||
},
|
||||
store: super::store_diagnostic(&profile),
|
||||
wallet: super::DemoConfigWalletDiagnosticPayload {
|
||||
wallet_directory_state: super::configured_state(profile.wallet.wallet_dir.as_str()),
|
||||
cluster: profile.wallet.cluster.clone(),
|
||||
@@ -563,7 +595,7 @@ mod tests {
|
||||
};
|
||||
for secret in [
|
||||
"POSTGRES-SECRET-CANARY",
|
||||
"SQLITE-PATH-CANARY",
|
||||
"STORE-PATH-CANARY",
|
||||
"WALLET-PATH-CANARY",
|
||||
"HELIUS-SECRET-CANARY",
|
||||
"WS-SECRET-CANARY",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: kb-app-demo-desktop/src/demo_core_extraction.rs
|
||||
// version: 14
|
||||
// version: 17
|
||||
|
||||
//! Tauri commands and UI payloads for canonical transaction to core extraction.
|
||||
|
||||
@@ -221,7 +221,7 @@ pub(crate) async fn demo_core_extraction_execute(
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let store_result = crate::connect_postgres_store(state.active_profile()).await;
|
||||
let store_result = crate::shared_store(state).await;
|
||||
let store = match store_result {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: kb-app-demo-desktop/src/demo_decode_replay.rs
|
||||
// version: 40
|
||||
// version: 44
|
||||
|
||||
//! Tauri commands and UI payloads for contextual instruction decode replay.
|
||||
|
||||
@@ -255,8 +255,8 @@ pub(crate) struct DemoDecodeCoverageSummaryPayload {
|
||||
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_decode_replay/DemoDecodeDiagnosticsPayload.ts"
|
||||
)]
|
||||
pub(crate) struct DemoDecodeDiagnosticsPayload {
|
||||
/// Decode, materialization and ledger table diagnostics.
|
||||
pub(crate) tables: std::vec::Vec<crate::DemoSqlTableSnapshot>,
|
||||
/// Decode, materialization and ledger resource diagnostics.
|
||||
pub(crate) resources: std::vec::Vec<crate::DemoStoreResourceSnapshot>,
|
||||
/// Aggregated declared and observed coverage.
|
||||
pub(crate) coverage: std::vec::Vec<crate::DemoDecodeCoverageSummaryPayload>,
|
||||
}
|
||||
@@ -495,7 +495,7 @@ pub(crate) async fn demo_decode_replay_execute(
|
||||
materialize_after_decode = pipeline_request.materialize_after_decode,
|
||||
"start contextual decode replay pipeline"
|
||||
);
|
||||
let store_result = crate::connect_postgres_store(state.active_profile()).await;
|
||||
let store_result = crate::shared_store(state.inner()).await;
|
||||
let store = match store_result {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => {
|
||||
@@ -563,24 +563,24 @@ pub(crate) async fn demo_decode_replay_diagnostics(
|
||||
state: tauri::State<'_, crate::AppState>,
|
||||
) -> std::result::Result<crate::DemoDecodeDiagnosticsPayload, std::string::String> {
|
||||
tracing::debug!(target: crate::TRACING_TARGET, action = "load_diagnostics", coverage_limit = 500_u32, "load contextual decode replay diagnostics");
|
||||
let store_result = crate::connect_postgres_store(state.active_profile()).await;
|
||||
let store_result = crate::shared_store(state.inner()).await;
|
||||
let store = match store_result {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let table_result = store.known_table_diagnostics().await;
|
||||
let all_tables = match table_result {
|
||||
let resource_result = store.known_resource_diagnostics().await;
|
||||
let all_resources = match resource_result {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
|
||||
};
|
||||
let selected_tables: std::vec::Vec<crate::DemoSqlTableSnapshot> = all_tables
|
||||
let selected_resources: std::vec::Vec<crate::DemoStoreResourceSnapshot> = all_resources
|
||||
.iter()
|
||||
.filter(|table| {
|
||||
return table.table_name.starts_with("kb_sol_decode_")
|
||||
|| table.table_name.starts_with("kb_sol_mat_")
|
||||
|| table.table_name == "kb_sol_ops_processing_ledger";
|
||||
.filter(|resource| {
|
||||
return resource.model_code == "processing"
|
||||
|| resource.model_code == "decode"
|
||||
|| resource.model_code == "materialization";
|
||||
})
|
||||
.map(crate::table_snapshot_from_pg)
|
||||
.map(crate::store_resource_snapshot)
|
||||
.collect();
|
||||
let coverage_result = ks_store::DecodePipelineStore::list_decode_coverage_summary(
|
||||
&store,
|
||||
@@ -593,9 +593,9 @@ pub(crate) async fn demo_decode_replay_diagnostics(
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
|
||||
};
|
||||
tracing::debug!(target: crate::TRACING_TARGET, action = "load_diagnostics", table_count = selected_tables.len(), coverage_count = coverage.len(), "contextual decode replay diagnostics loaded");
|
||||
tracing::debug!(target: crate::TRACING_TARGET, action = "load_diagnostics", resource_count = selected_resources.len(), coverage_count = coverage.len(), "contextual decode replay diagnostics loaded");
|
||||
return std::result::Result::Ok(crate::DemoDecodeDiagnosticsPayload {
|
||||
tables: selected_tables,
|
||||
resources: selected_resources,
|
||||
coverage: coverage.into_iter().map(coverage_payload).collect(),
|
||||
});
|
||||
}
|
||||
@@ -616,7 +616,7 @@ pub(crate) async fn demo_decode_replay_annotations(
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
|
||||
};
|
||||
tracing::debug!(target: crate::TRACING_TARGET, action = "load_transaction_annotations", signature_contains = ?filter.signature_contains, limit = filter.limit, "load bounded committed transaction annotation journal");
|
||||
let store_result = crate::connect_postgres_store(state.active_profile()).await;
|
||||
let store_result = crate::shared_store(state.inner()).await;
|
||||
let store = match store_result {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: kb-app-demo-desktop/src/demo_devnet_common.rs
|
||||
// version: 6
|
||||
// version: 7
|
||||
|
||||
//! Shared Devnet demo UI contracts.
|
||||
|
||||
@@ -7,7 +7,7 @@ use ts_rs::TS; // rust-rules: derive-import
|
||||
|
||||
const DEMO_WALLET_PASSWORD_ENV: &str = "KB_SECRET_DEMO_WALLET_PASSWORD";
|
||||
|
||||
/// Readiness report for one Devnet profile PostgreSQL store.
|
||||
/// Backend-agnostic readiness report for one Devnet profile store.
|
||||
#[derive(Clone, Debug, serde::Serialize, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(
|
||||
@@ -17,16 +17,18 @@ const DEMO_WALLET_PASSWORD_ENV: &str = "KB_SECRET_DEMO_WALLET_PASSWORD";
|
||||
pub(crate) struct DemoExecutionDevnetStoreReadinessPayload {
|
||||
/// Selected Devnet profile.
|
||||
pub(crate) profile_name: std::string::String,
|
||||
/// Selected backend code.
|
||||
pub(crate) backend: std::string::String,
|
||||
/// Whether schema creation was allowed by configuration.
|
||||
pub(crate) auto_initialize_schema: bool,
|
||||
/// Number of known tables already present before preparation.
|
||||
pub(crate) existing_tables_before: u32,
|
||||
/// Number of known tables created during preparation.
|
||||
pub(crate) created_tables: u32,
|
||||
/// Number of known tables present after preparation.
|
||||
pub(crate) existing_tables_after: u32,
|
||||
/// Total number of known tables expected by the current store.
|
||||
pub(crate) expected_tables: u32,
|
||||
/// Number of logical resources already present before preparation.
|
||||
pub(crate) available_resources_before: u32,
|
||||
/// Number of logical resources created during preparation.
|
||||
pub(crate) created_resources: u32,
|
||||
/// Number of logical resources present after preparation.
|
||||
pub(crate) available_resources_after: u32,
|
||||
/// Total number of logical resources expected by the current store contract.
|
||||
pub(crate) expected_resources: u32,
|
||||
}
|
||||
|
||||
/// Converts the reusable scenario readiness report to the desktop TS-RS payload.
|
||||
@@ -35,11 +37,12 @@ pub(crate) fn devnet_store_readiness_payload(
|
||||
) -> DemoExecutionDevnetStoreReadinessPayload {
|
||||
return DemoExecutionDevnetStoreReadinessPayload {
|
||||
profile_name: readiness.profile_name,
|
||||
backend: readiness.backend,
|
||||
auto_initialize_schema: readiness.auto_initialize_schema,
|
||||
existing_tables_before: bounded_u32(readiness.existing_tables_before),
|
||||
created_tables: bounded_u32(readiness.created_tables),
|
||||
existing_tables_after: bounded_u32(readiness.existing_tables_after),
|
||||
expected_tables: bounded_u32(readiness.expected_tables),
|
||||
available_resources_before: bounded_u32(readiness.available_resources_before),
|
||||
created_resources: bounded_u32(readiness.created_resources),
|
||||
available_resources_after: bounded_u32(readiness.available_resources_after),
|
||||
expected_resources: bounded_u32(readiness.expected_resources),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: kb-app-demo-desktop/src/demo_execution_metadata_metaplex_token_metadata.rs
|
||||
// version: 11
|
||||
// version: 13
|
||||
|
||||
//! Desktop adapters for generic and qualified Metaplex Token Metadata execution workflows.
|
||||
|
||||
@@ -190,13 +190,10 @@ pub(crate) async fn demo_execution_metadata_metaplex_token_metadata_execute(
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
|
||||
};
|
||||
let store = match crate::connect_postgres_store(&profile).await {
|
||||
let store = match crate::open_store(&profile).await {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
|
||||
};
|
||||
if let std::result::Result::Err(error) = store.initialize_store_schema().await {
|
||||
return std::result::Result::Err(error.to_string());
|
||||
}
|
||||
let mut execution_request = match ks_pipeline_demo_scenarios::DevnetMetaplexTokenMetadataExecutionRequest::from_operation_json(
|
||||
request.intent_id,
|
||||
request.operation_json.as_str(),
|
||||
@@ -1044,13 +1041,10 @@ pub(crate) async fn demo_execution_metadata_metaplex_execute_campaign(
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
|
||||
};
|
||||
let store = match crate::connect_postgres_store(&profile).await {
|
||||
let store = match crate::open_store(&profile).await {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
|
||||
};
|
||||
if let std::result::Result::Err(error) = store.initialize_store_schema().await {
|
||||
return std::result::Result::Err(error.to_string());
|
||||
}
|
||||
let workspace_root = crate::workspace_root_dir();
|
||||
let configured_wallet_dir =
|
||||
std::path::PathBuf::from(profile.wallet.temporary_wallet_dir.as_str());
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: kb-app-demo-desktop/src/demo_execution_metadata_token_2022.rs
|
||||
// version: 5
|
||||
// version: 7
|
||||
|
||||
//! Thin desktop adapter for the complete Token-2022 Token Metadata Devnet campaign.
|
||||
|
||||
@@ -88,13 +88,10 @@ pub(crate) async fn demo_execution_metadata_token_2022_execute_campaign(
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
|
||||
};
|
||||
let store = match crate::connect_postgres_store(&profile).await {
|
||||
let store = match crate::open_store(&profile).await {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
|
||||
};
|
||||
if let std::result::Result::Err(error) = store.initialize_store_schema().await {
|
||||
return std::result::Result::Err(error.to_string());
|
||||
}
|
||||
let workspace_root = crate::workspace_root_dir();
|
||||
let configured_wallet_dir =
|
||||
std::path::PathBuf::from(profile.wallet.temporary_wallet_dir.as_str());
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: kb-app-demo-desktop/src/demo_execution_solana_core.rs
|
||||
// version: 20
|
||||
// version: 23
|
||||
|
||||
//! Tauri adapter for bounded Solana Core execution on Devnet.
|
||||
|
||||
@@ -394,7 +394,7 @@ pub(crate) fn confirmation_status_code(
|
||||
};
|
||||
}
|
||||
|
||||
/// Verifies or initializes the PostgreSQL schema selected by one Devnet profile.
|
||||
/// Verifies or initializes the configured store selected by one Devnet profile.
|
||||
pub(crate) async fn demo_execution_devnet_prepare_profile(
|
||||
state: tauri::State<'_, crate::AppState>,
|
||||
profile_name: std::string::String,
|
||||
@@ -473,14 +473,10 @@ pub(crate) async fn demo_execution_solana_core_execute(
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
|
||||
};
|
||||
let store = match crate::connect_postgres_store(&profile).await {
|
||||
let store = match crate::open_store(&profile).await {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
|
||||
};
|
||||
let initialize_result = store.initialize_store_schema().await;
|
||||
if let std::result::Result::Err(error) = initialize_result {
|
||||
return std::result::Result::Err(error.to_string());
|
||||
}
|
||||
let mut pipeline_request = ks_pipeline_demo_scenarios::DevnetSystemTransferRequest::new(
|
||||
format!("demo-execution-{}", chrono::Utc::now().timestamp_micros()),
|
||||
ks_lib::MdPubkey(request.recipient.trim().to_string()),
|
||||
@@ -561,13 +557,10 @@ pub(crate) async fn demo_execution_spl_memo_execute(
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
|
||||
};
|
||||
let store = match crate::connect_postgres_store(&profile).await {
|
||||
let store = match crate::open_store(&profile).await {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
|
||||
};
|
||||
if let std::result::Result::Err(error) = store.initialize_store_schema().await {
|
||||
return std::result::Result::Err(error.to_string());
|
||||
}
|
||||
let mut pipeline_request = ks_pipeline_demo_scenarios::DevnetMemoExecutionRequest::new(
|
||||
format!("demo-memo-execution-{}", chrono::Utc::now().timestamp_micros()),
|
||||
request.message,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: kb-app-demo-desktop/src/demo_spl_ata.rs
|
||||
// version: 16
|
||||
// version: 18
|
||||
|
||||
//! Thin Tauri adapter for ATA execution, derivation and lifecycle journal reads.
|
||||
|
||||
@@ -260,13 +260,10 @@ pub(crate) async fn demo_execution_spl_ata_execute(
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
|
||||
};
|
||||
let store = match crate::connect_postgres_store(&profile).await {
|
||||
let store = match crate::open_store(&profile).await {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
|
||||
};
|
||||
if let std::result::Result::Err(error) = store.initialize_store_schema().await {
|
||||
return std::result::Result::Err(error.to_string());
|
||||
}
|
||||
let decoders: std::vec::Vec<std::sync::Arc<dyn ks_lib::DcApiInstructionDecoder>> = std::vec![
|
||||
std::sync::Arc::new(ks_lib::DcSplAssociatedTokenAccountDecoder),
|
||||
std::sync::Arc::new(ks_lib::DcSplTokenDecoder),
|
||||
@@ -315,7 +312,7 @@ pub(crate) async fn demo_spl_ata_journal(
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let store = match crate::connect_postgres_store(&profile).await {
|
||||
let store = match crate::open_store(&profile).await {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: kb-app-demo-desktop/src/demo_spl_token.rs
|
||||
// version: 13
|
||||
// version: 16
|
||||
|
||||
//! Thin Tauri adapter for classic SPL Token execution and materialized journals.
|
||||
|
||||
@@ -99,7 +99,7 @@ pub(crate) struct DemoExecutionSplTokenSummaryPayload {
|
||||
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_spl_token/DemoSplTokenJournalRequest.ts"
|
||||
)]
|
||||
pub(crate) struct DemoSplTokenJournalRequest {
|
||||
/// Devnet profile whose PostgreSQL store is queried.
|
||||
/// Devnet profile whose configured store is queried.
|
||||
pub(crate) profile_name: std::string::String,
|
||||
/// Optional partial signature handled by the bounded store query.
|
||||
pub(crate) signature_contains: std::option::Option<std::string::String>,
|
||||
@@ -183,13 +183,10 @@ pub(crate) async fn demo_execution_spl_token_execute(
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
|
||||
};
|
||||
let store = match crate::connect_postgres_store(&profile).await {
|
||||
let store = match crate::open_store(&profile).await {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
|
||||
};
|
||||
if let std::result::Result::Err(error) = store.initialize_store_schema().await {
|
||||
return std::result::Result::Err(error.to_string());
|
||||
}
|
||||
let amount_raw = request.amount_raw.trim().to_string();
|
||||
let operation = ks_lib::ExSplClassicTokenOperation::Instruction {
|
||||
value: ks_lib::ExSplClassicTokenSingleOperation::TransferChecked {
|
||||
@@ -259,13 +256,10 @@ pub(crate) async fn demo_spl_token_journal(
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let store = match crate::connect_postgres_store(&profile).await {
|
||||
let store = match crate::open_store(&profile).await {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
|
||||
};
|
||||
if let std::result::Result::Err(error) = store.initialize_store_schema().await {
|
||||
return std::result::Result::Err(error.to_string());
|
||||
}
|
||||
let needs_payload_filter =
|
||||
validated.mint.is_some() || validated.account.is_some() || validated.operation.is_some();
|
||||
let query_limit = if needs_payload_filter {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: kb-app-demo-desktop/src/demo_spl_token_2022.rs
|
||||
// version: 18
|
||||
// version: 20
|
||||
|
||||
//! Thin Tauri adapter for independent public Token-2022 Devnet validation scenarios.
|
||||
|
||||
@@ -159,13 +159,10 @@ pub(crate) async fn demo_execution_spl_token_2022_execute(
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
|
||||
};
|
||||
let store = match crate::connect_postgres_store(&profile).await {
|
||||
let store = match crate::open_store(&profile).await {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
|
||||
};
|
||||
if let std::result::Result::Err(error) = store.initialize_store_schema().await {
|
||||
return std::result::Result::Err(error.to_string());
|
||||
}
|
||||
let operation = match operation_from_request(&request) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
|
||||
@@ -1,312 +0,0 @@
|
||||
// file: kb-app-demo-desktop/src/demo_sql_common.rs
|
||||
// version: 13
|
||||
|
||||
//! Shared SQL demo helpers and serializable payloads.
|
||||
|
||||
use ts_rs::TS; // rust-rules: derive-import
|
||||
|
||||
/// UI-safe table diagnostics shown by SQL demo windows.
|
||||
#[derive(Clone, Debug, serde::Serialize, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(
|
||||
export,
|
||||
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_sql/DemoSqlTableSnapshot.ts"
|
||||
)]
|
||||
pub(crate) struct DemoSqlTableSnapshot {
|
||||
/// Table name.
|
||||
pub(crate) table_name: std::string::String,
|
||||
/// Logical Solana domain encoded in the table name.
|
||||
pub(crate) domain: std::string::String,
|
||||
/// Human-readable table role.
|
||||
pub(crate) role: std::string::String,
|
||||
/// Whether the table currently exists.
|
||||
pub(crate) exists: bool,
|
||||
/// Row count when the table exists.
|
||||
#[ts(type = "number | null")]
|
||||
pub(crate) row_count: std::option::Option<i64>,
|
||||
/// Minimum slot when available.
|
||||
#[ts(type = "number | null")]
|
||||
pub(crate) min_slot: std::option::Option<i64>,
|
||||
/// Maximum slot when available.
|
||||
#[ts(type = "number | null")]
|
||||
pub(crate) max_slot: std::option::Option<i64>,
|
||||
/// Latest insertion timestamp rendered by PostgreSQL when available.
|
||||
pub(crate) latest_created_at: std::option::Option<std::string::String>,
|
||||
}
|
||||
|
||||
/// Builds PostgreSQL options from the active profile without coupling ks-store to ks-config.
|
||||
pub(crate) fn postgres_store_options_from_profile(
|
||||
profile: &ks_config::ProfileConfig,
|
||||
auto_initialize_schema: bool,
|
||||
) -> ks_core::Result<ks_store::PostgresStoreOptions> {
|
||||
return ks_store::PostgresStoreOptions::new(
|
||||
profile.database.postgres.url.clone(),
|
||||
profile.database.postgres.max_connections,
|
||||
profile.database.postgres.connect_timeout_ms,
|
||||
auto_initialize_schema,
|
||||
);
|
||||
}
|
||||
|
||||
/// Connects to PostgreSQL without reapplying schemas initialized at application startup.
|
||||
pub(crate) async fn connect_postgres_store(
|
||||
profile: &ks_config::ProfileConfig,
|
||||
) -> std::result::Result<ks_store::PostgresStore, std::string::String> {
|
||||
if !profile.database.enabled {
|
||||
return std::result::Result::Err(std::string::String::from(
|
||||
"database configuration is disabled in the active profile",
|
||||
));
|
||||
}
|
||||
let options_result = postgres_store_options_from_profile(profile, false);
|
||||
let mut options = match options_result {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
|
||||
};
|
||||
options.auto_initialize_schema = false;
|
||||
let store_result = ks_store::PostgresStore::connect(options).await;
|
||||
return match store_result {
|
||||
std::result::Result::Ok(store) => std::result::Result::Ok(store),
|
||||
std::result::Result::Err(error) => std::result::Result::Err(error.to_string()),
|
||||
};
|
||||
}
|
||||
|
||||
/// Converts store table diagnostics to the UI payload shape.
|
||||
pub(crate) fn table_snapshot_from_pg(
|
||||
value: &ks_store::PostgresTableDiagnostics,
|
||||
) -> crate::DemoSqlTableSnapshot {
|
||||
let row_count = match &value.statistics {
|
||||
std::option::Option::Some(statistics) => std::option::Option::Some(statistics.row_count),
|
||||
std::option::Option::None => std::option::Option::None,
|
||||
};
|
||||
let min_slot = match &value.statistics {
|
||||
std::option::Option::Some(statistics) => statistics.min_slot,
|
||||
std::option::Option::None => std::option::Option::None,
|
||||
};
|
||||
let max_slot = match &value.statistics {
|
||||
std::option::Option::Some(statistics) => statistics.max_slot,
|
||||
std::option::Option::None => std::option::Option::None,
|
||||
};
|
||||
let latest_created_at = match &value.statistics {
|
||||
std::option::Option::Some(statistics) => statistics.latest_created_at.clone(),
|
||||
std::option::Option::None => std::option::Option::None,
|
||||
};
|
||||
return crate::DemoSqlTableSnapshot {
|
||||
table_name: value.table_name.clone(),
|
||||
domain: value.domain.clone(),
|
||||
role: value.role.clone(),
|
||||
exists: value.exists,
|
||||
row_count,
|
||||
min_slot,
|
||||
max_slot,
|
||||
latest_created_at,
|
||||
};
|
||||
}
|
||||
|
||||
/// Converts many table diagnostics to UI payloads.
|
||||
pub(crate) fn table_snapshots_from_pg(
|
||||
values: &[ks_store::PostgresTableDiagnostics],
|
||||
) -> std::vec::Vec<crate::DemoSqlTableSnapshot> {
|
||||
let mut output = std::vec::Vec::new();
|
||||
for value in values {
|
||||
output.push(crate::table_snapshot_from_pg(value));
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
/// Initializes PostgreSQL raw, core, decode and materialization schemas during Tauri startup.
|
||||
pub(crate) async fn initialize_postgres_schema_for_startup(
|
||||
state: &crate::AppState,
|
||||
splash_window: &tauri::WebviewWindow,
|
||||
) {
|
||||
let profile = state.active_profile();
|
||||
if !profile.database.enabled {
|
||||
emit_sql_startup_splash(
|
||||
splash_window,
|
||||
"SQL store disabled; schema initialization skipped.",
|
||||
"info",
|
||||
false,
|
||||
);
|
||||
tracing::debug!(target: crate::TRACING_TARGET, "database is disabled; skip SQL schema initialization");
|
||||
return;
|
||||
}
|
||||
if profile.database.backend != "postgres" {
|
||||
emit_sql_startup_splash(
|
||||
splash_window,
|
||||
"SQL backend is not PostgreSQL; schema initialization skipped.",
|
||||
"info",
|
||||
false,
|
||||
);
|
||||
tracing::debug!(target: crate::TRACING_TARGET, backend = profile.database.backend.as_str(), "database backend is not postgres; skip SQL schema initialization");
|
||||
return;
|
||||
}
|
||||
let options_result = postgres_store_options_from_profile(
|
||||
profile,
|
||||
profile.database.postgres.auto_initialize_schema,
|
||||
);
|
||||
let mut options = match options_result {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => {
|
||||
let error_message = error.to_string();
|
||||
emit_sql_startup_error(splash_window, error_message.as_str());
|
||||
tracing::error!(target: crate::TRACING_TARGET, error = error_message.as_str(), "cannot build PostgreSQL store options");
|
||||
return;
|
||||
},
|
||||
};
|
||||
let initialize_schema = options.auto_initialize_schema;
|
||||
let masked_dsn = options.masked_dsn();
|
||||
options.auto_initialize_schema = false;
|
||||
emit_sql_startup_splash(
|
||||
splash_window,
|
||||
"Connexion PostgreSQL et vérification des tables raw/core/decode...",
|
||||
"info",
|
||||
false,
|
||||
);
|
||||
if !initialize_schema {
|
||||
emit_sql_startup_splash(
|
||||
splash_window,
|
||||
"Auto-initialisation désactivée : vérification sans création de table.",
|
||||
"info",
|
||||
true,
|
||||
);
|
||||
}
|
||||
tracing::debug!(target: crate::TRACING_TARGET, dsn = masked_dsn.as_str(), "start PostgreSQL schema initialization");
|
||||
let store_result = ks_store::PostgresStore::connect(options).await;
|
||||
let store = match store_result {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => {
|
||||
let error_message = error.to_string();
|
||||
emit_sql_startup_error(splash_window, error_message.as_str());
|
||||
tracing::error!(target: crate::TRACING_TARGET, error = error_message.as_str(), "PostgreSQL connection failed during startup schema initialization");
|
||||
return;
|
||||
},
|
||||
};
|
||||
let before_result = store.known_table_diagnostics().await;
|
||||
let before_tables = match before_result {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => {
|
||||
let error_message = error.to_string();
|
||||
emit_sql_startup_error(splash_window, error_message.as_str());
|
||||
tracing::error!(target: crate::TRACING_TARGET, error = error_message.as_str(), "cannot read table diagnostics before schema initialization");
|
||||
return;
|
||||
},
|
||||
};
|
||||
if initialize_schema {
|
||||
let initialize_result = store.initialize_store_schema().await;
|
||||
if let std::result::Result::Err(error) = initialize_result {
|
||||
let error_message = error.to_string();
|
||||
emit_sql_startup_error(splash_window, error_message.as_str());
|
||||
tracing::error!(target: crate::TRACING_TARGET, error = error_message.as_str(), "PostgreSQL store schema initialization failed");
|
||||
return;
|
||||
}
|
||||
}
|
||||
let after_result = store.known_table_diagnostics().await;
|
||||
let after_tables = match after_result {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => {
|
||||
let error_message = error.to_string();
|
||||
emit_sql_startup_error(splash_window, error_message.as_str());
|
||||
tracing::error!(target: crate::TRACING_TARGET, error = error_message.as_str(), "cannot read table diagnostics after schema initialization");
|
||||
return;
|
||||
},
|
||||
};
|
||||
emit_sql_startup_table_report(splash_window, before_tables.as_slice(), after_tables.as_slice());
|
||||
}
|
||||
|
||||
/// Formats debug enums for UI display.
|
||||
pub(crate) fn debug_status<T: std::fmt::Debug>(value: T) -> std::string::String {
|
||||
return format!("{value:?}");
|
||||
}
|
||||
|
||||
fn emit_sql_startup_table_report(
|
||||
splash_window: &tauri::WebviewWindow,
|
||||
before_tables: &[ks_store::PostgresTableDiagnostics],
|
||||
after_tables: &[ks_store::PostgresTableDiagnostics],
|
||||
) {
|
||||
let mut created_count = 0_u32;
|
||||
let mut existing_count = 0_u32;
|
||||
let mut missing_count = 0_u32;
|
||||
for table in after_tables {
|
||||
let before_exists = table_exists_in(before_tables, table.table_name.as_str());
|
||||
if table.exists && before_exists == std::option::Option::Some(false) {
|
||||
created_count += 1;
|
||||
emit_sql_startup_splash(
|
||||
splash_window,
|
||||
format!("Created table {}", table.table_name).as_str(),
|
||||
"success",
|
||||
true,
|
||||
);
|
||||
tracing::debug!(target: crate::TRACING_TARGET, table = table.table_name.as_str(), "PostgreSQL table created");
|
||||
} else if table.exists {
|
||||
existing_count += 1;
|
||||
emit_sql_startup_splash(
|
||||
splash_window,
|
||||
format!("Table {} already exists", table.table_name).as_str(),
|
||||
"info",
|
||||
true,
|
||||
);
|
||||
tracing::debug!(target: crate::TRACING_TARGET, table = table.table_name.as_str(), "PostgreSQL table already exists");
|
||||
} else {
|
||||
missing_count += 1;
|
||||
emit_sql_startup_splash(
|
||||
splash_window,
|
||||
format!("Table {} is missing after initialization", table.table_name).as_str(),
|
||||
"warning",
|
||||
true,
|
||||
);
|
||||
tracing::error!(target: crate::TRACING_TARGET, table = table.table_name.as_str(), "PostgreSQL table missing after initialization");
|
||||
}
|
||||
}
|
||||
emit_sql_startup_splash(
|
||||
splash_window,
|
||||
format!(
|
||||
"PostgreSQL schema ready: {created_count} created, {existing_count} already present, {missing_count} missing."
|
||||
)
|
||||
.as_str(),
|
||||
schema_report_status(missing_count),
|
||||
false,
|
||||
);
|
||||
tracing::debug!(target: crate::TRACING_TARGET, created = created_count, existing = existing_count, missing = missing_count, "PostgreSQL schema initialization completed");
|
||||
}
|
||||
|
||||
fn table_exists_in(
|
||||
tables: &[ks_store::PostgresTableDiagnostics],
|
||||
table_name: &str,
|
||||
) -> std::option::Option<bool> {
|
||||
for table in tables {
|
||||
if table.table_name.as_str() == table_name {
|
||||
return std::option::Option::Some(table.exists);
|
||||
}
|
||||
}
|
||||
return std::option::Option::None;
|
||||
}
|
||||
|
||||
fn schema_report_status(missing_count: u32) -> &'static str {
|
||||
if missing_count == 0 {
|
||||
return "success";
|
||||
}
|
||||
return "warning";
|
||||
}
|
||||
|
||||
fn emit_sql_startup_error(splash_window: &tauri::WebviewWindow, message: &str) {
|
||||
emit_sql_startup_splash(
|
||||
splash_window,
|
||||
format!("PostgreSQL schema initialization error: {message}").as_str(),
|
||||
"danger",
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
fn emit_sql_startup_splash(
|
||||
splash_window: &tauri::WebviewWindow,
|
||||
message: &str,
|
||||
status: &str,
|
||||
log_only: bool,
|
||||
) {
|
||||
let order = if log_only { "add_log" } else { "add_msg" };
|
||||
crate::emit_splash_order(
|
||||
splash_window,
|
||||
order,
|
||||
std::option::Option::Some(message),
|
||||
std::option::Option::Some(status),
|
||||
std::option::Option::None,
|
||||
);
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
// file: kb-app-demo-desktop/src/demo_sql_diag.rs
|
||||
// version: 5
|
||||
|
||||
//! SQL diagnostic demo commands.
|
||||
|
||||
use ts_rs::TS; // rust-rules: derive-import
|
||||
|
||||
/// Complete SQL diagnostic payload shown by `demo_sql_diag`.
|
||||
#[derive(Clone, Debug, serde::Serialize, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(
|
||||
export,
|
||||
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_sql_diag/DemoSqlDiagPayload.ts"
|
||||
)]
|
||||
pub(crate) struct DemoSqlDiagPayload {
|
||||
/// Configuration file path.
|
||||
pub(crate) config_path: std::string::String,
|
||||
/// Active profile name.
|
||||
pub(crate) active_profile_name: std::string::String,
|
||||
/// Configured database backend.
|
||||
pub(crate) backend: std::string::String,
|
||||
/// Masked PostgreSQL DSN.
|
||||
pub(crate) masked_dsn: std::string::String,
|
||||
/// PostgreSQL current schema.
|
||||
pub(crate) current_schema: std::option::Option<std::string::String>,
|
||||
/// PostgreSQL health status.
|
||||
pub(crate) health_status: std::string::String,
|
||||
/// PostgreSQL health message.
|
||||
pub(crate) health_message: std::option::Option<std::string::String>,
|
||||
/// Migration status.
|
||||
pub(crate) migration_status: std::string::String,
|
||||
/// Migration diagnostic message.
|
||||
pub(crate) migration_message: std::option::Option<std::string::String>,
|
||||
/// PostgreSQL server version when available.
|
||||
pub(crate) server_version: std::option::Option<std::string::String>,
|
||||
/// Known raw/core table diagnostics.
|
||||
pub(crate) tables: std::vec::Vec<crate::DemoSqlTableSnapshot>,
|
||||
}
|
||||
|
||||
/// Loads read-only SQL diagnostics from the active profile.
|
||||
pub(crate) async fn load_demo_sql_diag(
|
||||
state: tauri::State<'_, crate::AppState>,
|
||||
) -> std::result::Result<crate::DemoSqlDiagPayload, std::string::String> {
|
||||
let profile = state.active_profile().clone();
|
||||
let store_result = crate::connect_postgres_store(&profile).await;
|
||||
let store = match store_result {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let diagnostics_result = store.backend_diagnostics().await;
|
||||
let diagnostics = match diagnostics_result {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
|
||||
};
|
||||
let tables_result = store.known_table_diagnostics().await;
|
||||
let tables = match tables_result {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
|
||||
};
|
||||
return std::result::Result::Ok(crate::DemoSqlDiagPayload {
|
||||
config_path: state.config_path().to_string(),
|
||||
active_profile_name: profile.name.clone(),
|
||||
backend: profile.database.backend.clone(),
|
||||
masked_dsn: store.options().masked_dsn(),
|
||||
current_schema: diagnostics.descriptor.current_schema,
|
||||
health_status: crate::debug_status(diagnostics.health.status),
|
||||
health_message: diagnostics.health.message,
|
||||
migration_status: crate::debug_status(diagnostics.migrations.status),
|
||||
migration_message: diagnostics.migrations.message,
|
||||
server_version: diagnostics.server_version,
|
||||
tables: crate::table_snapshots_from_pg(tables.as_slice()),
|
||||
});
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
// file: kb-app-demo-desktop/src/demo_sql_pg_core.rs
|
||||
// version: 6
|
||||
|
||||
//! PostgreSQL core store demo commands.
|
||||
|
||||
use ts_rs::TS; // rust-rules: derive-import
|
||||
|
||||
/// PostgreSQL core store payload shown by `demo_sql_pg_core`.
|
||||
#[derive(Clone, Debug, serde::Serialize, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(
|
||||
export,
|
||||
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_sql_pg_core/DemoSqlPgCorePayload.ts"
|
||||
)]
|
||||
pub(crate) struct DemoSqlPgCorePayload {
|
||||
/// Active profile name.
|
||||
pub(crate) active_profile_name: std::string::String,
|
||||
/// Masked PostgreSQL DSN.
|
||||
pub(crate) masked_dsn: std::string::String,
|
||||
/// Core table diagnostics.
|
||||
pub(crate) tables: std::vec::Vec<crate::DemoSqlTableSnapshot>,
|
||||
}
|
||||
|
||||
/// Loads read-only core table diagnostics from PostgreSQL.
|
||||
pub(crate) async fn load_demo_sql_pg_core(
|
||||
state: tauri::State<'_, crate::AppState>,
|
||||
) -> std::result::Result<crate::DemoSqlPgCorePayload, std::string::String> {
|
||||
let profile = state.active_profile().clone();
|
||||
let store_result = crate::connect_postgres_store(&profile).await;
|
||||
let store = match store_result {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let tables_result = store.core_table_diagnostics().await;
|
||||
let tables = match tables_result {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
|
||||
};
|
||||
return std::result::Result::Ok(crate::DemoSqlPgCorePayload {
|
||||
active_profile_name: profile.name.clone(),
|
||||
masked_dsn: store.options().masked_dsn(),
|
||||
tables: crate::table_snapshots_from_pg(tables.as_slice()),
|
||||
});
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
// file: kb-app-demo-desktop/src/demo_sql_pg_raw.rs
|
||||
// version: 7
|
||||
|
||||
//! PostgreSQL canonical acquisition store demo commands.
|
||||
|
||||
use ts_rs::TS; // rust-rules: derive-import
|
||||
|
||||
/// PostgreSQL canonical acquisition payload shown by `demo_sql_pg_raw`.
|
||||
#[derive(Clone, Debug, serde::Serialize, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(
|
||||
export,
|
||||
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_sql_pg_raw/DemoSqlPgRawPayload.ts"
|
||||
)]
|
||||
pub(crate) struct DemoSqlPgRawPayload {
|
||||
/// Active profile name.
|
||||
pub(crate) active_profile_name: std::string::String,
|
||||
/// Masked PostgreSQL DSN.
|
||||
pub(crate) masked_dsn: std::string::String,
|
||||
/// Raw table diagnostics.
|
||||
pub(crate) tables: std::vec::Vec<crate::DemoSqlTableSnapshot>,
|
||||
}
|
||||
|
||||
/// Loads read-only raw table diagnostics from PostgreSQL.
|
||||
pub(crate) async fn load_demo_sql_pg_raw(
|
||||
state: tauri::State<'_, crate::AppState>,
|
||||
) -> std::result::Result<crate::DemoSqlPgRawPayload, std::string::String> {
|
||||
let profile = state.active_profile().clone();
|
||||
let store_result = crate::connect_postgres_store(&profile).await;
|
||||
let store = match store_result {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let tables_result = store.raw_table_diagnostics().await;
|
||||
let tables = match tables_result {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
|
||||
};
|
||||
return std::result::Result::Ok(crate::DemoSqlPgRawPayload {
|
||||
active_profile_name: profile.name.clone(),
|
||||
masked_dsn: store.options().masked_dsn(),
|
||||
tables: crate::table_snapshots_from_pg(tables.as_slice()),
|
||||
});
|
||||
}
|
||||
264
kb-app-demo-desktop/src/demo_store_common.rs
Normal file
264
kb-app-demo-desktop/src/demo_store_common.rs
Normal file
@@ -0,0 +1,264 @@
|
||||
// file: kb-app-demo-desktop/src/demo_store_common.rs
|
||||
// version: 19
|
||||
|
||||
//! Shared backend-agnostic store demo helpers and serializable payloads.
|
||||
|
||||
use ts_rs::TS; // rust-rules: derive-import
|
||||
|
||||
/// UI-safe diagnostics for one logical store resource.
|
||||
#[derive(Clone, Debug, serde::Serialize, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(
|
||||
export,
|
||||
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_store/DemoStoreResourceSnapshot.ts"
|
||||
)]
|
||||
pub(crate) struct DemoStoreResourceSnapshot {
|
||||
/// Stable logical resource code independent of the active backend.
|
||||
pub(crate) resource_code: std::string::String,
|
||||
/// Logical store model containing the resource.
|
||||
pub(crate) model_code: std::string::String,
|
||||
/// Human-readable resource role.
|
||||
pub(crate) role: std::string::String,
|
||||
/// Whether the logical resource is currently available.
|
||||
pub(crate) available: bool,
|
||||
/// Record count when the resource is available.
|
||||
#[ts(type = "number | null")]
|
||||
pub(crate) record_count: std::option::Option<i64>,
|
||||
/// Minimum slot when available.
|
||||
#[ts(type = "number | null")]
|
||||
pub(crate) min_slot: std::option::Option<i64>,
|
||||
/// Maximum slot when available.
|
||||
#[ts(type = "number | null")]
|
||||
pub(crate) max_slot: std::option::Option<i64>,
|
||||
/// Latest persistence timestamp when available.
|
||||
pub(crate) latest_created_at: std::option::Option<std::string::String>,
|
||||
}
|
||||
|
||||
/// Builds backend-agnostic store-open options from one resolved profile.
|
||||
fn store_open_options_from_profile(
|
||||
profile: &ks_config::ProfileConfig,
|
||||
) -> ks_core::Result<ks_store::StoreOpenOptions> {
|
||||
return ks_store::StoreOpenOptions::new(
|
||||
profile.database.enabled,
|
||||
profile.database.backend.clone(),
|
||||
profile.database.backend_options.clone(),
|
||||
);
|
||||
}
|
||||
|
||||
/// Opens a backend-agnostic store from one resolved profile.
|
||||
///
|
||||
/// Prefer [`crate::AppState::store`] for desktop commands that already own application state.
|
||||
pub(crate) async fn open_store(
|
||||
profile: &ks_config::ProfileConfig,
|
||||
) -> std::result::Result<ks_store::Store, std::string::String> {
|
||||
let options = match store_open_options_from_profile(profile) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
|
||||
};
|
||||
return match ks_store::Store::open(options).await {
|
||||
std::result::Result::Ok(store) => std::result::Result::Ok(store),
|
||||
std::result::Result::Err(error) => std::result::Result::Err(error.to_string()),
|
||||
};
|
||||
}
|
||||
|
||||
/// Clones the persistent store held by application state.
|
||||
pub(crate) async fn shared_store(
|
||||
state: &crate::AppState,
|
||||
) -> std::result::Result<ks_store::Store, std::string::String> {
|
||||
let store_result = state.store().await;
|
||||
return match store_result {
|
||||
std::result::Result::Ok(store) => std::result::Result::Ok(store.clone()),
|
||||
std::result::Result::Err(error) => std::result::Result::Err(error.to_string()),
|
||||
};
|
||||
}
|
||||
|
||||
/// Converts one logical store resource diagnostic to the UI payload shape.
|
||||
pub(crate) fn store_resource_snapshot(
|
||||
value: &ks_store::StoreResourceDiagnostics,
|
||||
) -> crate::DemoStoreResourceSnapshot {
|
||||
let record_count = match &value.statistics {
|
||||
std::option::Option::Some(statistics) => std::option::Option::Some(statistics.record_count),
|
||||
std::option::Option::None => std::option::Option::None,
|
||||
};
|
||||
let min_slot = match &value.statistics {
|
||||
std::option::Option::Some(statistics) => statistics.min_slot,
|
||||
std::option::Option::None => std::option::Option::None,
|
||||
};
|
||||
let max_slot = match &value.statistics {
|
||||
std::option::Option::Some(statistics) => statistics.max_slot,
|
||||
std::option::Option::None => std::option::Option::None,
|
||||
};
|
||||
let latest_created_at = match &value.statistics {
|
||||
std::option::Option::Some(statistics) => statistics.latest_created_at.clone(),
|
||||
std::option::Option::None => std::option::Option::None,
|
||||
};
|
||||
return crate::DemoStoreResourceSnapshot {
|
||||
resource_code: value.resource_code.clone(),
|
||||
model_code: value.model_code.clone(),
|
||||
role: value.role.clone(),
|
||||
available: value.available,
|
||||
record_count,
|
||||
min_slot,
|
||||
max_slot,
|
||||
latest_created_at,
|
||||
};
|
||||
}
|
||||
|
||||
/// Converts many logical store resource diagnostics to UI payloads.
|
||||
pub(crate) fn store_resource_snapshots(
|
||||
values: &[ks_store::StoreResourceDiagnostics],
|
||||
) -> std::vec::Vec<crate::DemoStoreResourceSnapshot> {
|
||||
let mut output = std::vec::Vec::with_capacity(values.len());
|
||||
for value in values {
|
||||
output.push(crate::store_resource_snapshot(value));
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
/// Opens, auto-initializes when configured, and verifies the persistent store during startup.
|
||||
pub(crate) async fn initialize_store_for_startup(
|
||||
state: &crate::AppState,
|
||||
splash_window: &tauri::WebviewWindow,
|
||||
) {
|
||||
let store_result = state.store().await;
|
||||
let store = match store_result {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => {
|
||||
let error_message = error.to_string();
|
||||
emit_store_startup_error(splash_window, error_message.as_str());
|
||||
tracing::error!(target: crate::TRACING_TARGET, error = error_message.as_str(), "store startup failed");
|
||||
return;
|
||||
},
|
||||
};
|
||||
let configuration = store.configuration_summary();
|
||||
if !configuration.enabled {
|
||||
emit_store_startup_splash(
|
||||
splash_window,
|
||||
"Store disabled; initialization skipped.",
|
||||
"info",
|
||||
false,
|
||||
);
|
||||
tracing::debug!(target: crate::TRACING_TARGET, backend = configuration.backend_code.as_str(), "store disabled; startup initialization skipped");
|
||||
return;
|
||||
}
|
||||
emit_store_startup_splash(
|
||||
splash_window,
|
||||
format!(
|
||||
"Store backend {}: vérification des modèles de persistance...",
|
||||
configuration.backend_code
|
||||
)
|
||||
.as_str(),
|
||||
"info",
|
||||
false,
|
||||
);
|
||||
let initialization = store.initialization_summary();
|
||||
for model in &initialization.models {
|
||||
emit_store_startup_splash(
|
||||
splash_window,
|
||||
format!(
|
||||
"Modèle {}: {}/{} ressources disponibles, {} créées.",
|
||||
model.model_code,
|
||||
model.available_resource_count,
|
||||
model.expected_resource_count,
|
||||
model.created_resource_count
|
||||
)
|
||||
.as_str(),
|
||||
model_status(&model.status),
|
||||
true,
|
||||
);
|
||||
}
|
||||
for object in &initialization.objects {
|
||||
emit_store_startup_splash(
|
||||
splash_window,
|
||||
format!(
|
||||
"Objets {}: {}/{} disponibles, {} créés.",
|
||||
object.object_kind,
|
||||
object.available_count,
|
||||
object.expected_count,
|
||||
object.created_count
|
||||
)
|
||||
.as_str(),
|
||||
"info",
|
||||
true,
|
||||
);
|
||||
}
|
||||
emit_store_startup_splash(
|
||||
splash_window,
|
||||
format!(
|
||||
"Store {:?}: ressources {}/{}, {} créées.",
|
||||
initialization.status,
|
||||
initialization.available_resource_count,
|
||||
initialization.expected_resource_count,
|
||||
initialization.created_resource_count
|
||||
)
|
||||
.as_str(),
|
||||
initialization_status(&initialization.status),
|
||||
false,
|
||||
);
|
||||
tracing::debug!(target: crate::TRACING_TARGET, backend = configuration.backend_code.as_str(), available_resources = initialization.available_resource_count, expected_resources = initialization.expected_resource_count, created_resources = initialization.created_resource_count, "store startup verification completed");
|
||||
}
|
||||
|
||||
/// Returns the safe connection descriptor exposed by the active backend.
|
||||
pub(crate) async fn store_connection_descriptor(
|
||||
store: &ks_store::Store,
|
||||
) -> std::result::Result<std::string::String, std::string::String> {
|
||||
let runtime = match store.runtime_summary().await {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
|
||||
};
|
||||
let backend = match runtime.backend {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => return std::result::Result::Ok("disabled".to_string()),
|
||||
};
|
||||
let descriptor = match backend.descriptor.masked_connection_descriptor {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => "configured".to_string(),
|
||||
};
|
||||
return std::result::Result::Ok(descriptor);
|
||||
}
|
||||
|
||||
/// Formats debug enums for UI display.
|
||||
pub(crate) fn debug_status<T: std::fmt::Debug>(value: T) -> std::string::String {
|
||||
return format!("{value:?}");
|
||||
}
|
||||
|
||||
fn model_status(status: &ks_store::StoreModelVerificationStatus) -> &'static str {
|
||||
return match status {
|
||||
ks_store::StoreModelVerificationStatus::Ready => "success",
|
||||
ks_store::StoreModelVerificationStatus::Incomplete => "warning",
|
||||
};
|
||||
}
|
||||
|
||||
fn initialization_status(status: &ks_store::StoreInitializationStatus) -> &'static str {
|
||||
return match status {
|
||||
ks_store::StoreInitializationStatus::Ready => "success",
|
||||
ks_store::StoreInitializationStatus::Disabled => "info",
|
||||
ks_store::StoreInitializationStatus::Partial => "warning",
|
||||
ks_store::StoreInitializationStatus::Failed => "danger",
|
||||
};
|
||||
}
|
||||
|
||||
fn emit_store_startup_error(splash_window: &tauri::WebviewWindow, message: &str) {
|
||||
emit_store_startup_splash(
|
||||
splash_window,
|
||||
format!("Store initialization error: {message}").as_str(),
|
||||
"danger",
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
fn emit_store_startup_splash(
|
||||
splash_window: &tauri::WebviewWindow,
|
||||
message: &str,
|
||||
status: &str,
|
||||
log_only: bool,
|
||||
) {
|
||||
let order = if log_only { "add_log" } else { "add_msg" };
|
||||
crate::emit_splash_order(
|
||||
splash_window,
|
||||
order,
|
||||
std::option::Option::Some(message),
|
||||
std::option::Option::Some(status),
|
||||
std::option::Option::None,
|
||||
);
|
||||
}
|
||||
45
kb-app-demo-desktop/src/demo_store_core.rs
Normal file
45
kb-app-demo-desktop/src/demo_store_core.rs
Normal file
@@ -0,0 +1,45 @@
|
||||
// file: kb-app-demo-desktop/src/demo_store_core.rs
|
||||
// version: 9
|
||||
|
||||
//! Backend-agnostic Core store demo commands retained by the store window.
|
||||
|
||||
use ts_rs::TS; // rust-rules: derive-import
|
||||
|
||||
/// Backend-agnostic Core store payload shown by the existing `demo_store_core` window.
|
||||
#[derive(Clone, Debug, serde::Serialize, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(
|
||||
export,
|
||||
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_store_core/DemoStoreCorePayload.ts"
|
||||
)]
|
||||
pub(crate) struct DemoStoreCorePayload {
|
||||
/// Active profile name.
|
||||
pub(crate) active_profile_name: std::string::String,
|
||||
/// Safe backend connection descriptor.
|
||||
pub(crate) connection_descriptor: std::string::String,
|
||||
/// Logical Core-resource diagnostics.
|
||||
pub(crate) resources: std::vec::Vec<crate::DemoStoreResourceSnapshot>,
|
||||
}
|
||||
|
||||
/// Loads read-only Core resource diagnostics from the active store.
|
||||
pub(crate) async fn load_demo_store_core(
|
||||
state: tauri::State<'_, crate::AppState>,
|
||||
) -> std::result::Result<crate::DemoStoreCorePayload, std::string::String> {
|
||||
let store = match crate::shared_store(state.inner()).await {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let resources = match store.core_resource_diagnostics().await {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
|
||||
};
|
||||
let connection_descriptor = match crate::store_connection_descriptor(&store).await {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
return std::result::Result::Ok(crate::DemoStoreCorePayload {
|
||||
active_profile_name: state.active_profile().name.clone(),
|
||||
connection_descriptor,
|
||||
resources: crate::store_resource_snapshots(resources.as_slice()),
|
||||
});
|
||||
}
|
||||
81
kb-app-demo-desktop/src/demo_store_diag.rs
Normal file
81
kb-app-demo-desktop/src/demo_store_diag.rs
Normal file
@@ -0,0 +1,81 @@
|
||||
// file: kb-app-demo-desktop/src/demo_store_diag.rs
|
||||
// version: 9
|
||||
|
||||
//! Backend-agnostic store diagnostic demo commands retained by the Store diagnostics window.
|
||||
|
||||
use ts_rs::TS; // rust-rules: derive-import
|
||||
|
||||
/// Complete backend-neutral store diagnostic payload shown by `demo_store_diag`.
|
||||
#[derive(Clone, Debug, serde::Serialize, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(
|
||||
export,
|
||||
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_store_diag/DemoStoreDiagPayload.ts"
|
||||
)]
|
||||
pub(crate) struct DemoStoreDiagPayload {
|
||||
/// Configuration file path.
|
||||
pub(crate) config_path: std::string::String,
|
||||
/// Active profile name.
|
||||
pub(crate) active_profile_name: std::string::String,
|
||||
/// Active backend code.
|
||||
pub(crate) backend: std::string::String,
|
||||
/// Safe backend connection descriptor.
|
||||
pub(crate) connection_descriptor: std::string::String,
|
||||
/// Backend namespace, schema, or equivalent logical location.
|
||||
pub(crate) namespace: std::option::Option<std::string::String>,
|
||||
/// Store health status.
|
||||
pub(crate) health_status: std::string::String,
|
||||
/// Store health message.
|
||||
pub(crate) health_message: std::option::Option<std::string::String>,
|
||||
/// Migration status.
|
||||
pub(crate) migration_status: std::string::String,
|
||||
/// Migration diagnostic message.
|
||||
pub(crate) migration_message: std::option::Option<std::string::String>,
|
||||
/// Backend version when available.
|
||||
pub(crate) backend_version: std::option::Option<std::string::String>,
|
||||
/// Known logical store resource diagnostics.
|
||||
pub(crate) resources: std::vec::Vec<crate::DemoStoreResourceSnapshot>,
|
||||
}
|
||||
|
||||
/// Loads read-only diagnostics from the active store.
|
||||
pub(crate) async fn load_demo_store_diag(
|
||||
state: tauri::State<'_, crate::AppState>,
|
||||
) -> std::result::Result<crate::DemoStoreDiagPayload, std::string::String> {
|
||||
let store = match crate::shared_store(state.inner()).await {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let runtime = match store.runtime_summary().await {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
|
||||
};
|
||||
let resources = match store.known_resource_diagnostics().await {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
|
||||
};
|
||||
let backend = match runtime.backend {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => {
|
||||
return std::result::Result::Err(
|
||||
"store backend diagnostics are unavailable".to_string(),
|
||||
);
|
||||
},
|
||||
};
|
||||
let connection_descriptor = match backend.descriptor.masked_connection_descriptor.clone() {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => "configured".to_string(),
|
||||
};
|
||||
return std::result::Result::Ok(crate::DemoStoreDiagPayload {
|
||||
config_path: state.config_path().to_string(),
|
||||
active_profile_name: state.active_profile().name.clone(),
|
||||
backend: backend.descriptor.backend_code,
|
||||
connection_descriptor,
|
||||
namespace: backend.descriptor.namespace,
|
||||
health_status: crate::debug_status(backend.health.status),
|
||||
health_message: backend.health.message,
|
||||
migration_status: crate::debug_status(backend.migrations.status),
|
||||
migration_message: backend.migrations.message,
|
||||
backend_version: backend.backend_version,
|
||||
resources: crate::store_resource_snapshots(resources.as_slice()),
|
||||
});
|
||||
}
|
||||
45
kb-app-demo-desktop/src/demo_store_raw.rs
Normal file
45
kb-app-demo-desktop/src/demo_store_raw.rs
Normal file
@@ -0,0 +1,45 @@
|
||||
// file: kb-app-demo-desktop/src/demo_store_raw.rs
|
||||
// version: 10
|
||||
|
||||
//! Backend-agnostic level-1 raw store demo commands retained by the store window.
|
||||
|
||||
use ts_rs::TS; // rust-rules: derive-import
|
||||
|
||||
/// Backend-agnostic raw store payload shown by the existing `demo_store_raw` window.
|
||||
#[derive(Clone, Debug, serde::Serialize, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(
|
||||
export,
|
||||
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_store_raw/DemoStoreRawPayload.ts"
|
||||
)]
|
||||
pub(crate) struct DemoStoreRawPayload {
|
||||
/// Active profile name.
|
||||
pub(crate) active_profile_name: std::string::String,
|
||||
/// Safe backend connection descriptor.
|
||||
pub(crate) connection_descriptor: std::string::String,
|
||||
/// Logical raw-resource diagnostics.
|
||||
pub(crate) resources: std::vec::Vec<crate::DemoStoreResourceSnapshot>,
|
||||
}
|
||||
|
||||
/// Loads read-only level-1 raw resource diagnostics from the active store.
|
||||
pub(crate) async fn load_demo_store_raw(
|
||||
state: tauri::State<'_, crate::AppState>,
|
||||
) -> std::result::Result<crate::DemoStoreRawPayload, std::string::String> {
|
||||
let store = match crate::shared_store(state.inner()).await {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let resources = match store.raw_resource_diagnostics().await {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
|
||||
};
|
||||
let connection_descriptor = match crate::store_connection_descriptor(&store).await {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
return std::result::Result::Ok(crate::DemoStoreRawPayload {
|
||||
active_profile_name: state.active_profile().name.clone(),
|
||||
connection_descriptor,
|
||||
resources: crate::store_resource_snapshots(resources.as_slice()),
|
||||
});
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
// file: kb-app-demo-desktop/src/demo_sql_replay_candidates.rs
|
||||
// version: 13
|
||||
// file: kb-app-demo-desktop/src/demo_store_replay_candidates.rs
|
||||
// version: 16
|
||||
|
||||
//! Read-only SQL replay candidate browser commands.
|
||||
//! Backend-agnostic read-only replay candidate browser commands.
|
||||
|
||||
use ts_rs::TS; // rust-rules: derive-import
|
||||
|
||||
@@ -10,9 +10,9 @@ use ts_rs::TS; // rust-rules: derive-import
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(
|
||||
export,
|
||||
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_sql_replay_candidates/DemoSqlReplayKnownProgramOption.ts"
|
||||
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_store_replay_candidates/DemoStoreReplayKnownProgramOption.ts"
|
||||
)]
|
||||
pub(crate) struct DemoSqlReplayKnownProgramOption {
|
||||
pub(crate) struct DemoStoreReplayKnownProgramOption {
|
||||
/// Stable lower snake case program code.
|
||||
pub(crate) code: std::string::String,
|
||||
/// Base58 Solana program identifier.
|
||||
@@ -24,17 +24,17 @@ pub(crate) struct DemoSqlReplayKnownProgramOption {
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(
|
||||
export,
|
||||
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_sql_replay_candidates/DemoSqlReplayOptionsPayload.ts"
|
||||
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_store_replay_candidates/DemoStoreReplayOptionsPayload.ts"
|
||||
)]
|
||||
pub(crate) struct DemoSqlReplayOptionsPayload {
|
||||
pub(crate) struct DemoStoreReplayOptionsPayload {
|
||||
/// Active profile name.
|
||||
pub(crate) active_profile_name: std::string::String,
|
||||
/// Masked PostgreSQL DSN.
|
||||
pub(crate) masked_dsn: std::string::String,
|
||||
/// Safe backend connection descriptor.
|
||||
pub(crate) connection_descriptor: std::string::String,
|
||||
/// Maximum rows accepted by one query.
|
||||
pub(crate) maximum_limit: u32,
|
||||
/// Program identifiers enumerable from `ks_program_ids`.
|
||||
pub(crate) known_programs: std::vec::Vec<crate::DemoSqlReplayKnownProgramOption>,
|
||||
pub(crate) known_programs: std::vec::Vec<crate::DemoStoreReplayKnownProgramOption>,
|
||||
}
|
||||
|
||||
/// UI request for bounded transaction replay candidates.
|
||||
@@ -42,9 +42,9 @@ pub(crate) struct DemoSqlReplayOptionsPayload {
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(
|
||||
export,
|
||||
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_sql_replay_candidates/DemoSqlReplayTransactionRequest.ts"
|
||||
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_store_replay_candidates/DemoStoreReplayTransactionRequest.ts"
|
||||
)]
|
||||
pub(crate) struct DemoSqlReplayTransactionRequest {
|
||||
pub(crate) struct DemoStoreReplayTransactionRequest {
|
||||
/// Optional partial signature search.
|
||||
pub(crate) signature_contains: std::option::Option<std::string::String>,
|
||||
/// Optional inclusive minimum slot.
|
||||
@@ -59,7 +59,7 @@ pub(crate) struct DemoSqlReplayTransactionRequest {
|
||||
pub(crate) ledger_status: std::option::Option<std::string::String>,
|
||||
/// Optional exact program id.
|
||||
pub(crate) program_id: std::option::Option<std::string::String>,
|
||||
/// Program scope code: any, outer, inner or logs.
|
||||
/// Program scope code: any, top_level, inner or logs.
|
||||
pub(crate) program_scope: std::string::String,
|
||||
/// Optional entity kind: mint, owner or account_key.
|
||||
pub(crate) entity_kind: std::option::Option<std::string::String>,
|
||||
@@ -76,9 +76,9 @@ pub(crate) struct DemoSqlReplayTransactionRequest {
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(
|
||||
export,
|
||||
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_sql_replay_candidates/DemoSqlReplayTransactionRow.ts"
|
||||
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_store_replay_candidates/DemoStoreReplayTransactionRow.ts"
|
||||
)]
|
||||
pub(crate) struct DemoSqlReplayTransactionRow {
|
||||
pub(crate) struct DemoStoreReplayTransactionRow {
|
||||
/// Canonical transaction signature.
|
||||
pub(crate) signature: std::string::String,
|
||||
/// Transaction slot.
|
||||
@@ -100,13 +100,13 @@ pub(crate) struct DemoSqlReplayTransactionRow {
|
||||
pub(crate) attempt_count: i32,
|
||||
/// Number of top-level instructions.
|
||||
#[ts(type = "number")]
|
||||
pub(crate) outer_instruction_count: i64,
|
||||
pub(crate) top_level_instruction_count: i64,
|
||||
/// Number of inner instructions.
|
||||
#[ts(type = "number")]
|
||||
pub(crate) inner_instruction_count: i64,
|
||||
/// Number of distinct top-level programs.
|
||||
#[ts(type = "number")]
|
||||
pub(crate) outer_program_count: i64,
|
||||
pub(crate) top_level_program_count: i64,
|
||||
/// Number of distinct inner programs.
|
||||
#[ts(type = "number")]
|
||||
pub(crate) inner_program_count: i64,
|
||||
@@ -119,9 +119,9 @@ pub(crate) struct DemoSqlReplayTransactionRow {
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(
|
||||
export,
|
||||
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_sql_replay_candidates/DemoSqlReplayProgramRequest.ts"
|
||||
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_store_replay_candidates/DemoStoreReplayProgramRequest.ts"
|
||||
)]
|
||||
pub(crate) struct DemoSqlReplayProgramRequest {
|
||||
pub(crate) struct DemoStoreReplayProgramRequest {
|
||||
/// Optional partial program id search.
|
||||
pub(crate) program_id_contains: std::option::Option<std::string::String>,
|
||||
/// Maximum returned rows.
|
||||
@@ -133,9 +133,9 @@ pub(crate) struct DemoSqlReplayProgramRequest {
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(
|
||||
export,
|
||||
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_sql_replay_candidates/DemoSqlReplayProgramRow.ts"
|
||||
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_store_replay_candidates/DemoStoreReplayProgramRow.ts"
|
||||
)]
|
||||
pub(crate) struct DemoSqlReplayProgramRow {
|
||||
pub(crate) struct DemoStoreReplayProgramRow {
|
||||
/// Optional stable code from `ks_program_ids`.
|
||||
pub(crate) program_code: std::option::Option<std::string::String>,
|
||||
/// Program id.
|
||||
@@ -145,7 +145,7 @@ pub(crate) struct DemoSqlReplayProgramRow {
|
||||
pub(crate) transaction_count: i64,
|
||||
/// Number of top-level instruction occurrences.
|
||||
#[ts(type = "number")]
|
||||
pub(crate) outer_instruction_count: i64,
|
||||
pub(crate) top_level_instruction_count: i64,
|
||||
/// Number of inner instruction occurrences.
|
||||
#[ts(type = "number")]
|
||||
pub(crate) inner_instruction_count: i64,
|
||||
@@ -165,9 +165,9 @@ pub(crate) struct DemoSqlReplayProgramRow {
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(
|
||||
export,
|
||||
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_sql_replay_candidates/DemoSqlReplayEntityRequest.ts"
|
||||
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_store_replay_candidates/DemoStoreReplayEntityRequest.ts"
|
||||
)]
|
||||
pub(crate) struct DemoSqlReplayEntityRequest {
|
||||
pub(crate) struct DemoStoreReplayEntityRequest {
|
||||
/// Entity kind: mint, owner or account_key.
|
||||
pub(crate) entity_kind: std::string::String,
|
||||
/// Optional partial entity value search.
|
||||
@@ -181,9 +181,9 @@ pub(crate) struct DemoSqlReplayEntityRequest {
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(
|
||||
export,
|
||||
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_sql_replay_candidates/DemoSqlReplayEntityRow.ts"
|
||||
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_store_replay_candidates/DemoStoreReplayEntityRow.ts"
|
||||
)]
|
||||
pub(crate) struct DemoSqlReplayEntityRow {
|
||||
pub(crate) struct DemoStoreReplayEntityRow {
|
||||
/// Stable entity kind code.
|
||||
pub(crate) entity_kind: std::string::String,
|
||||
/// Mint, owner or account-key address.
|
||||
@@ -203,7 +203,7 @@ pub(crate) struct DemoSqlReplayEntityRow {
|
||||
}
|
||||
|
||||
/// Writes a bounded replay-candidate CSV file into `./data/exports_csv/`.
|
||||
pub(crate) async fn export_demo_sql_replay_csv(
|
||||
pub(crate) async fn export_demo_store_replay_csv(
|
||||
file_name: std::string::String,
|
||||
content: std::string::String,
|
||||
) -> std::result::Result<std::string::String, std::string::String> {
|
||||
@@ -240,37 +240,40 @@ pub(crate) async fn export_demo_sql_replay_csv(
|
||||
return std::result::Result::Ok(display_path);
|
||||
}
|
||||
|
||||
/// Loads static browser options from the active PostgreSQL profile.
|
||||
pub(crate) async fn demo_sql_replay_options(
|
||||
/// Loads static browser options from the active store.
|
||||
pub(crate) async fn demo_store_replay_options(
|
||||
state: tauri::State<'_, crate::AppState>,
|
||||
) -> std::result::Result<crate::DemoSqlReplayOptionsPayload, std::string::String> {
|
||||
) -> std::result::Result<crate::DemoStoreReplayOptionsPayload, std::string::String> {
|
||||
let profile = state.active_profile().clone();
|
||||
let options_result = crate::postgres_store_options_from_profile(&profile, false);
|
||||
let options = match options_result {
|
||||
let store = match crate::shared_store(state.inner()).await {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let connection_descriptor = match crate::store_connection_descriptor(&store).await {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let mut known_programs =
|
||||
std::vec::Vec::with_capacity(ks_program_ids::registered_program_ids().len());
|
||||
for entry in ks_program_ids::registered_program_ids() {
|
||||
known_programs.push(crate::DemoSqlReplayKnownProgramOption {
|
||||
known_programs.push(crate::DemoStoreReplayKnownProgramOption {
|
||||
code: entry.code().to_owned(),
|
||||
program_id: entry.program_id().to_owned(),
|
||||
});
|
||||
}
|
||||
return std::result::Result::Ok(crate::DemoSqlReplayOptionsPayload {
|
||||
return std::result::Result::Ok(crate::DemoStoreReplayOptionsPayload {
|
||||
active_profile_name: profile.name,
|
||||
masked_dsn: options.masked_dsn(),
|
||||
connection_descriptor,
|
||||
maximum_limit: ks_store::MAX_REPLAY_CANDIDATE_ROWS,
|
||||
known_programs,
|
||||
});
|
||||
}
|
||||
|
||||
/// Loads bounded transaction replay candidates from PostgreSQL.
|
||||
pub(crate) async fn load_demo_sql_replay_transactions(
|
||||
/// Loads bounded transaction replay candidates from the active store.
|
||||
pub(crate) async fn load_demo_store_replay_transactions(
|
||||
state: tauri::State<'_, crate::AppState>,
|
||||
request: crate::DemoSqlReplayTransactionRequest,
|
||||
) -> std::result::Result<std::vec::Vec<crate::DemoSqlReplayTransactionRow>, std::string::String> {
|
||||
request: crate::DemoStoreReplayTransactionRequest,
|
||||
) -> std::result::Result<std::vec::Vec<crate::DemoStoreReplayTransactionRow>, std::string::String> {
|
||||
let program_scope_result = program_scope_from_code(request.program_scope.as_str());
|
||||
let program_scope = match program_scope_result {
|
||||
std::result::Result::Ok(value) => value,
|
||||
@@ -281,7 +284,7 @@ pub(crate) async fn load_demo_sql_replay_transactions(
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let filter_result = ks_store::PostgresReplayTransactionFilter::new(
|
||||
let filter_result = ks_store::ReplayTransactionFilter::new(
|
||||
request.signature_contains,
|
||||
request.min_slot,
|
||||
request.max_slot,
|
||||
@@ -298,7 +301,7 @@ pub(crate) async fn load_demo_sql_replay_transactions(
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
|
||||
};
|
||||
let store_result = crate::connect_postgres_store(state.active_profile()).await;
|
||||
let store_result = crate::shared_store(state.inner()).await;
|
||||
let store = match store_result {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
@@ -311,23 +314,23 @@ pub(crate) async fn load_demo_sql_replay_transactions(
|
||||
tracing::debug!(target: crate::TRACING_TARGET, rows = rows.len(), "loaded replay transaction candidates");
|
||||
let mut output = std::vec::Vec::with_capacity(rows.len());
|
||||
for row in rows {
|
||||
output.push(transaction_row_from_pg(row));
|
||||
output.push(transaction_row_from_store(row));
|
||||
}
|
||||
return std::result::Result::Ok(output);
|
||||
}
|
||||
|
||||
/// Loads bounded program summaries from PostgreSQL.
|
||||
pub(crate) async fn load_demo_sql_replay_programs(
|
||||
/// Loads bounded program summaries from the active store.
|
||||
pub(crate) async fn load_demo_store_replay_programs(
|
||||
state: tauri::State<'_, crate::AppState>,
|
||||
request: crate::DemoSqlReplayProgramRequest,
|
||||
) -> std::result::Result<std::vec::Vec<crate::DemoSqlReplayProgramRow>, std::string::String> {
|
||||
request: crate::DemoStoreReplayProgramRequest,
|
||||
) -> std::result::Result<std::vec::Vec<crate::DemoStoreReplayProgramRow>, std::string::String> {
|
||||
let filter_result =
|
||||
ks_store::PostgresReplayProgramFilter::new(request.program_id_contains, request.limit);
|
||||
ks_store::ReplayProgramFilter::new(request.program_id_contains, request.limit);
|
||||
let filter = match filter_result {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
|
||||
};
|
||||
let store_result = crate::connect_postgres_store(state.active_profile()).await;
|
||||
let store_result = crate::shared_store(state.inner()).await;
|
||||
let store = match store_result {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
@@ -340,22 +343,22 @@ pub(crate) async fn load_demo_sql_replay_programs(
|
||||
tracing::debug!(target: crate::TRACING_TARGET, rows = rows.len(), "loaded replay program summaries");
|
||||
let mut output = std::vec::Vec::with_capacity(rows.len());
|
||||
for row in rows {
|
||||
output.push(program_row_from_pg(row));
|
||||
output.push(program_row_from_store(row));
|
||||
}
|
||||
return std::result::Result::Ok(output);
|
||||
}
|
||||
|
||||
/// Loads bounded mint, owner or account-key summaries from PostgreSQL core tables.
|
||||
pub(crate) async fn load_demo_sql_replay_entities(
|
||||
/// Loads bounded mint, owner or account-key summaries from Core facts.
|
||||
pub(crate) async fn load_demo_store_replay_entities(
|
||||
state: tauri::State<'_, crate::AppState>,
|
||||
request: crate::DemoSqlReplayEntityRequest,
|
||||
) -> std::result::Result<std::vec::Vec<crate::DemoSqlReplayEntityRow>, std::string::String> {
|
||||
request: crate::DemoStoreReplayEntityRequest,
|
||||
) -> std::result::Result<std::vec::Vec<crate::DemoStoreReplayEntityRow>, std::string::String> {
|
||||
let entity_kind_result = entity_kind_from_code(request.entity_kind.as_str());
|
||||
let entity_kind = match entity_kind_result {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let filter_result = ks_store::PostgresReplayEntityFilter::new(
|
||||
let filter_result = ks_store::ReplayEntityFilter::new(
|
||||
entity_kind,
|
||||
request.entity_value_contains,
|
||||
request.limit,
|
||||
@@ -364,7 +367,7 @@ pub(crate) async fn load_demo_sql_replay_entities(
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
|
||||
};
|
||||
let store_result = crate::connect_postgres_store(state.active_profile()).await;
|
||||
let store_result = crate::shared_store(state.inner()).await;
|
||||
let store = match store_result {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
@@ -377,15 +380,15 @@ pub(crate) async fn load_demo_sql_replay_entities(
|
||||
tracing::debug!(target: crate::TRACING_TARGET, rows = rows.len(), "loaded replay entity summaries");
|
||||
let mut output = std::vec::Vec::with_capacity(rows.len());
|
||||
for row in rows {
|
||||
output.push(entity_row_from_pg(row));
|
||||
output.push(entity_row_from_store(row));
|
||||
}
|
||||
return std::result::Result::Ok(output);
|
||||
}
|
||||
|
||||
fn transaction_row_from_pg(
|
||||
row: ks_store::PostgresReplayTransactionCandidate,
|
||||
) -> crate::DemoSqlReplayTransactionRow {
|
||||
return crate::DemoSqlReplayTransactionRow {
|
||||
fn transaction_row_from_store(
|
||||
row: ks_store::ReplayTransactionCandidate,
|
||||
) -> crate::DemoStoreReplayTransactionRow {
|
||||
return crate::DemoStoreReplayTransactionRow {
|
||||
signature: row.signature,
|
||||
slot: row.slot,
|
||||
raw_processing_state: row.raw_processing_state,
|
||||
@@ -395,24 +398,22 @@ fn transaction_row_from_pg(
|
||||
ledger_status: row.ledger_status,
|
||||
processor_version: row.processor_version,
|
||||
attempt_count: row.attempt_count,
|
||||
outer_instruction_count: row.outer_instruction_count,
|
||||
top_level_instruction_count: row.top_level_instruction_count,
|
||||
inner_instruction_count: row.inner_instruction_count,
|
||||
outer_program_count: row.outer_program_count,
|
||||
top_level_program_count: row.top_level_program_count,
|
||||
inner_program_count: row.inner_program_count,
|
||||
updated_at: row.updated_at,
|
||||
};
|
||||
}
|
||||
|
||||
fn program_row_from_pg(
|
||||
row: ks_store::PostgresReplayProgramSummary,
|
||||
) -> crate::DemoSqlReplayProgramRow {
|
||||
fn program_row_from_store(row: ks_store::ReplayProgramSummary) -> crate::DemoStoreReplayProgramRow {
|
||||
let program_code = ks_program_ids::find_registered_program_id(&row.program_id)
|
||||
.map(|entry| return entry.code().to_owned());
|
||||
return crate::DemoSqlReplayProgramRow {
|
||||
return crate::DemoStoreReplayProgramRow {
|
||||
program_code,
|
||||
program_id: row.program_id,
|
||||
transaction_count: row.transaction_count,
|
||||
outer_instruction_count: row.outer_instruction_count,
|
||||
top_level_instruction_count: row.top_level_instruction_count,
|
||||
inner_instruction_count: row.inner_instruction_count,
|
||||
log_count: row.log_count,
|
||||
min_slot: row.min_slot,
|
||||
@@ -420,8 +421,8 @@ fn program_row_from_pg(
|
||||
};
|
||||
}
|
||||
|
||||
fn entity_row_from_pg(row: ks_store::PostgresReplayEntitySummary) -> crate::DemoSqlReplayEntityRow {
|
||||
return crate::DemoSqlReplayEntityRow {
|
||||
fn entity_row_from_store(row: ks_store::ReplayEntitySummary) -> crate::DemoStoreReplayEntityRow {
|
||||
return crate::DemoStoreReplayEntityRow {
|
||||
entity_kind: row.entity_kind,
|
||||
entity_value: row.entity_value,
|
||||
transaction_count: row.transaction_count,
|
||||
@@ -480,20 +481,19 @@ async fn available_csv_export_path(
|
||||
|
||||
fn program_scope_from_code(
|
||||
code: &str,
|
||||
) -> std::result::Result<ks_store::PostgresReplayProgramScope, std::string::String> {
|
||||
) -> std::result::Result<ks_store::ReplayProgramScope, std::string::String> {
|
||||
return match code {
|
||||
"any" => std::result::Result::Ok(ks_store::PostgresReplayProgramScope::Any),
|
||||
"outer" => std::result::Result::Ok(ks_store::PostgresReplayProgramScope::Outer),
|
||||
"inner" => std::result::Result::Ok(ks_store::PostgresReplayProgramScope::Inner),
|
||||
"logs" => std::result::Result::Ok(ks_store::PostgresReplayProgramScope::Logs),
|
||||
"any" => std::result::Result::Ok(ks_store::ReplayProgramScope::Any),
|
||||
"top_level" => std::result::Result::Ok(ks_store::ReplayProgramScope::TopLevel),
|
||||
"inner" => std::result::Result::Ok(ks_store::ReplayProgramScope::Inner),
|
||||
"logs" => std::result::Result::Ok(ks_store::ReplayProgramScope::Logs),
|
||||
_ => std::result::Result::Err(format!("unsupported replay program scope: {code}")),
|
||||
};
|
||||
}
|
||||
|
||||
fn optional_entity_kind_from_code(
|
||||
code: std::option::Option<&str>,
|
||||
) -> std::result::Result<std::option::Option<ks_store::PostgresReplayEntityKind>, std::string::String>
|
||||
{
|
||||
) -> std::result::Result<std::option::Option<ks_store::ReplayEntityKind>, std::string::String> {
|
||||
return match code {
|
||||
std::option::Option::Some(value) => {
|
||||
let result = entity_kind_from_code(value);
|
||||
@@ -510,11 +510,11 @@ fn optional_entity_kind_from_code(
|
||||
|
||||
fn entity_kind_from_code(
|
||||
code: &str,
|
||||
) -> std::result::Result<ks_store::PostgresReplayEntityKind, std::string::String> {
|
||||
) -> std::result::Result<ks_store::ReplayEntityKind, std::string::String> {
|
||||
return match code {
|
||||
"mint" => std::result::Result::Ok(ks_store::PostgresReplayEntityKind::Mint),
|
||||
"owner" => std::result::Result::Ok(ks_store::PostgresReplayEntityKind::Owner),
|
||||
"account_key" => std::result::Result::Ok(ks_store::PostgresReplayEntityKind::AccountKey),
|
||||
"mint" => std::result::Result::Ok(ks_store::ReplayEntityKind::Mint),
|
||||
"owner" => std::result::Result::Ok(ks_store::ReplayEntityKind::Owner),
|
||||
"account_key" => std::result::Result::Ok(ks_store::ReplayEntityKind::AccountKey),
|
||||
_ => std::result::Result::Err(format!("unsupported replay entity kind: {code}")),
|
||||
};
|
||||
}
|
||||
@@ -524,7 +524,7 @@ mod tests {
|
||||
#[test]
|
||||
fn program_scope_parser_accepts_logs() {
|
||||
let result = super::program_scope_from_code("logs");
|
||||
assert_eq!(result, std::result::Result::Ok(ks_store::PostgresReplayProgramScope::Logs));
|
||||
assert_eq!(result, std::result::Result::Ok(ks_store::ReplayProgramScope::Logs));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -551,16 +551,16 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn program_row_exposes_registered_code() {
|
||||
let row = ks_store::PostgresReplayProgramSummary {
|
||||
let row = ks_store::ReplayProgramSummary {
|
||||
program_id: ks_program_ids::SYSTEM_PROGRAM_ID.to_owned(),
|
||||
transaction_count: 1,
|
||||
outer_instruction_count: 1,
|
||||
top_level_instruction_count: 1,
|
||||
inner_instruction_count: 0,
|
||||
log_count: 1,
|
||||
min_slot: 1,
|
||||
max_slot: 1,
|
||||
};
|
||||
let converted = super::program_row_from_pg(row);
|
||||
let converted = super::program_row_from_store(row);
|
||||
assert_eq!(converted.program_code.as_deref(), std::option::Option::Some("system"));
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: kb-app-demo-desktop/src/demo_ws.rs
|
||||
// version: 23
|
||||
// version: 24
|
||||
|
||||
//! Standard Solana WebSocket demo commands backed by `ks_onchain_transport::WsSession`.
|
||||
|
||||
@@ -1031,7 +1031,7 @@ mod tests {
|
||||
<crate::DemoWsUnsubscribeRequest as TS>::decl(&config),
|
||||
<crate::DemoBackfillProgressPayload as TS>::decl(&config),
|
||||
<crate::DemoBackfillSummaryPayload as TS>::decl(&config),
|
||||
<crate::DemoSqlTableSnapshot as TS>::decl(&config),
|
||||
<crate::DemoStoreResourceSnapshot as TS>::decl(&config),
|
||||
];
|
||||
for declaration in declarations {
|
||||
assert!(!declaration.contains("bigint"));
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: kb-app-demo-desktop/src/lib.rs
|
||||
// version: 43
|
||||
// version: 47
|
||||
|
||||
//! Tauri desktop demo application for `khadhroony-bot3`.
|
||||
|
||||
@@ -24,11 +24,11 @@ mod demo_http;
|
||||
mod demo_spl_ata;
|
||||
mod demo_spl_token;
|
||||
mod demo_spl_token_2022;
|
||||
mod demo_sql_common;
|
||||
mod demo_sql_diag;
|
||||
mod demo_sql_pg_core;
|
||||
mod demo_sql_pg_raw;
|
||||
mod demo_sql_replay_candidates;
|
||||
mod demo_store_common;
|
||||
mod demo_store_core;
|
||||
mod demo_store_diag;
|
||||
mod demo_store_raw;
|
||||
mod demo_store_replay_candidates;
|
||||
mod demo_transport;
|
||||
mod demo_wallet;
|
||||
mod demo_ws;
|
||||
@@ -147,7 +147,7 @@ pub(crate) use self::demo_decode_replay::demo_decode_replay_diagnostics;
|
||||
pub(crate) use self::demo_decode_replay::demo_decode_replay_execute;
|
||||
/// Returns available decoders and default replay bounds.
|
||||
pub(crate) use self::demo_decode_replay::demo_decode_replay_options;
|
||||
/// Readiness report for one Devnet profile PostgreSQL store.
|
||||
/// Backend-agnostic readiness report for one Devnet profile store.
|
||||
pub(crate) use self::demo_devnet_common::DemoExecutionDevnetStoreReadinessPayload;
|
||||
/// Restores the shared single-execution state flag when a Devnet run finishes.
|
||||
pub(crate) use self::demo_devnet_common::DemoExecutionRunGuard;
|
||||
@@ -315,58 +315,60 @@ pub(crate) use self::demo_spl_token_2022::DemoSplToken2022FixturePayload;
|
||||
pub(crate) use self::demo_spl_token_2022::demo_execution_spl_token_2022_execute;
|
||||
/// Loads public Token-2022 fixture values without reading private key bytes.
|
||||
pub(crate) use self::demo_spl_token_2022::demo_spl_token_2022_fixture;
|
||||
/// UI-safe SQL table diagnostic snapshot.
|
||||
pub(crate) use self::demo_sql_common::DemoSqlTableSnapshot;
|
||||
/// Connects to the configured PostgreSQL store.
|
||||
pub(crate) use self::demo_sql_common::connect_postgres_store;
|
||||
/// UI-safe logical store resource diagnostic snapshot.
|
||||
pub(crate) use self::demo_store_common::DemoStoreResourceSnapshot;
|
||||
/// Formats diagnostic statuses.
|
||||
pub(crate) use self::demo_sql_common::debug_status;
|
||||
/// Initializes the PostgreSQL schema during startup.
|
||||
pub(crate) use self::demo_sql_common::initialize_postgres_schema_for_startup;
|
||||
/// Builds PostgreSQL options from the active profile.
|
||||
pub(crate) use self::demo_sql_common::postgres_store_options_from_profile;
|
||||
/// Converts one PostgreSQL table diagnostic.
|
||||
pub(crate) use self::demo_sql_common::table_snapshot_from_pg;
|
||||
/// Converts PostgreSQL table diagnostics.
|
||||
pub(crate) use self::demo_sql_common::table_snapshots_from_pg;
|
||||
/// SQL diagnostic payload.
|
||||
pub(crate) use self::demo_sql_diag::DemoSqlDiagPayload;
|
||||
/// Loads read-only SQL diagnostics from the active profile.
|
||||
pub(crate) use self::demo_sql_diag::load_demo_sql_diag;
|
||||
/// PostgreSQL core-table payload.
|
||||
pub(crate) use self::demo_sql_pg_core::DemoSqlPgCorePayload;
|
||||
/// Loads read-only core table diagnostics from PostgreSQL.
|
||||
pub(crate) use self::demo_sql_pg_core::load_demo_sql_pg_core;
|
||||
/// PostgreSQL raw-table payload.
|
||||
pub(crate) use self::demo_sql_pg_raw::DemoSqlPgRawPayload;
|
||||
/// Loads read-only raw table diagnostics from PostgreSQL.
|
||||
pub(crate) use self::demo_sql_pg_raw::load_demo_sql_pg_raw;
|
||||
/// SQL replay entity request.
|
||||
pub(crate) use self::demo_sql_replay_candidates::DemoSqlReplayEntityRequest;
|
||||
/// SQL replay entity row.
|
||||
pub(crate) use self::demo_sql_replay_candidates::DemoSqlReplayEntityRow;
|
||||
pub(crate) use self::demo_store_common::debug_status;
|
||||
/// Opens and verifies the configured store during startup.
|
||||
pub(crate) use self::demo_store_common::initialize_store_for_startup;
|
||||
/// Opens the configured backend-agnostic store.
|
||||
pub(crate) use self::demo_store_common::open_store;
|
||||
/// Clones the persistent application store.
|
||||
pub(crate) use self::demo_store_common::shared_store;
|
||||
/// Returns the safe connection descriptor exposed by the active store backend.
|
||||
pub(crate) use self::demo_store_common::store_connection_descriptor;
|
||||
/// Converts one logical store resource diagnostic.
|
||||
pub(crate) use self::demo_store_common::store_resource_snapshot;
|
||||
/// Converts logical store resource diagnostics.
|
||||
pub(crate) use self::demo_store_common::store_resource_snapshots;
|
||||
/// Backend-agnostic Core store diagnostic payload.
|
||||
pub(crate) use self::demo_store_core::DemoStoreCorePayload;
|
||||
/// Loads read-only Core resource diagnostics from the active store.
|
||||
pub(crate) use self::demo_store_core::load_demo_store_core;
|
||||
/// Backend-agnostic store diagnostic payload.
|
||||
pub(crate) use self::demo_store_diag::DemoStoreDiagPayload;
|
||||
/// Loads read-only Store diagnostics from the active profile.
|
||||
pub(crate) use self::demo_store_diag::load_demo_store_diag;
|
||||
/// Backend-agnostic raw store diagnostic payload.
|
||||
pub(crate) use self::demo_store_raw::DemoStoreRawPayload;
|
||||
/// Loads read-only raw resource diagnostics from the active store.
|
||||
pub(crate) use self::demo_store_raw::load_demo_store_raw;
|
||||
/// Store replay entity request.
|
||||
pub(crate) use self::demo_store_replay_candidates::DemoStoreReplayEntityRequest;
|
||||
/// Store replay entity row.
|
||||
pub(crate) use self::demo_store_replay_candidates::DemoStoreReplayEntityRow;
|
||||
/// Known program option.
|
||||
pub(crate) use self::demo_sql_replay_candidates::DemoSqlReplayKnownProgramOption;
|
||||
/// SQL replay options payload.
|
||||
pub(crate) use self::demo_sql_replay_candidates::DemoSqlReplayOptionsPayload;
|
||||
/// SQL replay program request.
|
||||
pub(crate) use self::demo_sql_replay_candidates::DemoSqlReplayProgramRequest;
|
||||
/// SQL replay program row.
|
||||
pub(crate) use self::demo_sql_replay_candidates::DemoSqlReplayProgramRow;
|
||||
/// SQL replay transaction request.
|
||||
pub(crate) use self::demo_sql_replay_candidates::DemoSqlReplayTransactionRequest;
|
||||
/// SQL replay transaction row.
|
||||
pub(crate) use self::demo_sql_replay_candidates::DemoSqlReplayTransactionRow;
|
||||
/// Loads static browser options from the active PostgreSQL profile.
|
||||
pub(crate) use self::demo_sql_replay_candidates::demo_sql_replay_options;
|
||||
pub(crate) use self::demo_store_replay_candidates::DemoStoreReplayKnownProgramOption;
|
||||
/// Store replay options payload.
|
||||
pub(crate) use self::demo_store_replay_candidates::DemoStoreReplayOptionsPayload;
|
||||
/// Store replay program request.
|
||||
pub(crate) use self::demo_store_replay_candidates::DemoStoreReplayProgramRequest;
|
||||
/// Store replay program row.
|
||||
pub(crate) use self::demo_store_replay_candidates::DemoStoreReplayProgramRow;
|
||||
/// Store replay transaction request.
|
||||
pub(crate) use self::demo_store_replay_candidates::DemoStoreReplayTransactionRequest;
|
||||
/// Store replay transaction row.
|
||||
pub(crate) use self::demo_store_replay_candidates::DemoStoreReplayTransactionRow;
|
||||
/// Loads static replay browser options from the active store profile.
|
||||
pub(crate) use self::demo_store_replay_candidates::demo_store_replay_options;
|
||||
/// Writes a bounded replay-candidate CSV file into `./data/exports_csv/`.
|
||||
pub(crate) use self::demo_sql_replay_candidates::export_demo_sql_replay_csv;
|
||||
/// Loads bounded mint, owner or account-key summaries from PostgreSQL core tables.
|
||||
pub(crate) use self::demo_sql_replay_candidates::load_demo_sql_replay_entities;
|
||||
/// Loads bounded program summaries from PostgreSQL.
|
||||
pub(crate) use self::demo_sql_replay_candidates::load_demo_sql_replay_programs;
|
||||
/// Loads bounded transaction replay candidates from PostgreSQL.
|
||||
pub(crate) use self::demo_sql_replay_candidates::load_demo_sql_replay_transactions;
|
||||
pub(crate) use self::demo_store_replay_candidates::export_demo_store_replay_csv;
|
||||
/// Loads bounded mint, owner or account-key summaries from Core store resources.
|
||||
pub(crate) use self::demo_store_replay_candidates::load_demo_store_replay_entities;
|
||||
/// Loads bounded program summaries from the active store.
|
||||
pub(crate) use self::demo_store_replay_candidates::load_demo_store_replay_programs;
|
||||
/// Loads bounded transaction replay candidates from the active store.
|
||||
pub(crate) use self::demo_store_replay_candidates::load_demo_store_replay_transactions;
|
||||
/// UI-safe endpoint role shared by HTTP and WebSocket diagnostics.
|
||||
pub(crate) use self::demo_transport::DemoEndpointRolePayload;
|
||||
/// Exact public SOL balance projected by the wallet demo.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: kb-app-demo-desktop/src/splash.rs
|
||||
// version: 5
|
||||
// version: 6
|
||||
|
||||
//! Splash-window payloads and startup sequencing helpers.
|
||||
|
||||
@@ -112,7 +112,7 @@ pub(crate) async fn splash_frontend_ready(
|
||||
std::option::Option::Some("success"),
|
||||
std::option::Option::None,
|
||||
);
|
||||
crate::initialize_postgres_schema_for_startup(state, &window).await;
|
||||
crate::initialize_store_for_startup(state, &window).await;
|
||||
crate::emit_splash_order(
|
||||
&window,
|
||||
"add_msg",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: kb-app-demo-desktop/src/tauri.rs
|
||||
// version: 44
|
||||
// version: 46
|
||||
|
||||
//! Tauri runtime assembly and private command wrappers.
|
||||
|
||||
@@ -71,18 +71,18 @@ pub fn run() -> ks_core::Result<()> {
|
||||
demo_decode_replay_cancel,
|
||||
demo_decode_replay_diagnostics,
|
||||
demo_decode_replay_annotations,
|
||||
open_demo_sql_diag_window,
|
||||
load_demo_sql_diag,
|
||||
open_demo_sql_pg_raw_window,
|
||||
load_demo_sql_pg_raw,
|
||||
open_demo_sql_pg_core_window,
|
||||
load_demo_sql_pg_core,
|
||||
open_demo_sql_replay_candidates_window,
|
||||
export_demo_sql_replay_csv,
|
||||
demo_sql_replay_options,
|
||||
load_demo_sql_replay_transactions,
|
||||
load_demo_sql_replay_programs,
|
||||
load_demo_sql_replay_entities,
|
||||
open_demo_store_diag_window,
|
||||
load_demo_store_diag,
|
||||
open_demo_store_raw_window,
|
||||
load_demo_store_raw,
|
||||
open_demo_store_core_window,
|
||||
load_demo_store_core,
|
||||
open_demo_store_replay_candidates_window,
|
||||
export_demo_store_replay_csv,
|
||||
demo_store_replay_options,
|
||||
load_demo_store_replay_transactions,
|
||||
load_demo_store_replay_programs,
|
||||
load_demo_store_replay_entities,
|
||||
open_demo_execution_solana_core_window,
|
||||
demo_execution_devnet_prepare_profile,
|
||||
demo_execution_solana_core_options,
|
||||
@@ -518,111 +518,111 @@ async fn demo_decode_replay_annotations(
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn open_demo_sql_diag_window(
|
||||
fn open_demo_store_diag_window(
|
||||
app_handle: tauri::AppHandle,
|
||||
) -> std::result::Result<(), std::string::String> {
|
||||
return open_or_focus_demo_window(
|
||||
app_handle,
|
||||
"demo_sql_diag",
|
||||
"demo_sql_diag.html",
|
||||
"SQL diagnostics",
|
||||
"demo_store_diag",
|
||||
"demo_store_diag.html",
|
||||
"Store diagnostics",
|
||||
);
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn load_demo_sql_diag(
|
||||
async fn load_demo_store_diag(
|
||||
state: tauri::State<'_, crate::AppState>,
|
||||
) -> std::result::Result<crate::DemoSqlDiagPayload, std::string::String> {
|
||||
return crate::load_demo_sql_diag(state).await;
|
||||
) -> std::result::Result<crate::DemoStoreDiagPayload, std::string::String> {
|
||||
return crate::load_demo_store_diag(state).await;
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn open_demo_sql_pg_raw_window(
|
||||
fn open_demo_store_raw_window(
|
||||
app_handle: tauri::AppHandle,
|
||||
) -> std::result::Result<(), std::string::String> {
|
||||
return open_or_focus_demo_window(
|
||||
app_handle,
|
||||
"demo_sql_pg_raw",
|
||||
"demo_sql_pg_raw.html",
|
||||
"PostgreSQL canonical acquisition",
|
||||
"demo_store_raw",
|
||||
"demo_store_raw.html",
|
||||
"Raw store",
|
||||
);
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn load_demo_sql_pg_raw(
|
||||
async fn load_demo_store_raw(
|
||||
state: tauri::State<'_, crate::AppState>,
|
||||
) -> std::result::Result<crate::DemoSqlPgRawPayload, std::string::String> {
|
||||
return crate::load_demo_sql_pg_raw(state).await;
|
||||
) -> std::result::Result<crate::DemoStoreRawPayload, std::string::String> {
|
||||
return crate::load_demo_store_raw(state).await;
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn open_demo_sql_pg_core_window(
|
||||
fn open_demo_store_core_window(
|
||||
app_handle: tauri::AppHandle,
|
||||
) -> std::result::Result<(), std::string::String> {
|
||||
return open_or_focus_demo_window(
|
||||
app_handle,
|
||||
"demo_sql_pg_core",
|
||||
"demo_sql_pg_core.html",
|
||||
"PostgreSQL core store",
|
||||
"demo_store_core",
|
||||
"demo_store_core.html",
|
||||
"Core store",
|
||||
);
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn load_demo_sql_pg_core(
|
||||
async fn load_demo_store_core(
|
||||
state: tauri::State<'_, crate::AppState>,
|
||||
) -> std::result::Result<crate::DemoSqlPgCorePayload, std::string::String> {
|
||||
return crate::load_demo_sql_pg_core(state).await;
|
||||
) -> std::result::Result<crate::DemoStoreCorePayload, std::string::String> {
|
||||
return crate::load_demo_store_core(state).await;
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn open_demo_sql_replay_candidates_window(
|
||||
fn open_demo_store_replay_candidates_window(
|
||||
app_handle: tauri::AppHandle,
|
||||
) -> std::result::Result<(), std::string::String> {
|
||||
return open_or_focus_demo_window(
|
||||
app_handle,
|
||||
"demo_sql_replay_candidates",
|
||||
"demo_sql_replay_candidates.html",
|
||||
"SQL replay candidates",
|
||||
"demo_store_replay_candidates",
|
||||
"demo_store_replay_candidates.html",
|
||||
"Store replay candidates",
|
||||
);
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn export_demo_sql_replay_csv(
|
||||
async fn export_demo_store_replay_csv(
|
||||
file_name: std::string::String,
|
||||
content: std::string::String,
|
||||
) -> std::result::Result<std::string::String, std::string::String> {
|
||||
return crate::export_demo_sql_replay_csv(file_name, content).await;
|
||||
return crate::export_demo_store_replay_csv(file_name, content).await;
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn demo_sql_replay_options(
|
||||
async fn demo_store_replay_options(
|
||||
state: tauri::State<'_, crate::AppState>,
|
||||
) -> std::result::Result<crate::DemoSqlReplayOptionsPayload, std::string::String> {
|
||||
return crate::demo_sql_replay_options(state).await;
|
||||
) -> std::result::Result<crate::DemoStoreReplayOptionsPayload, std::string::String> {
|
||||
return crate::demo_store_replay_options(state).await;
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn load_demo_sql_replay_transactions(
|
||||
async fn load_demo_store_replay_transactions(
|
||||
state: tauri::State<'_, crate::AppState>,
|
||||
request: crate::DemoSqlReplayTransactionRequest,
|
||||
) -> std::result::Result<std::vec::Vec<crate::DemoSqlReplayTransactionRow>, std::string::String> {
|
||||
return crate::load_demo_sql_replay_transactions(state, request).await;
|
||||
request: crate::DemoStoreReplayTransactionRequest,
|
||||
) -> std::result::Result<std::vec::Vec<crate::DemoStoreReplayTransactionRow>, std::string::String> {
|
||||
return crate::load_demo_store_replay_transactions(state, request).await;
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn load_demo_sql_replay_programs(
|
||||
async fn load_demo_store_replay_programs(
|
||||
state: tauri::State<'_, crate::AppState>,
|
||||
request: crate::DemoSqlReplayProgramRequest,
|
||||
) -> std::result::Result<std::vec::Vec<crate::DemoSqlReplayProgramRow>, std::string::String> {
|
||||
return crate::load_demo_sql_replay_programs(state, request).await;
|
||||
request: crate::DemoStoreReplayProgramRequest,
|
||||
) -> std::result::Result<std::vec::Vec<crate::DemoStoreReplayProgramRow>, std::string::String> {
|
||||
return crate::load_demo_store_replay_programs(state, request).await;
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn load_demo_sql_replay_entities(
|
||||
async fn load_demo_store_replay_entities(
|
||||
state: tauri::State<'_, crate::AppState>,
|
||||
request: crate::DemoSqlReplayEntityRequest,
|
||||
) -> std::result::Result<std::vec::Vec<crate::DemoSqlReplayEntityRow>, std::string::String> {
|
||||
return crate::load_demo_sql_replay_entities(state, request).await;
|
||||
request: crate::DemoStoreReplayEntityRequest,
|
||||
) -> std::result::Result<std::vec::Vec<crate::DemoStoreReplayEntityRow>, std::string::String> {
|
||||
return crate::load_demo_store_replay_entities(state, request).await;
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://schema.tauri.app/config/2",
|
||||
"productName": "Khadhroony Bot3 Demo Desktop",
|
||||
"version": "0.5.2",
|
||||
"version": "0.5.3",
|
||||
"identifier": "com.sasedev.kb-app-demo-desktop",
|
||||
"build": {
|
||||
"beforeDevCommand": "npm run dev",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: kb-app-demo-desktop/vite.config.ts
|
||||
// version: 10
|
||||
// version: 11
|
||||
|
||||
import { defineConfig, normalizePath } from "vite";
|
||||
import { NodePackageImporter } from "sass-embedded";
|
||||
@@ -30,10 +30,10 @@ export default defineConfig(() => ({
|
||||
"demo_backfill": normalizePath(resolve(__dirname, 'frontend/demo_backfill.html')),
|
||||
"demo_core_extraction": normalizePath(resolve(__dirname, 'frontend/demo_core_extraction.html')),
|
||||
"demo_decode_replay": normalizePath(resolve(__dirname, 'frontend/demo_decode_replay.html')),
|
||||
"demo_sql_diag": normalizePath(resolve(__dirname, 'frontend/demo_sql_diag.html')),
|
||||
"demo_sql_pg_raw": normalizePath(resolve(__dirname, 'frontend/demo_sql_pg_raw.html')),
|
||||
"demo_sql_pg_core": normalizePath(resolve(__dirname, 'frontend/demo_sql_pg_core.html')),
|
||||
"demo_sql_replay_candidates": normalizePath(resolve(__dirname, 'frontend/demo_sql_replay_candidates.html')),
|
||||
"demo_store_diag": normalizePath(resolve(__dirname, 'frontend/demo_store_diag.html')),
|
||||
"demo_store_raw": normalizePath(resolve(__dirname, 'frontend/demo_store_raw.html')),
|
||||
"demo_store_core": normalizePath(resolve(__dirname, 'frontend/demo_store_core.html')),
|
||||
"demo_store_replay_candidates": normalizePath(resolve(__dirname, 'frontend/demo_store_replay_candidates.html')),
|
||||
"demo_execution_solana_core": normalizePath(resolve(__dirname, 'frontend/demo_execution_solana_core.html')),
|
||||
"demo_execution_spl": normalizePath(resolve(__dirname, 'frontend/demo_execution_spl.html')),
|
||||
"demo_execution_metatdata": normalizePath(resolve(__dirname, 'frontend/demo_execution_metadata.html')),
|
||||
|
||||
Reference in New Issue
Block a user