v0.4.8-pre.014

This commit is contained in:
2026-08-09 12:19:06 +02:00
parent 8b831f8692
commit aec7b4b76a
13 changed files with 1938 additions and 408 deletions

View File

@@ -1,5 +1,5 @@
// file: kb-app-demo-desktop/frontend/ts/demo_execution_metadata.ts
// version: 12
// version: 14
import * as bootstrap from "bootstrap";
import "simplebar";
@@ -7,30 +7,32 @@ 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 { DemoExecutionMetadataMetaplexTokenMetadataPreparedStepPayload } from "./bindings/kb_app_demo_desktop/demo_execution_metadata/DemoExecutionMetadataMetaplexTokenMetadataPreparedStepPayload.ts";
import type { DemoExecutionMetadataMetaplexTokenMetadataRequest } from "./bindings/kb_app_demo_desktop/demo_execution_metadata/DemoExecutionMetadataMetaplexTokenMetadataRequest.ts";
import type { DemoExecutionMetadataMetaplexTokenMetadataSummaryPayload } from "./bindings/kb_app_demo_desktop/demo_execution_metadata/DemoExecutionMetadataMetaplexTokenMetadataSummaryPayload.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[] = [];
let preparedScenarioId: string | null = null;
let preparedStepIndex: number | null = null;
const metadataLogLines: string[] = [];
const maximumMetadataLogLines = 1200;
@@ -57,10 +59,10 @@ function element<T extends HTMLElement>(selector: string): T {
return value;
}
const profileSelectSelectors = ["#metadataProfileSelect", "#solanaProgramMetadataProfileSelect"] as const;
const profileHelpSelectors = ["#metadataProfileHelp", "#solanaProgramMetadataProfileHelp"] as const;
const profileStatusSelectors = ["#metadataProfileStatusBadge", "#solanaProgramMetadataProfileStatusBadge"] as const;
const profileOutputSelectors = ["#metadataProfileOutput", "#solanaProgramMetadataProfileOutput"] as const;
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) {
@@ -80,50 +82,19 @@ function updateProfileStatus(text: string, className: string): void {
}
}
function activeSelect(): HTMLSelectElement {
return document.querySelector("#metaplexDevnetCollapse.show") !== null ? element<HTMLSelectElement>("#metadataDevnetScenarioSelect") : element<HTMLSelectElement>("#metadataSyntheticScenarioSelect");
}
function selectedScenario(): DemoExecutionMetadataMetaplexTokenMetadataScenarioPayload | null {
const id = activeSelect().value;
const id = element<HTMLSelectElement>("#metadataSyntheticScenarioSelect").value;
return scenarios.find(scenario => scenario.id === id) ?? null;
}
function fillSelect(selector: string, mode: string): void {
const select = element<HTMLSelectElement>(selector);
select.replaceChildren();
for (const scenario of scenarios.filter(candidate => candidate.mode === mode)) {
const option = document.createElement("option");
option.value = scenario.id;
option.textContent = `${scenario.assetFamily}${scenario.label}`;
select.append(option);
}
function selectedMetaplexCampaign(): DemoExecutionMetadataMetaplexCampaignOptionPayload | null {
const id = element<HTMLSelectElement>("#metadataMetaplexCampaignSelect").value;
return metaplexCampaigns.find(campaign => campaign.id === id) ?? null;
}
function operationName(operationCode: string): string {
const parts = operationCode.split(".");
return parts.length === 0 ? operationCode : parts[parts.length - 1] ?? operationCode;
}
function renderScenarioSteps(scenario: DemoExecutionMetadataMetaplexTokenMetadataScenarioPayload): void {
const select = element<HTMLSelectElement>("#metadataStepSelect");
const previousValue = Number.parseInt(select.value, 10);
select.replaceChildren();
scenario.operationCodes.forEach((operationCode, index) => {
const option = document.createElement("option");
option.value = String(index);
option.textContent = `${index + 1}. ${operationName(operationCode)}`;
select.append(option);
});
if (Number.isInteger(previousValue) && previousValue >= 0 && previousValue < scenario.operationCodes.length) {
select.value = String(previousValue);
}
const index = Number.parseInt(select.value, 10);
element<HTMLButtonElement>("#previousMetadataStepButton").disabled = !Number.isInteger(index) || index <= 0;
element<HTMLButtonElement>("#nextMetadataStepButton").disabled = !Number.isInteger(index) || index >= scenario.operationCodes.length - 1;
element<HTMLButtonElement>("#prepareMetadataScenarioStepButton").textContent = Number.isInteger(index) && scenario.operationCodes[index] !== undefined
? `Préparer létape ${index + 1}${operationName(scenario.operationCodes[index])}`
: "Préparer létape courante";
function selectedSolanaProgramMetadataScenario(): DemoExecutionMetadataSolanaProgramScenarioPayload | null {
const id = element<HTMLSelectElement>("#solanaProgramMetadataScenarioSelect").value;
return solanaProgramMetadataScenarios.find(scenario => scenario.id === id) ?? null;
}
function renderSelectedScenario(): void {
@@ -132,95 +103,62 @@ function renderSelectedScenario(): void {
return;
}
window.localStorage.setItem(storageKey, scenario.id);
const networkMode = scenario.mode !== "synthetic";
renderScenarioSteps(scenario);
preparedScenarioId = null;
preparedStepIndex = null;
element<HTMLTextAreaElement>("#metadataOperationJson").value = "";
element<HTMLTextAreaElement>("#metadataPreflightReadsJson").value = "[]";
element<HTMLTextAreaElement>("#metadataPostconditionReadsJson").value = "[]";
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">Profil</dt><dd class="col-6"><code>${networkMode ? selectedProfileName ?? "non chargé" : "non applicable"}</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("Préflight et opérations", {
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", {
mode: scenario.mode,
policy: "simulation_first",
networkExecutionPerformed: false,
automaticSubmission: false,
policy: "specialized_campaign_simulation_first",
operatorConfirmationRequired: true,
expectedState: scenario.validationStatus,
note: networkMode ? "Renseignez un intent typé puis lancez une simulation RPC réelle." : "Scénario synthétique déterministe.",
stopOnFirstFailure: true,
isolatedUnavailableSubmission: false,
qualification: campaign.qualification,
networkExecutionPerformed: false,
}));
renderJsonViewer("#metadataPostconditionsOutput", titled("Postconditions et preuves", {
required: scenario.requiresPostcondition,
evidenceKinds: scenario.requiredEvidence,
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,
materializationTargets: scenario.materializationTargets,
rule: "Aucune validation réseau n'est déclarée sans simulation RPC réelle, signature éventuelle, slot et postconditions.",
}));
}
async function prepareScenarioStep(): Promise<void> {
const profileName = element<HTMLSelectElement>("#metadataProfileSelect").value;
const scenario = selectedScenario();
const stepIndex = Number.parseInt(element<HTMLSelectElement>("#metadataStepSelect").value, 10);
if (profileName.length === 0) { throw new Error("Sélectionnez un profil Devnet."); }
if (scenario === null || scenario.mode === "synthetic") { throw new Error("Sélectionnez un scénario Devnet."); }
if (!Number.isInteger(stepIndex) || stepIndex < 0) { throw new Error("Sélectionnez une étape valide."); }
appendMetadataLog({ timestamp: new Date().toISOString(), level: "info", stage: "fixture", message: `Préparation de ${scenario.id} étape ${stepIndex + 1}` });
const prepared = await invoke<DemoExecutionMetadataMetaplexTokenMetadataPreparedStepPayload>("demo_execution_metadata_metaplex_token_metadata_prepare_step", {
profileName,
scenarioId: scenario.id,
stepIndex,
previousOperationJson: element<HTMLTextAreaElement>("#metadataOperationJson").value.trim().length === 0
? null
: element<HTMLTextAreaElement>("#metadataOperationJson").value,
});
element<HTMLTextAreaElement>("#metadataOperationJson").value = prepared.operationJson;
element<HTMLTextAreaElement>("#metadataPreflightReadsJson").value = prepared.preflightReadsJson;
element<HTMLTextAreaElement>("#metadataPostconditionReadsJson").value = prepared.postconditionReadsJson;
preparedScenarioId = prepared.scenarioId;
preparedStepIndex = prepared.stepIndex;
renderJsonViewer("#metadataPreflightOutput", titled("Étape de scénario préparée", {
scenarioId: prepared.scenarioId,
stepIndex: prepared.stepIndex,
operation: prepared.operation,
fixtureState: prepared.fixtureState,
preflightReads: JSON.parse(prepared.preflightReadsJson),
postconditionReads: JSON.parse(prepared.postconditionReadsJson),
simulationReady: prepared.simulationReady,
submissionReady: prepared.submissionReady,
}));
appendMetadataLog({ timestamp: new Date().toISOString(), level: "info", stage: "fixture", message: `${prepared.label} prête (${prepared.fixtureState})` });
setExecutionRunning(executionRunning);
}
function moveScenarioStep(offset: number): void {
const select = element<HTMLSelectElement>("#metadataStepSelect");
const current = Number.parseInt(select.value, 10);
const next = current + offset;
if (Number.isInteger(next) && next >= 0 && next < select.options.length) {
select.value = String(next);
const scenario = selectedScenario();
if (scenario !== null) { renderScenarioSteps(scenario); }
}
}
function parseJsonText(value: string, label: string): unknown {
try {
return JSON.parse(value);
} catch (caught) {
const message = caught instanceof Error ? caught.message : String(caught);
throw new Error(`${label} invalide : ${message}`);
}
}
function selectedProfile(): DemoExecutionSolanaCoreProfileOption | null {
if (selectedProfileName === null) {
return null;
@@ -241,92 +179,158 @@ function updateProfileHelp(): void {
function setExecutionRunning(value: boolean): void {
executionRunning = value;
const profile = selectedProfile();
const missingOperation = element<HTMLTextAreaElement>("#metadataOperationJson").value.trim().length === 0;
const unavailable = value || profile === null || !profileStoreReady || missingOperation;
element<HTMLButtonElement>("#simulateMetadataButton").disabled = unavailable;
element<HTMLButtonElement>("#submitMetadataButton").disabled = unavailable || !profile?.sendEnabled;
element<HTMLButtonElement>("#prepareMetadataScenarioStepButton").disabled = value || profile === null || !profileStoreReady;
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 executeMetadata(submit: boolean): Promise<void> {
async function executeMetaplexCampaign(): Promise<void> {
if (executionRunning) {
return;
}
const profileName = element<HTMLSelectElement>("#metadataProfileSelect").value;
if (profileName.length === 0 || !profileStoreReady) {
const profile = selectedProfile();
const campaign = selectedMetaplexCampaign();
if (profile === null || selectedProfileName === null || !profileStoreReady) {
throw new Error("Sélectionnez et préparez un profil Devnet.");
}
const operationJson = element<HTMLTextAreaElement>("#metadataOperationJson").value;
const scenario = selectedScenario();
const stepIndex = Number.parseInt(element<HTMLSelectElement>("#metadataStepSelect").value, 10);
if (operationJson.trim().length === 0) {
throw new Error("Préparez létape courante avant lexécution.");
if (campaign === null) {
throw new Error("Sélectionnez une campagne Metaplex qualifiée.");
}
if (scenario === null || preparedScenarioId !== scenario.id || preparedStepIndex !== stepIndex) {
throw new Error("Préparez de nouveau létape après toute modification du scénario ou de létape.");
if (!profile.sendEnabled) {
throw new Error("Le profil sélectionné nautorise pas les soumissions Devnet requises par les fixtures Metaplex.");
}
const preflightReadsJson = element<HTMLTextAreaElement>("#metadataPreflightReadsJson").value;
const postconditionReadsJson = element<HTMLTextAreaElement>("#metadataPostconditionReadsJson").value;
parseJsonText(operationJson, "Intent Metaplex");
parseJsonText(preflightReadsJson, "Lectures de préflight");
parseJsonText(postconditionReadsJson, "Lectures de postcondition");
const operatorConfirmed = element<HTMLInputElement>("#metadataOperatorConfirmed").checked;
if (submit && !operatorConfirmed) {
throw new Error("La soumission exige la confirmation explicite de lopérateur.");
const operatorConfirmed = element<HTMLInputElement>("#metadataMetaplexCampaignOperatorConfirmed").checked;
if (!operatorConfirmed) {
throw new Error("La campagne Metaplex exige la confirmation explicite de lopérateur.");
}
const request: DemoExecutionMetadataMetaplexTokenMetadataRequest = {
profileName,
intentId: `desktop-metaplex-${Date.now()}`,
operationJson,
preflightReadsJson,
postconditionReadsJson,
submit,
const request: DemoExecutionMetadataMetaplexCampaignRequest = {
profileName: selectedProfileName,
campaignId: campaign.id,
operatorConfirmed,
materializeAfterConfirmation: element<HTMLInputElement>("#metadataMaterializeInput").checked,
};
setExecutionRunning(true);
const badge = element<HTMLElement>("#metadataStatusBadge");
badge.textContent = submit ? "Soumission" : "Simulation";
badge.textContent = "Campagne Metaplex";
badge.className = "badge text-bg-warning";
appendMetadataLog({ timestamp: new Date().toISOString(), level: "info", stage: submit ? "submit" : "simulation", message: submit ? "Démarrage du parcours Devnet complet." : "Démarrage de la simulation RPC exacte." });
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<DemoExecutionMetadataMetaplexTokenMetadataSummaryPayload>("demo_execution_metadata_metaplex_token_metadata_execute", { request });
renderJsonViewer("#metadataPreflightOutput", titled("Préflight et opérations", JSON.parse(summary.preflightJson)));
renderJsonViewer("#metadataSimulationOutput", titled("Simulation et confirmation", {
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,
cluster: summary.cluster,
genesisHash: summary.genesisHash,
walletPublicKey: summary.walletPublicKey,
balanceLamports: summary.balanceLamports,
simulationSuccess: summary.simulationSuccess,
transactionSignature: summary.transactionSignature,
confirmationStatus: summary.confirmationStatus,
materializationRequested: summary.materializationRequested,
materializedSnapshotCount: summary.materializedSnapshotCount,
plan: JSON.parse(summary.planJson),
simulation: JSON.parse(summary.simulationJson),
campaignId: summary.campaignId,
label: summary.label,
operationCount: summary.operationCount,
confirmedOperationCount: summary.confirmedOperationCount,
unavailableOperationCount: summary.unavailableOperationCount,
completed: summary.completed,
}));
renderJsonViewer("#metadataPostconditionsOutput", titled("Postconditions et preuves", JSON.parse(summary.evidenceJson)));
badge.textContent = summary.transactionSignature === null ? "Simulé" : "Confirmé";
badge.className = summary.simulationSuccess ? "badge text-bg-success" : "badge text-bg-danger";
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 selectedSolanaProgramMetadataScenario(): DemoExecutionMetadataSolanaProgramScenarioPayload | null {
const id = element<HTMLSelectElement>("#solanaProgramMetadataScenarioSelect").value;
return solanaProgramMetadataScenarios.find(scenario => scenario.id === id) ?? null;
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,
}));
}
function renderActiveMetadataSelection(): void {
if (document.querySelector("#solanaProgramMetadataCollapse.show") !== null) {
renderSolanaProgramMetadataScenario();
async function executeToken2022MetadataCampaign(): Promise<void> {
if (executionRunning) {
return;
}
renderSelectedScenario();
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 {
@@ -340,26 +344,29 @@ function renderSolanaProgramMetadataScenario(): void {
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>`;
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"],
currentStatus: "not_run",
desktopValidation: "confirmed_pre_014",
networkEvidenceCollected: false,
}));
}
@@ -414,6 +421,22 @@ async function executeSolanaProgramMetadataCampaign(): Promise<void> {
}
}
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;
@@ -481,15 +504,31 @@ async function loadScenarios(): Promise<void> {
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");
fillSelect("#metadataSyntheticScenarioSelect", "synthetic");
fillSelect("#metadataDevnetScenarioSelect", "network_simulation");
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);
for (const selector of ["#metadataSyntheticScenarioSelect", "#metadataDevnetScenarioSelect"]) {
const select = element<HTMLSelectElement>(selector);
if (restored !== null && Array.from(select.options).some(option => option.value === restored)) {
select.value = restored;
}
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();
@@ -504,8 +543,10 @@ async function loadScenarios(): Promise<void> {
solanaSelect.value = restoredSolana;
}
renderActiveMetadataSelection();
badge.textContent = `${scenarios.length + solanaProgramMetadataScenarios.length} contrats`;
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;
@@ -518,12 +559,14 @@ async function loadScenarios(): Promise<void> {
document.addEventListener("DOMContentLoaded", () => {
installFrontendConsoleBridge("kb-app-demo-desktop.frontend.execution_metadata");
for (const selector of ["#metadataSyntheticScenarioSelect", "#metadataDevnetScenarioSelect"]) {
element<HTMLSelectElement>(selector).addEventListener("change", renderSelectedScenario);
}
for (const id of ["metaplexSyntheticCollapse", "metaplexDevnetCollapse"]) {
element<HTMLElement>(`#${id}`).addEventListener("shown.bs.collapse", renderSelectedScenario);
}
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) {
@@ -538,27 +581,18 @@ document.addEventListener("DOMContentLoaded", () => {
});
}
element<HTMLButtonElement>("#prepareMetadataProfileButton").addEventListener("click", () => void prepareProfile());
element<HTMLButtonElement>("#prepareToken2022MetadataProfileButton").addEventListener("click", () => void prepareProfile());
element<HTMLButtonElement>("#prepareSolanaProgramMetadataProfileButton").addEventListener("click", () => void prepareProfile());
element<HTMLButtonElement>("#refreshMetadataScenariosButton").addEventListener("click", () => void loadScenarios());
element<HTMLSelectElement>("#metadataStepSelect").addEventListener("change", () => {
const scenario = selectedScenario();
if (scenario !== null) { renderScenarioSteps(scenario); }
});
element<HTMLButtonElement>("#previousMetadataStepButton").addEventListener("click", () => moveScenarioStep(-1));
element<HTMLButtonElement>("#nextMetadataStepButton").addEventListener("click", () => moveScenarioStep(1));
element<HTMLTextAreaElement>("#metadataOperationJson").addEventListener("input", () => {
preparedScenarioId = null;
preparedStepIndex = null;
setExecutionRunning(executionRunning);
});
element<HTMLButtonElement>("#prepareMetadataScenarioStepButton").addEventListener("click", () => { void prepareScenarioStep().catch(caught => frontendError("kb-app-demo-desktop.frontend.execution_metadata", caught instanceof Error ? caught.message : String(caught))); });
element<HTMLTextAreaElement>("#metadataExecutionLogOutput").addEventListener("app-log-clear", () => { metadataLogLines.length = 0; });
void listen<DemoExecutionMetadataProgressPayload>("demo-execution-metadata-progress", event => appendMetadataLog(event.payload));
element<HTMLButtonElement>("#simulateMetadataButton").addEventListener("click", () => {
void executeMetadata(false).catch(caught => frontendError("kb-app-demo-desktop.frontend.execution_metadata", caught instanceof Error ? caught.message : String(caught)));
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<HTMLButtonElement>("#submitMetadataButton").addEventListener("click", () => {
void executeMetadata(true).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", () => {