v0.3.8-pre.003

This commit is contained in:
2026-09-03 11:39:09 +02:00
parent 74890a6268
commit 9300dccbe2
22 changed files with 1109 additions and 40 deletions

View File

@@ -1,12 +1,12 @@
# file: Cargo.toml
# version: 447
# version: 448
[workspace]
resolver = "3"
members = ["crates/ksp-app-backfill-desk", "crates/ksp-app-config-desk", "crates/ksp-app-solprices-desk", "crates/ksp-app-store-desk", "crates/ksp-app-wallet-desk", "crates/ksp-config-lib", "crates/ksp-core-lib", "crates/ksp-interface-lib", "crates/ksp-job-api", "crates/ksp-job-backfill-lib", "crates/ksp-logging-lib", "crates/ksp-offchain-transport-lib", "crates/ksp-onchain-transport-lib", "crates/ksp-program-api", "crates/ksp-store-api", "crates/ksp-store-lib", "crates/ksp-store-postgres-lib", "crates/ksp-wallet-lib"]
[workspace.package]
version = "0.3.8-pre.2.fix.1"
version = "0.3.8-pre.3"
edition = "2024"
license = "MIT"
repository = "https://git.sasedev.com/Sasedev/khadhroony-solana-project"

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-app-store-desk/frontend/ts/main.ts
// version: 1
// version: 2
import DataTable from "datatables.net-bs5";
import "datatables.net-bs5/css/dataTables.bootstrap5.css";
@@ -130,13 +130,41 @@ function installInteractions(): void {
frontendDebug("main", "Store Desk diagnostics refresh button clicked");
void refreshDiagnostics();
});
document.querySelectorAll<HTMLButtonElement>("button").forEach(button => {
if (button.dataset.view || button.id === "refreshDiagnostics") {
document.addEventListener("click", event => {
const eventTarget = event.target;
if (!(eventTarget instanceof Element)) {
return;
}
button.addEventListener("click", () => frontendDebug("main", "Store Desk generic button clicked", { buttonId: button.id || "anonymous" }));
const control = eventTarget.closest<HTMLElement>("button, [role='tab'], [data-bs-toggle='tab']");
if (!control) {
return;
}
if (control instanceof HTMLButtonElement && (control.dataset.view || control.id === "refreshDiagnostics")) {
return;
}
const controlId = control.id || control.getAttribute("data-bs-target") || control.getAttribute("aria-controls") || "anonymous";
if (control.matches("[role='tab'], [data-bs-toggle='tab']")) {
frontendDebug("main", "Store Desk tab control clicked", { controlId });
return;
}
frontendDebug("main", "Store Desk generic button clicked", { buttonId: controlId });
});
frontendTrace("main", "Store Desk frontend interactions installed");
document.addEventListener("shown.bs.tab", event => {
const target = event.target;
const tabId = target instanceof HTMLElement ? target.id || target.getAttribute("data-bs-target") || target.textContent?.trim() || "anonymous" : "unknown";
frontendDebug("main", "Store Desk tab activated", { tabId });
});
document.addEventListener("change", event => {
const target = event.target;
if (!(target instanceof HTMLInputElement || target instanceof HTMLSelectElement || target instanceof HTMLTextAreaElement)) {
return;
}
frontendDebug("main", "Store Desk interactive control changed", {
controlId: target.id || target.getAttribute("name") || "anonymous",
controlType: target instanceof HTMLSelectElement ? "select" : target.type || "text",
});
});
frontendTrace("main", "Store Desk frontend interactions installed", { buttons: true, controls: true, tabs: true });
}
async function initializeMain(): Promise<void> {

View File

@@ -1,7 +1,7 @@
// file: crates/ksp-app-store-desk/src/bootstrap.rs
// version: 1
// version: 2
//! Transient standard Config and Logging bootstrap for Store Desk scaffold.
//! Standard Config and verbose development Logging bootstrap for Store Desk.
/// Crate-internal Logging startup state shared by the application state.
pub(crate) struct LoggingStartup {
@@ -31,7 +31,7 @@ pub(crate) fn config_management(arguments: &[std::ffi::OsString]) -> ksp_core_li
return std::result::Result::Ok(ksp_config_lib::ConfigManagement::new(engine));
}
/// Initializes Logging from the standard Config document, with a trace-level in-memory fallback.
/// Initializes development Logging from the standard `supertrace` profile, with a trace-level in-memory fallback.
pub(crate) fn initialize_logging(
management: &ksp_config_lib::ConfigManagement,
runtime_identity: &ksp_logging_lib::LoggingRuntimeIdentity,
@@ -41,7 +41,7 @@ pub(crate) fn initialize_logging(
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return initialize_fallback_logging(error, runtime_identity),
};
let resolved = management.engine().load_resolved_logging_config(std::option::Option::None, &environment);
let resolved = management.engine().load_resolved_logging_config(std::option::Option::Some(crate::DEVELOPMENT_LOGGING_PROFILE), &environment);
let resolved = match resolved {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return initialize_fallback_logging(error, runtime_identity),
@@ -55,7 +55,7 @@ pub(crate) fn initialize_logging(
target: crate::TRACING_TARGET,
domain = crate::TRACING_DOMAIN_BOOTSTRAP,
active_profile = active_profile_id.as_str(),
"initialized Store Desk scaffold logging from standard Config"
"initialized Store Desk development logging from explicit supertrace Config profile"
);
std::result::Result::Ok(crate::LoggingStartup {
guard,

View File

@@ -1,8 +1,10 @@
// file: crates/ksp-app-store-desk/src/constants.rs
// version: 1
// version: 2
//! Application-owned tracing targets and domains.
/// Logging profile used while Store Desk is under active development.
pub(crate) const DEVELOPMENT_LOGGING_PROFILE: &str = "supertrace";
/// Structured domain used while bootstrapping Config and Logging.
pub(crate) const TRACING_DOMAIN_BOOTSTRAP: &str = "store.bootstrap";
/// Structured domain used by technical frontend events.

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-app-store-desk/src/lib.rs
// version: 1
// version: 2
//! Tauri desktop scaffold for backend-neutral KSP Store inspection.
@@ -28,8 +28,10 @@ pub(crate) use self::app_state::AppState;
pub(crate) use self::bootstrap::LoggingStartup;
/// Builds the Config management facade from the common KSP CLI bootstrap contract.
pub(crate) use self::bootstrap::config_management;
/// Initializes Logging from the standard Config document for the scaffold.
/// Initializes verbose development Logging from the standard Config document.
pub(crate) use self::bootstrap::initialize_logging;
/// Logging profile used while Store Desk is under active development.
pub(crate) use self::constants::DEVELOPMENT_LOGGING_PROFILE;
/// Structured domain used while bootstrapping Config and Logging.
pub(crate) use self::constants::TRACING_DOMAIN_BOOTSTRAP;
/// Structured domain used by technical frontend events.

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-app-store-desk/tests/desktop_security.rs
// version: 2
// version: 3
//! Security boundary tests for the Store Desk scaffold.
@@ -41,13 +41,27 @@ fn pre_002_frontend_avoids_network_browser_storage_and_blocking_dialogs() {
}
#[test]
fn pre_002_frontend_tracing_covers_navigation_buttons_refresh_and_ipc() {
fn pre_003_frontend_tracing_covers_navigation_buttons_tabs_controls_refresh_and_ipc() {
let main = include_str!("../frontend/ts/main.ts");
let invoke = include_str!("../frontend/ts/invoke.ts");
assert!(main.contains("navigation button clicked"));
assert!(main.contains("diagnostics refresh button clicked"));
assert!(main.contains("generic button clicked"));
assert!(main.contains("tab control clicked"));
assert!(main.contains("tab activated"));
assert!(main.contains("interactive control changed"));
assert!(main.contains("view activated"));
assert!(invoke.contains("IPC command requested"));
assert!(invoke.contains("IPC command completed"));
assert!(invoke.contains("IPC command failed"));
}
#[test]
fn pre_003_store_desk_development_logging_explicitly_selects_supertrace() {
let constants = include_str!("../src/constants.rs");
let bootstrap = include_str!("../src/bootstrap.rs");
assert!(constants.contains("DEVELOPMENT_LOGGING_PROFILE: &str = \"supertrace\""));
assert!(bootstrap.contains("std::option::Option::Some(crate::DEVELOPMENT_LOGGING_PROFILE)"));
assert!(bootstrap.contains("LogFilterLevel::Trace"));
return;
}

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-store-api/src/capability/raw_account.rs
// version: 2
// version: 3
/// Read capability for complete canonical RAW account states.
///
@@ -24,6 +24,20 @@ pub trait RawAccountStateRead: std::marker::Send + std::marker::Sync {
) -> crate::StoreApiFuture<'a, crate::Result<crate::RawPage<crate::RawAccountStateReference>>>;
}
/// Read capability for data-free random-access RAW account-state inspection.
///
/// This capability is intended for bounded operator/admin inspection. It must
/// return exact logical counts and summaries only; complete account bytes stay
/// behind [`RawAccountStateRead::get_raw_account_state`]. Worker, replay and
/// backfill traversal continue to use the opaque-cursor list operation.
pub trait RawAccountStateInspectionRead: std::marker::Send + std::marker::Sync {
/// Inspects one random-access account-state window with exact logical counts.
fn inspect_raw_account_states<'a>(
&'a self,
query: &'a crate::RawAccountStateInspectionQuery,
) -> crate::StoreApiFuture<'a, crate::Result<crate::RawInspectionPage<crate::RawAccountStateSummary>>>;
}
/// Write capability for complete RAW account-state acquisitions.
///
/// The account state and its acquisition observation form one logical

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-store-api/src/capability/raw_transaction.rs
// version: 2
// version: 3
/// Read capability for canonical RAW transactions.
///
@@ -24,6 +24,20 @@ pub trait RawTransactionRead: std::marker::Send + std::marker::Sync {
) -> crate::StoreApiFuture<'a, crate::Result<crate::RawPage<crate::RawTransactionReference>>>;
}
/// Read capability for payload-free random-access RAW transaction inspection.
///
/// This capability is intended for bounded operator/admin inspection. It must
/// return exact logical counts and summaries only; canonical payload bytes stay
/// behind [`RawTransactionRead::get_raw_transaction`]. Worker, replay and
/// backfill traversal continue to use the opaque-cursor list operation.
pub trait RawTransactionInspectionRead: std::marker::Send + std::marker::Sync {
/// Inspects one random-access transaction window with exact logical counts.
fn inspect_raw_transactions<'a>(
&'a self,
query: &'a crate::RawTransactionInspectionQuery,
) -> crate::StoreApiFuture<'a, crate::Result<crate::RawInspectionPage<crate::RawTransactionSummary>>>;
}
/// Write capability for canonical RAW transaction acquisitions.
///
/// The transaction and its acquisition observation form one logical persistence

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-store-api/src/lib.rs
// version: 5
// version: 6
#![warn(missing_docs)]
#![deny(unreachable_pub)]
@@ -26,6 +26,8 @@ pub use self::capability::StoreApiFuture;
pub use self::capability::raw_account::RawAccountObservationRead;
/// Write capability for additional observations of already persisted RAW account states.
pub use self::capability::raw_account::RawAccountObservationWrite;
/// Read capability for data-free random-access RAW account-state inspection.
pub use self::capability::raw_account::RawAccountStateInspectionRead;
/// Read capability for complete canonical RAW account states.
pub use self::capability::raw_account::RawAccountStateRead;
/// Write capability for complete canonical RAW account-state acquisitions.
@@ -34,6 +36,8 @@ pub use self::capability::raw_account::RawAccountStateWrite;
pub use self::capability::raw_retention::RawTransactionRetentionRead;
/// Write capability for policy-authorized RAW transaction retention transitions.
pub use self::capability::raw_retention::RawTransactionRetentionWrite;
/// Read capability for payload-free random-access RAW transaction inspection.
pub use self::capability::raw_transaction::RawTransactionInspectionRead;
/// Read capability for persisted RAW transaction observations.
pub use self::capability::raw_transaction::RawTransactionObservationRead;
/// Write capability for additional observations of already persisted RAW transactions.
@@ -60,6 +64,18 @@ pub use self::model::raw_account::RawAccountObservation;
pub use self::model::raw_account::RawAccountState;
/// Durable backend-independent identity of one canonical RAW account state.
pub use self::model::raw_account::RawAccountStateReference;
/// Backend-independent random-access inspection query for RAW account states.
pub use self::model::raw_inspection::RawAccountStateInspectionQuery;
/// Data-free summary of one canonical RAW account state for operator inspection.
pub use self::model::raw_inspection::RawAccountStateSummary;
/// One bounded random-access inspection page with exact logical counts.
pub use self::model::raw_inspection::RawInspectionPage;
/// Random-access page request dedicated to bounded interactive RAW inspection.
pub use self::model::raw_inspection::RawInspectionPageRequest;
/// Backend-independent random-access inspection query for RAW transactions.
pub use self::model::raw_inspection::RawTransactionInspectionQuery;
/// Payload-free summary of one canonical RAW transaction for operator inspection.
pub use self::model::raw_inspection::RawTransactionSummary;
/// Combined outcome of one atomic canonical RAW entity plus observation acquisition.
pub use self::model::raw_outcome::RawAcquisitionWriteOutcome;
/// Outcome for one canonical RAW entity in an idempotent persistence operation.

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-store-api/src/model.rs
// version: 4
// version: 5
//! Private home for persistent Store models.
//!
@@ -9,6 +9,7 @@
//! one or more models.
pub(crate) mod raw_account;
pub(crate) mod raw_inspection;
pub(crate) mod raw_outcome;
pub(crate) mod raw_pagination;
pub(crate) mod raw_primitives;

View File

@@ -0,0 +1,366 @@
// file: crates/ksp-store-api/src/model/raw_inspection.rs
// version: 1
/// Random-access page request dedicated to bounded interactive RAW inspection.
///
/// This request is intentionally distinct from [`crate::RawPageRequest`]. It
/// provides offset/limit semantics for operator-facing inspection while the
/// canonical RAW list APIs keep their opaque backend cursors for replay,
/// backfill and worker traversal.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct RawInspectionPageRequest {
limit: crate::RawPageLimit,
offset: u64,
}
impl RawInspectionPageRequest {
/// Creates one inspection request from an absolute zero-based offset and positive page limit.
#[must_use]
pub const fn new(offset: u64, limit: crate::RawPageLimit) -> Self {
return Self { limit, offset };
}
/// Returns the exact caller-requested page size.
#[must_use]
pub const fn limit(&self) -> crate::RawPageLimit {
return self.limit;
}
/// Returns the absolute zero-based item offset within the filtered result set.
#[must_use]
pub const fn offset(&self) -> u64 {
return self.offset;
}
}
/// One bounded random-access inspection page with exact logical counts.
///
/// `total_items` is the exact count inside the mandatory query scope before
/// optional filters. `filtered_items` is the exact count after optional query
/// filters. The item vector contains only the requested page window.
#[derive(Debug)]
pub struct RawInspectionPage<T> {
filtered_items: u64,
items: std::vec::Vec<T>,
total_items: u64,
}
impl<T> RawInspectionPage<T> {
/// Creates one inspection page after validating count consistency.
pub fn try_new(items: std::vec::Vec<T>, total_items: u64, filtered_items: u64) -> crate::Result<Self> {
if filtered_items > total_items {
return std::result::Result::Err(raw_model_error("filtered_items"));
}
let item_count = u64::try_from(items.len());
let item_count = match item_count {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return std::result::Result::Err(raw_model_error("items")),
};
if item_count > filtered_items {
return std::result::Result::Err(raw_model_error("items"));
}
return std::result::Result::Ok(Self { filtered_items, items, total_items });
}
/// Returns the exact count after optional query filters.
#[must_use]
pub const fn filtered_items(&self) -> u64 {
return self.filtered_items;
}
/// Returns the current inspection page items.
#[must_use]
pub fn items(&self) -> &[T] {
return self.items.as_slice();
}
/// Consumes the page and returns its bounded inspection items.
#[must_use]
pub fn into_items(self) -> std::vec::Vec<T> {
return self.items;
}
/// Returns the exact count before optional query filters inside the mandatory scope.
#[must_use]
pub const fn total_items(&self) -> u64 {
return self.total_items;
}
}
/// Backend-independent random-access inspection query for RAW transactions.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct RawTransactionInspectionQuery {
direction: crate::RawSortDirection,
network: crate::RawNetworkId,
page: crate::RawInspectionPageRequest,
slots: crate::RawSlotRange,
}
impl RawTransactionInspectionQuery {
/// Creates one transaction inspection query.
#[must_use]
pub fn new(network: crate::RawNetworkId, slots: crate::RawSlotRange, direction: crate::RawSortDirection, page: crate::RawInspectionPageRequest) -> Self {
return Self { direction, network, page, slots };
}
/// Returns the requested deterministic traversal direction.
#[must_use]
pub const fn direction(&self) -> crate::RawSortDirection {
return self.direction;
}
/// Returns the mandatory logical network scope.
#[must_use]
pub fn network(&self) -> &crate::RawNetworkId {
return &self.network;
}
/// Returns the random-access inspection page request.
#[must_use]
pub const fn page(&self) -> crate::RawInspectionPageRequest {
return self.page;
}
/// Returns optional inclusive slot filters.
#[must_use]
pub const fn slots(&self) -> crate::RawSlotRange {
return self.slots;
}
}
/// Backend-independent random-access inspection query for RAW account states.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct RawAccountStateInspectionQuery {
direction: crate::RawSortDirection,
network: crate::RawNetworkId,
page: crate::RawInspectionPageRequest,
pubkey: std::option::Option<crate::Pubkey>,
slots: crate::RawSlotRange,
}
impl RawAccountStateInspectionQuery {
/// Creates one account-state inspection query.
#[must_use]
pub fn new(
network: crate::RawNetworkId,
pubkey: std::option::Option<crate::Pubkey>,
slots: crate::RawSlotRange,
direction: crate::RawSortDirection,
page: crate::RawInspectionPageRequest,
) -> Self {
return Self { direction, network, page, pubkey, slots };
}
/// Returns the requested deterministic traversal direction.
#[must_use]
pub const fn direction(&self) -> crate::RawSortDirection {
return self.direction;
}
/// Returns the mandatory logical network scope.
#[must_use]
pub fn network(&self) -> &crate::RawNetworkId {
return &self.network;
}
/// Returns the random-access inspection page request.
#[must_use]
pub const fn page(&self) -> crate::RawInspectionPageRequest {
return self.page;
}
/// Returns an optional exact account-address filter.
#[must_use]
pub fn pubkey(&self) -> std::option::Option<&crate::Pubkey> {
return self.pubkey.as_ref();
}
/// Returns optional inclusive slot filters.
#[must_use]
pub const fn slots(&self) -> crate::RawSlotRange {
return self.slots;
}
}
/// Payload-free summary of one canonical RAW transaction for operator inspection.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct RawTransactionSummary {
block_time: std::option::Option<crate::RawTimestamp>,
content_hash: crate::RawContentHash,
format_id: crate::RawFormatId,
format_version: u32,
payload_size_bytes: std::option::Option<u64>,
reference: crate::RawTransactionReference,
retention_state: crate::RawRetentionState,
slot: u64,
}
impl RawTransactionSummary {
/// Creates one payload-free transaction summary after validating summary invariants.
pub fn try_new(
reference: crate::RawTransactionReference,
slot: u64,
block_time: std::option::Option<crate::RawTimestamp>,
format_id: crate::RawFormatId,
format_version: u32,
content_hash: crate::RawContentHash,
payload_size_bytes: std::option::Option<u64>,
retention_state: crate::RawRetentionState,
) -> crate::Result<Self> {
if format_version == 0 {
return std::result::Result::Err(raw_model_error("format_version"));
}
if let std::option::Option::Some(size) = payload_size_bytes {
let size = usize::try_from(size);
let size = match size {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return std::result::Result::Err(raw_model_error("payload_size_bytes")),
};
if size == 0 || size > crate::MAX_RAW_PAYLOAD_BYTES {
return std::result::Result::Err(raw_model_error("payload_size_bytes"));
}
}
if retention_state == crate::RawRetentionState::Purged {
if payload_size_bytes.is_some() {
return std::result::Result::Err(raw_model_error("payload_size_bytes"));
}
} else if payload_size_bytes.is_none() {
return std::result::Result::Err(raw_model_error("payload_size_bytes"));
}
return std::result::Result::Ok(Self {
block_time,
content_hash,
format_id,
format_version,
payload_size_bytes,
reference,
retention_state,
slot,
});
}
/// Returns the optional canonical block timestamp.
#[must_use]
pub const fn block_time(&self) -> std::option::Option<crate::RawTimestamp> {
return self.block_time;
}
/// Returns the deterministic canonical content digest.
#[must_use]
pub const fn content_hash(&self) -> crate::RawContentHash {
return self.content_hash;
}
/// Returns the KSP-owned canonical RAW format identifier.
#[must_use]
pub fn format_id(&self) -> &crate::RawFormatId {
return &self.format_id;
}
/// Returns the KSP-owned canonical RAW format version.
#[must_use]
pub const fn format_version(&self) -> u32 {
return self.format_version;
}
/// Returns the canonical payload size when payload content is logically retained.
#[must_use]
pub const fn payload_size_bytes(&self) -> std::option::Option<u64> {
return self.payload_size_bytes;
}
/// Returns the durable backend-independent transaction identity.
#[must_use]
pub fn reference(&self) -> &crate::RawTransactionReference {
return &self.reference;
}
/// Returns the logical RAW transaction retention state.
#[must_use]
pub const fn retention_state(&self) -> crate::RawRetentionState {
return self.retention_state;
}
/// Returns the Solana slot containing the transaction.
#[must_use]
pub const fn slot(&self) -> u64 {
return self.slot;
}
}
/// Data-free summary of one canonical RAW account state for operator inspection.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct RawAccountStateSummary {
data_length_bytes: u64,
executable: bool,
lamports: u64,
owner: crate::Pubkey,
reference: crate::RawAccountStateReference,
rent_epoch: u64,
}
impl RawAccountStateSummary {
/// Creates one data-free account-state summary after validating its bounded data length.
pub fn try_new(
reference: crate::RawAccountStateReference,
lamports: u64,
owner: crate::Pubkey,
executable: bool,
rent_epoch: u64,
data_length_bytes: u64,
) -> crate::Result<Self> {
let data_length = usize::try_from(data_length_bytes);
let data_length = match data_length {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return std::result::Result::Err(raw_model_error("data_length_bytes")),
};
if data_length > crate::MAX_RAW_ACCOUNT_DATA_BYTES {
return std::result::Result::Err(raw_model_error("data_length_bytes"));
}
return std::result::Result::Ok(Self { data_length_bytes, executable, lamports, owner, reference, rent_epoch });
}
/// Returns the complete canonical account-data length without exposing account bytes.
#[must_use]
pub const fn data_length_bytes(&self) -> u64 {
return self.data_length_bytes;
}
/// Returns whether the account is executable.
#[must_use]
pub const fn executable(&self) -> bool {
return self.executable;
}
/// Returns the account lamport balance.
#[must_use]
pub const fn lamports(&self) -> u64 {
return self.lamports;
}
/// Returns the account owner program public key.
#[must_use]
pub const fn owner(&self) -> &crate::Pubkey {
return &self.owner;
}
/// Returns the durable backend-independent account-state identity.
#[must_use]
pub fn reference(&self) -> &crate::RawAccountStateReference {
return &self.reference;
}
/// Returns the rent epoch reported for this account state.
#[must_use]
pub const fn rent_epoch(&self) -> u64 {
return self.rent_epoch;
}
}
fn raw_model_error(field: &'static str) -> crate::Error {
return crate::Error::new(crate::ERROR_CODE_RAW_MODEL_INVALID, "invalid backend-agnostic RAW inspection model").with_context("field", field);
}
#[cfg(test)]
#[path = "../../unit_tests/model/raw_inspection.rs"]
mod tests;

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-store-api/tests/dependency_boundary.rs
// version: 5
// version: 6
//! Dependency canaries for the Store API RAW foundation.
@@ -53,6 +53,7 @@ fn pre_006_source_boundary_keeps_models_and_capabilities_backend_free() {
let crate_root = include_str!("../src/lib.rs");
let model_home = include_str!("../src/model.rs");
let raw_account = include_str!("../src/model/raw_account.rs");
let raw_inspection = include_str!("../src/model/raw_inspection.rs");
let raw_outcome = include_str!("../src/model/raw_outcome.rs");
let raw_pagination = include_str!("../src/model/raw_pagination.rs");
let raw_primitives = include_str!("../src/model/raw_primitives.rs");
@@ -66,6 +67,7 @@ fn pre_006_source_boundary_keeps_models_and_capabilities_backend_free() {
assert!(crate_root.contains("mod error;"));
assert!(crate_root.contains("mod model;"));
assert!(model_home.contains("raw_account"));
assert!(model_home.contains("raw_inspection"));
assert!(model_home.contains("raw_outcome"));
assert!(model_home.contains("raw_pagination"));
assert!(model_home.contains("raw_primitives"));
@@ -78,6 +80,7 @@ fn pre_006_source_boundary_keeps_models_and_capabilities_backend_free() {
crate_root,
model_home,
raw_account,
raw_inspection,
raw_outcome,
raw_pagination,
raw_primitives,
@@ -109,12 +112,14 @@ fn pre_006_source_boundary_keeps_models_and_capabilities_backend_free() {
assert!(!crate_root.contains(forbidden), "deferred pre.006 model leaked into Store API surface: {forbidden}");
}
assert!(raw_transaction_capability.contains("trait RawTransactionRead"));
assert!(raw_transaction_capability.contains("trait RawTransactionInspectionRead"));
assert!(raw_transaction_capability.contains("trait RawTransactionWrite"));
assert!(raw_transaction_capability.contains("trait RawTransactionObservationRead"));
assert!(raw_transaction_capability.contains("trait RawTransactionObservationWrite"));
assert!(raw_retention_capability.contains("trait RawTransactionRetentionRead"));
assert!(raw_retention_capability.contains("trait RawTransactionRetentionWrite"));
assert!(raw_account_capability.contains("trait RawAccountStateRead"));
assert!(raw_account_capability.contains("trait RawAccountStateInspectionRead"));
assert!(raw_account_capability.contains("trait RawAccountStateWrite"));
assert!(raw_account_capability.contains("trait RawAccountObservationRead"));
assert!(raw_account_capability.contains("trait RawAccountObservationWrite"));
@@ -126,6 +131,9 @@ fn pre_006_source_boundary_keeps_models_and_capabilities_backend_free() {
}
assert!(!raw_pagination.contains("u64::MAX"));
assert!(!raw_pagination.contains("MAX_RAW_PAGE_ITEMS"));
for forbidden in ["DataTable", "recordsTotal", "recordsFiltered", "draw", "SQL", "OFFSET ", "LIMIT "] {
assert!(!raw_inspection.contains(forbidden), "UI/physical inspection concept leaked into Store API: {forbidden}");
}
return;
}

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-store-api/tests/external_backend.rs
// version: 2
// version: 3
//! External-implementation canary for object-safe Store API capabilities.
@@ -27,6 +27,18 @@ impl ksp_store_api::RawTransactionRead for ExternalMemoryBackend {
}
}
impl ksp_store_api::RawTransactionInspectionRead for ExternalMemoryBackend {
fn inspect_raw_transactions<'a>(
&'a self,
query: &'a ksp_store_api::RawTransactionInspectionQuery,
) -> ksp_store_api::StoreApiFuture<'a, ksp_store_api::Result<ksp_store_api::RawInspectionPage<ksp_store_api::RawTransactionSummary>>> {
let _ = query;
return std::boxed::Box::pin(async {
return ksp_store_api::RawInspectionPage::try_new(std::vec::Vec::new(), 0, 0);
});
}
}
impl ksp_store_api::RawTransactionWrite for ExternalMemoryBackend {
fn persist_raw_transaction_acquisition<'a>(
&'a self,
@@ -92,6 +104,18 @@ impl ksp_store_api::RawAccountStateRead for ExternalMemoryBackend {
}
}
impl ksp_store_api::RawAccountStateInspectionRead for ExternalMemoryBackend {
fn inspect_raw_account_states<'a>(
&'a self,
query: &'a ksp_store_api::RawAccountStateInspectionQuery,
) -> ksp_store_api::StoreApiFuture<'a, ksp_store_api::Result<ksp_store_api::RawInspectionPage<ksp_store_api::RawAccountStateSummary>>> {
let _ = query;
return std::boxed::Box::pin(async {
return ksp_store_api::RawInspectionPage::try_new(std::vec::Vec::new(), 0, 0);
});
}
}
impl ksp_store_api::RawAccountStateWrite for ExternalMemoryBackend {
fn persist_raw_account_acquisition<'a>(
&'a self,
@@ -168,24 +192,28 @@ impl ksp_store_api::RawTransactionRetentionWrite for ExternalMemoryBackend {
}
#[test]
fn pre_006_external_backend_implements_each_capability_without_store_runtime_crate() {
fn v0_3_8_pre_003_external_backend_implements_canonical_and_inspection_capabilities_without_runtime_crate() {
let backend = ExternalMemoryBackend;
let transaction_read: &dyn ksp_store_api::RawTransactionRead = &backend;
let transaction_write: &dyn ksp_store_api::RawTransactionWrite = &backend;
let transaction_inspection: &dyn ksp_store_api::RawTransactionInspectionRead = &backend;
let transaction_observation_read: &dyn ksp_store_api::RawTransactionObservationRead = &backend;
let transaction_observation_write: &dyn ksp_store_api::RawTransactionObservationWrite = &backend;
let account_read: &dyn ksp_store_api::RawAccountStateRead = &backend;
let account_write: &dyn ksp_store_api::RawAccountStateWrite = &backend;
let account_inspection: &dyn ksp_store_api::RawAccountStateInspectionRead = &backend;
let account_observation_read: &dyn ksp_store_api::RawAccountObservationRead = &backend;
let account_observation_write: &dyn ksp_store_api::RawAccountObservationWrite = &backend;
let retention_read: &dyn ksp_store_api::RawTransactionRetentionRead = &backend;
let retention_write: &dyn ksp_store_api::RawTransactionRetentionWrite = &backend;
let _ = transaction_read;
let _ = transaction_write;
let _ = transaction_inspection;
let _ = transaction_observation_read;
let _ = transaction_observation_write;
let _ = account_read;
let _ = account_write;
let _ = account_inspection;
let _ = account_observation_read;
let _ = account_observation_write;
let _ = retention_read;

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-store-api/tests/public_api.rs
// version: 6
// version: 7
//! Integration canaries for the public `ksp-store-api` surface.
@@ -186,3 +186,21 @@ fn public_pre_007_retention_race_outcome_is_available_from_crate_root() {
assert_ne!(ksp_store_api::RawRetentionWriteOutcome::AlreadyAtTarget, ksp_store_api::RawRetentionWriteOutcome::ExpectedStateMismatch);
return;
}
#[test]
fn public_v0_3_8_pre_003_inspection_contracts_are_backend_neutral_and_dyn_compatible() {
let limit = match ksp_store_api::RawPageLimit::new(25) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let request = ksp_store_api::RawInspectionPageRequest::new(50, limit);
assert_eq!(request.offset(), 50);
assert_eq!(request.limit().get(), 25);
let page = ksp_store_api::RawInspectionPage::<u64>::try_new(std::vec![1, 2], 10, 5);
assert!(page.is_ok());
let transaction_inspection: std::option::Option<&dyn ksp_store_api::RawTransactionInspectionRead> = std::option::Option::None;
let account_inspection: std::option::Option<&dyn ksp_store_api::RawAccountStateInspectionRead> = std::option::Option::None;
assert!(transaction_inspection.is_none());
assert!(account_inspection.is_none());
return;
}

View File

@@ -1,10 +1,10 @@
// file: crates/ksp-store-api/tests/release_completeness.rs
// version: 1
// version: 2
//! Release-level boundary and completeness canaries for the `0.3.1` Store API RAW surface.
//! Release-level boundary and completeness canaries for the backend-neutral Store API RAW surface.
#[test]
fn pre_007_exact_crate_root_export_inventory_is_stable() {
fn v0_3_8_pre_003_exact_crate_root_export_inventory_is_stable() {
let crate_root = include_str!("../src/lib.rs");
let mut actual = std::vec::Vec::new();
for line in crate_root.lines() {
@@ -24,12 +24,14 @@ fn pre_007_exact_crate_root_export_inventory_is_stable() {
"pub use self::capability::raw_account::RawAccountObservationRead;",
"pub use self::capability::raw_account::RawAccountObservationWrite;",
"pub use self::capability::raw_account::RawAccountStateRead;",
"pub use self::capability::raw_account::RawAccountStateInspectionRead;",
"pub use self::capability::raw_account::RawAccountStateWrite;",
"pub use self::capability::raw_retention::RawTransactionRetentionRead;",
"pub use self::capability::raw_retention::RawTransactionRetentionWrite;",
"pub use self::capability::raw_transaction::RawTransactionObservationRead;",
"pub use self::capability::raw_transaction::RawTransactionObservationWrite;",
"pub use self::capability::raw_transaction::RawTransactionRead;",
"pub use self::capability::raw_transaction::RawTransactionInspectionRead;",
"pub use self::capability::raw_transaction::RawTransactionWrite;",
"pub use self::error::ERROR_CODE_RAW_CONFLICT;",
"pub use self::error::ERROR_CODE_RAW_MODEL_INVALID;",
@@ -40,6 +42,12 @@ fn pre_007_exact_crate_root_export_inventory_is_stable() {
"pub use self::model::raw_account::RawAccountObservation;",
"pub use self::model::raw_account::RawAccountState;",
"pub use self::model::raw_account::RawAccountStateReference;",
"pub use self::model::raw_inspection::RawAccountStateInspectionQuery;",
"pub use self::model::raw_inspection::RawAccountStateSummary;",
"pub use self::model::raw_inspection::RawInspectionPage;",
"pub use self::model::raw_inspection::RawInspectionPageRequest;",
"pub use self::model::raw_inspection::RawTransactionInspectionQuery;",
"pub use self::model::raw_inspection::RawTransactionSummary;",
"pub use self::model::raw_outcome::RawAcquisitionWriteOutcome;",
"pub use self::model::raw_outcome::RawEntityWriteOutcome;",
"pub use self::model::raw_outcome::RawObservationWriteOutcome;",
@@ -83,7 +91,7 @@ fn pre_007_exact_crate_root_export_inventory_is_stable() {
}
#[test]
fn pre_007_exact_production_module_inventory_is_raw_only() {
fn v0_3_8_pre_003_exact_production_module_inventory_is_raw_only() {
let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("src");
let root_names = rust_file_names(root.as_path());
assert!(root_names.is_ok());
@@ -100,7 +108,7 @@ fn pre_007_exact_production_module_inventory_is_raw_only() {
};
assert_eq!(
model_names,
std::vec!["raw_account.rs", "raw_outcome.rs", "raw_pagination.rs", "raw_primitives.rs", "raw_retention.rs", "raw_transaction.rs"]
std::vec!["raw_account.rs", "raw_inspection.rs", "raw_outcome.rs", "raw_pagination.rs", "raw_primitives.rs", "raw_retention.rs", "raw_transaction.rs"]
);
let capability_names = rust_file_names(root.join("capability").as_path());
assert!(capability_names.is_ok());
@@ -141,6 +149,7 @@ fn pre_007_interface_store_ownership_and_negative_scope_remain_explicit() {
include_str!("../src/lib.rs"),
include_str!("../src/model.rs"),
include_str!("../src/model/raw_account.rs"),
include_str!("../src/model/raw_inspection.rs"),
include_str!("../src/model/raw_outcome.rs"),
include_str!("../src/model/raw_pagination.rs"),
include_str!("../src/model/raw_primitives.rs"),
@@ -176,7 +185,7 @@ fn pre_007_interface_store_ownership_and_negative_scope_remain_explicit() {
}
#[test]
fn pre_007_capability_inventory_stays_fine_grained_without_runtime_facade() {
fn v0_3_8_pre_003_capability_inventory_stays_fine_grained_without_runtime_facade() {
let sources = [
include_str!("../src/capability/raw_account.rs"),
include_str!("../src/capability/raw_retention.rs"),
@@ -196,10 +205,12 @@ fn pre_007_capability_inventory_stays_fine_grained_without_runtime_facade() {
"pub trait RawAccountObservationRead: std::marker::Send + std::marker::Sync {",
"pub trait RawAccountObservationWrite: std::marker::Send + std::marker::Sync {",
"pub trait RawAccountStateRead: std::marker::Send + std::marker::Sync {",
"pub trait RawAccountStateInspectionRead: std::marker::Send + std::marker::Sync {",
"pub trait RawAccountStateWrite: std::marker::Send + std::marker::Sync {",
"pub trait RawTransactionObservationRead: std::marker::Send + std::marker::Sync {",
"pub trait RawTransactionObservationWrite: std::marker::Send + std::marker::Sync {",
"pub trait RawTransactionRead: std::marker::Send + std::marker::Sync {",
"pub trait RawTransactionInspectionRead: std::marker::Send + std::marker::Sync {",
"pub trait RawTransactionRetentionRead: std::marker::Send + std::marker::Sync {",
"pub trait RawTransactionRetentionWrite: std::marker::Send + std::marker::Sync {",
"pub trait RawTransactionWrite: std::marker::Send + std::marker::Sync {",

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-store-api/tests/security_hardening.rs
// version: 1
// version: 2
//! Adversarial and retention-race canaries for the Store API RAW foundation.
@@ -122,3 +122,47 @@ fn pre_007_retention_outcome_distinguishes_lost_compare_and_transition_race() {
assert_ne!(ksp_store_api::RawRetentionWriteOutcome::AlreadyAtTarget, ksp_store_api::RawRetentionWriteOutcome::ExpectedStateMismatch);
return;
}
#[test]
fn v0_3_8_pre_003_inspection_summaries_and_counts_reject_payload_shaped_or_inconsistent_state() {
let network = match ksp_store_api::RawNetworkId::new("mainnet-beta".to_owned()) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let format = match ksp_store_api::RawFormatId::new("ksp.solana.raw_transaction".to_owned()) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let transaction_reference = ksp_store_api::RawTransactionReference::new(network.clone(), ksp_store_api::RawTransactionSignature::new([0x61_u8; 64]));
let summary = ksp_store_api::RawTransactionSummary::try_new(
transaction_reference,
42,
std::option::Option::None,
format,
1,
ksp_store_api::RawContentHash::new([0x62_u8; 32]),
std::option::Option::Some(512),
ksp_store_api::RawRetentionState::Full,
);
assert!(summary.is_ok());
let summary = match summary {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let debug = std::format!("{summary:?}");
assert!(!debug.contains(HOSTILE_MARKER));
assert!(!debug.contains("payload:"));
let account_reference = ksp_store_api::RawAccountStateReference::new(
network,
ksp_store_api::Pubkey::new_from_array([0x63_u8; 32]),
43,
ksp_store_api::RawContentHash::new([0x64_u8; 32]),
);
assert!(
ksp_store_api::RawAccountStateSummary::try_new(account_reference, 1, ksp_store_api::Pubkey::new_from_array([0x65_u8; 32]), false, 0, 16_777_217,)
.is_err()
);
assert!(ksp_store_api::RawInspectionPage::<u64>::try_new(std::vec![1], 0, 0).is_err());
assert!(ksp_store_api::RawInspectionPage::<u64>::try_new(std::vec::Vec::new(), 1, 2).is_err());
return;
}

View File

@@ -0,0 +1,156 @@
// file: crates/ksp-store-api/unit_tests/model/raw_inspection.rs
// version: 1
//! Unit tests for backend-neutral RAW inspection contracts.
fn network() -> std::option::Option<crate::RawNetworkId> {
return match crate::RawNetworkId::new("mainnet-beta".to_owned()) {
std::result::Result::Ok(value) => std::option::Option::Some(value),
std::result::Result::Err(_) => std::option::Option::None,
};
}
fn format_id() -> std::option::Option<crate::RawFormatId> {
return match crate::RawFormatId::new("ksp.solana.raw_transaction".to_owned()) {
std::result::Result::Ok(value) => std::option::Option::Some(value),
std::result::Result::Err(_) => std::option::Option::None,
};
}
#[test]
fn inspection_page_request_preserves_large_offset_and_caller_limit_without_cursor_semantics() {
let limit = match crate::RawPageLimit::new(100) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let request = crate::RawInspectionPageRequest::new(u64::MAX, limit);
assert_eq!(request.offset(), u64::MAX);
assert_eq!(request.limit().get(), 100);
return;
}
#[test]
fn inspection_page_rejects_inconsistent_exact_counts() {
assert!(crate::RawInspectionPage::<u64>::try_new(std::vec![1], 0, 0).is_err());
assert!(crate::RawInspectionPage::<u64>::try_new(std::vec::Vec::new(), 4, 5).is_err());
let page = crate::RawInspectionPage::<u64>::try_new(std::vec![1, 2], 5, 3);
assert!(page.is_ok());
let page = match page {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
assert_eq!(page.total_items(), 5);
assert_eq!(page.filtered_items(), 3);
assert_eq!(page.items(), &[1, 2]);
return;
}
#[test]
fn inspection_queries_keep_backend_neutral_filters_and_random_access_page() {
let network = match network() {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let slots = match crate::RawSlotRange::new(std::option::Option::Some(10), std::option::Option::Some(20)) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let limit = match crate::RawPageLimit::new(25) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let transaction =
crate::RawTransactionInspectionQuery::new(network.clone(), slots, crate::RawSortDirection::Descending, crate::RawInspectionPageRequest::new(50, limit));
assert_eq!(transaction.network().as_str(), "mainnet-beta");
assert_eq!(transaction.page().offset(), 50);
assert_eq!(transaction.slots().end_inclusive(), std::option::Option::Some(20));
assert_eq!(transaction.direction(), crate::RawSortDirection::Descending);
let pubkey = crate::Pubkey::new_from_array([0x21_u8; 32]);
let account = crate::RawAccountStateInspectionQuery::new(
network,
std::option::Option::Some(pubkey),
slots,
crate::RawSortDirection::Ascending,
crate::RawInspectionPageRequest::new(75, limit),
);
assert_eq!(account.page().offset(), 75);
assert_eq!(account.pubkey(), std::option::Option::Some(&pubkey));
return;
}
#[test]
fn transaction_summary_is_payload_free_and_retention_consistent() {
let network = match network() {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let format_id = match format_id() {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let reference = crate::RawTransactionReference::new(network, crate::RawTransactionSignature::new([0x31_u8; 64]));
let full = crate::RawTransactionSummary::try_new(
reference.clone(),
42,
std::option::Option::None,
format_id.clone(),
1,
crate::RawContentHash::new([0x32_u8; 32]),
std::option::Option::Some(123),
crate::RawRetentionState::Full,
);
assert!(full.is_ok());
let full = match full {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
assert_eq!(full.slot(), 42);
assert_eq!(full.payload_size_bytes(), std::option::Option::Some(123));
assert_eq!(full.retention_state(), crate::RawRetentionState::Full);
assert!(
crate::RawTransactionSummary::try_new(
reference.clone(),
42,
std::option::Option::None,
format_id.clone(),
1,
crate::RawContentHash::new([0x32_u8; 32]),
std::option::Option::None,
crate::RawRetentionState::Full,
)
.is_err()
);
assert!(
crate::RawTransactionSummary::try_new(
reference,
42,
std::option::Option::None,
format_id,
1,
crate::RawContentHash::new([0x32_u8; 32]),
std::option::Option::Some(123),
crate::RawRetentionState::Purged,
)
.is_err()
);
return;
}
#[test]
fn account_summary_exposes_length_only_and_enforces_raw_account_bound() {
let network = match network() {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let reference = crate::RawAccountStateReference::new(network, crate::Pubkey::new_from_array([0x41_u8; 32]), 88, crate::RawContentHash::new([0x42_u8; 32]));
let summary = crate::RawAccountStateSummary::try_new(reference.clone(), 1000, crate::Pubkey::new_from_array([0x43_u8; 32]), false, 9, 512);
assert!(summary.is_ok());
let summary = match summary {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
assert_eq!(summary.reference().slot(), 88);
assert_eq!(summary.data_length_bytes(), 512);
assert!(crate::RawAccountStateSummary::try_new(reference, 1000, crate::Pubkey::new_from_array([0x43_u8; 32]), false, 9, 16_777_217,).is_err());
return;
}

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-store-lib/src/lib.rs
// version: 9
// version: 10
#![warn(missing_docs)]
#![deny(unreachable_pub)]
@@ -10,8 +10,9 @@
//! The runtime facade owns backend selection, lifecycle, safe diagnostics and
//! backend-neutral capability dispatch. `0.3.3-pre.008` completed the six PostgreSQL
//! `RawTransaction` capabilities. `0.3.4-pre.008` adds the four `RawAccount*` capabilities
//! on both the physical backend and this common facade, completing the RAW inventory at
//! ten capabilities without exposing physical types.
//! on both the physical backend and this common facade, completing the canonical RAW runtime inventory at
//! ten capabilities without exposing physical types. The `0.3.8` inspection
//! contracts are reexported here before backend/facade dispatch is opened.
//!
//! The default `postgres` feature compiles the official PostgreSQL backend as
//! an optional implementation dependency. No backend implementation type is
@@ -125,12 +126,18 @@ pub use ksp_store_api::RawAccountObservationRead;
pub use ksp_store_api::RawAccountObservationWrite;
/// Canonical complete N1 RAW account state independent from acquisition transport.
pub use ksp_store_api::RawAccountState;
/// Backend-independent random-access inspection query for RAW account states.
pub use ksp_store_api::RawAccountStateInspectionQuery;
/// Read capability for data-free random-access RAW account-state inspection.
pub use ksp_store_api::RawAccountStateInspectionRead;
/// Backend-independent list query for complete canonical RAW account states.
pub use ksp_store_api::RawAccountStateQuery;
/// Read capability for complete canonical RAW account states.
pub use ksp_store_api::RawAccountStateRead;
/// Durable backend-independent identity of one canonical RAW account state.
pub use ksp_store_api::RawAccountStateReference;
/// Data-free summary of one canonical RAW account state for operator inspection.
pub use ksp_store_api::RawAccountStateSummary;
/// Write capability for complete canonical RAW account-state acquisitions.
pub use ksp_store_api::RawAccountStateWrite;
/// Origin category describing why one acquisition was performed.
@@ -145,6 +152,10 @@ pub use ksp_store_api::RawContentHash;
pub use ksp_store_api::RawEntityWriteOutcome;
/// Bounded identifier of one KSP-owned source-independent RAW persistence format.
pub use ksp_store_api::RawFormatId;
/// One bounded random-access inspection page with exact logical counts.
pub use ksp_store_api::RawInspectionPage;
/// Random-access page request dedicated to bounded interactive RAW inspection.
pub use ksp_store_api::RawInspectionPageRequest;
/// Bounded logical network/cluster identifier used in backend-independent Store identities.
pub use ksp_store_api::RawNetworkId;
/// Stable deterministic idempotence key for one persisted acquisition observation.
@@ -177,6 +188,10 @@ pub use ksp_store_api::RawTimestamp;
pub use ksp_store_api::RawTransaction;
/// Explicit write mode for canonical RAW transaction acquisitions.
pub use ksp_store_api::RawTransactionAcquisitionMode;
/// Backend-independent random-access inspection query for RAW transactions.
pub use ksp_store_api::RawTransactionInspectionQuery;
/// Read capability for payload-free random-access RAW transaction inspection.
pub use ksp_store_api::RawTransactionInspectionRead;
/// Persistable acquisition observation linked to one canonical RAW transaction.
pub use ksp_store_api::RawTransactionObservation;
/// Read capability for persisted RAW transaction observations.
@@ -197,6 +212,8 @@ pub use ksp_store_api::RawTransactionRetentionTransition;
pub use ksp_store_api::RawTransactionRetentionWrite;
/// Canonical 64-byte Solana transaction signature used by Store identities.
pub use ksp_store_api::RawTransactionSignature;
/// Payload-free summary of one canonical RAW transaction for operator inspection.
pub use ksp_store_api::RawTransactionSummary;
/// Minimal durable identity retained after a canonical RAW transaction payload is purged.
pub use ksp_store_api::RawTransactionTombstone;
/// Write capability for canonical RAW transaction acquisitions.

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-store-lib/tests/public_api.rs
// version: 8
// version: 9
#![warn(missing_docs)]
#![deny(unreachable_pub)]
@@ -57,11 +57,14 @@ fn pre_005_common_and_postgres_error_codes_are_stable_and_store_owned() {
}
#[test]
fn pre_003_facade_reexports_backend_agnostic_store_api_types() {
fn v0_3_8_pre_003_facade_reexports_backend_agnostic_inspection_types_without_dispatch() {
let _raw_transaction = std::mem::size_of::<std::option::Option<ksp_store_lib::RawTransaction>>();
let _raw_account_state = std::mem::size_of::<std::option::Option<ksp_store_lib::RawAccountState>>();
let _query = std::mem::size_of::<std::option::Option<ksp_store_lib::RawTransactionQuery>>();
let _inspection_query = std::mem::size_of::<std::option::Option<ksp_store_lib::RawTransactionInspectionQuery>>();
let _inspection_page = std::mem::size_of::<std::option::Option<ksp_store_lib::RawInspectionPage<ksp_store_lib::RawTransactionSummary>>>();
let _capability = std::mem::size_of::<std::option::Option<&dyn ksp_store_lib::RawTransactionRead>>();
let _inspection_capability = std::mem::size_of::<std::option::Option<&dyn ksp_store_lib::RawTransactionInspectionRead>>();
let _result: ksp_store_lib::Result<()> = std::result::Result::Ok(());
return;
}

273
deltas/0.3.8/pre.003.md Normal file
View File

@@ -0,0 +1,273 @@
<!-- file: deltas/0.3.8/pre.003.md -->
<!-- version: 1 -->
# Delta `0.3.8-pre.003` — contrats Store dinspection et tracing Store Desk
## Base requise
Base directe attendue :
```text
0.3.8-pre.002-fix.001
workspace.package.version = 0.3.8-pre.2.fix.1
```
Le gate opérateur fourni pour cette base est vert : audits Rust/Markdown, `cargo check --workspace`, `cargo clippy --workspace --all-targets`, suite `ksp-app-store-desk` et arbres Cargo ont tous été exécutés sans échec après le fix Vite/Clippy.
La livraison est :
```text
0.3.8-pre.003
workspace.package.version = 0.3.8-pre.3
commit = v0.3.8-pre.003
tag = aucun
```
## Objectif
Matérialiser la primitive backend-neutral dinspection RAW décidée en `pre.001`, sans modifier le chemin cursor/keyset canonique et sans ouvrir PostgreSQL dans cette tranche.
Le delta ferme également deux invariants Store Desk explicitement demandés après `pre.002-fix.001` :
```text
tracing développement Store Desk = supertrace
interaction frontend significative = événement KSP tracé
```
Aucun Store runtime nest ouvert par lapplication et aucun SQL nest ajouté.
## Contrats Store dinspection
`ksp-store-api` ajoute un modèle séparé de la pagination cursor :
```text
RawInspectionPageRequest
offset: u64
limit: RawPageLimit
RawInspectionPage<T>
items
total_items
filtered_items
```
La sémantique des counts est exacte :
- `total_items` = nombre dentités dans le scope obligatoire de la query avant filtres optionnels ;
- `filtered_items` = nombre dentités après filtres optionnels ;
- `items` = uniquement la fenêtre random-access demandée.
Les incohérences `filtered_items > total_items` ou `items.len() > filtered_items` sont rejetées par le modèle.
Les queries ajoutées sont :
```text
RawTransactionInspectionQuery
network
slots
direction
RawInspectionPageRequest
RawAccountStateInspectionQuery
network
pubkey?
slots
direction
RawInspectionPageRequest
```
Le `u64` offset ne reçoit aucun plafond politique artificiel dans `ksp-store-api`. La conversion vers une limite physique backend et le rejet pré-I/O des offsets non représentables appartiennent aux vertical slices PostgreSQL `pre.004` / `pre.005`.
## Summaries sans gros bytes
`RawTransactionSummary` expose uniquement :
```text
reference
slot
block_time
format_id
format_version
content_hash
payload_size_bytes?
retention_state
```
Le payload canonique nest jamais contenu dans le summary. La taille est `None` pour `Purged`; un état qui conserve logiquement le payload exige une taille valide, non nulle et bornée par `MAX_RAW_PAYLOAD_BYTES`.
`RawAccountStateSummary` expose uniquement :
```text
reference
lamports
owner
executable
rent_epoch
data_length_bytes
```
Les bytes account data ne sont jamais contenus dans le summary et `data_length_bytes` reste borné par `MAX_RAW_ACCOUNT_DATA_BYTES`.
## Capabilities
Deux traits read object-safe sont ajoutés sans modifier les traits RAW historiques :
```text
RawTransactionInspectionRead::inspect_raw_transactions
RawAccountStateInspectionRead::inspect_raw_account_states
```
Ils retournent `RawInspectionPage<...Summary>`.
`ksp-store-lib` réexporte les nouveaux types et traits afin que les consommateurs futurs restent dépendants de la façade commune. En revanche :
```text
Store nimplémente pas encore les deux traits dinspection
PostgresBackend nimplémente pas encore les deux traits dinspection
```
Linventaire canonique existant reste donc exactement à dix implementations RAW sur la façade et dix sur le backend PostgreSQL. Les implementations dinspection sont réservées à `pre.004` et `pre.005`.
## Pagination canonique préservée
Aucune modification nest apportée à :
```text
RawPageCursor
RawPageRequest
RawPage<T>
RawTransactionQuery
RawAccountStateQuery
list_raw_transactions
list_raw_account_states
```
La frontière reste :
```text
cursor/keyset = workers/backfill/replay/navigation durable
inspection offset/count = operator/admin random-access
DataTables = futur unique pager visuel de Store Desk
```
Aucun terme ou DTO DataTables (`draw`, `recordsTotal`, `recordsFiltered`) nentre dans `ksp-store-api`.
## Tracing Store Desk
Le scaffold Store Desk demandait auparavant le profil par défaut de `std.logging` (`local_dev`). Il sélectionne maintenant explicitement :
```text
DEVELOPMENT_LOGGING_PROFILE = supertrace
```
Le fallback local reste `LogFilterLevel::Trace` si la Config managed nest pas disponible.
Le frontend conserve les événements spécifiques existants pour navigation et Refresh et renforce le logging générique :
- clics des boutons non spécialisés via un listener délégué au `document`, ce qui couvre aussi les boutons dynamiques créés ultérieurement par DataTables ;
- clic dun contrôle tab ;
- activation effective dun tab Bootstrap ;
- changement dun `input`, `select` ou `textarea` ;
- IPC requested/completed/failed via le bridge existant.
Les valeurs de filtres, payloads, URI et secrets ne sont pas ajoutés aux logs. Les tranches DataTables métier devront instrumenter explicitement page, page-length, redraw/query et actions détail sans logguer les données RAW.
## Fichiers ajoutés
```text
crates/ksp-store-api/src/model/raw_inspection.rs
crates/ksp-store-api/unit_tests/model/raw_inspection.rs
deltas/0.3.8/pre.003.md
```
## Fichiers modifiés
```text
Cargo.toml
crates/ksp-app-store-desk/frontend/ts/main.ts
crates/ksp-app-store-desk/src/bootstrap.rs
crates/ksp-app-store-desk/src/constants.rs
crates/ksp-app-store-desk/src/lib.rs
crates/ksp-app-store-desk/tests/desktop_security.rs
crates/ksp-store-api/src/capability/raw_account.rs
crates/ksp-store-api/src/capability/raw_transaction.rs
crates/ksp-store-api/src/lib.rs
crates/ksp-store-api/src/model.rs
crates/ksp-store-api/tests/dependency_boundary.rs
crates/ksp-store-api/tests/external_backend.rs
crates/ksp-store-api/tests/public_api.rs
crates/ksp-store-api/tests/release_completeness.rs
crates/ksp-store-api/tests/security_hardening.rs
crates/ksp-store-lib/src/lib.rs
crates/ksp-store-lib/tests/public_api.rs
docs/plans/029-V0_3_8_STORE_DESK_PLAN.md
docs/validation/025-V0_3_8_STORE_DESK.md
```
## Fichiers supprimés
```text
aucun
```
## Validations exécutées dans lenvironnement dassemblage
```text
python3 scripts/audit_rust_workspace_rules.py
-> General Rust rule audit: clean
-> Rust export completeness audit: 0 candidate(s)
-> KSP workspace Rust rule audit: clean
python3 scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts crates deltas
-> Markdown table audit: clean
```
Contrôles ciblés supplémentaires :
```text
Store API exact root export inventory: clean
Store API production module inventory: raw_inspection ajouté, aucun module physique
Store API UI/SQL boundary: clean
External backend object-safety canary: source complete pour 12 traits
ksp-store-lib inspection reexports: clean
Store facade canonical RAW implementations: 10 inchangées
PostgreSQL backend canonical RAW implementations: 10 inchangées
Store/PostgreSQL inspection implementations: absentes comme prévu
Store Desk supertrace selection: clean
Store Desk delegated button/tab/control tracing: clean
TypeScript syntax audit: clean (5 fichiers, TS 5.8.3)
Manifest parse: clean
```
## Validations non exécutées dans lenvironnement dassemblage
`cargo` et `rustfmt` ne sont pas disponibles dans le sandbox dassemblage. Le gate opérateur requis est :
```bash
cargo fmt --all
python3 scripts/audit_rust_workspace_rules.py
python3 scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts crates deltas
cargo check --workspace
cargo clippy --workspace --all-targets
cargo test -p ksp-store-api
cargo test -p ksp-store-lib
cargo check -p ksp-store-lib --no-default-features
cargo test -p ksp-app-store-desk
cargo tree -p ksp-store-api --edges normal
cargo tree -p ksp-store-lib --edges normal
```
## Invariants de fermeture
- aucune reprise de code kbot3 ;
- aucun SQL ou type PostgreSQL dans `ksp-store-api`, `ksp-store-lib` ou Store Desk ;
- aucun remplacement du cursor/keyset RAW ;
- aucune implementation backend/facade des nouveaux traits dans cette tranche ;
- aucun `Store::open` supplémentaire dans Store Desk ;
- aucune dépendance Store directe ajoutée à Store Desk ;
- un seul pager DataTables restera visible quand lUI métier sera branchée ;
- `supertrace` et le tracing des interactions frontend sont désormais des invariants explicites de la phase de développement.
## Suite prévue
Après gate opérateur vert, `pre.004` implémente **uniquement le vertical slice PostgreSQL + façade `RawTransactionInspectionRead`** : summary query, counts exacts, offset/limit checked et maintien intégral du listage keyset existant.

View File

@@ -1,5 +1,5 @@
<!-- file: docs/plans/029-V0_3_8_STORE_DESK_PLAN.md -->
<!-- version: 2 -->
<!-- version: 3 -->
# Plan v0.3.8 — Store Desk V1 RAW
@@ -96,6 +96,24 @@ kbot3 n'est jamais une source de package, HTML, SASS, assets, Rust, TypeScript o
La sélection DataTables n'est pas justifiée par une action V1 : le détail s'ouvre par une action explicite dans la row. `datatables.net-select-bs5` n'est donc pas requis tant qu'une sélection persistante n'apporte pas une fonction réelle.
### 4.2 Invariant tracing Store Desk
Pendant le développement de `ksp-app-store-desk`, le profil Logging demandé explicitement est `supertrace`; le fallback local reste `Trace`. Cette sélection est temporairement volontairement verbeuse et sera réévaluée avant `rel.001`.
Toute interaction opérateur significative du frontend doit être tracée via le bridge KSP, sans contenu sensible :
```text
clic bouton / action explicite
activation tab / vue
changement de filtre ou contrôle
Refresh
ouverture détail
pagination / changement de page length DataTables
redraw/query serverSide et échec IPC associé
```
Les événements transportent uniquement des identifiants de contrôle, catégories d'action et compteurs sûrs. Les valeurs de filtres, payloads, URI, credentials et contenu RAW ne sont jamais injectés dans les logs. Les handlers métier peuvent ajouter un événement plus précis, mais aucune interaction nouvellement introduite ne doit rester silencieuse.
## 5. npm / DataTables map
Les ranges KSP v0.3.7 observés dans Config/Wallet Desk sont :

View File

@@ -1,5 +1,5 @@
<!-- file: docs/validation/025-V0_3_8_STORE_DESK.md -->
<!-- version: 3 -->
<!-- version: 4 -->
# Validation v0.3.8 — Store Desk V1 RAW
@@ -89,7 +89,7 @@ Le journal opérateur fourni pour v0.3.7 rapporte un gate complet. Il reste une
## 8. Gates futures
- [ ] `pre.002` scaffold KSP strict + DataTables skeleton ;
- [X] `pre.002` scaffold KSP strict + DataTables skeleton ;
- [ ] `pre.003` contrats Store inspection offset/count/summaries ;
- [ ] `pre.004` PostgreSQL + façade inspection RawTransaction ;
- [ ] `pre.005` PostgreSQL + façade inspection RawAccountState ;
@@ -220,3 +220,39 @@ Le correctif `pre.002-fix.001` reste strictement dans le couloir scaffold de `pr
- aucune capability Store, aucun SQL, aucun changement darchitecture pagination.
La case `pre.002` reste ouverte jusquau gate opérateur du fix.
## 14. Gate opérateur `pre.002-fix.001`
Le gate complet rejoué par lopérateur après le fix est vert :
```text
General Rust rule audit: clean
Rust export completeness audit: 0 candidate(s)
KSP workspace Rust rule audit: clean
Markdown table audit: clean (314 table(s), 693 file(s))
cargo check --workspace: PASS
cargo clippy --workspace --all-targets: PASS
cargo test -p ksp-app-store-desk: PASS
```
Détail Store Desk communiqué : 12 tests unitaires, 2 dependency-boundary, 6 desktop-contract, 3 desktop-security, 1 public-API, tous verts. Les arbres `cargo tree` normal/features ont également été produits. `pre.002` est donc fermé et `pre.003` peut souvrir.
## 15. `pre.003` — contrats Store dinspection et tracing développement
- [X] `workspace.package.version = 0.3.8-pre.3` ;
- [X] `RawInspectionPageRequest` sépare explicitement offset/limit de `RawPageRequest` cursor ;
- [X] `RawInspectionPage<T>` expose `total_items` / `filtered_items` exacts avec validation de cohérence ;
- [X] `RawTransactionInspectionQuery` conserve network/range/direction et page random-access ;
- [X] `RawAccountStateInspectionQuery` conserve network/pubkey?/range/direction et page random-access ;
- [X] `RawTransactionSummary` exclut les payload bytes et expose uniquement metadata/retention/size ;
- [X] `RawAccountStateSummary` exclut les data bytes et expose uniquement metadata/data length ;
- [X] `RawTransactionInspectionRead` et `RawAccountStateInspectionRead` sont object-safe et backend-neutral ;
- [X] `ksp-store-lib` réexporte les nouveaux contrats sans encore les implémenter sur `Store` ;
- [X] le chemin cursor/keyset existant reste inchangé ;
- [X] aucun SQL, migration ou backend physique nest introduit ;
- [X] aucun nom/protocole DataTables nentre dans `ksp-store-api` ;
- [X] Store Desk demande explicitement le profil Logging `supertrace` pendant le développement ;
- [X] fallback Logging Store Desk reste `Trace` ;
- [X] tracing frontend couvre boutons, navigation, tabs, changements de contrôles, Refresh et IPC ;
- [X] les futurs événements DataTables page/length/query doivent être instrumentés lors de `pre.007`/`pre.008` sans logguer les valeurs de filtres ni payloads.