Files
khadhroony-bot3/kb-app-demo-desktop/frontend/ts/demo_execution_metadata.ts

617 lines
39 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// file: kb-app-demo-desktop/frontend/ts/demo_execution_metadata.ts
// version: 15
import * as bootstrap from "bootstrap";
import "simplebar";
import { invoke } from "@tauri-apps/api/core";
import { listen } from "@tauri-apps/api/event";
import { frontendDebug, frontendError, installFrontendConsoleBridge } from "./frontend_log.ts";
import { renderJsonViewer } from "./json_viewer.ts";
import type { DemoExecutionMetadataMetaplexCampaignOptionPayload } from "./bindings/kb_app_demo_desktop/demo_execution_metadata/DemoExecutionMetadataMetaplexCampaignOptionPayload.ts";
import type { DemoExecutionMetadataMetaplexCampaignRequest } from "./bindings/kb_app_demo_desktop/demo_execution_metadata/DemoExecutionMetadataMetaplexCampaignRequest.ts";
import type { DemoExecutionMetadataMetaplexCampaignSummaryPayload } from "./bindings/kb_app_demo_desktop/demo_execution_metadata/DemoExecutionMetadataMetaplexCampaignSummaryPayload.ts";
import type { DemoExecutionMetadataMetaplexTokenMetadataScenarioPayload } from "./bindings/kb_app_demo_desktop/demo_execution_metadata/DemoExecutionMetadataMetaplexTokenMetadataScenarioPayload.ts";
import type { DemoExecutionMetadataProgressPayload } from "./bindings/kb_app_demo_desktop/demo_execution_metadata/DemoExecutionMetadataProgressPayload.ts";
import type { DemoExecutionMetadataSolanaProgramCampaignRequest } from "./bindings/kb_app_demo_desktop/demo_execution_metadata/DemoExecutionMetadataSolanaProgramCampaignRequest.ts";
import type { DemoExecutionMetadataSolanaProgramCampaignSummaryPayload } from "./bindings/kb_app_demo_desktop/demo_execution_metadata/DemoExecutionMetadataSolanaProgramCampaignSummaryPayload.ts";
import type { DemoExecutionMetadataSolanaProgramScenarioPayload } from "./bindings/kb_app_demo_desktop/demo_execution_metadata/DemoExecutionMetadataSolanaProgramScenarioPayload.ts";
import type { DemoExecutionMetadataToken2022CampaignRequest } from "./bindings/kb_app_demo_desktop/demo_execution_metadata/DemoExecutionMetadataToken2022CampaignRequest.ts";
import type { DemoExecutionMetadataToken2022CampaignSummaryPayload } from "./bindings/kb_app_demo_desktop/demo_execution_metadata/DemoExecutionMetadataToken2022CampaignSummaryPayload.ts";
import type { DemoExecutionDevnetStoreReadinessPayload } from "./bindings/kb_app_demo_desktop/demo_execution_solana_core/DemoExecutionDevnetStoreReadinessPayload.ts";
import type { DemoExecutionSolanaCoreOptionsPayload } from "./bindings/kb_app_demo_desktop/demo_execution_solana_core/DemoExecutionSolanaCoreOptionsPayload.ts";
import type { DemoExecutionSolanaCoreProfileOption } from "./bindings/kb_app_demo_desktop/demo_execution_solana_core/DemoExecutionSolanaCoreProfileOption.ts";
const storageKey = "kb-app-demo-desktop.metadata.selected-scenario";
const metaplexCampaignStorageKey = "kb-app-demo-desktop.metadata.metaplex.selected-campaign";
const solanaProgramMetadataStorageKey = "kb-app-demo-desktop.metadata.solana-program.selected-scenario";
const profileStorageKey = "kb-app-demo-desktop.metadata.selected-profile";
(window as Window & typeof globalThis & { bootstrap?: typeof bootstrap }).bootstrap = bootstrap;
let scenarios: DemoExecutionMetadataMetaplexTokenMetadataScenarioPayload[] = [];
let metaplexCampaigns: DemoExecutionMetadataMetaplexCampaignOptionPayload[] = [];
let solanaProgramMetadataScenarios: DemoExecutionMetadataSolanaProgramScenarioPayload[] = [];
let selectedProfileName: string | null = null;
let executionRunning = false;
let profileStoreReady = false;
let profileOptions: DemoExecutionSolanaCoreProfileOption[] = [];
const metadataLogLines: string[] = [];
const maximumMetadataLogLines = 1200;
type MetadataProgressInput = Omit<DemoExecutionMetadataProgressPayload, "signature"> & { signature?: string | null };
function appendMetadataLog(payload: MetadataProgressInput): void {
const signature = payload.signature ? ` signature=${payload.signature}` : "";
metadataLogLines.push(`${payload.timestamp} ${payload.level.toUpperCase()} [${payload.stage}] ${payload.message}${signature}`);
if (metadataLogLines.length > maximumMetadataLogLines) {
metadataLogLines.splice(0, metadataLogLines.length - maximumMetadataLogLines);
}
const output = element<HTMLTextAreaElement>("#metadataExecutionLogOutput");
output.value = metadataLogLines.join("\n");
output.scrollTop = output.scrollHeight;
}
function titled(title: string, value: unknown): unknown { return { title, value }; }
function element<T extends HTMLElement>(selector: string): T {
const value = document.querySelector<T>(selector);
if (value === null) {
throw new Error(`Élément introuvable : ${selector}`);
}
return value;
}
const profileSelectSelectors = ["#metadataProfileSelect", "#token2022MetadataProfileSelect", "#solanaProgramMetadataProfileSelect"] as const;
const profileHelpSelectors = ["#metadataProfileHelp", "#token2022MetadataProfileHelp", "#solanaProgramMetadataProfileHelp"] as const;
const profileStatusSelectors = ["#metadataProfileStatusBadge", "#token2022MetadataProfileStatusBadge", "#solanaProgramMetadataProfileStatusBadge"] as const;
const profileOutputSelectors = ["#metadataProfileOutput", "#token2022MetadataProfileOutput", "#solanaProgramMetadataProfileOutput"] as const;
function synchronizeProfileSelections(profileName: string): void {
for (const selector of profileSelectSelectors) {
const select = element<HTMLSelectElement>(selector);
if (Array.from(select.options).some(option => option.value === profileName)) {
select.value = profileName;
}
}
selectedProfileName = profileName.length === 0 ? null : profileName;
}
function updateProfileStatus(text: string, className: string): void {
for (const selector of profileStatusSelectors) {
const badge = element<HTMLElement>(selector);
badge.textContent = text;
badge.className = className;
}
}
function selectedScenario(): DemoExecutionMetadataMetaplexTokenMetadataScenarioPayload | null {
const id = element<HTMLSelectElement>("#metadataSyntheticScenarioSelect").value;
return scenarios.find(scenario => scenario.id === id) ?? null;
}
function selectedMetaplexCampaign(): DemoExecutionMetadataMetaplexCampaignOptionPayload | null {
const id = element<HTMLSelectElement>("#metadataMetaplexCampaignSelect").value;
return metaplexCampaigns.find(campaign => campaign.id === id) ?? null;
}
function selectedSolanaProgramMetadataScenario(): DemoExecutionMetadataSolanaProgramScenarioPayload | null {
const id = element<HTMLSelectElement>("#solanaProgramMetadataScenarioSelect").value;
return solanaProgramMetadataScenarios.find(scenario => scenario.id === id) ?? null;
}
function renderSelectedScenario(): void {
const scenario = selectedScenario();
if (scenario === null) {
return;
}
window.localStorage.setItem(storageKey, scenario.id);
element<HTMLElement>("#metadataRequirements").innerHTML = `<dt class="col-6">Parcours</dt><dd class="col-6">${scenario.label}</dd><dt class="col-6">Famille</dt><dd class="col-6"><code>${scenario.assetFamily}</code></dd><dt class="col-6">Fixture</dt><dd class="col-6"><code>${scenario.fixtureKind}</code></dd><dt class="col-6">État initial</dt><dd class="col-6"><code>${scenario.initialState}</code></dd><dt class="col-6">État attendu</dt><dd class="col-6"><code>${scenario.resultingState}</code></dd><dt class="col-6">Mode</dt><dd class="col-6"><code>${scenario.mode}</code></dd><dt class="col-6">Collection</dt><dd class="col-6">${scenario.requiresCollection ? "requise" : "non requise"}</dd><dt class="col-6">Rule set</dt><dd class="col-6">${scenario.requiresProgrammableRules ? "requis" : "non requis"}</dd><dt class="col-6">Validation</dt><dd class="col-6"><code>${scenario.validationStatus}</code></dd>`;
renderJsonViewer("#metadataPreflightOutput", titled("Contrat synthétique — opérations", {
scenarioId: scenario.id,
operationCodes: scenario.operationCodes,
fixtureKind: scenario.fixtureKind,
checks: ["program_id", "owner", "pda_and_seeds", "required_accounts", "authorities", "stateful_correlations"],
}));
renderJsonViewer("#metadataSimulationOutput", titled("Contrat synthétique — politique", {
mode: scenario.mode,
networkExecutionPerformed: false,
automaticSubmission: false,
validationStatus: scenario.validationStatus,
note: "Ce mode inspecte le contrat. Les exécutions Devnet utilisent exclusivement les campagnes qualifiées de pre.013.",
}));
renderJsonViewer("#metadataPostconditionsOutput", titled("Contrat synthétique — preuves", {
required: scenario.requiresPostcondition,
evidenceKinds: scenario.requiredEvidence,
materializationTargets: scenario.materializationTargets,
networkEvidenceCollected: false,
}));
}
function renderMetaplexCampaign(): void {
const campaign = selectedMetaplexCampaign();
if (campaign === null) {
return;
}
window.localStorage.setItem(metaplexCampaignStorageKey, campaign.id);
renderJsonViewer("#metadataMetaplexCampaignOutput", titled("Campagne Metaplex qualifiée", campaign));
element<HTMLElement>("#metadataRequirements").innerHTML = `<dt class="col-6">Famille</dt><dd class="col-6"><code>metadata.metaplex_token_metadata</code></dd><dt class="col-6">Campagne</dt><dd class="col-6">${campaign.label}</dd><dt class="col-6">Asset</dt><dd class="col-6"><code>${campaign.assetFamily}</code></dd><dt class="col-6">Profil</dt><dd class="col-6"><code>${selectedProfileName ?? "non chargé"}</code></dd><dt class="col-6">Qualification</dt><dd class="col-6"><code>${campaign.qualification}</code></dd><dt class="col-6">Opérations cibles</dt><dd class="col-6">${campaign.operationNames.length}</dd>`;
renderJsonViewer("#metadataPreflightOutput", titled("Préflight et opérations", {
campaignId: campaign.id,
label: campaign.label,
assetFamily: campaign.assetFamily,
setup: campaign.setup,
targetOperations: campaign.operationNames,
runner: "qualified_pre_013_specialized_campaign",
networkExecutionPerformed: false,
}));
renderJsonViewer("#metadataSimulationOutput", titled("Simulation et confirmation", {
policy: "specialized_campaign_simulation_first",
operatorConfirmationRequired: true,
stopOnFirstFailure: true,
isolatedUnavailableSubmission: false,
qualification: campaign.qualification,
networkExecutionPerformed: false,
}));
renderJsonViewer("#metadataPostconditionsOutput", titled("Postconditions et preuves", {
campaignId: campaign.id,
qualification: campaign.qualification,
requiredEvidence: ["rpc_simulation", "confirmed_signature_when_submitted", "canonical_replay", "stateful_postconditions", "materialization", "idempotence"],
unavailablePolicy: "simulation_only_probe_without_submission",
networkEvidenceCollected: false,
}));
}
function selectedProfile(): DemoExecutionSolanaCoreProfileOption | null {
if (selectedProfileName === null) {
return null;
}
return profileOptions.find(profile => profile.name === selectedProfileName) ?? null;
}
function updateProfileHelp(): void {
const profile = selectedProfile();
const text = profile === null
? "Aucun profil Devnet compatible."
: `wallet=${profile.walletAlias}, spendMax=${profile.maxSpendLamports}, airdropMax=${profile.maxAirdropLamports}, send=${profile.sendEnabled ? "enabled" : "disabled"}`;
for (const selector of profileHelpSelectors) {
element<HTMLElement>(selector).textContent = text;
}
}
function setExecutionRunning(value: boolean): void {
executionRunning = value;
const profile = selectedProfile();
const metaplexConfirmed = element<HTMLInputElement>("#metadataMetaplexCampaignOperatorConfirmed").checked;
element<HTMLButtonElement>("#executeMetadataMetaplexCampaignButton").disabled = value || profile === null || !profileStoreReady || !profile.sendEnabled || !metaplexConfirmed || selectedMetaplexCampaign() === null;
const token2022Confirmed = element<HTMLInputElement>("#token2022MetadataOperatorConfirmed").checked;
element<HTMLButtonElement>("#executeToken2022MetadataCampaignButton").disabled = value || profile === null || !profileStoreReady || !profile.sendEnabled || !token2022Confirmed;
const solanaProgramConfirmed = element<HTMLInputElement>("#solanaProgramMetadataOperatorConfirmed").checked;
element<HTMLButtonElement>("#executeSolanaProgramMetadataCampaignButton").disabled = value || profile === null || !profileStoreReady || !profile.sendEnabled || !solanaProgramConfirmed;
for (const selector of ["#prepareMetadataProfileButton", "#prepareToken2022MetadataProfileButton", "#prepareSolanaProgramMetadataProfileButton", "#refreshMetadataScenariosButton"]) {
element<HTMLButtonElement>(selector).disabled = value;
}
}
async function executeMetaplexCampaign(): Promise<void> {
if (executionRunning) {
return;
}
const profile = selectedProfile();
const campaign = selectedMetaplexCampaign();
if (profile === null || selectedProfileName === null || !profileStoreReady) {
throw new Error("Sélectionnez et préparez un profil Devnet.");
}
if (campaign === null) {
throw new Error("Sélectionnez une campagne Metaplex qualifiée.");
}
if (!profile.sendEnabled) {
throw new Error("Le profil sélectionné nautorise pas les soumissions Devnet requises par les fixtures Metaplex.");
}
const operatorConfirmed = element<HTMLInputElement>("#metadataMetaplexCampaignOperatorConfirmed").checked;
if (!operatorConfirmed) {
throw new Error("La campagne Metaplex exige la confirmation explicite de lopérateur.");
}
const request: DemoExecutionMetadataMetaplexCampaignRequest = {
profileName: selectedProfileName,
campaignId: campaign.id,
operatorConfirmed,
};
setExecutionRunning(true);
const badge = element<HTMLElement>("#metadataStatusBadge");
badge.textContent = "Campagne Metaplex";
badge.className = "badge text-bg-warning";
appendMetadataLog({ timestamp: new Date().toISOString(), level: "info", stage: "metaplex_campaign", message: `Démarrage de la campagne qualifiée ${campaign.id}${campaign.label}.` });
try {
const summary = await invoke<DemoExecutionMetadataMetaplexCampaignSummaryPayload>("demo_execution_metadata_metaplex_execute_campaign", { request });
const executions = JSON.parse(summary.executionsJson) as unknown;
const postconditions = JSON.parse(summary.postconditionsJson) as unknown;
renderJsonViewer("#metadataPreflightOutput", titled("Fixture et setup Metaplex", JSON.parse(summary.fixtureJson)));
renderJsonViewer("#metadataSimulationOutput", titled("Synthèse de campagne Metaplex", {
profileName: summary.profileName,
campaignId: summary.campaignId,
label: summary.label,
operationCount: summary.operationCount,
confirmedOperationCount: summary.confirmedOperationCount,
unavailableOperationCount: summary.unavailableOperationCount,
completed: summary.completed,
}));
renderJsonViewer("#metadataPostconditionsOutput", titled("Exécutions et postconditions Metaplex", { executions, postconditions }));
element<HTMLElement>("#metadataRequirements").innerHTML = `<dt class="col-6">Famille</dt><dd class="col-6"><code>metadata.metaplex_token_metadata</code></dd><dt class="col-6">Profil</dt><dd class="col-6"><code>${summary.profileName}</code></dd><dt class="col-6">Campagne</dt><dd class="col-6">${summary.label}</dd><dt class="col-6">Qualification</dt><dd class="col-6"><code>${campaign.qualification}</code></dd><dt class="col-6">Confirmées</dt><dd class="col-6">${summary.confirmedOperationCount}</dd><dt class="col-6">Indisponibles</dt><dd class="col-6">${summary.unavailableOperationCount}</dd><dt class="col-6">Éléments de preuve</dt><dd class="col-6">${summary.operationCount}</dd>`;
badge.textContent = summary.completed ? "Campagne qualifiée" : "Campagne incomplète";
badge.className = summary.completed ? "badge text-bg-success" : "badge text-bg-danger";
appendMetadataLog({ timestamp: new Date().toISOString(), level: summary.completed ? "info" : "error", stage: "metaplex_campaign", message: `Campagne terminée : confirmed=${summary.confirmedOperationCount}, unavailable=${summary.unavailableOperationCount}, evidence=${summary.operationCount}.` });
} finally {
setExecutionRunning(false);
}
}
function renderToken2022MetadataCampaign(): void {
const operations = [
"initialize_token_metadata",
"update_token_metadata_field",
"emit_token_metadata",
"remove_token_metadata_key",
"update_token_metadata_authority",
];
renderJsonViewer("#token2022MetadataCampaignOutput", titled("Campagne Token-2022 Token Metadata", {
family: "metadata.token_2022",
operations,
fixture: "fresh_token_2022_mint_with_self_metadata_pointer",
transactionCount: 6,
policy: "simulation_first_then_confirmed_submission",
desktopValidation: "confirmed_pre_014",
}));
element<HTMLElement>("#metadataRequirements").innerHTML = `<dt class="col-6">Famille</dt><dd class="col-6"><code>metadata.token_2022</code></dd><dt class="col-6">Programme</dt><dd class="col-6"><code>TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb</code></dd><dt class="col-6">Opérations</dt><dd class="col-6">${operations.length}</dd><dt class="col-6">Transactions</dt><dd class="col-6">6</dd><dt class="col-6">Profil</dt><dd class="col-6"><code>${selectedProfileName ?? "non chargé"}</code></dd><dt class="col-6">Validation desktop</dt><dd class="col-6"><code>confirmed_pre_014</code></dd>`;
renderJsonViewer("#metadataPreflightOutput", titled("Préflight et opérations", {
family: "metadata.token_2022",
operations,
fixturePreparation: ["fresh_mint", "metadata_pointer_to_self", "rent_budget_for_metadata_growth"],
desktopValidation: "confirmed_pre_014",
networkExecutionPerformed: false,
}));
renderJsonViewer("#metadataSimulationOutput", titled("Simulation et confirmation", {
policy: "simulation_first",
submission: "required",
operatorConfirmationRequired: true,
stopOnFirstFailure: true,
fixtureTransactionCount: 1,
metadataTransactionCount: operations.length,
desktopValidation: "confirmed_pre_014",
networkExecutionPerformed: false,
}));
renderJsonViewer("#metadataPostconditionsOutput", titled("Postconditions et preuves", {
requiredEvidence: ["metadata_pointer", "stateful_snapshot", "rpc_simulation", "confirmed_signature", "canonical_replay", "materialization", "idempotence", "emit_return_data"],
operationCount: operations.length,
desktopValidation: "confirmed_pre_014",
networkEvidenceCollected: false,
}));
}
async function executeToken2022MetadataCampaign(): Promise<void> {
if (executionRunning) {
return;
}
const profile = selectedProfile();
if (profile === null || selectedProfileName === null || !profileStoreReady) {
throw new Error("Sélectionnez et préparez un profil Devnet.");
}
if (!profile.sendEnabled) {
throw new Error("Le profil sélectionné nautorise pas les soumissions Devnet.");
}
const operatorConfirmed = element<HTMLInputElement>("#token2022MetadataOperatorConfirmed").checked;
if (!operatorConfirmed) {
throw new Error("La campagne exige la confirmation explicite de la fixture et des cinq mutations.");
}
const request: DemoExecutionMetadataToken2022CampaignRequest = {
profileName: selectedProfileName,
operatorConfirmed,
};
setExecutionRunning(true);
const badge = element<HTMLElement>("#metadataStatusBadge");
badge.textContent = "Campagne Token-2022";
badge.className = "badge text-bg-warning";
appendMetadataLog({ timestamp: new Date().toISOString(), level: "info", stage: "token_2022_metadata_campaign", message: "Démarrage : création de la fixture puis cinq opérations Token Metadata." });
try {
const summary = await invoke<DemoExecutionMetadataToken2022CampaignSummaryPayload>("demo_execution_metadata_token_2022_execute_campaign", { request });
renderJsonViewer("#metadataPreflightOutput", titled("Fixture Token-2022 Metadata", JSON.parse(summary.fixtureJson)));
renderJsonViewer("#metadataSimulationOutput", titled("Synthèse de campagne", {
profileName: summary.profileName,
mint: summary.mint,
initialAuthority: summary.initialAuthority,
finalAuthority: summary.finalAuthority,
preparationSignature: summary.preparationSignature,
stepCount: summary.stepCount,
confirmedStepCount: summary.confirmedStepCount,
materializationCount: summary.materializationCount,
completed: summary.completed,
}));
renderJsonViewer("#metadataPostconditionsOutput", titled("Preuves ordonnées des cinq opérations", JSON.parse(summary.stepsJson)));
element<HTMLElement>("#metadataRequirements").innerHTML = `<dt class="col-6">Famille</dt><dd class="col-6"><code>metadata.token_2022</code></dd><dt class="col-6">Profil</dt><dd class="col-6"><code>${summary.profileName}</code></dd><dt class="col-6">Mint</dt><dd class="col-6"><code>${summary.mint}</code></dd><dt class="col-6">Confirmées</dt><dd class="col-6">${summary.confirmedStepCount}/${summary.stepCount}</dd><dt class="col-6">Matérialisations</dt><dd class="col-6">${summary.materializationCount}</dd><dt class="col-6">Autorité finale</dt><dd class="col-6"><code>${summary.finalAuthority}</code></dd>`;
badge.textContent = summary.completed ? "Campagne confirmée" : "Campagne incomplète";
badge.className = summary.completed ? "badge text-bg-success" : "badge text-bg-danger";
appendMetadataLog({ timestamp: new Date().toISOString(), level: summary.completed ? "info" : "error", stage: "token_2022_metadata_campaign", message: `Campagne terminée : confirmed=${summary.confirmedStepCount}/${summary.stepCount}, materializations=${summary.materializationCount}` });
} finally {
setExecutionRunning(false);
}
}
function renderSolanaProgramMetadataScenario(): void {
const scenario = selectedSolanaProgramMetadataScenario();
if (scenario === null) {
return;
}
window.localStorage.setItem(solanaProgramMetadataStorageKey, scenario.id);
renderJsonViewer("#solanaProgramMetadataScenarioOutput", titled("Parcours Solana Program Metadata", {
scenarioId: scenario.id,
label: scenario.label,
fixtureAccount: scenario.fixtureAccount,
steps: scenario.steps,
desktopValidation: "confirmed_pre_014",
}));
const allSteps = solanaProgramMetadataScenarios.flatMap(candidate => candidate.steps);
element<HTMLElement>("#metadataRequirements").innerHTML = `<dt class="col-6">Famille</dt><dd class="col-6"><code>metadata.solana_program_metadata</code></dd><dt class="col-6">Program ID</dt><dd class="col-6"><code>ProgM6JCCvbYkfKqJYHePx4xxSUSqJp7rh8Lyv7nk7S</code></dd><dt class="col-6">Parcours affiché</dt><dd class="col-6">${scenario.label}</dd><dt class="col-6">Étapes totales</dt><dd class="col-6">${allSteps.length}</dd><dt class="col-6">Transactions</dt><dd class="col-6">11 minimum</dd><dt class="col-6">Profil</dt><dd class="col-6"><code>${selectedProfileName ?? "non chargé"}</code></dd><dt class="col-6">Validation desktop</dt><dd class="col-6"><code>confirmed_pre_014</code></dd>`;
renderJsonViewer("#metadataPreflightOutput", titled("Préflight et opérations", {
family: "metadata.solana_program_metadata",
journeys: solanaProgramMetadataScenarios,
operationCount: allSteps.length,
fixtureAccounts: ["buffer", "metadata"],
prefundingTransactionCount: 2,
desktopValidation: "confirmed_pre_014",
}));
renderJsonViewer("#metadataSimulationOutput", titled("Simulation et confirmation", {
policy: "simulation_first",
submission: "required",
operatorConfirmationRequired: true,
stopOnFirstFailure: true,
desktopValidation: "confirmed_pre_014",
networkExecutionPerformed: false,
}));
renderJsonViewer("#metadataPostconditionsOutput", titled("Postconditions et preuves", {
requiredEvidence: ["stateful_preflight", "rpc_simulation", "confirmed_signature", "stateful_postcondition", "materialized_snapshot_or_account_absence"],
desktopValidation: "confirmed_pre_014",
networkEvidenceCollected: false,
}));
}
async function executeSolanaProgramMetadataCampaign(): Promise<void> {
if (executionRunning) {
return;
}
const profile = selectedProfile();
if (profile === null || selectedProfileName === null || !profileStoreReady) {
throw new Error("Sélectionnez et préparez un profil Devnet.");
}
if (!profile.sendEnabled) {
throw new Error("Le profil sélectionné nautorise pas les soumissions Devnet.");
}
const operatorConfirmed = element<HTMLInputElement>("#solanaProgramMetadataOperatorConfirmed").checked;
if (!operatorConfirmed) {
throw new Error("La campagne exige la confirmation explicite du préfinancement et des neuf mutations.");
}
const request: DemoExecutionMetadataSolanaProgramCampaignRequest = {
profileName: selectedProfileName,
operatorConfirmed,
};
setExecutionRunning(true);
const badge = element<HTMLElement>("#metadataStatusBadge");
badge.textContent = "Campagne ProgM6";
badge.className = "badge text-bg-warning";
appendMetadataLog({ timestamp: new Date().toISOString(), level: "info", stage: "spm_campaign", message: "Démarrage de la campagne complète : 2 préfinancements et 9 opérations." });
try {
const summary = await invoke<DemoExecutionMetadataSolanaProgramCampaignSummaryPayload>("demo_execution_metadata_solana_program_execute_campaign", { request });
renderJsonViewer("#metadataPreflightOutput", titled("Fixture et opérations préparées", JSON.parse(summary.fixtureJson)));
renderJsonViewer("#metadataSimulationOutput", titled("Synthèse de campagne", {
profileName: summary.profileName,
authority: summary.authority,
describedProgram: summary.describedProgram,
buffer: summary.buffer,
bufferPrefundSignature: summary.bufferPrefundSignature,
metadata: summary.metadata,
metadataPrefundSignature: summary.metadataPrefundSignature,
stepCount: summary.stepCount,
confirmedStepCount: summary.confirmedStepCount,
materializedSnapshotCount: summary.materializedSnapshotCount,
completed: summary.completed,
}));
renderJsonViewer("#metadataPostconditionsOutput", titled("Preuves ordonnées des neuf opérations", JSON.parse(summary.stepsJson)));
element<HTMLElement>("#metadataRequirements").innerHTML = `<dt class="col-6">Famille</dt><dd class="col-6"><code>metadata.solana_program_metadata</code></dd><dt class="col-6">Profil</dt><dd class="col-6"><code>${summary.profileName}</code></dd><dt class="col-6">Buffer</dt><dd class="col-6"><code>${summary.buffer}</code></dd><dt class="col-6">Metadata</dt><dd class="col-6"><code>${summary.metadata}</code></dd><dt class="col-6">Confirmées</dt><dd class="col-6">${summary.confirmedStepCount}/${summary.stepCount}</dd><dt class="col-6">Snapshots</dt><dd class="col-6">${summary.materializedSnapshotCount}</dd>`;
badge.textContent = summary.completed ? "Campagne confirmée" : "Campagne incomplète";
badge.className = summary.completed ? "badge text-bg-success" : "badge text-bg-danger";
appendMetadataLog({ timestamp: new Date().toISOString(), level: summary.completed ? "info" : "error", stage: "spm_campaign", message: `Campagne terminée : confirmed=${summary.confirmedStepCount}/${summary.stepCount}, snapshots=${summary.materializedSnapshotCount}` });
} finally {
setExecutionRunning(false);
}
}
function renderActiveMetadataSelection(): void {
if (document.querySelector("#token2022MetadataCollapse.show") !== null) {
renderToken2022MetadataCampaign();
return;
}
if (document.querySelector("#solanaProgramMetadataCollapse.show") !== null) {
renderSolanaProgramMetadataScenario();
return;
}
if (document.querySelector("#metaplexDevnetCollapse.show") !== null) {
renderMetaplexCampaign();
return;
}
renderSelectedScenario();
}
async function loadProfiles(): Promise<void> {
const options = await invoke<DemoExecutionSolanaCoreOptionsPayload>("demo_execution_metadata_options");
profileOptions = options.profiles;
for (const selector of profileSelectSelectors) {
const select = element<HTMLSelectElement>(selector);
select.replaceChildren();
for (const profile of options.profiles) {
const option = document.createElement("option");
option.value = profile.name;
option.textContent = `${profile.name}${profile.walletAlias}`;
select.append(option);
}
}
const restored = window.localStorage.getItem(profileStorageKey);
const availableNames = options.profiles.map(profile => profile.name);
const selected = restored !== null && availableNames.includes(restored)
? restored
: options.defaultProfileName ?? availableNames[0] ?? "";
synchronizeProfileSelections(selected);
updateProfileHelp();
if (options.profiles.length === 0) {
const message = "Aucun profil Devnet compatible na été retourné par la configuration active.";
appendMetadataLog({ timestamp: new Date().toISOString(), level: "error", stage: "profile_options", message });
updateProfileStatus("Aucun profil", "badge text-bg-danger");
renderActiveMetadataSelection();
return;
}
appendMetadataLog({ timestamp: new Date().toISOString(), level: "info", stage: "profile_options", message: `${options.profiles.length} profil(s) Devnet chargé(s).` });
renderActiveMetadataSelection();
await prepareProfile();
}
async function prepareProfile(): Promise<void> {
const profileName = selectedProfileName ?? "";
if (profileName.length === 0) {
updateProfileStatus("Aucun profil", "badge text-bg-danger");
return;
}
profileStoreReady = false;
setExecutionRunning(executionRunning);
updateProfileStatus("Préparation", "badge text-bg-warning");
try {
frontendDebug("kb-app-demo-desktop.frontend.execution_metadata", `Invoke demo_execution_devnet_prepare_profile profile=${profileName}`);
const readiness = await invoke<DemoExecutionDevnetStoreReadinessPayload>("demo_execution_devnet_prepare_profile", { profileName });
frontendDebug("kb-app-demo-desktop.frontend.execution_metadata", `Invoke demo_execution_devnet_prepare_profile completed profile=${profileName} resources=${readiness.availableResourcesAfter}/${readiness.expectedResources}`);
synchronizeProfileSelections(profileName);
window.localStorage.setItem(profileStorageKey, profileName);
for (const selector of profileOutputSelectors) {
renderJsonViewer(selector, readiness);
}
profileStoreReady = true;
updateProfileStatus("Base prête", "badge text-bg-success");
setExecutionRunning(executionRunning);
renderActiveMetadataSelection();
} catch (caught) {
const message = caught instanceof Error ? caught.message : String(caught);
profileStoreReady = false;
updateProfileStatus("Erreur", "badge text-bg-danger");
setExecutionRunning(executionRunning);
frontendError("kb-app-demo-desktop.frontend.execution_metadata", message);
}
}
async function loadScenarios(): Promise<void> {
const badge = element<HTMLElement>("#metadataStatusBadge");
badge.textContent = "Chargement";
badge.className = "badge text-bg-warning";
try {
scenarios = await invoke<DemoExecutionMetadataMetaplexTokenMetadataScenarioPayload[]>("demo_execution_metadata_metaplex_token_metadata_scenarios");
metaplexCampaigns = await invoke<DemoExecutionMetadataMetaplexCampaignOptionPayload[]>("demo_execution_metadata_metaplex_campaigns");
solanaProgramMetadataScenarios = await invoke<DemoExecutionMetadataSolanaProgramScenarioPayload[]>("demo_execution_metadata_solana_program_scenarios");
const syntheticSelect = element<HTMLSelectElement>("#metadataSyntheticScenarioSelect");
syntheticSelect.replaceChildren();
for (const scenario of scenarios.filter(candidate => candidate.mode === "synthetic")) {
const option = document.createElement("option");
option.value = scenario.id;
option.textContent = `${scenario.assetFamily}${scenario.label}`;
syntheticSelect.append(option);
}
const restored = window.localStorage.getItem(storageKey);
if (restored !== null && Array.from(syntheticSelect.options).some(option => option.value === restored)) {
syntheticSelect.value = restored;
}
const metaplexSelect = element<HTMLSelectElement>("#metadataMetaplexCampaignSelect");
metaplexSelect.replaceChildren();
for (const campaign of metaplexCampaigns) {
const option = document.createElement("option");
option.value = campaign.id;
option.textContent = `${campaign.assetFamily}${campaign.label}`;
metaplexSelect.append(option);
}
const restoredMetaplex = window.localStorage.getItem(metaplexCampaignStorageKey);
if (restoredMetaplex !== null && Array.from(metaplexSelect.options).some(option => option.value === restoredMetaplex)) {
metaplexSelect.value = restoredMetaplex;
}
const solanaSelect = element<HTMLSelectElement>("#solanaProgramMetadataScenarioSelect");
solanaSelect.replaceChildren();
for (const scenario of solanaProgramMetadataScenarios) {
const option = document.createElement("option");
option.value = scenario.id;
option.textContent = `${scenario.fixtureAccount}${scenario.label}`;
solanaSelect.append(option);
}
const restoredSolana = window.localStorage.getItem(solanaProgramMetadataStorageKey);
if (restoredSolana !== null && Array.from(solanaSelect.options).some(option => option.value === restoredSolana)) {
solanaSelect.value = restoredSolana;
}
renderActiveMetadataSelection();
const syntheticCount = scenarios.filter(candidate => candidate.mode === "synthetic").length;
badge.textContent = `${syntheticCount + metaplexCampaigns.length + solanaProgramMetadataScenarios.length + 1} contrats`;
badge.className = "badge text-bg-success";
setExecutionRunning(executionRunning);
} catch (caught) {
const message = caught instanceof Error ? caught.message : String(caught);
profileStoreReady = false;
badge.textContent = "Erreur";
badge.className = "badge text-bg-danger";
setExecutionRunning(executionRunning);
frontendError("kb-app-demo-desktop.frontend.execution_metadata", message);
}
}
document.addEventListener("DOMContentLoaded", () => {
installFrontendConsoleBridge("kb-app-demo-desktop.frontend.execution_metadata");
element<HTMLSelectElement>("#metadataSyntheticScenarioSelect").addEventListener("change", renderSelectedScenario);
element<HTMLElement>("#metaplexSyntheticCollapse").addEventListener("shown.bs.collapse", renderSelectedScenario);
element<HTMLElement>("#metaplexDevnetCollapse").addEventListener("shown.bs.collapse", renderMetaplexCampaign);
element<HTMLSelectElement>("#metadataMetaplexCampaignSelect").addEventListener("change", () => {
renderMetaplexCampaign();
setExecutionRunning(executionRunning);
});
element<HTMLElement>("#token2022MetadataCollapse").addEventListener("shown.bs.collapse", renderToken2022MetadataCampaign);
element<HTMLElement>("#solanaProgramMetadataCollapse").addEventListener("shown.bs.collapse", renderSolanaProgramMetadataScenario);
element<HTMLSelectElement>("#solanaProgramMetadataScenarioSelect").addEventListener("change", renderSolanaProgramMetadataScenario);
for (const selector of profileSelectSelectors) {
element<HTMLSelectElement>(selector).addEventListener("change", event => {
const target = event.currentTarget as HTMLSelectElement;
synchronizeProfileSelections(target.value);
profileStoreReady = false;
updateProfileHelp();
setExecutionRunning(executionRunning);
renderActiveMetadataSelection();
void prepareProfile();
});
}
element<HTMLButtonElement>("#prepareMetadataProfileButton").addEventListener("click", () => {
frontendDebug("kb-app-demo-desktop.frontend.execution_metadata", "Metaplex profile load button clicked");
void prepareProfile();
});
element<HTMLButtonElement>("#prepareToken2022MetadataProfileButton").addEventListener("click", () => {
frontendDebug("kb-app-demo-desktop.frontend.execution_metadata", "Token-2022 Metadata profile load button clicked");
void prepareProfile();
});
element<HTMLButtonElement>("#prepareSolanaProgramMetadataProfileButton").addEventListener("click", () => {
frontendDebug("kb-app-demo-desktop.frontend.execution_metadata", "Solana Program Metadata profile load button clicked");
void prepareProfile();
});
element<HTMLButtonElement>("#refreshMetadataScenariosButton").addEventListener("click", () => void loadScenarios());
element<HTMLTextAreaElement>("#metadataExecutionLogOutput").addEventListener("app-log-clear", () => { metadataLogLines.length = 0; });
void listen<DemoExecutionMetadataProgressPayload>("demo-execution-metadata-progress", event => appendMetadataLog(event.payload));
element<HTMLInputElement>("#metadataMetaplexCampaignOperatorConfirmed").addEventListener("change", () => setExecutionRunning(executionRunning));
element<HTMLButtonElement>("#executeMetadataMetaplexCampaignButton").addEventListener("click", () => {
void executeMetaplexCampaign().catch(caught => frontendError("kb-app-demo-desktop.frontend.execution_metadata", caught instanceof Error ? caught.message : String(caught)));
});
element<HTMLInputElement>("#token2022MetadataOperatorConfirmed").addEventListener("change", () => setExecutionRunning(executionRunning));
element<HTMLButtonElement>("#executeToken2022MetadataCampaignButton").addEventListener("click", () => {
void executeToken2022MetadataCampaign().catch(caught => frontendError("kb-app-demo-desktop.frontend.execution_metadata", caught instanceof Error ? caught.message : String(caught)));
});
element<HTMLInputElement>("#solanaProgramMetadataOperatorConfirmed").addEventListener("change", () => setExecutionRunning(executionRunning));
element<HTMLButtonElement>("#executeSolanaProgramMetadataCampaignButton").addEventListener("click", () => {
void executeSolanaProgramMetadataCampaign().catch(caught => frontendError("kb-app-demo-desktop.frontend.execution_metadata", caught instanceof Error ? caught.message : String(caught)));
});
setExecutionRunning(false);
frontendDebug("kb-app-demo-desktop.frontend.execution_metadata", "Metadata execution window loaded");
void loadProfiles().catch(caught => frontendError("kb-app-demo-desktop.frontend.execution_metadata", caught instanceof Error ? caught.message : String(caught)));
void loadScenarios();
});