// file: kb-app-demo-desktop/frontend/ts/demo_execution_metadata.ts // version: 14 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 & { 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("#metadataExecutionLogOutput"); output.value = metadataLogLines.join("\n"); output.scrollTop = output.scrollHeight; } function titled(title: string, value: unknown): unknown { return { title, value }; } function element(selector: string): T { const value = document.querySelector(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(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(selector); badge.textContent = text; badge.className = className; } } function selectedScenario(): DemoExecutionMetadataMetaplexTokenMetadataScenarioPayload | null { const id = element("#metadataSyntheticScenarioSelect").value; return scenarios.find(scenario => scenario.id === id) ?? null; } function selectedMetaplexCampaign(): DemoExecutionMetadataMetaplexCampaignOptionPayload | null { const id = element("#metadataMetaplexCampaignSelect").value; return metaplexCampaigns.find(campaign => campaign.id === id) ?? null; } function selectedSolanaProgramMetadataScenario(): DemoExecutionMetadataSolanaProgramScenarioPayload | null { const id = element("#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("#metadataRequirements").innerHTML = `
Parcours
${scenario.label}
Famille
${scenario.assetFamily}
Fixture
${scenario.fixtureKind}
État initial
${scenario.initialState}
État attendu
${scenario.resultingState}
Mode
${scenario.mode}
Collection
${scenario.requiresCollection ? "requise" : "non requise"}
Rule set
${scenario.requiresProgrammableRules ? "requis" : "non requis"}
Validation
${scenario.validationStatus}
`; 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("#metadataRequirements").innerHTML = `
Famille
metadata.metaplex_token_metadata
Campagne
${campaign.label}
Asset
${campaign.assetFamily}
Profil
${selectedProfileName ?? "non chargé"}
Qualification
${campaign.qualification}
Opérations cibles
${campaign.operationNames.length}
`; 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(selector).textContent = text; } } function setExecutionRunning(value: boolean): void { executionRunning = value; const profile = selectedProfile(); const metaplexConfirmed = element("#metadataMetaplexCampaignOperatorConfirmed").checked; element("#executeMetadataMetaplexCampaignButton").disabled = value || profile === null || !profileStoreReady || !profile.sendEnabled || !metaplexConfirmed || selectedMetaplexCampaign() === null; const token2022Confirmed = element("#token2022MetadataOperatorConfirmed").checked; element("#executeToken2022MetadataCampaignButton").disabled = value || profile === null || !profileStoreReady || !profile.sendEnabled || !token2022Confirmed; const solanaProgramConfirmed = element("#solanaProgramMetadataOperatorConfirmed").checked; element("#executeSolanaProgramMetadataCampaignButton").disabled = value || profile === null || !profileStoreReady || !profile.sendEnabled || !solanaProgramConfirmed; for (const selector of ["#prepareMetadataProfileButton", "#prepareToken2022MetadataProfileButton", "#prepareSolanaProgramMetadataProfileButton", "#refreshMetadataScenariosButton"]) { element(selector).disabled = value; } } async function executeMetaplexCampaign(): Promise { 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é n’autorise pas les soumissions Devnet requises par les fixtures Metaplex."); } const operatorConfirmed = element("#metadataMetaplexCampaignOperatorConfirmed").checked; if (!operatorConfirmed) { throw new Error("La campagne Metaplex exige la confirmation explicite de l’opérateur."); } const request: DemoExecutionMetadataMetaplexCampaignRequest = { profileName: selectedProfileName, campaignId: campaign.id, operatorConfirmed, }; setExecutionRunning(true); const badge = element("#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("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("#metadataRequirements").innerHTML = `
Famille
metadata.metaplex_token_metadata
Profil
${summary.profileName}
Campagne
${summary.label}
Qualification
${campaign.qualification}
Confirmées
${summary.confirmedOperationCount}
Indisponibles
${summary.unavailableOperationCount}
Éléments de preuve
${summary.operationCount}
`; 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("#metadataRequirements").innerHTML = `
Famille
metadata.token_2022
Programme
TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb
Opérations
${operations.length}
Transactions
6
Profil
${selectedProfileName ?? "non chargé"}
Validation desktop
confirmed_pre_014
`; 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 { 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é n’autorise pas les soumissions Devnet."); } const operatorConfirmed = element("#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("#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("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("#metadataRequirements").innerHTML = `
Famille
metadata.token_2022
Profil
${summary.profileName}
Mint
${summary.mint}
Confirmées
${summary.confirmedStepCount}/${summary.stepCount}
Matérialisations
${summary.materializationCount}
Autorité finale
${summary.finalAuthority}
`; 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("#metadataRequirements").innerHTML = `
Famille
metadata.solana_program_metadata
Program ID
ProgM6JCCvbYkfKqJYHePx4xxSUSqJp7rh8Lyv7nk7S
Parcours affiché
${scenario.label}
Étapes totales
${allSteps.length}
Transactions
11 minimum
Profil
${selectedProfileName ?? "non chargé"}
Validation desktop
confirmed_pre_014
`; 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 { 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é n’autorise pas les soumissions Devnet."); } const operatorConfirmed = element("#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("#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("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("#metadataRequirements").innerHTML = `
Famille
metadata.solana_program_metadata
Profil
${summary.profileName}
Buffer
${summary.buffer}
Metadata
${summary.metadata}
Confirmées
${summary.confirmedStepCount}/${summary.stepCount}
Snapshots
${summary.materializedSnapshotCount}
`; 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 { const options = await invoke("demo_execution_metadata_options"); profileOptions = options.profiles; for (const selector of profileSelectSelectors) { const select = element(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 n’a é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 { 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 { const readiness = await invoke("demo_execution_devnet_prepare_profile", { profileName }); 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 { const badge = element("#metadataStatusBadge"); badge.textContent = "Chargement"; badge.className = "badge text-bg-warning"; try { scenarios = await invoke("demo_execution_metadata_metaplex_token_metadata_scenarios"); metaplexCampaigns = await invoke("demo_execution_metadata_metaplex_campaigns"); solanaProgramMetadataScenarios = await invoke("demo_execution_metadata_solana_program_scenarios"); const syntheticSelect = element("#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("#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("#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("#metadataSyntheticScenarioSelect").addEventListener("change", renderSelectedScenario); element("#metaplexSyntheticCollapse").addEventListener("shown.bs.collapse", renderSelectedScenario); element("#metaplexDevnetCollapse").addEventListener("shown.bs.collapse", renderMetaplexCampaign); element("#metadataMetaplexCampaignSelect").addEventListener("change", () => { renderMetaplexCampaign(); setExecutionRunning(executionRunning); }); element("#token2022MetadataCollapse").addEventListener("shown.bs.collapse", renderToken2022MetadataCampaign); element("#solanaProgramMetadataCollapse").addEventListener("shown.bs.collapse", renderSolanaProgramMetadataScenario); element("#solanaProgramMetadataScenarioSelect").addEventListener("change", renderSolanaProgramMetadataScenario); for (const selector of profileSelectSelectors) { element(selector).addEventListener("change", event => { const target = event.currentTarget as HTMLSelectElement; synchronizeProfileSelections(target.value); profileStoreReady = false; updateProfileHelp(); setExecutionRunning(executionRunning); renderActiveMetadataSelection(); void prepareProfile(); }); } element("#prepareMetadataProfileButton").addEventListener("click", () => void prepareProfile()); element("#prepareToken2022MetadataProfileButton").addEventListener("click", () => void prepareProfile()); element("#prepareSolanaProgramMetadataProfileButton").addEventListener("click", () => void prepareProfile()); element("#refreshMetadataScenariosButton").addEventListener("click", () => void loadScenarios()); element("#metadataExecutionLogOutput").addEventListener("app-log-clear", () => { metadataLogLines.length = 0; }); void listen("demo-execution-metadata-progress", event => appendMetadataLog(event.payload)); element("#metadataMetaplexCampaignOperatorConfirmed").addEventListener("change", () => setExecutionRunning(executionRunning)); element("#executeMetadataMetaplexCampaignButton").addEventListener("click", () => { void executeMetaplexCampaign().catch(caught => frontendError("kb-app-demo-desktop.frontend.execution_metadata", caught instanceof Error ? caught.message : String(caught))); }); element("#token2022MetadataOperatorConfirmed").addEventListener("change", () => setExecutionRunning(executionRunning)); element("#executeToken2022MetadataCampaignButton").addEventListener("click", () => { void executeToken2022MetadataCampaign().catch(caught => frontendError("kb-app-demo-desktop.frontend.execution_metadata", caught instanceof Error ? caught.message : String(caught))); }); element("#solanaProgramMetadataOperatorConfirmed").addEventListener("change", () => setExecutionRunning(executionRunning)); element("#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(); });