/** * Chain client — smoldot light client integration for the browser. * * Uses the `smoldot` NPM package for the actual light client, with WASM-side * state management via `ChainClientHandle` from `host-wasm`. * * ## Architecture * * ``` * TypeScript (this module) Rust WASM (ChainClientHandle) * ───────────────────────── ──────────────────────────── * SmoldotDriver ChainStateMachine * creates smoldot Client processes JSON-RPC responses * adds/removes chains tracks chain status * drives event loops generates request payloads * forwards responses → WASM persists DB to localStorage * ``` * * @example * ```ts * import { start as startSmoldot } from "smoldot"; * import { SmoldotDriver } from "@polkadot-apps/host-sdk"; * * const smoldotClient = startSmoldot(); * const driver = new SmoldotDriver(chainHandle, smoldotClient); * * await driver.connect("PaseoAssetHub"); * console.log(driver.status("PaseoAssetHub")); // { state: "Connecting", ... } * ``` */ /** Chain status as returned by the WASM handle. */ export interface ChainStatus { id: string; name: string; state: "Disconnected" | "Connecting" | "Syncing" | "Live" | "Error"; bestBlock: number; finalizedBlock: number | null; peers: number; error?: string; } /** Supported chain names for smoldot on WASM. */ export type SmoldotChainName = "PolkadotAssetHub" | "PaseoAssetHub" | "PolkadotPeople" | "PaseoPeople" | "PaseoBulletin"; /** * Minimal interface for the smoldot NPM package's `Client`. * Matches the `smoldot` package's `Client` type without requiring the package as a dependency. */ export interface SmoldotClient { addChain(options: { chainSpec: string; potentialRelayChains?: SmoldotChain[]; databaseContent?: string; }): Promise; terminate(): Promise; } /** Minimal interface for a smoldot chain handle. */ export interface SmoldotChain { sendJsonRpc(rpc: string): void; nextJsonRpcResponse(): Promise; remove(): void; } /** * JS-side chain store passed to `ChainClientHandle.fromStore()`. * Implement this to provide a custom persistence backend (e.g. IndexedDB). * * Mirrors the `JsChainStore` extern type in `crates/host-wasm/src/chain.rs`. */ export interface JsChainStore { /** * Load persisted data for the given key. * Return an empty string, null, or undefined if the key is not found. * Returning null or undefined is safe — the Rust adapter treats both as * an empty string to prevent smoldot from reading "null" as corrupt data. */ load(key: string): string | null | undefined; /** Persist data for the given key. */ save(key: string, data: string): void; } /** WASM handle interface (matches ChainClientHandle from host-wasm). */ export interface WasmChainClientHandle { registerChain(chainName: string): void; unregisterChain(chainName: string): void; processResponse(chainName: string, text: string): void; processRelayResponse(chainName: string, text: string): void; setError(chainName: string, msg: string): void; status(chainName: string): ChainStatus; allStatuses(): ChainStatus[]; subscribeNewHeadsRequest(): string; subscribeFinalizedHeadsRequest(): string; knownGenesisHash(chainName: string): string | null; healthCheckRequest(chainName: string): string; paraDbSaveRequest(): string; relayDbSaveRequest(): string; chainSpecs(chainName: string): [string, string] | null; loadRelayDb(chainName: string): string; loadParaDb(chainName: string): string; statementSubmitRequest(encodedHex: string, requestId: number): string; statementSubscribeRequest(requestId: number): string; statementUnsubscribeRequest(subId: string, requestId: number): string; statementBroadcastsRequest(topicHexes: string[], requestId: number): string; parseStatementNotification(text: string): string[]; free(): void; } /** * Boot stage event emitted during chain warmup. * * `"ready"` is emitted immediately after `"first-finalized-block"` today. * It exists as a separate stage so that readiness criteria can be tightened * in the future (e.g. require peer count > 0) without breaking consumers * that listen for `"ready"`. */ export interface BootDiagnosticsEvent { chainName: string; stage: "connect-start" | "relay-added" | "para-added" | "first-best-block" | "first-finalized-block" | "ready"; elapsedMs: number; } /** Callback for boot-stage diagnostics. */ export type BootDiagnosticsCallback = (event: BootDiagnosticsEvent) => void; /** Options for chain readiness waiting. */ export interface ChainReadinessOptions { /** Milliseconds before waitUntilReady rejects. Default: 120_000. */ timeoutMs?: number; /** Poll interval in milliseconds. Default: 500. */ pollIntervalMs?: number; } /** Thrown when waitUntilReady exceeds the timeout. */ export declare class ChainReadinessTimeoutError extends Error { constructor(chainName: string, timeoutMs: number); } /** * Drives a smoldot light client in the browser, wired to the WASM state machine. * * Manages the lifecycle of smoldot chains: adding/removing chains, forwarding * responses to WASM for state tracking, and running periodic health checks * and database saves. */ export declare class SmoldotDriver { private handle; private client; private chains; private responseInterceptors; private relayResponseInterceptors; private diagnosticsCallbacks; constructor(handle: WasmChainClientHandle, client: SmoldotClient); /** Get the WASM chain client handle. */ get chainHandle(): WasmChainClientHandle; /** * Get the raw para SmoldotChain handle for a chain. * Used by StatementStoreDriver to send RPC on the same connection. */ getParaChain(chainName: string): SmoldotChain | null; /** Get the raw relay SmoldotChain handle for a chain. */ getRelayChain(chainName: string): SmoldotChain | null; /** * Register a response interceptor for a chain. The interceptor is called * with each JSON-RPC response text before it reaches the WASM state machine. * Return `true` to consume the message (skip WASM processing), `false` to * let it pass through. */ registerResponseInterceptor(chainName: string, handler: (text: string) => boolean): void; /** Unregister a response interceptor. */ unregisterResponseInterceptor(chainName: string): void; registerRelayResponseInterceptor(chainName: string, handler: (text: string) => boolean): void; unregisterRelayResponseInterceptor(chainName: string): void; /** Connect a smoldot-backed chain. Starts syncing in the background. */ connect(chainName: SmoldotChainName): Promise; /** Disconnect a chain. */ disconnect(chainName: string): void; /** Get current status of a chain. */ status(chainName: string): ChainStatus; /** Get all chain statuses. */ allStatuses(): ChainStatus[]; /** Disconnect all chains and terminate the smoldot client. */ destroy(): Promise; /** * Persist the current relay/parachain databases for one or all connected chains. * * This is best-effort only: smoldot persists asynchronously after receiving the * JSON-RPC save requests, so callers should treat this as a freshness hint rather * than a durability barrier. */ persistState(chainName?: SmoldotChainName): void; /** * Wait until the chain has a finalized head, indicating it is query-ready. * Resolves when status.finalizedBlock is non-null, or rejects after timeoutMs. */ waitUntilReady(chainName: SmoldotChainName, options?: ChainReadinessOptions): Promise; /** * Register a diagnostics callback for a chain's boot process. * The callback receives events at each boot stage transition. * Pass null to remove an existing callback. */ setBootDiagnostics(chainName: string, callback: BootDiagnosticsCallback | null): void; private runParaResponseLoop; private runRelayResponseLoop; private runMaintenanceLoop; private persistEntryState; } //# sourceMappingURL=chain.d.ts.map