@parity/product-sdk-host 0.10.3 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +681 -462
- package/dist/index.js +890 -219
- package/dist/index.js.map +1 -1
- package/package.json +5 -3
- package/src/accounts.ts +544 -0
- package/src/chain-spec.ts +272 -0
- package/src/chain-transaction.ts +241 -0
- package/src/chat.ts +81 -85
- package/src/container.ts +211 -246
- package/src/entropy.ts +63 -25
- package/src/errors.ts +198 -0
- package/src/features.ts +172 -0
- package/src/index.ts +47 -22
- package/src/navigation.ts +128 -0
- package/src/notifications.ts +59 -69
- package/src/papi-provider.ts +673 -0
- package/src/payments.ts +77 -61
- package/src/permissions.ts +107 -105
- package/src/result.ts +56 -0
- package/src/theme.ts +35 -63
- package/src/transport.ts +71 -0
- package/src/truapi.ts +166 -409
- package/src/types.ts +69 -61
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/container.ts","../src/chains.ts","../src/truapi.ts","../src/permissions.ts","../src/theme.ts","../src/entropy.ts","../src/chat.ts","../src/payments.ts","../src/notifications.ts"],"names":["log","createLogger","enumValue"],"mappings":"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","file":"index.js","sourcesContent":["// Copyright 2026 Parity Technologies (UK) Ltd.\n// SPDX-License-Identifier: Apache-2.0\nimport type { JsonRpcProvider } from \"polkadot-api\";\nimport { createLogger } from \"@parity/product-sdk-logger\";\nimport { enumValue, type Transport } from \"@novasamatech/host-api\";\n\nimport type { HostLocalStorage, HostStatementStore } from \"./types.js\";\n\nconst log = createLogger(\"host:container\");\n\n/**\n * Thrown by {@link getHostProvider} when the host container is reachable but does\n * not support the requested chain — e.g. the chain isn't enabled in this host\n * build, or the descriptor's genesis hash has drifted from the host's after a\n * network reset.\n *\n * Surfacing this as a thrown error (rather than handing back a provider that\n * silently swallows every JSON-RPC request) is what lets callers of\n * `createChainClient` detect the failure. Without it, the host's fallback no-op\n * provider drops every request on the floor and queries await forever.\n */\nexport class ChainNotSupportedError extends Error {\n /** Genesis hash of the chain the host refused, for programmatic detection. */\n readonly genesisHash: string;\n\n constructor(genesisHash: string) {\n super(\n `Chain ${genesisHash} is not supported by the current host. It may not be enabled in this host build, or its genesis hash may have drifted after a network reset.`,\n );\n this.name = \"ChainNotSupportedError\";\n this.genesisHash = genesisHash;\n }\n}\n\n/**\n * Ask the host whether it can serve the given chain, using the same\n * `host_feature_supported` check the wrapper's provider performs internally\n * before it decides whether to start a real provider or a no-op one.\n *\n * @throws If the host connection never becomes ready, or the host rejects the\n * support check outright. Both are non-hanging, catchable failures.\n */\nasync function isChainSupportedByHost(\n sdk: typeof import(\"@novasamatech/host-api-wrapper\"),\n genesisHash: `0x${string}`,\n): Promise<boolean> {\n const ready = await sdk.sandboxTransport.isReady();\n if (!ready) {\n throw new Error(\n `Host connection did not become ready; cannot verify support for chain ${genesisHash}.`,\n );\n }\n const result = await sdk.hostApi.featureSupported(\n enumValue(\"v1\", enumValue(\"Chain\", genesisHash)),\n );\n return result.match(\n (ok) => ok.value === true,\n (err) => {\n // The reason lives at value.payload.reason for host-protocol errors and\n // value.reason for request-level ones; tolerate both against upstream drift.\n const value = (err as { value?: { payload?: { reason?: string }; reason?: string } })\n ?.value;\n const reason = value?.payload?.reason ?? value?.reason ?? \"unknown reason\";\n throw new Error(`Host rejected the chain-support check for ${genesisHash}: ${reason}`);\n },\n );\n}\n\n/**\n * Detect if running inside a Host container (Polkadot Browser / Polkadot Desktop).\n *\n * The SDK is designed to run exclusively inside a host container. This function\n * is primarily useful for early validation or informational purposes.\n *\n * Uses product-sdk's sandboxProvider as primary detection.\n * Falls back to manual signal checks when product-sdk is not installed.\n */\nexport async function isInsideContainer(): Promise<boolean> {\n if (typeof window === \"undefined\") return false;\n\n try {\n const sdk = await import(\"@novasamatech/host-api-wrapper\");\n return sdk.sandboxProvider.isCorrectEnvironment();\n } catch {\n return isInsideContainerSync();\n }\n}\n\n/**\n * Get the Host API localStorage instance when running inside a container.\n * Returns null outside a container or when product-sdk is unavailable.\n */\nexport async function getHostLocalStorage(): Promise<HostLocalStorage | null> {\n if (!(await isInsideContainer())) return null;\n\n try {\n const sdk = await import(\"@novasamatech/host-api-wrapper\");\n return sdk.hostLocalStorage as HostLocalStorage;\n } catch (err) {\n log.debug(\"getHostLocalStorage unavailable\", err);\n return null;\n }\n}\n\n/**\n * Construct a fresh host-backed `HostLocalStorage` instance with an optional\n * custom transport. Use this when you need a non-default transport (e.g.\n * for tests); otherwise prefer {@link getHostLocalStorage}, which returns\n * the shared singleton.\n *\n * Mirrors `createLocalStorage` from `@novasamatech/host-api-wrapper`.\n *\n * @param transport - Optional transport; defaults to the sandbox transport.\n * @returns A new `HostLocalStorage` instance, or `null` if unavailable.\n */\nexport async function createHostLocalStorage(\n transport?: Transport,\n): Promise<HostLocalStorage | null> {\n if (!(await isInsideContainer())) return null;\n\n try {\n const sdk = await import(\"@novasamatech/host-api-wrapper\");\n return sdk.createLocalStorage(transport);\n } catch (err) {\n log.debug(\"createHostLocalStorage unavailable\", err);\n return null;\n }\n}\n\n/**\n * Get a PAPI-compatible JSON-RPC provider that routes through the host connection.\n *\n * When running inside a Polkadot container, this wraps the chain connection via the\n * host's `createPapiProvider`, enabling shared connections and efficient routing.\n * Returns `null` when `@novasamatech/host-api-wrapper` is unavailable or when not\n * running inside a container.\n *\n * @param genesisHash - Genesis hash of the target chain (`0x`-prefixed hex string).\n * @returns A host-routed `JsonRpcProvider`, or `null` if unavailable.\n * @throws {ChainNotSupportedError} When inside a container but the host can't serve\n * the chain — surfaced instead of returning a provider that would hang forever.\n */\nexport async function getHostProvider(genesisHash: `0x${string}`): Promise<JsonRpcProvider | null> {\n let sdk: typeof import(\"@novasamatech/host-api-wrapper\");\n try {\n sdk = await import(\"@novasamatech/host-api-wrapper\");\n } catch (err) {\n // Wrapper not installed — we're not running inside a container.\n log.debug(\"getHostProvider unavailable\", err);\n return null;\n }\n return resolveHostProvider(sdk, genesisHash);\n}\n\n/**\n * Decide whether to build a host provider for `genesisHash`, given the resolved\n * wrapper module. Split out of {@link getHostProvider} so the decision logic can\n * be unit-tested with a fake wrapper, without re-importing the real\n * (browser-only) module.\n *\n * @returns the provider, or `null` when not inside a container.\n * @throws {ChainNotSupportedError} when the host can't serve the chain.\n */\nasync function resolveHostProvider(\n sdk: typeof import(\"@novasamatech/host-api-wrapper\"),\n genesisHash: `0x${string}`,\n): Promise<JsonRpcProvider | null> {\n // Outside a host container there is no provider to hand back. Mirrors\n // createPapiProvider's own environment guard; callers treat null as\n // \"not inside a container\".\n if (!sdk.sandboxTransport.isCorrectEnvironment()) {\n return null;\n }\n\n // Inside a container: confirm the host can actually serve this chain before\n // handing PAPI a provider. When the host doesn't support the chain, the\n // wrapper's fallback provider silently swallows every JSON-RPC request and\n // the caller hangs forever with no rejection. Surface a catchable error.\n if (!(await isChainSupportedByHost(sdk, genesisHash))) {\n throw new ChainNotSupportedError(genesisHash);\n }\n\n return sdk.createPapiProvider(genesisHash);\n}\n\n/**\n * Synchronous container detection — fast heuristic check without product-sdk.\n *\n * Checks for iframe, webview marker, and host message port signals.\n * Use this when you need a quick sync check (e.g., in hot code paths).\n * For full detection including product-sdk, use {@link isInsideContainer} (async).\n */\nexport function isInsideContainerSync(): boolean {\n if (typeof window === \"undefined\") return false;\n\n const win = window as unknown as Record<string, unknown>;\n\n // Iframe detection (polkadot.com browser)\n try {\n if (window !== window.top) return true;\n } catch {\n // Cross-origin iframe — likely inside a container\n return true;\n }\n\n // Webview detection (Polkadot Desktop)\n if (win.__HOST_WEBVIEW_MARK__ === true) return true;\n\n // Desktop message-passing API\n if (win.__HOST_API_PORT__ != null) return true;\n\n return false;\n}\n\n/**\n * Get the host API statement store when running inside a container.\n *\n * Returns a statement store with `subscribe`, `createProof`, and `submit` methods\n * that communicate through the host's native binary protocol — bypassing JSON-RPC\n * entirely. Returns `null` when `@novasamatech/host-api-wrapper` is unavailable.\n *\n * @returns The host statement store, or `null` if unavailable.\n */\nexport async function getStatementStore(): Promise<HostStatementStore | null> {\n try {\n const sdk = await import(\"@novasamatech/host-api-wrapper\");\n return sdk.createStatementStore() as HostStatementStore;\n } catch (err) {\n log.debug(\"getStatementStore unavailable\", err);\n return null;\n }\n}\n\nif (import.meta.vitest) {\n const { test, expect, vi } = import.meta.vitest;\n\n // A self-contained stand-in for the host wrapper, so the chain-support\n // decision can be tested without re-importing the real (browser-only) module.\n const fakeProvider = (() => {}) as unknown as JsonRpcProvider;\n function makeFakeSdk(opts: {\n inContainer?: boolean;\n ready?: boolean;\n supported?: boolean;\n featureErr?: string | null;\n onCreate?: (genesisHash: string) => void;\n }) {\n const { inContainer = true, ready = true, supported = true, featureErr = null } = opts;\n return {\n sandboxTransport: {\n isCorrectEnvironment: () => inContainer,\n isReady: async () => ready,\n },\n hostApi: {\n featureSupported: (_payload: unknown) => ({\n match: (\n okFn: (ok: { tag: string; value: boolean }) => boolean,\n errFn: (err: { value: { payload: { reason: string } } }) => boolean,\n ) =>\n featureErr\n ? errFn({ value: { payload: { reason: featureErr } } })\n : okFn({ tag: \"v1\", value: supported }),\n }),\n },\n createPapiProvider: (genesisHash: string) => {\n opts.onCreate?.(genesisHash);\n return fakeProvider;\n },\n } as unknown as typeof import(\"@novasamatech/host-api-wrapper\");\n }\n\n test(\"returns false in Node environment (no window)\", async () => {\n expect(await isInsideContainer()).toBe(false);\n });\n\n test(\"manualDetection returns true for __HOST_WEBVIEW_MARK__\", async () => {\n const fakeWindow = {\n top: null,\n __HOST_WEBVIEW_MARK__: true,\n };\n vi.stubGlobal(\"window\", fakeWindow);\n const result = await isInsideContainer();\n expect(result).toBe(true);\n vi.unstubAllGlobals();\n });\n\n test(\"manualDetection returns true for __HOST_API_PORT__\", async () => {\n const fakeWindow = {\n top: null,\n __HOST_API_PORT__: 12345,\n };\n vi.stubGlobal(\"window\", fakeWindow);\n const result = await isInsideContainer();\n expect(result).toBe(true);\n vi.unstubAllGlobals();\n });\n\n test(\"manualDetection returns false when no signals present\", async () => {\n const fakeWindow = { top: null };\n Object.defineProperty(fakeWindow, \"top\", { get: () => fakeWindow });\n vi.stubGlobal(\"window\", fakeWindow);\n const result = await isInsideContainer();\n expect(result).toBe(false);\n vi.unstubAllGlobals();\n });\n\n test(\"manualDetection returns true for cross-origin iframe\", async () => {\n const fakeWindow = {};\n Object.defineProperty(fakeWindow, \"top\", {\n get: () => {\n throw new DOMException(\"cross-origin\");\n },\n });\n vi.stubGlobal(\"window\", fakeWindow);\n const result = await isInsideContainer();\n expect(result).toBe(true);\n vi.unstubAllGlobals();\n });\n\n test(\"manualDetection returns true when window !== window.top (iframe)\", async () => {\n const fakeWindow = { top: {} }; // top is a different object\n vi.stubGlobal(\"window\", fakeWindow);\n const result = await isInsideContainer();\n expect(result).toBe(true);\n vi.unstubAllGlobals();\n });\n\n test(\"getHostLocalStorage returns null outside container\", async () => {\n expect(await getHostLocalStorage()).toBeNull();\n });\n\n test(\"createHostLocalStorage returns null outside container\", async () => {\n expect(await createHostLocalStorage()).toBeNull();\n });\n\n // --- chain-support gating (resolveHostProvider) ---\n\n test(\"resolves to the provider when supported, and null outside a container\", async () => {\n const created: string[] = [];\n const onCreate = (g: string) => created.push(g);\n\n // Inside a container, supported chain -> real provider.\n expect(await resolveHostProvider(makeFakeSdk({ onCreate }), \"0xabc\")).toBe(fakeProvider);\n // Outside a container -> null, without constructing a provider.\n expect(\n await resolveHostProvider(makeFakeSdk({ inContainer: false, onCreate }), \"0xdef\"),\n ).toBeNull();\n\n expect(created).toEqual([\"0xabc\"]);\n });\n\n test.each([\n { when: \"the host doesn't support the chain\", opts: { supported: false } },\n { when: \"the host connection never becomes ready\", opts: { ready: false } },\n ])(\"throws (and never builds a provider) when $when\", async ({ opts }) => {\n const created: string[] = [];\n const sdk = makeFakeSdk({ ...opts, onCreate: (g) => created.push(g) });\n await expect(resolveHostProvider(sdk, \"0xabc\")).rejects.toThrow();\n // Crucially: no provider is created, so PAPI never receives a hanging no-op.\n expect(created).toEqual([]);\n });\n\n test(\"unsupported chains throw a ChainNotSupportedError carrying the genesis hash\", async () => {\n const err = await resolveHostProvider(makeFakeSdk({ supported: false }), \"0xfeed\").catch(\n (e) => e,\n );\n expect(err).toBeInstanceOf(ChainNotSupportedError);\n expect((err as ChainNotSupportedError).genesisHash).toBe(\"0xfeed\");\n });\n\n test(\"getStatementStore returns null when product-sdk unavailable\", async () => {\n const result = await getStatementStore();\n expect(result).toBeNull();\n });\n}\n","// Copyright 2026 Parity Technologies (UK) Ltd.\n// SPDX-License-Identifier: Apache-2.0\n/**\n * Shared chain network configuration — single source of truth for\n * chain-specific endpoints used by multiple packages.\n */\n\n/**\n * Bulletin Chain RPC endpoints per network environment. `paseo` and `summit`\n * are populated today; `polkadot` and `kusama` are reserved for when those\n * Bulletin deployments go live.\n */\nexport const BULLETIN_RPCS = {\n paseo: [\"wss://paseo-bulletin-next-rpc.polkadot.io\"],\n summit: [\"wss://summit-bulletin-rpc.polkadot.io\"],\n polkadot: [] as string[],\n kusama: [] as string[],\n} as const;\n\n/** Default Bulletin Chain endpoint — the first entry under {@link BULLETIN_RPCS}.paseo. */\nexport const DEFAULT_BULLETIN_ENDPOINT: string = BULLETIN_RPCS.paseo[0];\n\nif (import.meta.vitest) {\n const { describe, test, expect } = import.meta.vitest;\n\n describe(\"chains config\", () => {\n test(\"BULLETIN_RPCS has paseo endpoint\", () => {\n expect(BULLETIN_RPCS.paseo.length).toBeGreaterThan(0);\n expect(BULLETIN_RPCS.paseo[0]).toMatch(/^wss:\\/\\//);\n });\n\n test(\"BULLETIN_RPCS has summit endpoint\", () => {\n expect(BULLETIN_RPCS.summit.length).toBeGreaterThan(0);\n expect(BULLETIN_RPCS.summit[0]).toMatch(/^wss:\\/\\//);\n });\n\n test(\"BULLETIN_RPCS polkadot and kusama are empty until live\", () => {\n expect(BULLETIN_RPCS.polkadot).toEqual([]);\n expect(BULLETIN_RPCS.kusama).toEqual([]);\n });\n\n test(\"DEFAULT_BULLETIN_ENDPOINT matches first paseo endpoint\", () => {\n expect(DEFAULT_BULLETIN_ENDPOINT).toBe(BULLETIN_RPCS.paseo[0]);\n });\n });\n}\n","// Copyright 2026 Parity Technologies (UK) Ltd.\n// SPDX-License-Identifier: Apache-2.0\n/**\n * TruAPI - the protocol for communicating between apps and the Polkadot host container.\n *\n * This module centralizes access to @novasamatech/host-api-wrapper and @novasamatech/host-api,\n * allowing other @parity/product-sdk-* packages to import from here rather than depending\n * directly on novasama packages.\n *\n * @module\n */\n\nimport { createLogger } from \"@parity/product-sdk-logger\";\n\nimport { enumValue } from \"@novasamatech/host-api\";\nimport type {\n AllocatableResource as AllocatableResourceCodec,\n AllocationOutcome as AllocationOutcomeCodec,\n CodecType,\n RemotePermission as RemotePermissionCodec,\n} from \"@novasamatech/host-api\";\nimport type { createAccountsProvider, preimageManager } from \"@novasamatech/host-api-wrapper\";\n\nimport { isInsideContainer } from \"./container.js\";\nimport type { Statement, StatementProof } from \"./types.js\";\n\nconst log = createLogger(\"host\");\n\n/**\n * Extract a human-readable message from a host-side error. Hosts wrap\n * errors in versioned envelopes (`{ tag: \"v1\", value: CodecError }`); this\n * helper unwraps the envelope and renders the inner error's `name`/`message`\n * so callers see the host's actual diagnostic instead of `\"[object Object]\"`\n * (from `String(err)`) or a JSON-stringified envelope.\n *\n * Exported for the higher-level wrappers (`requestPermission`,\n * `deriveEntropy`, etc.) that build their `throw new Error(...)` messages.\n */\nexport function formatHostError(err: unknown): string {\n // Single-level unwrap only; nested envelopes fall through to JSON.stringify.\n const inner = isVersionedEnvelope(err) ? err.value : err;\n\n if (inner instanceof Error) return inner.message;\n if (typeof inner === \"string\") return inner;\n if (\n inner != null &&\n typeof inner === \"object\" &&\n \"message\" in inner &&\n typeof (inner as { message: unknown }).message === \"string\"\n ) {\n const named = inner as { name?: unknown; message: string };\n return typeof named.name === \"string\" ? `${named.name}: ${named.message}` : named.message;\n }\n try {\n return JSON.stringify(inner);\n } catch {\n return String(inner);\n }\n}\n\nfunction isVersionedEnvelope(value: unknown): value is { tag: string; value: unknown } {\n return (\n value != null &&\n typeof value === \"object\" &&\n \"tag\" in value &&\n \"value\" in value &&\n typeof (value as { tag: unknown }).tag === \"string\"\n );\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Helpers from @novasamatech/host-api (re-exported from @novasamatech/scale)\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport {\n /**\n * Construct an enum variant for TruAPI calls.\n *\n * @example\n * ```ts\n * import { enumValue, getTruApi } from \"@parity/product-sdk-host\";\n *\n * const truApi = await getTruApi();\n * if (truApi) {\n * await truApi.permission([enumValue(\"ChainSubmit\")]);\n * }\n * ```\n */\n enumValue,\n /**\n * Check if a value is a specific enum variant.\n */\n isEnumVariant,\n /**\n * Assert that a value is a specific enum variant, throwing if not.\n */\n assertEnumVariant,\n /**\n * Unwrap a Result, throwing on error.\n */\n unwrapResultOrThrow,\n /**\n * Create an Ok result.\n */\n resultOk,\n /**\n * Create an Err result.\n */\n resultErr,\n /**\n * Convert bytes to hex string.\n */\n toHex,\n /**\n * Convert hex string to bytes.\n */\n fromHex,\n} from \"@novasamatech/host-api\";\n\n/** A `0x`-prefixed hex string (the template literal type ``\\`0x${string}\\``) used by the host API surface for raw byte payloads. Re-exported from `@novasamatech/host-api` so consumers bridging between host APIs and SDK code can reach the host-side type without an additional dependency. */\nexport type { HexString } from \"@novasamatech/host-api\";\n\n// ─────────────────────────────────────────────────────────────────────────────\n// TruAPI accessor\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * The TruApi type - provides low-level methods for communicating with the host.\n *\n * Methods include:\n * - `navigateTo(url)` - Navigate to a URL within the host\n * - `permission(permissions)` - Request permissions from the host\n * - `localStorageRead/Write/Clear` - Host-backed storage\n * - `sign(payload)` - Request transaction signing\n * - `deriveEntropy(context)` - Derive deterministic entropy\n * - `themeSubscribe()` - Subscribe to host theme changes\n * - And many more...\n *\n * Type identical to `hostApi` from `@novasamatech/host-api-wrapper` so that\n * `truApi.X(...)` calls keep their full inference (return types, method\n * names, parameter shapes) instead of decaying to `any`.\n */\nexport type TruApi = typeof import(\"@novasamatech/host-api-wrapper\").hostApi;\n\n/** Cached TruApi instance */\nlet cachedTruApi: TruApi | null = null;\n\n/**\n * Get the TruAPI instance for direct low-level access.\n *\n * Returns the `hostApi` object from `@novasamatech/host-api-wrapper` which provides\n * methods for communicating directly with the host container. Returns `null`\n * when running outside a container or when the SDK is unavailable.\n *\n * For most use cases, prefer the higher-level functions like `getHostLocalStorage()`,\n * `getHostProvider()`, etc. Use this when you need direct access to host methods\n * like `navigateTo()`, `permission()`, or `deriveEntropy()`.\n *\n * @example\n * ```ts\n * import { getTruApi, enumValue } from \"@parity/product-sdk-host\";\n *\n * const truApi = await getTruApi();\n * if (truApi) {\n * // Request permission\n * const result = await truApi.permission([enumValue(\"ChainSubmit\")]);\n *\n * // Navigate to a URL\n * await truApi.navigateTo(\"polkadot://settings\");\n *\n * // Subscribe to theme changes\n * const sub = truApi.themeSubscribe(undefined, (theme) => {\n * console.log(\"Theme changed:\", theme);\n * });\n * }\n * ```\n *\n * @returns The TruAPI instance, or `null` if unavailable.\n */\nexport async function getTruApi(): Promise<TruApi | null> {\n if (cachedTruApi) return cachedTruApi;\n\n try {\n const sdk = await import(\"@novasamatech/host-api-wrapper\");\n cachedTruApi = sdk.hostApi;\n log.debug(\"TruAPI loaded\");\n return cachedTruApi;\n } catch {\n log.debug(\"TruAPI unavailable (not in container or SDK not installed)\");\n return null;\n }\n}\n\n/**\n * Get the preimage manager for bulletin chain operations.\n *\n * The preimage manager handles uploading and looking up preimages (arbitrary data)\n * on the bulletin chain through the host's optimized path.\n *\n * @returns The preimage manager, or `null` if unavailable.\n *\n * @example\n * ```ts\n * import { getPreimageManager } from \"@parity/product-sdk-host\";\n *\n * const manager = await getPreimageManager();\n * if (manager) {\n * // Submit a preimage\n * const key = await manager.submit(new Uint8Array([1, 2, 3]));\n *\n * // Look up a preimage\n * const sub = manager.lookup(key, (data) => {\n * if (data) console.log(\"Found:\", data);\n * });\n * }\n * ```\n */\nexport async function getPreimageManager(): Promise<PreimageManager | null> {\n try {\n const sdk = await import(\"@novasamatech/host-api-wrapper\");\n return sdk.preimageManager;\n } catch (err) {\n log.debug(\"getPreimageManager unavailable\", err);\n return null;\n }\n}\n\n/**\n * Preimage manager handle for bulletin chain operations. `lookup` returns a\n * `Subscription<void>` (`unsubscribe` + `onInterrupt`); `submit` returns a\n * `0x`-prefixed hex preimage key.\n *\n * Type identical to `preimageManager` from `@novasamatech/host-api-wrapper`.\n */\nexport type PreimageManager = typeof preimageManager;\n\n/**\n * Construct a fresh `PreimageManager` instance with an optional custom\n * transport. Use this when you need a non-default transport; otherwise\n * prefer {@link getPreimageManager}, which returns the shared singleton.\n *\n * Mirrors `createPreimageManager` from `@novasamatech/host-api-wrapper`.\n *\n * @param transport - Optional transport; defaults to the sandbox transport.\n * @returns A new `PreimageManager` instance, or `null` if unavailable.\n */\nexport async function createHostPreimageManager(\n transport?: import(\"@novasamatech/host-api\").Transport,\n): Promise<PreimageManager | null> {\n if (!(await isInsideContainer())) return null;\n try {\n const sdk = await import(\"@novasamatech/host-api-wrapper\");\n return sdk.createPreimageManager(transport);\n } catch (err) {\n log.debug(\"createHostPreimageManager unavailable\", err);\n return null;\n }\n}\n\n/**\n * Get the accounts provider for managing host accounts.\n *\n * @returns The accounts provider, or `null` if unavailable.\n */\nexport async function getAccountsProvider(): Promise<AccountsProvider | null> {\n try {\n const sdk = await import(\"@novasamatech/host-api-wrapper\");\n return sdk.createAccountsProvider();\n } catch (err) {\n log.debug(\"getAccountsProvider unavailable\", err);\n return null;\n }\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Resource allocation\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Resource types requestable via {@link requestResourceAllocation}.\n * Derived from the upstream codec so variant renames surface as compile\n * errors, not runtime failures.\n */\nexport type AllocatableResource = CodecType<typeof AllocatableResourceCodec>;\n\n/** Tag-only view of {@link AllocatableResource} for places that just need the variant name. */\nexport type AllocatableResourceTag = AllocatableResource[\"tag\"];\n\n/**\n * Per-resource outcome from {@link requestResourceAllocation}.\n * The host strips secret payloads from `Allocated` before returning, so\n * `value` is always `undefined` on the product side.\n */\nexport type AllocationOutcome = CodecType<typeof AllocationOutcomeCodec>;\n\n/** Tag-only view of {@link AllocationOutcome} (`\"Allocated\" | \"Rejected\" | \"NotAvailable\"`). */\nexport type AllocationOutcomeTag = AllocationOutcome[\"tag\"];\n\n/**\n * Remote permission the dapp can ask the host to grant via\n * {@link requestPermission}.\n *\n * Derived from the upstream codec so variant renames surface as compile\n * errors, not runtime failures.\n */\nexport type RemotePermission = CodecType<typeof RemotePermissionCodec>;\n\n/** Tag-only view of {@link RemotePermission}. */\nexport type RemotePermissionTag = RemotePermission[\"tag\"];\n\n/**\n * Request the host to pre-allocate one or more resource allowances.\n *\n * The host prompts the user once; subsequent operations covered by the\n * granted allowance don't re-prompt.\n *\n * @param resources - Resources to request.\n * @returns Per-resource outcomes in the same order as `resources`.\n * @throws If the host is unavailable or the request fails.\n *\n * @example\n * ```ts\n * const outcomes = await requestResourceAllocation([\n * { tag: \"BulletinAllowance\", value: undefined },\n * ]);\n * if (outcomes[0].tag === \"Allocated\") { ... }\n * ```\n */\nexport async function requestResourceAllocation(\n resources: AllocatableResource[],\n): Promise<AllocationOutcome[]> {\n const truApi = await getTruApi();\n if (!truApi) {\n throw new Error(\"requestResourceAllocation: TruAPI unavailable\");\n }\n log.debug(\"requestResourceAllocation\", { resources: resources.map((r) => r.tag) });\n\n // `.match()` because the host returns a neverthrow ResultAsync, not a Promise.\n return await truApi.requestResourceAllocation(enumValue(\"v1\", resources)).match(\n (envelope: { tag: \"v1\"; value: AllocationOutcome[] }) => envelope.value,\n (err: unknown) => {\n throw new Error(`requestResourceAllocation failed: ${formatHostError(err)}`, {\n cause: err,\n });\n },\n );\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Authorized Statement Store proof creation (RFC-10 §\"Statement Store allowance\")\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Have the host sign a Statement using an allowance-bearing account it\n * picks internally — RFC-10 §\"Statement Store allowance\".\n *\n * The product passes only the Statement payload; the host chooses the\n * `//allowance//statement-store//{productId}` account that holds SSS\n * allowance and signs with it. Allowance is provisioned implicitly on\n * first use if the host hasn't already pre-allocated via\n * {@link requestResourceAllocation}; products never see the signing\n * account or its key material.\n *\n * Pairs with {@link getStatementStore}'s `submit`: call this to obtain\n * a proof, attach it to the Statement, and submit the result.\n *\n * @param statement - The Statement to be signed.\n * @returns The proof to attach before submitting.\n * @throws If the host is unavailable or the host-side signing fails.\n *\n * @example\n * ```ts\n * import { createProofAuthorized, getStatementStore } from \"@parity/product-sdk-host\";\n *\n * const statement = {\n * proof: undefined,\n * decryptionKey: undefined,\n * expiry: undefined,\n * channel: undefined,\n * topics: [],\n * data: payload,\n * };\n * const proof = await createProofAuthorized(statement);\n * const store = await getStatementStore();\n * await store?.submit({ ...statement, proof });\n * ```\n *\n * @remarks\n * RFC-10 introduces this as a new, strictly additive TruAPI call. The\n * pre-existing `HostStatementStore.createProof(accountId, statement)`\n * surface stays available for products that own a non-allowance signing\n * account; this wrapper is the sponsored-submission path.\n */\nexport async function createProofAuthorized(statement: Statement): Promise<StatementProof> {\n const truApi = await getTruApi();\n if (!truApi) {\n throw new Error(\"createProofAuthorized: TruAPI unavailable\");\n }\n log.debug(\"createProofAuthorized\", {\n topics: statement.topics.length,\n dataLen: statement.data?.length ?? 0,\n });\n\n // `.match()` because the host returns a neverthrow ResultAsync, not a Promise.\n return await truApi.statementStoreCreateProofAuthorized(enumValue(\"v1\", statement)).match(\n (envelope: { tag: \"v1\"; value: StatementProof }) => envelope.value,\n (err: unknown) => {\n throw new Error(`createProofAuthorized failed: ${formatHostError(err)}`, {\n cause: err,\n });\n },\n );\n}\n\n/**\n * One of the user's existing wallet accounts, surfaced through the host and\n * identified by its public key and an optional name. Contrast with\n * {@link ProductAccount}, which is also user-controlled but derived by the\n * host for a specific app rather than picked from the user's existing keys.\n */\nexport interface HostAccount {\n publicKey: Uint8Array;\n name?: string;\n}\n\n/**\n * A product account — an app-scoped derived account managed by the host wallet.\n *\n * The host derives a unique keypair for each app (identified by `dotNsIdentifier`)\n * so apps get their own account that the user controls but is scoped to the app.\n */\nexport interface ProductAccount {\n /** App identifier (e.g., \"mark3t.dot\"). */\n dotNsIdentifier: string;\n /** Derivation index within the app scope. Default: 0 */\n derivationIndex: number;\n /** Raw public key (32 bytes). */\n publicKey: Uint8Array;\n}\n\n/**\n * A contextual alias obtained from Ring VRF.\n *\n * Proves account membership in a ring without revealing which account.\n */\nexport interface ContextualAlias {\n /** Ring context (32 bytes). */\n context: Uint8Array;\n /** The Ring VRF alias bytes. */\n alias: Uint8Array;\n}\n\n/**\n * Neverthrow-style ResultAsync returned by product-sdk methods.\n *\n * Use `.match(onOk, onErr)` to handle success/error cases.\n */\nexport interface ResultAsync<T, E> {\n match: <A, B = A>(ok: (t: T) => A, err: (e: E) => B) => Promise<A | B>;\n}\n\n/**\n * Accounts provider handle from `@novasamatech/host-api-wrapper`. Surfaces the\n * full upstream API - host wallet accounts, app-scoped product accounts,\n * Ring VRF, user identity (`getUserId`, `requestLogin`), and connection\n * status subscription.\n *\n * Type identical to `createAccountsProvider()` from\n * `@novasamatech/host-api-wrapper`; methods return neverthrow `ResultAsync`\n * values with typed `CodecError` variants in the error channel.\n */\nexport type AccountsProvider = ReturnType<typeof createAccountsProvider>;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Tests\n// ─────────────────────────────────────────────────────────────────────────────\n\nif (import.meta.vitest) {\n const { test, expect } = import.meta.vitest;\n\n test(\"getTruApi returns TruApi when SDK is available\", async () => {\n // Reset cache for test\n cachedTruApi = null;\n const api = await getTruApi();\n // In dev/test mode, product-sdk is installed\n expect(api === null || typeof api === \"object\").toBe(true);\n });\n\n test(\"getPreimageManager returns manager when SDK is available\", async () => {\n const manager = await getPreimageManager();\n // In dev/test mode, product-sdk is installed\n expect(manager === null || typeof manager === \"object\").toBe(true);\n });\n\n test(\"createHostPreimageManager returns null outside container\", async () => {\n expect(await createHostPreimageManager()).toBeNull();\n });\n\n test(\"formatHostError unwraps versioned envelopes and renders CodecError\", () => {\n expect(\n formatHostError({\n tag: \"v1\",\n value: { name: \"GenericError\", message: \"boom\" },\n }),\n ).toBe(\"GenericError: boom\");\n expect(formatHostError(new Error(\"plain\"))).toBe(\"plain\");\n expect(formatHostError(\"string err\")).toBe(\"string err\");\n expect(formatHostError({ tag: \"v1\", value: { message: \"no-name\" } })).toBe(\"no-name\");\n });\n\n test(\"getAccountsProvider returns provider when SDK is available\", async () => {\n // In dev/test mode, product-sdk is installed, so this returns a provider\n const provider = await getAccountsProvider();\n // Just verify it returns something (null when SDK unavailable, provider when available)\n expect(provider === null || typeof provider === \"object\").toBe(true);\n });\n\n test(\"enumValue is exported\", async () => {\n const { enumValue } = await import(\"./truapi.js\");\n expect(typeof enumValue).toBe(\"function\");\n });\n\n test(\"requestResourceAllocation throws when TruAPI is unavailable\", async () => {\n cachedTruApi = null;\n const api = await getTruApi();\n if (api === null) {\n await expect(\n requestResourceAllocation([{ tag: \"BulletinAllowance\", value: undefined }]),\n ).rejects.toThrow(/TruAPI unavailable/);\n } else {\n expect(typeof requestResourceAllocation).toBe(\"function\");\n }\n });\n\n test(\"createProofAuthorized throws when TruAPI is unavailable\", async () => {\n cachedTruApi = null;\n const api = await getTruApi();\n if (api === null) {\n await expect(\n createProofAuthorized({\n proof: undefined,\n decryptionKey: undefined,\n expiry: undefined,\n channel: undefined,\n topics: [],\n data: undefined,\n }),\n ).rejects.toThrow(/TruAPI unavailable/);\n } else {\n expect(typeof createProofAuthorized).toBe(\"function\");\n }\n });\n}\n","// Copyright 2026 Parity Technologies (UK) Ltd.\n// SPDX-License-Identifier: Apache-2.0\n/**\n * Higher-level wrappers for the host's single-permission flows.\n *\n * `hostApi.permission` / `hostApi.devicePermission` take a versioned\n * envelope (`enumValue(\"v1\", ...)`) and return a neverthrow `ResultAsync`\n * of an unwrapped versioned response. Consumers rebuild that wrap/unwrap\n * dance every time. {@link requestPermission} and\n * {@link requestDevicePermission} collapse it to one-liners that match the\n * shape of {@link requestResourceAllocation} (throws on error, returns\n * the unwrapped payload on success).\n *\n * @module\n */\n\nimport { createLogger } from \"@parity/product-sdk-logger\";\n\nimport type { CodecType } from \"@novasamatech/host-api\";\nimport type { DevicePermission as DevicePermissionCodec } from \"@novasamatech/host-api\";\n\nimport { enumValue, formatHostError, getTruApi, type RemotePermission } from \"./truapi.js\";\n\nconst log = createLogger(\"host:permissions\");\n\n/**\n * Device permission the dapp can ask the host to grant via\n * {@link requestDevicePermission}.\n *\n * Derived from the upstream codec so variant renames surface as compile\n * errors, not runtime failures.\n */\nexport type DevicePermissionKind = CodecType<typeof DevicePermissionCodec>;\n\n/**\n * Alias of {@link RemotePermission} matching the upstream\n * `@novasamatech/host-api-wrapper` name. Use either freely.\n */\nexport type RemotePermissionItem = RemotePermission;\n\n/**\n * Request a single remote permission from the host.\n *\n * Builds the `v1` envelope, calls `hostApi.permission`, unwraps the response,\n * and returns the host's boolean granted/denied outcome.\n *\n * @param permission - The remote permission to request.\n * @returns `true` if the host granted the permission, `false` if denied.\n * @throws If the host is unavailable or the request fails.\n *\n * @example\n * ```ts\n * const granted = await requestPermission({ tag: \"ChainSubmit\", value: undefined });\n * if (!granted) {\n * tellUserToReconnect();\n * }\n * ```\n */\nexport async function requestPermission(permission: RemotePermission): Promise<boolean> {\n const truApi = await getTruApi();\n if (!truApi) {\n throw new Error(\"requestPermission: TruAPI unavailable\");\n }\n log.debug(\"requestPermission\", { tag: permission.tag });\n\n return await truApi.permission(enumValue(\"v1\", permission)).match(\n (envelope: { tag: \"v1\"; value: boolean }) => envelope.value,\n (err: unknown) => {\n throw new Error(`requestPermission failed: ${formatHostError(err)}`, { cause: err });\n },\n );\n}\n\n/**\n * Request a single device permission (camera, microphone, etc.) from the\n * host.\n *\n * Builds the `v1` envelope, calls `hostApi.devicePermission`, unwraps the\n * response, and returns the host's boolean granted/denied outcome.\n *\n * @param permission - The device permission to request.\n * @returns `true` if the host granted the permission, `false` if denied.\n * @throws If the host is unavailable or the request fails.\n *\n * @example\n * ```ts\n * const granted = await requestDevicePermission(\"Camera\");\n * if (!granted) {\n * showCameraDeniedMessage();\n * }\n * ```\n */\nexport async function requestDevicePermission(permission: DevicePermissionKind): Promise<boolean> {\n const truApi = await getTruApi();\n if (!truApi) {\n throw new Error(\"requestDevicePermission: TruAPI unavailable\");\n }\n log.debug(\"requestDevicePermission\", { permission });\n\n return await truApi.devicePermission(enumValue(\"v1\", permission)).match(\n (envelope: { tag: \"v1\"; value: boolean }) => envelope.value,\n (err: unknown) => {\n throw new Error(`requestDevicePermission failed: ${formatHostError(err)}`, {\n cause: err,\n });\n },\n );\n}\n\nif (import.meta.vitest) {\n const { test, expect, describe, vi } = import.meta.vitest;\n\n async function withMockedTruApi<T>(\n bridge: {\n permission?: (req: unknown) => unknown;\n devicePermission?: (req: unknown) => unknown;\n } | null,\n fn: (mod: typeof import(\"./permissions.js\")) => Promise<T>,\n ): Promise<T> {\n vi.resetModules();\n vi.doMock(\"./truapi.js\", async (importOriginal) => {\n const original = await importOriginal<typeof import(\"./truapi.js\")>();\n return {\n ...original,\n getTruApi: async () => bridge,\n enumValue: (version: string, value: unknown) => ({ tag: version, value }),\n };\n });\n try {\n const mod = await import(\"./permissions.js\");\n return await fn(mod);\n } finally {\n vi.doUnmock(\"./truapi.js\");\n vi.resetModules();\n }\n }\n\n describe(\"requestPermission\", () => {\n test(\"throws when TruAPI is unavailable\", async () => {\n await withMockedTruApi(null, async (mod) => {\n await expect(\n mod.requestPermission({ tag: \"ChainSubmit\", value: undefined }),\n ).rejects.toThrow(/TruAPI unavailable/);\n });\n });\n\n test(\"unwraps the v1 boolean outcome\", async () => {\n await withMockedTruApi(\n {\n permission: vi.fn().mockReturnValue({\n match: async (onOk: (v: unknown) => unknown) =>\n onOk({ tag: \"v1\", value: true }),\n }),\n },\n async (mod) => {\n const granted = await mod.requestPermission({\n tag: \"ChainSubmit\",\n value: undefined,\n });\n expect(granted).toBe(true);\n },\n );\n });\n\n test(\"wraps host errors with a diagnostic message\", async () => {\n await withMockedTruApi(\n {\n permission: vi.fn().mockReturnValue({\n match: async (\n _onOk: (v: unknown) => unknown,\n onErr: (e: unknown) => unknown,\n ) =>\n onErr({\n tag: \"v1\",\n value: { name: \"GenericError\", message: \"boom\" },\n }),\n }),\n },\n async (mod) => {\n await expect(\n mod.requestPermission({ tag: \"ChainSubmit\", value: undefined }),\n ).rejects.toThrow(/requestPermission failed: GenericError: boom/);\n },\n );\n });\n });\n\n describe(\"requestDevicePermission\", () => {\n test(\"throws when TruAPI is unavailable\", async () => {\n await withMockedTruApi(null, async (mod) => {\n await expect(mod.requestDevicePermission(\"Camera\")).rejects.toThrow(\n /TruAPI unavailable/,\n );\n });\n });\n\n test(\"unwraps the v1 boolean outcome\", async () => {\n await withMockedTruApi(\n {\n devicePermission: vi.fn().mockReturnValue({\n match: async (onOk: (v: unknown) => unknown) =>\n onOk({ tag: \"v1\", value: true }),\n }),\n },\n async (mod) => {\n const granted = await mod.requestDevicePermission(\"Camera\");\n expect(granted).toBe(true);\n },\n );\n });\n\n test(\"wraps host errors with a diagnostic message\", async () => {\n await withMockedTruApi(\n {\n devicePermission: vi.fn().mockReturnValue({\n match: async (\n _onOk: (v: unknown) => unknown,\n onErr: (e: unknown) => unknown,\n ) =>\n onErr({\n tag: \"v1\",\n value: { name: \"GenericError\", message: \"boom\" },\n }),\n }),\n },\n async (mod) => {\n await expect(mod.requestDevicePermission(\"Camera\")).rejects.toThrow(\n /requestDevicePermission failed: GenericError: boom/,\n );\n },\n );\n });\n });\n}\n","// Copyright 2026 Parity Technologies (UK) Ltd.\n// SPDX-License-Identifier: Apache-2.0\n/**\n * Higher-level wrapper for the host's theme subscription.\n *\n * `hostApi.themeSubscribe` is reachable via {@link getTruApi}, but consumers\n * have to wire the subscription envelope themselves. `getThemeProvider`\n * returns the `@novasamatech/host-api-wrapper` theme provider object directly,\n * giving callers a `subscribeTheme(cb)` method that resolves to a typed\n * {@link ThemeMode} — a `{ name, variant }` struct where `variant` is\n * `\"Light\" | \"Dark\"` — and yields a `Subscription<void>` handle.\n *\n * @remarks\n * As of `host-api(-wrapper)` v0.8 the theme payload is a struct, not a flat\n * `\"light\" | \"dark\"` string: read {@link ThemeMode.variant} for the\n * light/dark value (now capitalized) and {@link ThemeMode.name} for the\n * active theme name (`Default`, or `Custom` carrying a string id).\n *\n * @module\n */\n\nimport { createLogger } from \"@parity/product-sdk-logger\";\n\nimport type {\n createThemeProvider,\n ThemeMode as NovasamaThemeMode,\n} from \"@novasamatech/host-api-wrapper\";\n\nconst log = createLogger(\"host:theme\");\n\n/**\n * Host theme provider handle. Exposes `subscribeTheme(callback)` which\n * receives a typed {@link ThemeMode} struct on every change and returns a\n * `Subscription<void>` (`unsubscribe` + `onInterrupt`).\n *\n * Type identical to `createThemeProvider()` from\n * `@novasamatech/host-api-wrapper`.\n */\nexport type ThemeProvider = ReturnType<typeof createThemeProvider>;\n\n/**\n * Host theme value. Re-exported from `@novasamatech/host-api-wrapper`.\n *\n * A `{ name, variant }` struct as of v0.8 (previously a flat\n * `\"light\" | \"dark\"` string).\n */\nexport type ThemeMode = NovasamaThemeMode;\n\n/** Light/dark variant of the active theme: `\"Light\" | \"Dark\"`. */\nexport type ThemeVariant = ThemeMode[\"variant\"];\n\n/**\n * Active theme name: `{ tag: \"Default\" }`, or `{ tag: \"Custom\", value }`\n * carrying the custom theme's string id.\n */\nexport type ThemeName = ThemeMode[\"name\"];\n\n/**\n * Get the host theme provider.\n *\n * Returns the theme-subscription handle exported by\n * `@novasamatech/host-api-wrapper`, or `null` if the package is unavailable\n * (running outside a host container or the optional peer dep isn't\n * installed).\n *\n * Implementation note: upstream `@novasamatech/host-api-wrapper` exports only\n * the `createThemeProvider` factory and no `themeProvider` singleton, so\n * this getter constructs a fresh instance on each call (unlike\n * {@link getPreimageManager} or {@link getHostLocalStorage}, which return\n * upstream singletons). The constructed provider is cheap to allocate; it\n * only opens a subscription when `subscribeTheme` is called.\n *\n * @returns The theme provider, or `null` if unavailable.\n *\n * @example\n * ```ts\n * import { getThemeProvider } from \"@parity/product-sdk-host\";\n *\n * const provider = await getThemeProvider();\n * if (provider) {\n * const sub = provider.subscribeTheme((theme) => {\n * document.documentElement.dataset.theme = theme.variant.toLowerCase();\n * if (theme.name.tag === \"Custom\") loadCustomTheme(theme.name.value);\n * });\n * // sub.unsubscribe() to stop listening\n * }\n * ```\n */\nexport async function getThemeProvider(): Promise<ThemeProvider | null> {\n try {\n const sdk = await import(\"@novasamatech/host-api-wrapper\");\n return sdk.createThemeProvider();\n } catch (err) {\n log.debug(\"getThemeProvider unavailable\", err);\n return null;\n }\n}\n\nif (import.meta.vitest) {\n const { test, expect } = import.meta.vitest;\n\n test(\"getThemeProvider returns provider when SDK is available\", async () => {\n const provider = await getThemeProvider();\n expect(provider === null || typeof provider === \"object\").toBe(true);\n });\n}\n","// Copyright 2026 Parity Technologies (UK) Ltd.\n// SPDX-License-Identifier: Apache-2.0\n/**\n * Higher-level wrapper for the host's entropy derivation (RFC-0007).\n *\n * `hostApi.deriveEntropy` is reachable via {@link getTruApi}, but consumers\n * have to wrap the value in the versioned envelope (`enumValue(\"v1\", ...)`)\n * and unwrap the neverthrow `ResultAsync` themselves. `deriveEntropy`\n * collapses that to a throw-on-error Promise that matches the shape of\n * {@link requestPermission} and {@link requestResourceAllocation}.\n *\n * @module\n */\n\nimport { createLogger } from \"@parity/product-sdk-logger\";\n\nimport { enumValue, formatHostError, getTruApi } from \"./truapi.js\";\n\nconst log = createLogger(\"host:entropy\");\n\n/**\n * Derive deterministic entropy from a context key (RFC-0007).\n *\n * The host derives entropy from the user's wallet + the provided context\n * key. Calling with the same key on the same wallet yields the same bytes;\n * different keys (or different wallets) yield uncorrelated entropy.\n *\n * @param key - Context key bytes (typically a SCALE-encoded discriminator).\n * @returns The derived entropy bytes.\n * @throws If the host is unavailable or the host-side derivation fails.\n *\n * @example\n * ```ts\n * import { deriveEntropy } from \"@parity/product-sdk-host\";\n *\n * const seed = await deriveEntropy(new TextEncoder().encode(\"my-app:seed-v1\"));\n * ```\n */\nexport async function deriveEntropy(key: Uint8Array): Promise<Uint8Array> {\n const truApi = await getTruApi();\n if (!truApi) {\n throw new Error(\"deriveEntropy: TruAPI unavailable\");\n }\n log.debug(\"deriveEntropy\", { keyLen: key.length });\n\n return await truApi.deriveEntropy(enumValue(\"v1\", key)).match(\n (envelope: { tag: \"v1\"; value: Uint8Array }) => envelope.value,\n (err: unknown) => {\n throw new Error(`deriveEntropy failed: ${formatHostError(err)}`, { cause: err });\n },\n );\n}\n\nif (import.meta.vitest) {\n const { test, expect } = import.meta.vitest;\n\n test(\"deriveEntropy throws when TruAPI is unavailable\", async () => {\n const api = await getTruApi();\n if (api === null) {\n await expect(deriveEntropy(new Uint8Array([1, 2, 3]))).rejects.toThrow(\n /TruAPI unavailable/,\n );\n } else {\n expect(typeof deriveEntropy).toBe(\"function\");\n }\n });\n}\n","// Copyright 2026 Parity Technologies (UK) Ltd.\n// SPDX-License-Identifier: Apache-2.0\n/**\n * Wrapper for the host's chat surface (`host_chat_*` family).\n *\n * Shipped flat-in-host rather than as `getTruApi().chat.*` (the shape\n * sketched in issue #93) because the upstream JS `hostApi` is itself a\n * flat object - there is no `.chat` accessor to mirror. A flat\n * `getChatManager()` matches the pattern already used by\n * {@link getThemeProvider}, {@link getAccountsProvider}, and\n * {@link getStatementStore}; if a namespaced view is desirable later, it\n * can be layered on top without breaking this surface.\n *\n * @module\n */\n\nimport { createLogger } from \"@parity/product-sdk-logger\";\n\nimport type {\n ChatBotRegistrationResult as NovasamaChatBotRegistrationResult,\n ChatCustomMessageRenderer as NovasamaChatCustomMessageRenderer,\n ChatCustomMessageRendererParams as NovasamaChatCustomMessageRendererParams,\n ChatMessageContent as NovasamaChatMessageContent,\n ChatReceivedAction as NovasamaChatReceivedAction,\n ChatRoom as NovasamaChatRoom,\n ChatRoomRegistrationResult as NovasamaChatRoomRegistrationResult,\n createProductChatManager,\n} from \"@novasamatech/host-api-wrapper\";\n\nconst log = createLogger(\"host:chat\");\n\n/** Chat message payload variants. Re-exported from `@novasamatech/host-api-wrapper`. */\nexport type ChatMessageContent = NovasamaChatMessageContent;\n\n/** Action received via {@link ChatManager.subscribeAction}. Re-exported from `@novasamatech/host-api-wrapper`. */\nexport type ChatReceivedAction = NovasamaChatReceivedAction;\n\n/** Room metadata delivered to {@link ChatManager.subscribeChatList}. Re-exported from `@novasamatech/host-api-wrapper`. */\nexport type ChatRoom = NovasamaChatRoom;\n\n/** Result of registering a chat room (`\"New\" | \"Exists\"`). Re-exported from `@novasamatech/host-api-wrapper`. */\nexport type ChatRoomRegistrationResult = NovasamaChatRoomRegistrationResult;\n\n/** Result of registering a bot (`\"New\" | \"Exists\"`). Re-exported from `@novasamatech/host-api-wrapper`. */\nexport type ChatBotRegistrationResult = NovasamaChatBotRegistrationResult;\n\n/** Renderer callback for custom message types. Re-exported from `@novasamatech/host-api-wrapper`. */\nexport type ChatCustomMessageRenderer = NovasamaChatCustomMessageRenderer;\n\n/** Parameters passed to a {@link ChatCustomMessageRenderer}. Re-exported from `@novasamatech/host-api-wrapper`. */\nexport type ChatCustomMessageRendererParams<T = Uint8Array> =\n NovasamaChatCustomMessageRendererParams<T>;\n\n/**\n * Chat manager handle. Exposes room/bot registration, message sending,\n * subscription to room list and incoming actions, and custom-renderer\n * registration.\n *\n * Type identical to `createProductChatManager()` from\n * `@novasamatech/host-api-wrapper`.\n */\nexport type ChatManager = ReturnType<typeof createProductChatManager>;\n\n/**\n * Get the host chat manager.\n *\n * Returns the chat manager from `@novasamatech/host-api-wrapper`, or `null` if\n * the package is unavailable (running outside a host container or the\n * optional peer dep isn't installed).\n *\n * @returns The chat manager, or `null` if unavailable.\n *\n * @example\n * ```ts\n * import { getChatManager } from \"@parity/product-sdk-host\";\n *\n * const chat = await getChatManager();\n * if (chat) {\n * await chat.registerBot({ botId: \"echo\", name: \"Echo Bot\", icon: \"\" });\n * chat.subscribeAction((action) => { ... });\n * }\n * ```\n */\nexport async function getChatManager(): Promise<ChatManager | null> {\n try {\n const sdk = await import(\"@novasamatech/host-api-wrapper\");\n return sdk.createProductChatManager();\n } catch (err) {\n log.debug(\"getChatManager unavailable\", err);\n return null;\n }\n}\n\n/**\n * Dispatch helper that composes multiple custom-message renderers into a\n * single {@link ChatCustomMessageRenderer} keyed by `messageType`.\n *\n * Mirrors `matchChatCustomRenderers` from `@novasamatech/host-api-wrapper`\n * inline (the upstream implementation is pure dispatch logic with no\n * transport / runtime dependency on Novasama), so callers get the same\n * sync signature instead of an async-with-null wrapper.\n *\n * @param map - Object mapping `messageType` strings to renderers.\n * @returns A composed renderer that dispatches to the entry matching\n * `params.messageType`, or throws if no renderer is registered.\n */\nexport function matchChatCustomRenderers(\n map: Record<string, ChatCustomMessageRenderer>,\n): ChatCustomMessageRenderer {\n return (params, render) => {\n const renderer = map[params.messageType];\n if (!renderer) {\n throw new Error(`Renderer for message type ${params.messageType} is not defined`);\n }\n return renderer(params, render);\n };\n}\n\nif (import.meta.vitest) {\n const { test, expect } = import.meta.vitest;\n\n test(\"getChatManager returns manager when SDK is available\", async () => {\n const chat = await getChatManager();\n expect(chat === null || typeof chat === \"object\").toBe(true);\n });\n}\n","// Copyright 2026 Parity Technologies (UK) Ltd.\n// SPDX-License-Identifier: Apache-2.0\n/**\n * Wrapper for the host's payment manager (RFC-0006).\n *\n * Shipped flat-in-host rather than as `getTruApi().payment.*` because the\n * upstream JS `hostApi` is itself a flat object - there is no `.payment`\n * accessor to mirror. A flat `getPaymentManager()` matches the singleton\n * pattern already used by {@link getPreimageManager},\n * {@link getHostLocalStorage}, and {@link getAccountsProvider}.\n *\n * Returns the shared `paymentManager` singleton from\n * `@novasamatech/host-api-wrapper` (not a fresh `createPaymentManager()`\n * instance) so callers share one wrapper + hostApi closure across the app.\n *\n * Distinct from the CoinPayment / merchant-payments surface tracked under\n * `@parity/product-sdk-merchant-payments` (RFC-0017). RFC-0006 is the\n * user-initiated balance / top-up / payment-request flow; RFC-0017 is the\n * merchant-initiated checkout flow.\n *\n * @module\n */\n\nimport { createLogger } from \"@parity/product-sdk-logger\";\n\nimport type {\n PaymentBalance as NovasamaPaymentBalance,\n PaymentStatus as NovasamaPaymentStatus,\n TopUpSource as NovasamaTopUpSource,\n paymentManager,\n} from \"@novasamatech/host-api-wrapper\";\n\nconst log = createLogger(\"host:payments\");\n\n/** Available balance for the user's payment account. Re-exported from `@novasamatech/host-api-wrapper`. */\nexport type PaymentBalance = NovasamaPaymentBalance;\n\n/** Status of an in-flight payment request. Re-exported from `@novasamatech/host-api-wrapper`. */\nexport type PaymentStatus = NovasamaPaymentStatus;\n\n/** Source for {@link PaymentManager.topUp}. Re-exported from `@novasamatech/host-api-wrapper`. */\nexport type TopUpSource = NovasamaTopUpSource;\n\n/**\n * Payment manager handle. Exposes balance subscription, top-up, payment\n * requests, and payment status subscription.\n *\n * Type identical to `paymentManager` from `@novasamatech/host-api-wrapper`.\n */\nexport type PaymentManager = typeof paymentManager;\n\n/**\n * Get the host payment manager.\n *\n * Returns the shared `paymentManager` singleton from\n * `@novasamatech/host-api-wrapper`, or `null` if the package is unavailable\n * (running outside a host container or the optional peer dep isn't\n * installed).\n *\n * @returns The payment manager, or `null` if unavailable.\n *\n * @example\n * ```ts\n * import { getPaymentManager } from \"@parity/product-sdk-host\";\n *\n * const payments = await getPaymentManager();\n * if (payments) {\n * const sub = payments.subscribeBalance((b) => { ... });\n * await payments.topUp(1_000_000n, { type: \"productAccount\", derivationIndex: 0 });\n * const destination = new Uint8Array(32);\n * const { id } = await payments.requestPayment(500n, destination);\n * sub.unsubscribe();\n * }\n * ```\n */\nexport async function getPaymentManager(): Promise<PaymentManager | null> {\n try {\n const sdk = await import(\"@novasamatech/host-api-wrapper\");\n return sdk.paymentManager;\n } catch (err) {\n log.debug(\"getPaymentManager unavailable\", err);\n return null;\n }\n}\n\nif (import.meta.vitest) {\n const { test, expect } = import.meta.vitest;\n\n test(\"getPaymentManager returns manager with full RFC-0006 surface when SDK is available\", async () => {\n const payments = await getPaymentManager();\n if (payments === null) {\n // Acceptable: SDK couldn't load (e.g. peer dep missing in some envs).\n return;\n }\n expect(typeof payments.subscribeBalance).toBe(\"function\");\n expect(typeof payments.topUp).toBe(\"function\");\n expect(typeof payments.requestPayment).toBe(\"function\");\n expect(typeof payments.subscribePaymentStatus).toBe(\"function\");\n });\n}\n","// Copyright 2026 Parity Technologies (UK) Ltd.\n// SPDX-License-Identifier: Apache-2.0\n/**\n * Wrapper for the host's scheduled push-notification surface.\n *\n * Shipped flat-in-host rather than as `getTruApi().notification.*` because\n * the upstream JS `hostApi` is itself a flat object - there is no\n * `.notification` accessor to mirror. A flat `getNotificationManager()`\n * matches the singleton pattern already used by {@link getPaymentManager},\n * {@link getPreimageManager}, and {@link getHostLocalStorage}.\n *\n * Returns the shared `notificationManager` singleton from\n * `@novasamatech/host-api-wrapper` (not a fresh `createNotificationManager()`\n * instance) so callers share one wrapper + hostApi closure across the app.\n *\n * {@link PushNotificationError} is re-exported from `@novasamatech/host-api`\n * so consumers can branch on `err instanceof\n * PushNotificationError.ScheduleLimitReached` (the host's pending-notification\n * cap) without importing the novasama packages directly.\n *\n * @module\n */\n\nimport { createLogger } from \"@parity/product-sdk-logger\";\n\nimport type { notificationManager } from \"@novasamatech/host-api-wrapper\";\n\nconst log = createLogger(\"host:notifications\");\n\n/**\n * Error variants the host raises when scheduling a push notification.\n *\n * A SCALE codec (with a `[Symbol.hasInstance]`), not a plain `Error`\n * subclass: branch with `err instanceof\n * PushNotificationError.ScheduleLimitReached` to detect the host's\n * platform-wide pending-notification cap, or `.Unknown` for everything\n * else. Re-exported from `@novasamatech/host-api` so consumers can\n * `instanceof`-branch without a direct novasama dependency.\n */\nexport { PushNotificationError } from \"@novasamatech/host-api\";\n\n/**\n * Host notification manager handle. Exposes `push(input)` (resolves to a\n * {@link NotificationId}) and `cancel(id)`.\n *\n * Type identical to `notificationManager` from\n * `@novasamatech/host-api-wrapper`.\n */\nexport type NotificationManager = typeof notificationManager;\n\n/**\n * Host-assigned id for a scheduled notification — pass to\n * {@link NotificationManager.cancel}. Derived from the manager's `push`\n * return type so codec changes surface here as compile errors.\n */\nexport type NotificationId = Awaited<ReturnType<NotificationManager[\"push\"]>>;\n\n/**\n * Push payload: `text`, an optional `deeplink`, and an optional\n * `scheduledAt` (omit for immediate delivery). Derived from the manager's\n * `push` parameter so the shape stays in lockstep with upstream.\n */\nexport type PushNotificationInput = Parameters<NotificationManager[\"push\"]>[0];\n\n/**\n * Get the host notification manager.\n *\n * Returns the shared `notificationManager` singleton from\n * `@novasamatech/host-api-wrapper`, or `null` if the package is unavailable\n * (running outside a host container or the optional peer dep isn't\n * installed).\n *\n * @returns The notification manager, or `null` if unavailable.\n *\n * @example\n * ```ts\n * import { getNotificationManager, PushNotificationError } from \"@parity/product-sdk-host\";\n *\n * const notifications = await getNotificationManager();\n * if (notifications) {\n * try {\n * const id = await notifications.push({\n * text: \"Doors open in 1h\",\n * scheduledAt: someUnixMs,\n * });\n * // later: await notifications.cancel(id);\n * } catch (err) {\n * if (err instanceof PushNotificationError.ScheduleLimitReached) {\n * // host hit its pending-notification cap — surface to the user\n * }\n * }\n * }\n * ```\n */\nexport async function getNotificationManager(): Promise<NotificationManager | null> {\n try {\n const sdk = await import(\"@novasamatech/host-api-wrapper\");\n return sdk.notificationManager;\n } catch (err) {\n log.debug(\"getNotificationManager unavailable\", err);\n return null;\n }\n}\n\nif (import.meta.vitest) {\n const { test, expect } = import.meta.vitest;\n\n test(\"getNotificationManager returns manager with push/cancel when SDK is available\", async () => {\n const notifications = await getNotificationManager();\n if (notifications === null) {\n // Acceptable: SDK couldn't load (e.g. peer dep missing in some envs).\n return;\n }\n expect(typeof notifications.push).toBe(\"function\");\n expect(typeof notifications.cancel).toBe(\"function\");\n });\n\n test(\"PushNotificationError is re-exported with its ScheduleLimitReached variant\", async () => {\n const { PushNotificationError } = await import(\"./notifications.js\");\n expect(PushNotificationError).toBeDefined();\n expect(PushNotificationError.ScheduleLimitReached).toBeDefined();\n });\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts","../src/transport.ts","../src/papi-provider.ts","../src/result.ts","../src/truapi.ts","../src/container.ts","../src/chains.ts","../src/accounts.ts","../src/permissions.ts","../src/theme.ts","../src/entropy.ts","../src/chat.ts","../src/payments.ts","../src/notifications.ts","../src/navigation.ts","../src/features.ts","../src/chain-spec.ts","../src/chain-transaction.ts"],"names":["log","createLogger"],"mappings":"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","file":"index.js","sourcesContent":["// Copyright 2026 Parity Technologies (UK) Ltd.\n// SPDX-License-Identifier: Apache-2.0\n/**\n * Typed errors carried on the `err` channel of the host public API's\n * {@link Result} returns.\n *\n * The hierarchy mirrors `@parity/product-sdk-signer`'s error classes\n * (`HostUnavailableError` / `HostRejectedError`), so the two layers share one\n * idiom: branch with `instanceof`, and every error is a real `Error` with a\n * stack trace and `cause`. The structured truapi wire error\n * ({@link HostErrorPayload}) rides along as {@link HostCallFailedError.payload}\n * for callers that want fine-grained tag-level handling.\n *\n * This module also owns {@link HostErrorPayload} (the wire-error shape) and\n * {@link formatHostError} (renders a payload to a message) — co-located with the\n * error classes that consume them so the host error model lives in one place.\n *\n * @module\n */\nimport type { GenericError } from \"@parity/truapi\";\n\n/**\n * The structured error payload `@parity/truapi` surfaces on the `Err` channel of\n * a host call, once unwrapped from the versioned wire envelope. Every host error\n * union is built from these:\n *\n * - the catch-all {@link GenericError} (`{ reason }`),\n * - a unit tagged variant (`{ tag }`), or\n * - a tagged variant carrying a reason (`{ tag, value: { reason } }`).\n *\n * `GenericError` is imported from `@parity/truapi`; the `{ tag }` members are a\n * deliberate widening of truapi's per-domain named variants (the formatter is\n * tag-agnostic). truapi has no umbrella error union to import today — once it\n * exports a canonical tagged-error union from codegen, replace these local\n * members with that import so the type is protocol-sourced rather than\n * hand-widened.\n *\n * This is the *payload* the host public API carries inside a\n * {@link HostCallFailedError} on the `err` channel of its `Result` returns — not\n * the error type consumers branch on.\n */\nexport type HostErrorPayload =\n | GenericError\n | { tag: string; value?: undefined }\n | { tag: string; value: { reason: string } };\n\n/** Narrow an unknown `Err`-channel value to a {@link HostErrorPayload}. */\nfunction isHostErrorPayload(error: unknown): error is HostErrorPayload {\n if (error == null || typeof error !== \"object\") return false;\n const obj = error as Record<string, unknown>;\n return typeof obj.reason === \"string\" || typeof obj.tag === \"string\";\n}\n\n/**\n * Extract a human-readable message from a host-side error.\n *\n * Renders the {@link HostErrorPayload} shapes `@parity/truapi` surfaces. Accepts\n * `unknown` because it is also the catch-all formatter for thrown adapter-method\n * `Error` messages, so it falls back to `Error`/string/JSON rendering for\n * anything that isn't a recognized host error payload.\n *\n * Used by {@link HostCallFailedError} to render its message, and by the throwing\n * adapter-method helper `unwrapHostResult`.\n */\nexport function formatHostError(error: unknown): string {\n if (error instanceof Error) return error.message;\n if (typeof error === \"string\") return error;\n\n if (isHostErrorPayload(error)) {\n if (\"tag\" in error) {\n // Tagged variant carrying a reason: { tag, value: { reason } }\n if (error.value != null && typeof error.value.reason === \"string\") {\n return `${error.tag}: ${error.value.reason}`;\n }\n // Unit tagged variant, e.g. { tag: \"Full\" } / { tag: \"PermissionDenied\" }\n return error.tag;\n }\n // GenericError: { reason }\n return error.reason;\n }\n\n if (error != null && typeof error === \"object\" && \"message\" in error) {\n const message = (error as { message: unknown }).message;\n if (typeof message === \"string\") return message;\n }\n\n try {\n return JSON.stringify(error);\n } catch {\n return String(error);\n }\n}\n\n/**\n * Base class for all host errors. Use `instanceof HostError` (or {@link isHostError})\n * to catch any host-related failure.\n */\nexport class HostError extends Error {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = \"HostError\";\n }\n}\n\n/**\n * The host API is not available — the app is running outside a Polkadot host\n * container (no injected TruAPI transport). The dominant case during local\n * development. Branch with `instanceof HostUnavailableError` to surface an\n * \"open this app in a Polkadot host\" message.\n */\nexport class HostUnavailableError extends HostError {\n constructor(message = \"Host API is not available\") {\n super(message);\n this.name = \"HostUnavailableError\";\n }\n}\n\n/**\n * A host call reached the container but failed on the `Err` channel. Wraps the\n * structured truapi {@link HostErrorPayload} as {@link payload} (also preserved\n * as `cause`); the message is rendered via {@link formatHostError}.\n */\nexport class HostCallFailedError extends HostError {\n readonly payload: HostErrorPayload;\n\n constructor(label: string, payload: HostErrorPayload) {\n super(`${label}: ${formatHostError(payload)}`, { cause: payload });\n this.name = \"HostCallFailedError\";\n this.payload = payload;\n }\n}\n\n/** Check whether a value is any {@link HostError}. */\nexport function isHostError(error: unknown): error is HostError {\n return error instanceof HostError;\n}\n\nif (import.meta.vitest) {\n const { test, expect, describe } = import.meta.vitest;\n\n describe(\"host error classes\", () => {\n test(\"HostError is the base class\", () => {\n const e = new HostUnavailableError();\n expect(e).toBeInstanceOf(HostError);\n expect(e).toBeInstanceOf(Error);\n });\n\n test(\"HostUnavailableError default message\", () => {\n const e = new HostUnavailableError();\n expect(e.name).toBe(\"HostUnavailableError\");\n expect(e.message).toBe(\"Host API is not available\");\n });\n\n test(\"HostUnavailableError custom message\", () => {\n expect(new HostUnavailableError(\"nope\").message).toBe(\"nope\");\n });\n\n test(\"HostCallFailedError renders payload and preserves it\", () => {\n const payload = { tag: \"PermissionDenied\", value: { reason: \"user said no\" } };\n const e = new HostCallFailedError(\"requestPermission failed\", payload);\n expect(e).toBeInstanceOf(HostError);\n expect(e.payload).toBe(payload);\n expect(e.cause).toBe(payload);\n expect(e.message).toBe(\"requestPermission failed: PermissionDenied: user said no\");\n });\n\n test(\"HostCallFailedError renders a GenericError payload\", () => {\n const e = new HostCallFailedError(\"submit failed\", { reason: \"timeout\" });\n expect(e.message).toBe(\"submit failed: timeout\");\n });\n\n test(\"isHostError narrows host errors only\", () => {\n expect(isHostError(new HostUnavailableError())).toBe(true);\n expect(isHostError(new HostCallFailedError(\"x\", { reason: \"y\" }))).toBe(true);\n expect(isHostError(new Error(\"plain\"))).toBe(false);\n expect(isHostError(\"string\")).toBe(false);\n });\n });\n\n describe(\"formatHostError\", () => {\n test(\"renders the TruAPI error payload shapes\", () => {\n // GenericError: { reason }\n expect(formatHostError({ reason: \"boom\" })).toBe(\"boom\");\n // Tagged variant carrying a reason: { tag, value: { reason } }\n expect(formatHostError({ tag: \"Unknown\", value: { reason: \"boom\" } })).toBe(\n \"Unknown: boom\",\n );\n // Unit tagged variant: { tag }\n expect(formatHostError({ tag: \"Full\" })).toBe(\"Full\");\n });\n\n test(\"falls back for non-host-error input\", () => {\n expect(formatHostError(new Error(\"plain\"))).toBe(\"plain\");\n expect(formatHostError(\"string err\")).toBe(\"string err\");\n expect(formatHostError({ message: \"loose\" })).toBe(\"loose\");\n });\n });\n}\n","// Copyright 2026 Parity Technologies (UK) Ltd.\n// SPDX-License-Identifier: Apache-2.0\n/**\n * Access to the in-house TruAPI client (`@parity/truapi`) for the host package.\n *\n * Environment detection and the lazily-built, cached client come from\n * `@parity/truapi/sandbox`; this module layers the product-sdk-specific glue on\n * top — an async {@link getClient} accessor and {@link subscribeWithInterrupt},\n * which adapts a truapi stream into the host's {@link HostSubscription} shape.\n *\n * @module\n */\n\nimport type { ObservableLike, TrUApiClient } from \"@parity/truapi\";\nimport { getClientSync, isCorrectEnvironment } from \"@parity/truapi/sandbox\";\n\nimport type { HostSubscription } from \"./types.js\";\n\nexport { getClientSync, isCorrectEnvironment };\n\n/**\n * Get the TruAPI client. Returns `null` outside a host container. Async wrapper\n * over {@link getClientSync} for the host wrappers that already `await` it.\n */\nexport async function getClient(): Promise<TrUApiClient | null> {\n return getClientSync();\n}\n\n/**\n * Adapt a truapi `ObservableLike` stream into the host's callback-style\n * {@link HostSubscription} (`unsubscribe` + `onInterrupt`). `onNext` fires for\n * each item; the registered `onInterrupt` callback fires when the host ends the\n * subscription server-side — which the generated client surfaces as either\n * `complete` (a host interrupt frame) or `error` (transport close). Shared by\n * the statement-store and preimage adapters, which both expose this shape.\n */\nexport function subscribeWithInterrupt<Item, Reason = never>(\n observable: ObservableLike<Item, Reason>,\n onNext: (item: Item) => void,\n): HostSubscription {\n let interruptCallback: ((reason?: unknown) => void) | undefined;\n const sub = observable.subscribe({\n next: onNext,\n error: (reason) => interruptCallback?.(reason),\n complete: () => interruptCallback?.(),\n });\n return {\n unsubscribe: () => sub.unsubscribe(),\n onInterrupt: (callback) => {\n interruptCallback = callback;\n return () => {\n if (interruptCallback === callback) interruptCallback = undefined;\n };\n },\n };\n}\n\nif (import.meta.vitest) {\n const { test, expect } = import.meta.vitest;\n\n // Environment detection and client building are covered by `@parity/truapi`'s\n // own sandbox tests; here we only assert the local glue degrades outside a\n // host container.\n test(\"getClientSync returns null outside a container\", () => {\n expect(getClientSync()).toBeNull();\n });\n\n test(\"getClient resolves null outside a container\", async () => {\n expect(await getClient()).toBeNull();\n });\n}\n","// Copyright 2026 Parity Technologies (UK) Ltd.\n// SPDX-License-Identifier: Apache-2.0\n/**\n * Host-routed PAPI `JsonRpcProvider`, backed by `truApi.chain.*`.\n *\n * This is a backport of `@novasamatech/host-api-wrapper`'s\n * `createPapiProvider` (`dist/papiProvider.js`) into product-sdk, with the\n * call layer swapped from the novasama `hostApi` to the\n * `@parity/truapi` client. The JSON-RPC ↔ chainHead bridge — request dispatch,\n * the `chainHead_v1_followEvent` notification synthesis, the synthetic\n * follow-subscription ids, and the operation/broadcast bookkeeping — is carried\n * over from the upstream module; only the per-method transport calls and their\n * error/response unwrapping are re-pointed at `truApi.chain.*`.\n *\n * **Why a bridge at all.** PAPI speaks the JSON-RPC `chainHead`/`chainSpec`/\n * `transaction` API; the host exposes the same operations as structured,\n * SCALE-typed calls. This module parses each inbound JSON-RPC message, routes it\n * to the matching `truApi.chain` method, and translates the typed follow stream\n * back into JSON-RPC notifications.\n *\n * **Divergences from the upstream:**\n * - No `getSyncProvider` wrapper / no-op fallback provider. The upstream had to\n * defer its async readiness check because `createPapiProvider` was synchronous;\n * here {@link module:container.getHostProvider} is async and runs the\n * `system.featureSupported` gate (throwing `ChainNotSupportedError`) *before*\n * building the provider, so there is no async work left to defer.\n * - `RuntimeSpec.apis` arrives as `Array<{ name, version }>` (truapi) rather than\n * the upstream's `[name, version]` tuples; {@link convertRuntimeToJsonRpc}\n * adjusts the loop accordingly.\n * - The follow stream's transport interrupt (`error`/`complete`) is surfaced as a\n * synthetic `stop` event so the consumer's substrate-client refollows; the\n * upstream's bare callback had no such channel.\n *\n * @module\n */\n\nimport type {\n JsonRpcConnection,\n JsonRpcMessage,\n JsonRpcRequest,\n} from \"@polkadot-api/json-rpc-provider\";\nimport type { JsonRpcProvider } from \"polkadot-api\";\nimport type {\n HexString,\n OperationStartedResult,\n RemoteChainHeadFollowItem,\n RuntimeType,\n StorageQueryType,\n TrUApiClient,\n} from \"@parity/truapi\";\nimport { createLogger } from \"@parity/product-sdk-logger\";\n\nimport { formatHostError } from \"./errors.js\";\nimport { subscribeWithInterrupt } from \"./transport.js\";\nimport type { HostSubscription } from \"./types.js\";\n\nconst log = createLogger(\"host:papi\");\n\n/** JSON-RPC internal-error code used for every host-side failure (matches the upstream). */\nconst JSON_RPC_INTERNAL_ERROR = -32603;\n/** JSON-RPC method-not-found code for chainHead methods the host doesn't serve. */\nconst JSON_RPC_METHOD_NOT_FOUND = -32601;\n\n/** A `chainHead_v1_followEvent` payload (loosely typed — consumed by PAPI's substrate-client). */\ntype FollowEvent = { event: string } & Record<string, unknown>;\n\n/** Map a JSON-RPC storage query-type string to the truapi `StorageQueryType` tag. */\nconst STORAGE_TYPE_MAP: Record<string, StorageQueryType> = {\n value: \"Value\",\n hash: \"Hash\",\n closestDescendantMerkleValue: \"ClosestDescendantMerkleValue\",\n descendantsValues: \"DescendantsValues\",\n descendantsHashes: \"DescendantsHashes\",\n};\n\n/**\n * Convert a truapi `RuntimeType` into the JSON-RPC `chainHead` runtime shape.\n * Returns `null` for absent/unknown runtimes. Note `RuntimeSpec.apis` is an\n * array of `{ name, version }` (truapi), flattened here into a `{ name: version }`\n * map as the JSON-RPC spec expects.\n */\nfunction convertRuntimeToJsonRpc(runtime: RuntimeType | undefined): unknown {\n if (!runtime || typeof runtime !== \"object\") return null;\n if (runtime.tag === \"Valid\") {\n const spec = runtime.value;\n const apis: Record<string, number> = {};\n for (const api of spec.apis) {\n apis[api.name] = api.version;\n }\n return {\n type: \"valid\",\n spec: {\n specName: spec.specName,\n implName: spec.implName,\n specVersion: spec.specVersion,\n implVersion: spec.implVersion,\n transactionVersion: spec.transactionVersion,\n apis,\n },\n };\n }\n if (runtime.tag === \"Invalid\") {\n return { type: \"invalid\", error: runtime.value.error };\n }\n return null;\n}\n\n/** Translate a typed follow-stream item into its JSON-RPC `followEvent` form. */\nfunction convertFollowEventToJsonRpc(item: RemoteChainHeadFollowItem): FollowEvent {\n switch (item.tag) {\n case \"Initialized\":\n return {\n event: \"initialized\",\n finalizedBlockHashes: item.value.finalizedBlockHashes,\n finalizedBlockRuntime: convertRuntimeToJsonRpc(item.value.finalizedBlockRuntime),\n };\n case \"NewBlock\":\n return {\n event: \"newBlock\",\n blockHash: item.value.blockHash,\n parentBlockHash: item.value.parentBlockHash,\n newRuntime: convertRuntimeToJsonRpc(item.value.newRuntime),\n };\n case \"BestBlockChanged\":\n return { event: \"bestBlockChanged\", bestBlockHash: item.value.bestBlockHash };\n case \"Finalized\":\n return {\n event: \"finalized\",\n finalizedBlockHashes: item.value.finalizedBlockHashes,\n prunedBlockHashes: item.value.prunedBlockHashes,\n };\n case \"OperationBodyDone\":\n return {\n event: \"operationBodyDone\",\n operationId: item.value.operationId,\n value: item.value.value,\n };\n case \"OperationCallDone\":\n return {\n event: \"operationCallDone\",\n operationId: item.value.operationId,\n output: item.value.output,\n };\n case \"OperationStorageItems\":\n return {\n event: \"operationStorageItems\",\n operationId: item.value.operationId,\n items: item.value.items,\n };\n case \"OperationStorageDone\":\n return { event: \"operationStorageDone\", operationId: item.value.operationId };\n case \"OperationWaitingForContinue\":\n return { event: \"operationWaitingForContinue\", operationId: item.value.operationId };\n case \"OperationInaccessible\":\n return { event: \"operationInaccessible\", operationId: item.value.operationId };\n case \"OperationError\":\n return {\n event: \"operationError\",\n operationId: item.value.operationId,\n error: item.value.error,\n };\n case \"Stop\":\n return { event: \"stop\" };\n default: {\n // Exhaustiveness: a new follow-item tag in @parity/truapi fails to compile here.\n const _exhaustive: never = item;\n return { event: \"stop\" };\n }\n }\n}\n\n/** Map a JSON-RPC storage query-type string to the truapi `StorageQueryType` tag. */\nfunction convertStorageType(type: string): StorageQueryType {\n return STORAGE_TYPE_MAP[type] ?? \"Value\";\n}\n\n/** Translate a truapi `OperationStartedResult` into the JSON-RPC operation-start shape. */\nfunction convertOperationResultToJsonRpc(result: OperationStartedResult): unknown {\n if (result.tag === \"Started\") {\n return { result: \"started\", operationId: result.value.operationId };\n }\n return { result: \"limitReached\" };\n}\n\n/**\n * Build a host-routed PAPI `JsonRpcProvider` for `genesisHash` over the given\n * TruAPI client. The caller is responsible for confirming the host supports the\n * chain (and for the handshake) before calling this — see\n * {@link module:container.getHostProvider}.\n */\nexport function createHostPapiProvider(\n client: TrUApiClient,\n genesisHash: HexString,\n): JsonRpcProvider {\n const chain = client.chain;\n\n return (onMessage: (message: JsonRpcMessage) => void): JsonRpcConnection => {\n const activeFollows = new Map<string, HostSubscription>();\n const activeBroadcasts = new Set<string>();\n let nextSubId = 0;\n\n const getNextSubId = () => `follow_${nextSubId++}`;\n\n function sendJsonRpcResponse(id: JsonRpcRequest[\"id\"], result: unknown): void {\n onMessage({ jsonrpc: \"2.0\", id, result } as JsonRpcMessage);\n }\n function sendJsonRpcError(id: JsonRpcRequest[\"id\"], code: number, message: string): void {\n onMessage({ jsonrpc: \"2.0\", id, error: { code, message } } as JsonRpcMessage);\n }\n function sendFollowEvent(subscription: string, event: FollowEvent): void {\n onMessage({\n jsonrpc: \"2.0\",\n method: \"chainHead_v1_followEvent\",\n params: { subscription, result: event },\n } as JsonRpcMessage);\n }\n\n /** Reject an inbound request with the host's error reason as the JSON-RPC message. */\n const hostError = (id: JsonRpcRequest[\"id\"]) => (error: unknown) =>\n sendJsonRpcError(id, JSON_RPC_INTERNAL_ERROR, formatHostError(error));\n\n function handleMessage(message: JsonRpcRequest): void {\n const { id, method } = message;\n // chainHead/chainSpec/transaction JSON-RPC params are positional arrays.\n const params = (message.params ?? []) as unknown[];\n\n switch (method) {\n case \"chainHead_v1_follow\": {\n const [withRuntime] = params as [boolean];\n const syntheticSubId = getNextSubId();\n // The Stop branch unsubscribes its own host subscription, but the\n // handle is this call's return value — the ref breaks that\n // chicken-and-egg. (Releasing before forwarding the Stop is just\n // cleanup; the consumer's synchronous refollow gets a fresh wire\n // subscription either way.)\n const ref: { handle?: HostSubscription } = {};\n ref.handle = subscribeWithInterrupt(\n chain.followHeadSubscribe({ request: { genesisHash, withRuntime } }),\n (item) => {\n if (item.tag === \"Stop\" && activeFollows.delete(syntheticSubId)) {\n ref.handle?.unsubscribe();\n }\n sendFollowEvent(syntheticSubId, convertFollowEventToJsonRpc(item));\n },\n );\n // A transport interrupt/close ends the stream without a Stop\n // item; synthesize one so the consumer refollows.\n ref.handle.onInterrupt(() => {\n if (activeFollows.delete(syntheticSubId)) {\n sendFollowEvent(syntheticSubId, { event: \"stop\" });\n }\n });\n activeFollows.set(syntheticSubId, ref.handle);\n sendJsonRpcResponse(id, syntheticSubId);\n break;\n }\n case \"chainHead_v1_unfollow\": {\n const [followSubId] = params as [string];\n const follow = activeFollows.get(followSubId);\n if (follow) {\n follow.unsubscribe();\n activeFollows.delete(followSubId);\n }\n sendJsonRpcResponse(id, null);\n break;\n }\n case \"chainHead_v1_header\": {\n const [followSubscriptionId, hash] = params as [string, HexString];\n chain\n .getHeadHeader({ genesisHash, followSubscriptionId, hash })\n .match(\n (response) => sendJsonRpcResponse(id, response.header ?? null),\n hostError(id),\n );\n break;\n }\n case \"chainHead_v1_body\": {\n const [followSubscriptionId, hash] = params as [string, HexString];\n chain\n .getHeadBody({ genesisHash, followSubscriptionId, hash })\n .match(\n (response) =>\n sendJsonRpcResponse(\n id,\n convertOperationResultToJsonRpc(response.operation),\n ),\n hostError(id),\n );\n break;\n }\n case \"chainHead_v1_storage\": {\n const [followSubscriptionId, hash, items, childTrie] = params as [\n string,\n HexString,\n Array<{ key: HexString; type: string }>,\n HexString | undefined,\n ];\n const queryItems = items.map((item) => ({\n key: item.key,\n queryType: convertStorageType(item.type),\n }));\n chain\n .getHeadStorage({\n genesisHash,\n followSubscriptionId,\n hash,\n items: queryItems,\n // PAPI passes `null` for an absent child trie, but the\n // truapi codec encodes the optional `childTrie` field as\n // `Option<Hex>` — it treats `undefined` as None yet runs\n // the inner Hex codec on `null`, which throws\n // (`null.startsWith`). Coerce `null` → `undefined`.\n childTrie: childTrie ?? undefined,\n })\n .match(\n (response) =>\n sendJsonRpcResponse(\n id,\n convertOperationResultToJsonRpc(response.operation),\n ),\n hostError(id),\n );\n break;\n }\n case \"chainHead_v1_call\": {\n const [followSubscriptionId, hash, fn, callParameters] = params as [\n string,\n HexString,\n string,\n HexString,\n ];\n chain\n .callHead({\n genesisHash,\n followSubscriptionId,\n hash,\n function: fn,\n callParameters,\n })\n .match(\n (response) =>\n sendJsonRpcResponse(\n id,\n convertOperationResultToJsonRpc(response.operation),\n ),\n hostError(id),\n );\n break;\n }\n case \"chainHead_v1_unpin\": {\n const [followSubscriptionId, hashOrHashes] = params as [\n string,\n HexString | HexString[],\n ];\n const hashes = Array.isArray(hashOrHashes) ? hashOrHashes : [hashOrHashes];\n chain\n .unpinHead({ genesisHash, followSubscriptionId, hashes })\n .match(() => sendJsonRpcResponse(id, null), hostError(id));\n break;\n }\n case \"chainHead_v1_continue\": {\n const [followSubscriptionId, operationId] = params as [string, string];\n chain\n .continueHead({ genesisHash, followSubscriptionId, operationId })\n .match(() => sendJsonRpcResponse(id, null), hostError(id));\n break;\n }\n case \"chainHead_v1_stopOperation\": {\n const [followSubscriptionId, operationId] = params as [string, string];\n chain\n .stopHeadOperation({ genesisHash, followSubscriptionId, operationId })\n .match(() => sendJsonRpcResponse(id, null), hostError(id));\n break;\n }\n case \"chainSpec_v1_genesisHash\": {\n chain\n .getSpecGenesisHash({ genesisHash })\n .match(\n (response) => sendJsonRpcResponse(id, response.genesisHash),\n hostError(id),\n );\n break;\n }\n case \"chainSpec_v1_chainName\": {\n chain\n .getSpecChainName({ genesisHash })\n .match(\n (response) => sendJsonRpcResponse(id, response.chainName),\n hostError(id),\n );\n break;\n }\n case \"chainSpec_v1_properties\": {\n chain.getSpecProperties({ genesisHash }).match((response) => {\n try {\n sendJsonRpcResponse(id, JSON.parse(response.properties));\n } catch {\n sendJsonRpcResponse(id, response.properties);\n }\n }, hostError(id));\n break;\n }\n case \"transaction_v1_broadcast\": {\n const [transaction] = params as [HexString];\n chain.broadcastTransaction({ genesisHash, transaction }).match((response) => {\n const operationId = response.operationId ?? null;\n if (operationId !== null) activeBroadcasts.add(operationId);\n sendJsonRpcResponse(id, operationId);\n }, hostError(id));\n break;\n }\n case \"transaction_v1_stop\": {\n const [operationId] = params as [string];\n activeBroadcasts.delete(operationId);\n chain\n .stopTransaction({ genesisHash, operationId })\n .match(() => sendJsonRpcResponse(id, null), hostError(id));\n break;\n }\n default:\n sendJsonRpcError(\n id,\n JSON_RPC_METHOD_NOT_FOUND,\n `Method \"${method}\" is not supported by the host`,\n );\n break;\n }\n }\n\n return {\n send(message) {\n // A synchronous throw inside a handler (e.g. a malformed positional\n // param) must not leave the JSON-RPC request unsettled — the caller\n // would hang. Surface it as an error response for the request id.\n try {\n handleMessage(message);\n } catch (error) {\n // Handlers must settle the request themselves; reaching here means a\n // synchronous throw before any response was sent. (A handler that\n // sends then throws would double-respond — none do today.)\n log.warn(\"send: handler threw before settling the request\", {\n error: formatHostError(error),\n });\n sendJsonRpcError(message.id, JSON_RPC_INTERNAL_ERROR, formatHostError(error));\n }\n },\n disconnect() {\n for (const handle of activeFollows.values()) {\n handle.unsubscribe();\n }\n activeFollows.clear();\n for (const operationId of activeBroadcasts) {\n // Fire-and-forget: the transport may already be torn down.\n chain.stopTransaction({ genesisHash, operationId }).match(\n () => {},\n () => {},\n );\n }\n activeBroadcasts.clear();\n },\n };\n };\n}\n\nif (import.meta.vitest) {\n const { test, expect, vi } = import.meta.vitest;\n\n // A minimal fake of the truapi chain domain. ResultAsync is approximated by a\n // `.match(ok, err)` thenable; the follow subscription is a hand-driven\n // ObservableLike whose observer we capture to push items at will.\n function makeFakeClient(opts: {\n onCall?: (method: string, args: unknown) => void;\n responses?: Record<string, unknown>;\n /** Methods named here resolve to their `.match` error arm carrying the given value. */\n errors?: Record<string, unknown>;\n /** Unsubscribe spy used by the follow subscription (defaults to a fresh `vi.fn()`). */\n unsubscribe?: () => void;\n captureObserver?: (observer: {\n next: (i: unknown) => void;\n error: (e: unknown) => void;\n complete: () => void;\n }) => void;\n }) {\n const okMatch = (value: unknown) => ({\n match: (ok: (v: unknown) => unknown, _err: (e: unknown) => unknown) => ok(value),\n });\n const errMatch = (error: unknown) => ({\n match: (_ok: (v: unknown) => unknown, err: (e: unknown) => unknown) => err(error),\n });\n const method = (name: string, response: unknown) => (args: unknown) => {\n opts.onCall?.(name, args);\n const errors = opts.errors ?? {};\n return name in errors ? errMatch(errors[name]) : okMatch(response);\n };\n return {\n chain: {\n followHeadSubscribe: (_args: unknown) => ({\n subscribe: (observer: {\n next: (i: unknown) => void;\n error: (e: unknown) => void;\n complete: () => void;\n }) => {\n opts.captureObserver?.(observer);\n return { unsubscribe: opts.unsubscribe ?? vi.fn() };\n },\n [Symbol.observable as symbol]() {\n return this;\n },\n }),\n getHeadHeader: method(\n \"getHeadHeader\",\n opts.responses?.getHeadHeader ?? { header: \"0x01\" },\n ),\n getHeadBody: method(\"getHeadBody\", {\n operation: { tag: \"Started\", value: { operationId: \"op1\" } },\n }),\n getHeadStorage: method(\"getHeadStorage\", { operation: { tag: \"LimitReached\" } }),\n callHead: method(\"callHead\", {\n operation: { tag: \"Started\", value: { operationId: \"op2\" } },\n }),\n unpinHead: method(\"unpinHead\", undefined),\n continueHead: method(\"continueHead\", undefined),\n stopHeadOperation: method(\"stopHeadOperation\", undefined),\n getSpecGenesisHash: method(\"getSpecGenesisHash\", { genesisHash: \"0xabc\" }),\n getSpecChainName: method(\"getSpecChainName\", { chainName: \"Polkadot\" }),\n getSpecProperties: method(\"getSpecProperties\", { properties: '{\"ss58Format\":0}' }),\n broadcastTransaction: method(\"broadcastTransaction\", { operationId: \"bc1\" }),\n stopTransaction: method(\"stopTransaction\", undefined),\n },\n } as unknown as TrUApiClient;\n }\n\n test(\"follow returns a synthetic id and forwards translated events\", () => {\n let observer:\n | { next: (i: unknown) => void; error: (e: unknown) => void; complete: () => void }\n | undefined;\n const client = makeFakeClient({\n captureObserver: (o) => {\n observer = o;\n },\n });\n const provider = createHostPapiProvider(client, \"0xfeed\");\n\n const messages: JsonRpcMessage[] = [];\n const conn = provider((m) => messages.push(m));\n\n conn.send({ jsonrpc: \"2.0\", id: 1, method: \"chainHead_v1_follow\", params: [true] });\n // The follow response carries the synthetic subscription id.\n expect(messages[0]).toEqual({ jsonrpc: \"2.0\", id: 1, result: \"follow_0\" });\n\n // A typed BestBlockChanged item becomes a chainHead_v1_followEvent.\n observer?.next({ tag: \"BestBlockChanged\", value: { bestBlockHash: \"0xbeef\" } });\n expect(messages[1]).toEqual({\n jsonrpc: \"2.0\",\n method: \"chainHead_v1_followEvent\",\n params: {\n subscription: \"follow_0\",\n result: { event: \"bestBlockChanged\", bestBlockHash: \"0xbeef\" },\n },\n });\n });\n\n test(\"chainSpec_v1_properties parses the JSON-encoded properties string\", () => {\n const client = makeFakeClient({});\n const provider = createHostPapiProvider(client, \"0xfeed\");\n const messages: JsonRpcMessage[] = [];\n provider((m) => messages.push(m)).send({\n jsonrpc: \"2.0\",\n id: 7,\n method: \"chainSpec_v1_properties\",\n params: [],\n });\n expect(messages[0]).toEqual({ jsonrpc: \"2.0\", id: 7, result: { ss58Format: 0 } });\n });\n\n test(\"chainHead_v1_body unwraps the operation into a JSON-RPC start result\", () => {\n const calls: Array<[string, unknown]> = [];\n const client = makeFakeClient({ onCall: (m, a) => calls.push([m, a]) });\n const provider = createHostPapiProvider(client, \"0xfeed\");\n const messages: JsonRpcMessage[] = [];\n provider((m) => messages.push(m)).send({\n jsonrpc: \"2.0\",\n id: 3,\n method: \"chainHead_v1_body\",\n params: [\"follow_0\", \"0xhash\"],\n });\n expect(calls[0]).toEqual([\n \"getHeadBody\",\n { genesisHash: \"0xfeed\", followSubscriptionId: \"follow_0\", hash: \"0xhash\" },\n ]);\n expect(messages[0]).toEqual({\n jsonrpc: \"2.0\",\n id: 3,\n result: { result: \"started\", operationId: \"op1\" },\n });\n });\n\n test(\"unknown methods produce a method-not-found error\", () => {\n const client = makeFakeClient({});\n const provider = createHostPapiProvider(client, \"0xfeed\");\n const messages: JsonRpcMessage[] = [];\n provider((m) => messages.push(m)).send({\n jsonrpc: \"2.0\",\n id: 9,\n method: \"system_health\",\n params: [],\n });\n expect(messages[0]).toMatchObject({ id: 9, error: { code: -32601 } });\n });\n\n test(\"a transport interrupt synthesizes a stop follow-event\", () => {\n let observer:\n | { next: (i: unknown) => void; error: (e: unknown) => void; complete: () => void }\n | undefined;\n const client = makeFakeClient({\n captureObserver: (o) => {\n observer = o;\n },\n });\n const provider = createHostPapiProvider(client, \"0xfeed\");\n const messages: JsonRpcMessage[] = [];\n const conn = provider((m) => messages.push(m));\n\n conn.send({ jsonrpc: \"2.0\", id: 1, method: \"chainHead_v1_follow\", params: [true] });\n // The stream ends via transport close (no Stop item) — the consumer must\n // still see a stop so its substrate-client refollows.\n observer?.complete();\n expect(messages[1]).toEqual({\n jsonrpc: \"2.0\",\n method: \"chainHead_v1_followEvent\",\n params: { subscription: \"follow_0\", result: { event: \"stop\" } },\n });\n });\n\n test(\"a host error becomes a JSON-RPC -32603 with the formatted reason\", () => {\n const client = makeFakeClient({ errors: { getHeadHeader: { reason: \"no such block\" } } });\n const provider = createHostPapiProvider(client, \"0xfeed\");\n const messages: JsonRpcMessage[] = [];\n provider((m) => messages.push(m)).send({\n jsonrpc: \"2.0\",\n id: 4,\n method: \"chainHead_v1_header\",\n params: [\"follow_0\", \"0xhash\"],\n });\n expect(messages[0]).toEqual({\n jsonrpc: \"2.0\",\n id: 4,\n error: { code: -32603, message: \"no such block\" },\n });\n });\n\n test(\"disconnect unsubscribes active follows and stops active broadcasts\", () => {\n const unsubscribe = vi.fn();\n const calls: Array<[string, unknown]> = [];\n const client = makeFakeClient({ unsubscribe, onCall: (m, a) => calls.push([m, a]) });\n const provider = createHostPapiProvider(client, \"0xfeed\");\n const conn = provider(() => {});\n\n // Open a follow and an in-flight broadcast (operationId \"bc1\" is tracked).\n conn.send({ jsonrpc: \"2.0\", id: 1, method: \"chainHead_v1_follow\", params: [true] });\n conn.send({ jsonrpc: \"2.0\", id: 2, method: \"transaction_v1_broadcast\", params: [\"0xtx\"] });\n\n conn.disconnect();\n\n // The follow subscription is torn down...\n expect(unsubscribe).toHaveBeenCalledTimes(1);\n // ...and the tracked broadcast is stopped on the way out.\n expect(calls).toContainEqual([\n \"stopTransaction\",\n { genesisHash: \"0xfeed\", operationId: \"bc1\" },\n ]);\n });\n}\n","// Copyright 2026 Parity Technologies (UK) Ltd.\n// SPDX-License-Identifier: Apache-2.0\n/**\n * A lightweight tagged `Result` type for the host public API.\n *\n * Host functions return `Promise<Result<T, HostError>>` rather than throwing, so\n * consumers get typed errors on the `err` channel instead of opaque thrown\n * `Error`s. The shape is intentionally identical to the one\n * `@parity/product-sdk-signer` exposes (`{ ok: true; value } | { ok: false; error }`),\n * so the two layers compose with no adapter — host's `Result` flows straight into\n * the signer's pattern matching.\n *\n * NOTE: host owns its own copy because the dependency edge runs `signer → host`,\n * so host cannot import the signer's definition. If a third package ever needs\n * this shape, extract it into a shared `@parity/product-sdk-result` package and\n * have both depend on that instead of duplicating.\n *\n * @module\n */\n\n/** A value that is either a success (`ok`) carrying `T`, or a failure (`err`) carrying `E`. */\nexport type Result<T, E> = { ok: true; value: T } | { ok: false; error: E };\n\n/** Create a successful {@link Result}. */\nexport function ok<T>(value: T): Result<T, never> {\n return { ok: true, value };\n}\n\n/** Create a failed {@link Result}. */\nexport function err<E>(error: E): Result<never, E> {\n return { ok: false, error };\n}\n\nif (import.meta.vitest) {\n const { test, expect, describe } = import.meta.vitest;\n\n describe(\"ok\", () => {\n test(\"produces an ok result with value\", () => {\n const result = ok(42);\n expect(result.ok).toBe(true);\n expect(result).toEqual({ ok: true, value: 42 });\n });\n\n test(\"works with null value\", () => {\n expect(ok(null)).toEqual({ ok: true, value: null });\n });\n });\n\n describe(\"err\", () => {\n test(\"produces an error result\", () => {\n const result = err(\"boom\");\n expect(result.ok).toBe(false);\n expect(result).toEqual({ ok: false, error: \"boom\" });\n });\n });\n}\n","// Copyright 2026 Parity Technologies (UK) Ltd.\n// SPDX-License-Identifier: Apache-2.0\n/**\n * TruAPI - the protocol for communicating between apps and the Polkadot host container.\n *\n * This module centralizes access to the in-house `@parity/truapi` client,\n * allowing other `@parity/product-sdk-*` packages to import from here rather\n * than depending directly on the protocol package. The client is built and\n * cached by {@link module:transport}; this module adds the accessor plus the\n * two helpers the convenience wrappers fold truapi's `ResultAsync` through —\n * {@link mapHostResult} (returns a `Result`, used by the public operations) and\n * {@link unwrapHostResult} (throws, used by the adapter-object methods).\n *\n * @module\n */\n\nimport { scale } from \"@parity/truapi\";\nimport type {\n AllocatableResource,\n AllocationOutcome,\n HexString,\n RemotePermission,\n TrUApiClient,\n} from \"@parity/truapi\";\nimport { createLogger } from \"@parity/product-sdk-logger\";\n\nimport {\n type HostError,\n type HostErrorPayload,\n HostCallFailedError,\n HostUnavailableError,\n formatHostError,\n} from \"./errors.js\";\nimport { type Result, err, ok } from \"./result.js\";\nimport { getClient, subscribeWithInterrupt } from \"./transport.js\";\nimport type { HostSubscription, Statement, StatementProof } from \"./types.js\";\n\nconst log = createLogger(\"host\");\n\n/**\n * Await a host `ResultAsync`, returning its Ok value or throwing a diagnostic\n * `Error` built from the host's error payload (preserved as `cause`).\n *\n * This is the *throwing* helper, retained for the methods of the adapter objects\n * returned by the feature-detection getters (`PreimageManager.submit`,\n * `HostLocalStorage.read`, `AccountsProvider` signing, …). Those objects often\n * implement external interfaces (e.g. polkadot-api's `JsonRpcProvider`) whose\n * method signatures can't carry a {@link Result}, so they keep the\n * throw convention. The flat public operations use {@link mapHostResult} instead.\n */\nexport function unwrapHostResult<T, E>(result: ResultAsync<T, E>, label: string): Promise<T> {\n return result.match(\n (value) => value,\n (error: E) => {\n throw new Error(`${label}: ${formatHostError(error)}`, { cause: error });\n },\n );\n}\n\n/**\n * Await a host `ResultAsync` and fold it into a tagged {@link Result}: maps the\n * Ok value through `map`, or wraps the host error payload in a\n * {@link HostCallFailedError} on the `err` channel. This is the non-throwing\n * boundary the flat public host operations (`requestPermission`, `deriveEntropy`,\n * `requestResourceAllocation`, …) return through.\n */\nexport function mapHostResult<T, U>(\n result: ResultAsync<T, HostErrorPayload>,\n map: (value: T) => U,\n label: string,\n): Promise<Result<U, HostError>> {\n return result.match(\n (value) => ok(map(value)),\n (error) => err(new HostCallFailedError(label, error)),\n );\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Hex helpers\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** Convert bytes to a `0x`-prefixed lower-case hex string. */\nexport function toHex(bytes: Uint8Array): HexString {\n return scale.bytesToHex(bytes);\n}\n\n/** Convert a hex string (with or without `0x`) to bytes. */\nexport function fromHex(hex: string): Uint8Array {\n return scale.hexToBytes(hex);\n}\n\n/** A `0x`-prefixed hex string used by the host API surface for raw byte payloads. */\nexport type { HexString };\n\n// ─────────────────────────────────────────────────────────────────────────────\n// TruAPI accessor\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * The TruApi client — namespaced access to every host protocol domain\n * (`permissions`, `entropy`, `signing`, `statementStore`, `system`,\n * `localStorage`, …). Identical to `TrUApiClient` from `@parity/truapi`.\n *\n * @example\n * ```ts\n * const truApi = await getTruApi();\n * if (truApi) {\n * await truApi.permissions.requestRemotePermission({\n * permission: { tag: \"ChainSubmit\", value: undefined },\n * });\n * await truApi.system.navigateTo({ url: \"polkadot://settings\" });\n * }\n * ```\n */\nexport type TruApi = TrUApiClient;\n\n/**\n * Get the TruAPI client for direct low-level access to host protocol domains.\n *\n * Returns the cached `@parity/truapi` client once the host transport is built\n * and the handshake has run, or `null` when running outside a container.\n *\n * For most use cases, prefer the higher-level functions like\n * {@link requestPermission}, {@link deriveEntropy}, or `getHostLocalStorage()`.\n *\n * @returns The TruAPI client, or `null` if unavailable.\n */\nexport async function getTruApi(): Promise<TruApi | null> {\n return getClient();\n}\n\n/**\n * Preimage manager handle for bulletin chain operations, backed by\n * `truApi.preimage.*`. `lookup` opens a {@link HostSubscription} (`unsubscribe`\n * + `onInterrupt`) that delivers the preimage bytes — or `null` until the host\n * finds them; `submit` uploads a preimage and resolves to its `0x`-prefixed hex\n * key.\n */\nexport interface PreimageManager {\n lookup(key: HexString, callback: (preimage: Uint8Array | null) => void): HostSubscription;\n submit(value: Uint8Array): Promise<HexString>;\n}\n\n/** Build a {@link PreimageManager} over a TruAPI client's `preimage` domain. */\nfunction adaptPreimageManager(client: TrUApiClient): PreimageManager {\n const preimage = client.preimage;\n return {\n lookup(key, callback) {\n return subscribeWithInterrupt(preimage.lookupSubscribe({ request: { key } }), (item) =>\n callback(item.value !== undefined ? fromHex(item.value) : null),\n );\n },\n submit(value) {\n return unwrapHostResult(preimage.submit(toHex(value)), \"preimage submit failed\");\n },\n };\n}\n\n/**\n * Get the preimage manager for bulletin chain operations.\n *\n * @returns The preimage manager, or `null` if unavailable (outside a container).\n */\nexport async function getPreimageManager(): Promise<PreimageManager | null> {\n const client = await getClient();\n return client ? adaptPreimageManager(client) : null;\n}\n\n/**\n * Construct a `PreimageManager`. Retained for API compatibility; with the single\n * cached TruAPI client this is equivalent to {@link getPreimageManager}.\n *\n * @returns A `PreimageManager` instance, or `null` if unavailable.\n */\nexport async function createHostPreimageManager(): Promise<PreimageManager | null> {\n return getPreimageManager();\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Resource allocation\n// ─────────────────────────────────────────────────────────────────────────────\n\n// Resource-allocation / permission types, re-exported verbatim from\n// `@parity/truapi` (imported above for the local signatures):\n// - `AllocatableResource` — resource types requestable via `requestResourceAllocation`.\n// - `AllocationOutcome` — per-resource outcome, the string union\n// `\"Allocated\" | \"Rejected\" | \"NotAvailable\"` (RFC-10).\n// - `RemotePermission` — permission the dapp asks the host to grant via `requestPermission`.\nexport type { AllocatableResource, AllocationOutcome, RemotePermission };\n\n/**\n * Request the host to pre-allocate one or more resource allowances.\n *\n * The host prompts the user once; subsequent operations covered by the\n * granted allowance don't re-prompt.\n *\n * @param resources - Resources to request.\n * @returns `ok` with per-resource outcomes in the same order as `resources`, or\n * `err(HostUnavailableError | HostCallFailedError)`.\n *\n * @example\n * ```ts\n * const r = await requestResourceAllocation([\n * { tag: \"BulletinAllowance\", value: undefined },\n * ]);\n * if (r.ok && r.value[0] === \"Allocated\") { ... }\n * ```\n */\nexport async function requestResourceAllocation(\n resources: AllocatableResource[],\n): Promise<Result<AllocationOutcome[], HostError>> {\n const truApi = await getTruApi();\n if (!truApi) {\n return err(new HostUnavailableError(\"requestResourceAllocation: TruAPI unavailable\"));\n }\n log.debug(\"requestResourceAllocation\", { resources: resources.map((r) => r.tag) });\n\n return mapHostResult(\n truApi.resourceAllocation.request({ resources }),\n (response) => response.outcomes,\n \"requestResourceAllocation failed\",\n );\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Authorized Statement Store proof creation (RFC-10 §\"Statement Store allowance\")\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Have the host sign a Statement using the product's allowance-bearing account,\n * which it picks internally — RFC-10 §\"Statement Store allowance\". No per-call\n * account id is needed (this is the sponsored-submission path).\n *\n * Pairs with {@link getStatementStore}'s `submit`: call this to obtain a proof,\n * attach it to the Statement, and submit the result.\n *\n * @param statement - The Statement to be signed.\n * @returns `ok` with the proof to attach before submitting, or\n * `err(HostUnavailableError | HostCallFailedError)`.\n */\nexport async function createProofAuthorized(\n statement: Statement,\n): Promise<Result<StatementProof, HostError>> {\n const truApi = await getTruApi();\n if (!truApi) {\n return err(new HostUnavailableError(\"createProofAuthorized: TruAPI unavailable\"));\n }\n log.debug(\"createProofAuthorized\", { topics: statement.topics.length });\n\n return mapHostResult(\n truApi.statementStore.createProofAuthorized(statement),\n (response) => response.proof,\n \"createProofAuthorized failed\",\n );\n}\n\n/**\n * Neverthrow-style ResultAsync returned by product-sdk methods.\n *\n * Use `.match(onOk, onErr)` to handle success/error cases.\n */\nexport interface ResultAsync<T, E> {\n match: <A, B = A>(ok: (t: T) => A, err: (e: E) => B) => Promise<A | B>;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Tests\n// ─────────────────────────────────────────────────────────────────────────────\n\nif (import.meta.vitest) {\n const { test, expect } = import.meta.vitest;\n\n test(\"getTruApi returns null outside a container\", async () => {\n const api = await getTruApi();\n expect(api === null || typeof api === \"object\").toBe(true);\n });\n\n test(\"getPreimageManager returns manager or null\", async () => {\n const manager = await getPreimageManager();\n expect(manager === null || typeof manager === \"object\").toBe(true);\n });\n\n test(\"createHostPreimageManager returns null outside container\", async () => {\n expect(await createHostPreimageManager()).toBeNull();\n });\n\n test(\"hex helpers\", () => {\n expect(toHex(new Uint8Array([0xde, 0xad]))).toBe(\"0xdead\");\n expect(Array.from(fromHex(\"0xdead\"))).toEqual([0xde, 0xad]);\n });\n\n test(\"requestResourceAllocation returns err when TruAPI is unavailable\", async () => {\n const api = await getTruApi();\n if (api === null) {\n const result = await requestResourceAllocation([\n { tag: \"BulletinAllowance\", value: undefined },\n ]);\n expect(result.ok).toBe(false);\n if (!result.ok) {\n expect(result.error).toBeInstanceOf(HostUnavailableError);\n }\n } else {\n expect(typeof requestResourceAllocation).toBe(\"function\");\n }\n });\n\n test(\"createProofAuthorized is callable\", () => {\n expect(typeof createProofAuthorized).toBe(\"function\");\n });\n}\n","// Copyright 2026 Parity Technologies (UK) Ltd.\n// SPDX-License-Identifier: Apache-2.0\nimport type { JsonRpcProvider } from \"polkadot-api\";\nimport type { HexString, TrUApiClient } from \"@parity/truapi\";\n\nimport { formatHostError } from \"./errors.js\";\nimport { createHostPapiProvider } from \"./papi-provider.js\";\nimport { getClient, isCorrectEnvironment, subscribeWithInterrupt } from \"./transport.js\";\nimport { fromHex, toHex, unwrapHostResult } from \"./truapi.js\";\nimport type { HostLocalStorage, HostStatementStore } from \"./types.js\";\n\nconst textEncoder = new TextEncoder();\nconst textDecoder = new TextDecoder();\n\n/**\n * Synchronous container detection — fast heuristic check (iframe, webview\n * marker, or injected host message port). Re-exported from the transport\n * bootstrap, which owns the detection logic.\n */\nexport { isCorrectEnvironment as isInsideContainerSync } from \"./transport.js\";\n\n/**\n * Thrown by {@link getHostProvider} when the host container is reachable but does\n * not support the requested chain — e.g. the chain isn't enabled in this host\n * build, or the descriptor's genesis hash has drifted from the host's after a\n * network reset.\n *\n * Surfacing this as a thrown error (rather than handing back a provider that\n * silently swallows every JSON-RPC request) is what lets callers of\n * `createChainClient` detect the failure. Without it, the host's fallback no-op\n * provider drops every request on the floor and queries await forever.\n */\nexport class ChainNotSupportedError extends Error {\n /** Genesis hash of the chain the host refused, for programmatic detection. */\n readonly genesisHash: string;\n\n constructor(genesisHash: string) {\n super(\n `Chain ${genesisHash} is not supported by the current host. It may not be enabled in this host build, or its genesis hash may have drifted after a network reset.`,\n );\n this.name = \"ChainNotSupportedError\";\n this.genesisHash = genesisHash;\n }\n}\n\n/**\n * Ask the host whether it can serve the given chain, via\n * `system.featureSupported({ tag: \"Chain\", … })`. Gates {@link getHostProvider}\n * the same way the upstream wrapper's provider gated itself internally before\n * deciding whether to start a real provider or a no-op one.\n *\n * @throws If the host rejects the support check outright — a non-hanging,\n * catchable failure.\n */\nasync function isChainSupportedByHost(\n client: TrUApiClient,\n genesisHash: HexString,\n): Promise<boolean> {\n return client.system.featureSupported({ tag: \"Chain\", value: { genesisHash } }).match(\n (response) => response.supported,\n (error) => {\n throw new Error(\n `Host rejected the chain-support check for ${genesisHash}: ${formatHostError(error)}`,\n );\n },\n );\n}\n\n/**\n * Detect if running inside a Host container (Polkadot Browser / Polkadot Desktop).\n *\n * The SDK is designed to run exclusively inside a host container. This function\n * is primarily useful for early validation or informational purposes.\n */\nexport async function isInsideContainer(): Promise<boolean> {\n return isCorrectEnvironment();\n}\n\n/**\n * Adapt the TruAPI client's raw `localStorage` domain (hex-encoded\n * `read`/`write`/`clear`) into the richer {@link HostLocalStorage} surface that\n * the Storage package's `KvStore` and other consumers expect.\n */\nfunction adaptLocalStorage(client: TrUApiClient): HostLocalStorage {\n const ls = client.localStorage;\n\n async function readBytes(key: string): Promise<Uint8Array | undefined> {\n const response = await unwrapHostResult(ls.read({ key }), \"host localStorage read failed\");\n return response.value !== undefined ? fromHex(response.value) : undefined;\n }\n\n async function writeBytes(key: string, value: Uint8Array): Promise<void> {\n await unwrapHostResult(\n ls.write({ key, value: toHex(value) }),\n \"host localStorage write failed\",\n );\n }\n\n async function readString(key: string): Promise<string> {\n const bytes = await readBytes(key);\n return bytes ? textDecoder.decode(bytes) : \"\";\n }\n\n async function writeString(key: string, value: string): Promise<void> {\n return writeBytes(key, textEncoder.encode(value));\n }\n\n async function readJSON(key: string): Promise<unknown> {\n const text = await readString(key);\n return text ? JSON.parse(text) : null;\n }\n\n async function writeJSON(key: string, value: unknown): Promise<void> {\n return writeString(key, JSON.stringify(value));\n }\n\n async function clear(key: string): Promise<void> {\n await unwrapHostResult(ls.clear({ key }), \"host localStorage clear failed\");\n }\n\n return { readString, writeString, readJSON, writeJSON, readBytes, writeBytes, clear };\n}\n\n/**\n * Get the Host API localStorage instance when running inside a container.\n * Returns null outside a container or when the host transport is unavailable.\n */\nexport async function getHostLocalStorage(): Promise<HostLocalStorage | null> {\n const client = await getClient();\n return client ? adaptLocalStorage(client) : null;\n}\n\n/**\n * Construct a host-backed `HostLocalStorage` instance. Retained for API\n * compatibility; with the single cached TruAPI client this is equivalent to\n * {@link getHostLocalStorage}.\n *\n * @returns A `HostLocalStorage` instance, or `null` if unavailable.\n */\nexport async function createHostLocalStorage(): Promise<HostLocalStorage | null> {\n return getHostLocalStorage();\n}\n\n/**\n * Get a PAPI-compatible JSON-RPC provider that routes through the host connection.\n *\n * When running inside a Polkadot container, this builds a `JsonRpcProvider` over\n * `truApi.chain.*` (see {@link module:papi-provider}), enabling shared\n * connections and efficient routing. Returns `null` when not running inside a\n * container.\n *\n * @param genesisHash - Genesis hash of the target chain (`0x`-prefixed hex string).\n * @returns A host-routed `JsonRpcProvider`, or `null` if unavailable.\n * @throws {ChainNotSupportedError} When inside a container but the host can't serve\n * the chain — surfaced instead of returning a provider that would hang forever.\n */\nexport async function getHostProvider(genesisHash: HexString): Promise<JsonRpcProvider | null> {\n const client = await getClient();\n if (!client) return null;\n return resolveHostProvider(client, genesisHash);\n}\n\n/**\n * Decide whether to build a host provider for `genesisHash`, given a ready\n * TruAPI client. Split out of {@link getHostProvider} so the decision logic can\n * be unit-tested with a fake client.\n *\n * @returns the provider.\n * @throws {ChainNotSupportedError} when the host can't serve the chain.\n */\nasync function resolveHostProvider(\n client: TrUApiClient,\n genesisHash: HexString,\n): Promise<JsonRpcProvider> {\n // Confirm the host can actually serve this chain before handing PAPI a\n // provider. When the host doesn't support the chain, a provider would silently\n // swallow every JSON-RPC request and the caller hangs forever with no\n // rejection. Surface a catchable error instead.\n if (!(await isChainSupportedByHost(client, genesisHash))) {\n throw new ChainNotSupportedError(genesisHash);\n }\n\n return createHostPapiProvider(client, genesisHash);\n}\n\n/** Build a {@link HostStatementStore} over a TruAPI client's `statementStore` domain. */\nfunction adaptStatementStore(client: TrUApiClient): HostStatementStore {\n const ss = client.statementStore;\n return {\n subscribe(filter, callback) {\n const request =\n \"matchAll\" in filter\n ? ({ tag: \"MatchAll\", value: filter.matchAll } as const)\n : ({ tag: \"MatchAny\", value: filter.matchAny } as const);\n // `RemoteStatementStoreSubscribeItem` is structurally a StatementsPage.\n return subscribeWithInterrupt(ss.subscribe({ request }), callback);\n },\n async createProofAuthorized(statement) {\n const response = await unwrapHostResult(\n ss.createProofAuthorized(statement),\n \"createProofAuthorized failed\",\n );\n return response.proof;\n },\n async submit(signedStatement) {\n await unwrapHostResult(ss.submit(signedStatement), \"statement submit failed\");\n },\n };\n}\n\n/**\n * Get the host statement store when running inside a container, backed by\n * `truApi.statementStore.*`.\n *\n * Returns a store with `subscribe`, `createProofAuthorized`, and `submit` that\n * communicate through the host's native binary protocol — bypassing JSON-RPC\n * entirely. Returns `null` outside a host container.\n *\n * @returns The host statement store, or `null` if unavailable.\n */\nexport async function getStatementStore(): Promise<HostStatementStore | null> {\n const client = await getClient();\n return client ? adaptStatementStore(client) : null;\n}\n\nif (import.meta.vitest) {\n const { test, expect, vi, afterEach } = import.meta.vitest;\n\n afterEach(() => {\n vi.unstubAllGlobals();\n });\n\n // A self-contained fake TruAPI client exposing just the `system` and `chain`\n // domains the provider gate touches, so the chain-support decision can be\n // tested without a real host connection.\n function makeFakeClient(opts: { supported?: boolean; featureErr?: string | null }) {\n const { supported = true, featureErr = null } = opts;\n return {\n system: {\n featureSupported: (_request: unknown) => ({\n match: (\n okFn: (ok: { supported: boolean }) => boolean,\n errFn: (err: { reason: string }) => boolean,\n ) => (featureErr ? errFn({ reason: featureErr }) : okFn({ supported })),\n }),\n },\n // Read synchronously by createHostPapiProvider; never invoked here.\n chain: {},\n } as unknown as TrUApiClient;\n }\n\n test(\"isInsideContainer is false in a Node environment (no window)\", async () => {\n expect(await isInsideContainer()).toBe(false);\n });\n\n test(\"isInsideContainer detects an injected host port\", async () => {\n const win = {};\n Object.defineProperty(win, \"top\", { get: () => win });\n (win as Record<string, unknown>).__HOST_API_PORT__ = 12345;\n vi.stubGlobal(\"window\", win);\n expect(await isInsideContainer()).toBe(true);\n });\n\n test(\"getHostLocalStorage returns null outside container\", async () => {\n expect(await getHostLocalStorage()).toBeNull();\n });\n\n test(\"createHostLocalStorage returns null outside container\", async () => {\n expect(await createHostLocalStorage()).toBeNull();\n });\n\n test(\"adaptLocalStorage round-trips strings, JSON, and bytes over the TruAPI client\", async () => {\n // Minimal in-memory fake of the TruAPI localStorage domain (hex values).\n const store = new Map<string, `0x${string}`>();\n const okAsync = <T>(value: T) => ({\n match: async (onOk: (v: T) => unknown) => onOk(value),\n });\n const fakeClient = {\n localStorage: {\n read: ({ key }: { key: string }) => okAsync({ value: store.get(key) }),\n write: ({ key, value }: { key: string; value: `0x${string}` }) => {\n store.set(key, value);\n return okAsync(undefined);\n },\n clear: ({ key }: { key: string }) => {\n store.delete(key);\n return okAsync(undefined);\n },\n },\n } as unknown as TrUApiClient;\n\n const ls = adaptLocalStorage(fakeClient);\n expect(await ls.readString(\"missing\")).toBe(\"\");\n expect(await ls.readJSON(\"missing\")).toBeNull();\n expect(await ls.readBytes(\"missing\")).toBeUndefined();\n\n await ls.writeString(\"s\", \"hello\");\n expect(await ls.readString(\"s\")).toBe(\"hello\");\n\n await ls.writeJSON(\"j\", { a: 1 });\n expect(await ls.readJSON(\"j\")).toEqual({ a: 1 });\n\n await ls.writeBytes(\"b\", new Uint8Array([1, 2, 3]));\n expect(Array.from((await ls.readBytes(\"b\")) ?? [])).toEqual([1, 2, 3]);\n\n await ls.clear(\"s\");\n expect(await ls.readString(\"s\")).toBe(\"\");\n });\n\n // --- chain-support gating (resolveHostProvider over truApi.system/chain) ---\n\n test(\"resolves to a provider when the host supports the chain\", async () => {\n const provider = await resolveHostProvider(makeFakeClient({ supported: true }), \"0xabc\");\n // createHostPapiProvider returns a JsonRpcProvider (an onMessage -> connection fn).\n expect(typeof provider).toBe(\"function\");\n });\n\n test(\"throws ChainNotSupportedError when the host doesn't support the chain\", async () => {\n const err = await resolveHostProvider(makeFakeClient({ supported: false }), \"0xfeed\").catch(\n (e) => e,\n );\n expect(err).toBeInstanceOf(ChainNotSupportedError);\n expect((err as ChainNotSupportedError).genesisHash).toBe(\"0xfeed\");\n });\n\n test(\"throws when the host rejects the support check\", async () => {\n await expect(\n resolveHostProvider(makeFakeClient({ featureErr: \"boom\" }), \"0xabc\"),\n ).rejects.toThrow(/boom/);\n });\n\n test(\"getHostProvider returns null outside a container\", async () => {\n expect(await getHostProvider(\"0xabc\")).toBeNull();\n });\n\n test(\"getStatementStore returns null outside a container\", async () => {\n expect(await getStatementStore()).toBeNull();\n });\n}\n","// Copyright 2026 Parity Technologies (UK) Ltd.\n// SPDX-License-Identifier: Apache-2.0\n/**\n * Shared chain network configuration — single source of truth for\n * chain-specific endpoints used by multiple packages.\n */\n\n/**\n * Bulletin Chain RPC endpoints per network environment. `paseo` and `summit`\n * are populated today; `polkadot` and `kusama` are reserved for when those\n * Bulletin deployments go live.\n */\nexport const BULLETIN_RPCS = {\n paseo: [\"wss://paseo-bulletin-next-rpc.polkadot.io\"],\n summit: [\"wss://summit-bulletin-rpc.polkadot.io\"],\n polkadot: [] as string[],\n kusama: [] as string[],\n} as const;\n\n/** Default Bulletin Chain endpoint — the first entry under {@link BULLETIN_RPCS}.paseo. */\nexport const DEFAULT_BULLETIN_ENDPOINT: string = BULLETIN_RPCS.paseo[0];\n\nif (import.meta.vitest) {\n const { describe, test, expect } = import.meta.vitest;\n\n describe(\"chains config\", () => {\n test(\"BULLETIN_RPCS has paseo endpoint\", () => {\n expect(BULLETIN_RPCS.paseo.length).toBeGreaterThan(0);\n expect(BULLETIN_RPCS.paseo[0]).toMatch(/^wss:\\/\\//);\n });\n\n test(\"BULLETIN_RPCS has summit endpoint\", () => {\n expect(BULLETIN_RPCS.summit.length).toBeGreaterThan(0);\n expect(BULLETIN_RPCS.summit[0]).toMatch(/^wss:\\/\\//);\n });\n\n test(\"BULLETIN_RPCS polkadot and kusama are empty until live\", () => {\n expect(BULLETIN_RPCS.polkadot).toEqual([]);\n expect(BULLETIN_RPCS.kusama).toEqual([]);\n });\n\n test(\"DEFAULT_BULLETIN_ENDPOINT matches first paseo endpoint\", () => {\n expect(DEFAULT_BULLETIN_ENDPOINT).toBe(BULLETIN_RPCS.paseo[0]);\n });\n });\n}\n","// Copyright 2026 Parity Technologies (UK) Ltd.\n// SPDX-License-Identifier: Apache-2.0\n/**\n * Host wallet accounts, backed by `truApi.account.*` and `truApi.signing.*`.\n *\n * `getAccountsProvider()` returns the full accounts surface — user identity\n * (`getUserId` / `requestLogin`), the user's existing wallet accounts\n * (`getLegacyAccounts`), app-scoped product accounts (`getProductAccount` /\n * `getProductAccountAlias`), Ring VRF proofs (`createRingVRFProof`), connection\n * status, and PAPI `PolkadotSigner` factories for both product and legacy\n * accounts.\n *\n * The signer factories build a PAPI `PolkadotSigner` directly over\n * `truApi.signing.createTransaction` (product) /\n * `createTransactionWithLegacyAccount` (legacy) — `signTx` derives the\n * metadata-driven `txExtVersion` and maps the signed extensions to the host's\n * wire shape; `signBytes` calls `signing.signRaw(WithLegacyAccount)`. No PJS\n * bridge is involved, so opaque signed extensions (e.g. Paseo Next's `AsPgas`)\n * survive end-to-end.\n *\n * @module\n */\n\nimport { decAnyMetadata, unifyMetadata } from \"@polkadot-api/substrate-bindings\";\nimport type { ResultAsync } from \"neverthrow\";\nimport { AccountId, type PolkadotSigner } from \"polkadot-api\";\n\nimport type {\n HostAccountConnectionStatusSubscribeItem,\n HostAccountCreateProofError,\n HostAccountGetAliasResponse as WireAlias,\n HostAccountGetError,\n HostGetUserIdError,\n HostRequestLoginError,\n HostRequestLoginResponse,\n LegacyAccount as WireLegacyAccount,\n ProductAccount as WireProductAccount,\n ProductAccountId,\n RingLocation,\n TrUApiClient,\n} from \"@parity/truapi\";\n\nimport { getClient, subscribeWithInterrupt } from \"./transport.js\";\nimport { fromHex, toHex, unwrapHostResult } from \"./truapi.js\";\nimport type { HostSubscription } from \"./types.js\";\n\n/** Ring location for Ring VRF proofs (`{ genesisHash, ringRootHash, hints? }`). Re-exported from `@parity/truapi`. */\nexport type { RingLocation } from \"@parity/truapi\";\n\n// The account/alias shapes come from `@parity/truapi`'s generated specs; we\n// derive the SDK-facing views from them so the field inventory tracks the\n// protocol automatically, and override only the byte fields the adapter\n// decodes (the wire types carry `0x`-prefixed `HexString`s, whereas these\n// surface decoded `Uint8Array`s). Same pattern as `@parity/product-sdk-statement-store`.\n\n/**\n * One of the user's existing wallet accounts, surfaced through the host and\n * identified by its public key and an optional name. Contrast with\n * {@link ProductAccount}, which is also user-controlled but derived by the\n * host for a specific app rather than picked from the user's existing keys.\n *\n * Derived from `@parity/truapi`'s `LegacyAccount`, with `publicKey` decoded to bytes.\n */\nexport type HostAccount = Omit<WireLegacyAccount, \"publicKey\"> & {\n /** Raw public key bytes. */\n publicKey: Uint8Array;\n};\n\n/**\n * A product account — an app-scoped derived account managed by the host wallet.\n *\n * The host derives a unique keypair for each app (identified by `dotNsIdentifier`)\n * so apps get their own account that the user controls but is scoped to the app.\n *\n * Combines `@parity/truapi`'s `ProductAccountId` (the `{ dotNsIdentifier,\n * derivationIndex }` lookup key) with the `ProductAccount` payload, with\n * `publicKey` decoded to bytes.\n */\nexport type ProductAccount = ProductAccountId &\n Omit<WireProductAccount, \"publicKey\"> & {\n /** Raw public key bytes. */\n publicKey: Uint8Array;\n };\n\n/**\n * A contextual alias obtained from Ring VRF.\n *\n * Proves account membership in a ring without revealing which account.\n *\n * Derived from `@parity/truapi`'s alias response, with both fields decoded to bytes.\n */\nexport type ContextualAlias = { [K in keyof WireAlias]: Uint8Array };\n\n/**\n * Accounts provider handle, backed by `truApi.account.*` / `truApi.signing.*`.\n * Surfaces the user's wallet accounts, app-scoped product accounts, Ring VRF,\n * user identity, connection status, and `PolkadotSigner` factories.\n *\n * Lookup methods return a neverthrow `ResultAsync` (use `.match(ok, err)`);\n * the signer factories return a synchronous PAPI `PolkadotSigner`.\n */\nexport interface AccountsProvider {\n getUserId(): ResultAsync<{ primaryUsername: string }, HostGetUserIdError>;\n requestLogin(reason?: string): ResultAsync<HostRequestLoginResponse, HostRequestLoginError>;\n getProductAccount(\n dotNsIdentifier: string,\n derivationIndex?: number,\n ): ResultAsync<ProductAccount, HostAccountGetError>;\n getProductAccountAlias(\n dotNsIdentifier: string,\n derivationIndex?: number,\n ): ResultAsync<ContextualAlias, HostAccountGetError>;\n getLegacyAccounts(): ResultAsync<HostAccount[], HostAccountGetError>;\n createRingVRFProof(\n dotNsIdentifier: string,\n derivationIndex: number,\n location: RingLocation,\n message: Uint8Array,\n ): ResultAsync<Uint8Array, HostAccountCreateProofError>;\n /**\n * Build a `PolkadotSigner` for a product account. Signing routes through the\n * host's `createTransaction` path: the host decodes the metadata and forwards\n * the opaque signed-extension bytes, so unknown extensions survive end-to-end.\n */\n getProductAccountSigner(account: ProductAccount): PolkadotSigner;\n /**\n * Build a `PolkadotSigner` for one of the user's existing wallet accounts.\n * `name` is accepted for callsite ergonomics but unused — the signer is\n * derived from `publicKey` alone.\n */\n getLegacyAccountSigner(account: { publicKey: Uint8Array; name?: string }): PolkadotSigner;\n subscribeAccountConnectionStatus(\n callback: (status: HostAccountConnectionStatusSubscribeItem) => void,\n ): HostSubscription;\n}\n\n/**\n * Derive the host's extrinsic-extension version from SCALE-encoded metadata:\n * v4 → 0, otherwise the latest supported version. `unifyMetadata` normalizes\n * v14/v15 so `.extrinsic.version` is an array.\n *\n * Indirected through {@link deps} so the SCALE decode (which needs a real\n * metadata blob) can be stubbed in unit tests while the rest of the `signTx`\n * flow — genesis extraction, extension mapping, the host call — is exercised.\n */\nfunction deriveTxExtVersion(metadata: Uint8Array): number {\n const versions = unifyMetadata(decAnyMetadata(metadata)).extrinsic.version;\n if (versions.length === 0) {\n throw new Error(\"No extrinsic version found in metadata\");\n }\n const latestVersion = versions.reduce((acc, v) => Math.max(acc, v), 0);\n return latestVersion === 4 ? 0 : latestVersion;\n}\n\n/** Internal seam so `import.meta.vitest` can stub the metadata decode. @internal */\nconst deps = { deriveTxExtVersion };\n\n/**\n * Map a PAPI `signTx` call's signed extensions onto the host's\n * `TxPayloadExtension` wire shape (hex-encoded `extra` / `additionalSigned`).\n */\nfunction toHostExtensions(\n signedExtensions: Record<\n string,\n { identifier: string; value: Uint8Array; additionalSigned: Uint8Array }\n >,\n) {\n return Object.values(signedExtensions).map((ext) => ({\n id: ext.identifier,\n extra: toHex(ext.value),\n additionalSigned: toHex(ext.additionalSigned),\n }));\n}\n\n/** Build an {@link AccountsProvider} over a TruAPI client's `account` / `signing` domains. */\nfunction adaptAccountsProvider(client: TrUApiClient): AccountsProvider {\n const account = client.account;\n const signing = client.signing;\n\n return {\n getUserId() {\n return account.getUserId().map((response) => ({\n primaryUsername: response.primaryUsername,\n }));\n },\n requestLogin(reason) {\n return account.requestLogin({ reason });\n },\n getProductAccount(dotNsIdentifier, derivationIndex = 0) {\n return account\n .getAccount({ productAccountId: { dotNsIdentifier, derivationIndex } })\n .map((response) => ({\n publicKey: fromHex(response.account.publicKey),\n dotNsIdentifier,\n derivationIndex,\n }));\n },\n getProductAccountAlias(dotNsIdentifier, derivationIndex = 0) {\n return account\n .getAccountAlias({ productAccountId: { dotNsIdentifier, derivationIndex } })\n .map((response) => ({\n context: fromHex(response.context),\n alias: fromHex(response.alias),\n }));\n },\n getLegacyAccounts() {\n return account.getLegacyAccounts().map((response) =>\n response.accounts.map((a) => ({\n publicKey: fromHex(a.publicKey),\n name: a.name,\n })),\n );\n },\n createRingVRFProof(dotNsIdentifier, derivationIndex, location, message) {\n return account\n .createAccountProof({\n productAccountId: { dotNsIdentifier, derivationIndex },\n ringLocation: location,\n context: toHex(message),\n })\n .map((response) => fromHex(response.proof));\n },\n getProductAccountSigner(account_) {\n const productAccountId = {\n dotNsIdentifier: account_.dotNsIdentifier,\n derivationIndex: account_.derivationIndex,\n };\n\n return {\n publicKey: account_.publicKey,\n async signTx(callData, signedExtensions, metadata) {\n const checkGenesis = signedExtensions.CheckGenesis;\n if (!checkGenesis) {\n throw new Error(\"Can't find genesis hash on transaction\");\n }\n\n const response = await unwrapHostResult(\n signing.createTransaction({\n signer: productAccountId,\n genesisHash: toHex(checkGenesis.additionalSigned),\n callData: toHex(callData),\n extensions: toHostExtensions(signedExtensions),\n txExtVersion: deps.deriveTxExtVersion(metadata),\n }),\n \"createTransaction failed\",\n );\n return fromHex(response.transaction);\n },\n async signBytes(data) {\n const response = await unwrapHostResult(\n signing.signRaw({\n account: productAccountId,\n payload: { tag: \"Bytes\", value: { bytes: toHex(data) } },\n }),\n \"signRaw failed\",\n );\n return fromHex(response.signature);\n },\n };\n },\n getLegacyAccountSigner(account_) {\n // `createTransactionWithLegacyAccount` identifies the signer by its\n // raw account id (hex public key); `signRawWithLegacyAccount` takes an\n // SS58 address the wallet can match. Compute both up front.\n const signerHex = toHex(account_.publicKey);\n const ss58Address = AccountId().dec(account_.publicKey);\n\n return {\n publicKey: account_.publicKey,\n async signTx(callData, signedExtensions, metadata) {\n const checkGenesis = signedExtensions.CheckGenesis;\n if (!checkGenesis) {\n throw new Error(\"Can't find genesis hash on transaction\");\n }\n\n const response = await unwrapHostResult(\n signing.createTransactionWithLegacyAccount({\n signer: signerHex,\n genesisHash: toHex(checkGenesis.additionalSigned),\n callData: toHex(callData),\n extensions: toHostExtensions(signedExtensions),\n txExtVersion: deps.deriveTxExtVersion(metadata),\n }),\n \"createTransactionWithLegacyAccount failed\",\n );\n return fromHex(response.transaction);\n },\n async signBytes(data) {\n const response = await unwrapHostResult(\n signing.signRawWithLegacyAccount({\n signer: ss58Address,\n payload: { tag: \"Bytes\", value: { bytes: toHex(data) } },\n }),\n \"signRawWithLegacyAccount failed\",\n );\n return fromHex(response.signature);\n },\n };\n },\n subscribeAccountConnectionStatus(callback) {\n return subscribeWithInterrupt(account.connectionStatusSubscribe(), callback);\n },\n };\n}\n\n/**\n * Get the accounts provider for managing host accounts, backed by\n * `truApi.account.*` / `truApi.signing.*`. Returns `null` when running outside\n * a host container.\n *\n * @returns The accounts provider, or `null` if unavailable.\n */\nexport async function getAccountsProvider(): Promise<AccountsProvider | null> {\n const client = await getClient();\n return client ? adaptAccountsProvider(client) : null;\n}\n\nif (import.meta.vitest) {\n const { test, expect, vi } = import.meta.vitest;\n\n /** Minimal fake of the truapi account/signing domains used to test the adapter. */\n function makeFakeClient(opts: { onCall?: (method: string, args: unknown) => void } = {}) {\n const okMatch = (value: unknown) => ({\n // neverthrow ResultAsync surface used by the adapter: .map + .match.\n map: (fn: (v: unknown) => unknown) => okMatch(fn(value)),\n match: (ok: (v: unknown) => unknown, _err: (e: unknown) => unknown) => ok(value),\n });\n const method = (name: string, response: unknown) => (args: unknown) => {\n opts.onCall?.(name, args);\n return okMatch(response);\n };\n return {\n account: {\n getUserId: method(\"getUserId\", { primaryUsername: \"alice.dot\" }),\n getAccount: method(\"getAccount\", { account: { publicKey: \"0xaa\" } }),\n getAccountAlias: method(\"getAccountAlias\", { context: \"0x01\", alias: \"0x02\" }),\n getLegacyAccounts: method(\"getLegacyAccounts\", {\n accounts: [{ publicKey: \"0xbb\", name: \"Bob\" }],\n }),\n createAccountProof: method(\"createAccountProof\", { proof: \"0xc0ffee\" }),\n connectionStatusSubscribe: () => ({\n subscribe: () => ({ unsubscribe: vi.fn() }),\n [Symbol.observable as symbol]() {\n return this;\n },\n }),\n },\n signing: {\n createTransaction: method(\"createTransaction\", { transaction: \"0xdead\" }),\n createTransactionWithLegacyAccount: method(\"createTransactionWithLegacyAccount\", {\n transaction: \"0xfeed\",\n }),\n signRaw: method(\"signRaw\", { signature: \"0xbeef\" }),\n signRawWithLegacyAccount: method(\"signRawWithLegacyAccount\", {\n signature: \"0xcafe\",\n }),\n },\n } as unknown as TrUApiClient;\n }\n\n test(\"getAccountsProvider returns null outside a container\", async () => {\n expect(await getAccountsProvider()).toBeNull();\n });\n\n test(\"getProductAccount decodes the public key and carries the identifier\", async () => {\n const calls: Array<[string, unknown]> = [];\n const client = makeFakeClient({ onCall: (m, a) => calls.push([m, a]) });\n const provider = adaptAccountsProvider(client);\n const account = await provider.getProductAccount(\"app.dot\", 2).match(\n (a) => a,\n () => null,\n );\n expect(calls[0]).toEqual([\n \"getAccount\",\n { productAccountId: { dotNsIdentifier: \"app.dot\", derivationIndex: 2 } },\n ]);\n expect(account).toEqual({\n publicKey: fromHex(\"0xaa\"),\n dotNsIdentifier: \"app.dot\",\n derivationIndex: 2,\n });\n });\n\n test(\"createRingVRFProof hex-encodes the message as the proof context\", async () => {\n const calls: Array<[string, unknown]> = [];\n const client = makeFakeClient({ onCall: (m, a) => calls.push([m, a]) });\n const provider = adaptAccountsProvider(client);\n const proof = await provider\n .createRingVRFProof(\n \"app.dot\",\n 0,\n { genesisHash: \"0x01\", ringRootHash: \"0x02\" },\n new Uint8Array([1, 2, 3]),\n )\n .match(\n (p) => p,\n () => null,\n );\n expect(calls[0][0]).toBe(\"createAccountProof\");\n expect((calls[0][1] as { context: string }).context).toBe(toHex(new Uint8Array([1, 2, 3])));\n expect(proof).toEqual(fromHex(\"0xc0ffee\"));\n });\n\n test(\"the product signer signs bytes via signing.signRaw\", async () => {\n const calls: Array<[string, unknown]> = [];\n const client = makeFakeClient({ onCall: (m, a) => calls.push([m, a]) });\n const provider = adaptAccountsProvider(client);\n const signer = provider.getProductAccountSigner({\n dotNsIdentifier: \"app.dot\",\n derivationIndex: 0,\n publicKey: new Uint8Array(32).fill(0xaa),\n });\n const signature = await signer.signBytes(new Uint8Array([9, 9]));\n expect(calls.at(-1)).toEqual([\n \"signRaw\",\n {\n account: { dotNsIdentifier: \"app.dot\", derivationIndex: 0 },\n payload: { tag: \"Bytes\", value: { bytes: toHex(new Uint8Array([9, 9])) } },\n },\n ]);\n expect(signature).toEqual(fromHex(\"0xbeef\"));\n });\n\n test(\"the legacy signer signs bytes via signing.signRawWithLegacyAccount (by SS58 address)\", async () => {\n const calls: Array<[string, unknown]> = [];\n const client = makeFakeClient({ onCall: (m, a) => calls.push([m, a]) });\n const provider = adaptAccountsProvider(client);\n const publicKey = new Uint8Array(32).fill(0xbb);\n const signer = provider.getLegacyAccountSigner({ publicKey });\n const signature = await signer.signBytes(new Uint8Array([7, 7]));\n // signRawWithLegacyAccount identifies the signer by SS58 address, not raw pubkey.\n expect(calls.at(-1)).toEqual([\n \"signRawWithLegacyAccount\",\n {\n signer: AccountId().dec(publicKey),\n payload: { tag: \"Bytes\", value: { bytes: toHex(new Uint8Array([7, 7])) } },\n },\n ]);\n expect(signature).toEqual(fromHex(\"0xcafe\"));\n });\n\n test(\"the legacy signer's signTx throws without a CheckGenesis extension\", async () => {\n const provider = adaptAccountsProvider(makeFakeClient());\n const signer = provider.getLegacyAccountSigner({\n publicKey: new Uint8Array(32).fill(0xbb),\n });\n await expect(signer.signTx(new Uint8Array([1]), {}, new Uint8Array(), 0)).rejects.toThrow(\n \"Can't find genesis hash on transaction\",\n );\n });\n\n // Signed extensions PAPI hands to `signTx`. `CheckGenesis.additionalSigned`\n // carries the genesis hash the signer pulls out; the rest are mapped to the\n // host's `{ id, extra, additionalSigned }` wire shape.\n const sampleExtensions = {\n CheckGenesis: {\n identifier: \"CheckGenesis\",\n value: new Uint8Array([]),\n additionalSigned: new Uint8Array([0x01, 0x02]),\n },\n CheckNonce: {\n identifier: \"CheckNonce\",\n value: new Uint8Array([0x05]),\n additionalSigned: new Uint8Array([]),\n },\n };\n const expectedHostExtensions = [\n {\n id: \"CheckGenesis\",\n extra: toHex(new Uint8Array([])),\n additionalSigned: toHex(new Uint8Array([0x01, 0x02])),\n },\n {\n id: \"CheckNonce\",\n extra: toHex(new Uint8Array([0x05])),\n additionalSigned: toHex(new Uint8Array([])),\n },\n ];\n\n test(\"the product signer's signTx builds createTransaction from genesis + extensions\", async () => {\n // Stub the metadata decode (needs a real SCALE blob) so the rest of the\n // signTx flow — genesis extraction, extension mapping, the host call,\n // response decode — is exercised against a fixed txExtVersion.\n vi.spyOn(deps, \"deriveTxExtVersion\").mockReturnValue(0);\n const calls: Array<[string, unknown]> = [];\n const client = makeFakeClient({ onCall: (m, a) => calls.push([m, a]) });\n const provider = adaptAccountsProvider(client);\n const signer = provider.getProductAccountSigner({\n dotNsIdentifier: \"app.dot\",\n derivationIndex: 0,\n publicKey: new Uint8Array(32).fill(0xaa),\n });\n\n const signed = await signer.signTx(\n new Uint8Array([0xca, 0x11]),\n sampleExtensions,\n new Uint8Array([0x6d]),\n 0,\n );\n\n expect(calls.at(-1)).toEqual([\n \"createTransaction\",\n {\n signer: { dotNsIdentifier: \"app.dot\", derivationIndex: 0 },\n genesisHash: toHex(new Uint8Array([0x01, 0x02])),\n callData: toHex(new Uint8Array([0xca, 0x11])),\n extensions: expectedHostExtensions,\n txExtVersion: 0,\n },\n ]);\n expect(signed).toEqual(fromHex(\"0xdead\"));\n vi.restoreAllMocks();\n });\n\n test(\"the legacy signer's signTx builds createTransactionWithLegacyAccount (signer = hex pubkey)\", async () => {\n vi.spyOn(deps, \"deriveTxExtVersion\").mockReturnValue(0);\n const calls: Array<[string, unknown]> = [];\n const client = makeFakeClient({ onCall: (m, a) => calls.push([m, a]) });\n const provider = adaptAccountsProvider(client);\n const publicKey = new Uint8Array(32).fill(0xbb);\n const signer = provider.getLegacyAccountSigner({ publicKey });\n\n const signed = await signer.signTx(\n new Uint8Array([0xca, 0x11]),\n sampleExtensions,\n new Uint8Array([0x6d]),\n 0,\n );\n\n expect(calls.at(-1)).toEqual([\n \"createTransactionWithLegacyAccount\",\n {\n // createTransactionWithLegacyAccount identifies the signer by raw hex pubkey.\n signer: toHex(publicKey),\n genesisHash: toHex(new Uint8Array([0x01, 0x02])),\n callData: toHex(new Uint8Array([0xca, 0x11])),\n extensions: expectedHostExtensions,\n txExtVersion: 0,\n },\n ]);\n expect(signed).toEqual(fromHex(\"0xfeed\"));\n vi.restoreAllMocks();\n });\n}\n","// Copyright 2026 Parity Technologies (UK) Ltd.\n// SPDX-License-Identifier: Apache-2.0\n/**\n * Higher-level wrappers for the host's single-permission flows.\n *\n * `truApi.permissions.requestRemotePermission` / `requestDevicePermission`\n * return a neverthrow `ResultAsync` of a `{ granted }` response.\n * {@link requestPermission} and {@link requestDevicePermission} collapse that\n * to one-liners returning a `Result<boolean, HostError>` — the granted flag on\n * success, a typed {@link HostError} on the `err` channel.\n *\n * @module\n */\n\nimport type { HostDevicePermissionRequest } from \"@parity/truapi\";\nimport { createLogger } from \"@parity/product-sdk-logger\";\n\nimport { type HostError, HostUnavailableError } from \"./errors.js\";\nimport { type Result, err } from \"./result.js\";\nimport { getTruApi, mapHostResult, type RemotePermission } from \"./truapi.js\";\n\nconst log = createLogger(\"host:permissions\");\n\n/**\n * Device permission the dapp can ask the host to grant via\n * {@link requestDevicePermission}. A string union (`\"Camera\"`, `\"Microphone\"`,\n * …) re-exported from `@parity/truapi`.\n */\nexport type DevicePermissionKind = HostDevicePermissionRequest;\n\n/**\n * Legacy alias of {@link RemotePermission}, kept for back-compat with code that\n * used the older name. Use either freely.\n */\nexport type RemotePermissionItem = RemotePermission;\n\n/**\n * Request a single remote permission from the host.\n *\n * Calls `truApi.permissions.requestRemotePermission` and returns the host's\n * boolean granted/denied outcome.\n *\n * @param permission - The remote permission to request.\n * @returns `ok(true)` if the host granted the permission, `ok(false)` if denied,\n * or `err(HostUnavailableError | HostCallFailedError)`.\n *\n * @example\n * ```ts\n * const r = await requestPermission({ tag: \"ChainSubmit\", value: undefined });\n * if (!r.ok || !r.value) {\n * tellUserToReconnect();\n * }\n * ```\n */\nexport async function requestPermission(\n permission: RemotePermission,\n): Promise<Result<boolean, HostError>> {\n const truApi = await getTruApi();\n if (!truApi) {\n return err(new HostUnavailableError(\"requestPermission: TruAPI unavailable\"));\n }\n log.debug(\"requestPermission\", { tag: permission.tag });\n\n return mapHostResult(\n truApi.permissions.requestRemotePermission({ permission }),\n (response) => response.granted,\n \"requestPermission failed\",\n );\n}\n\n/**\n * Request a single device permission (camera, microphone, etc.) from the\n * host.\n *\n * Calls `truApi.permissions.requestDevicePermission` and returns the host's\n * boolean granted/denied outcome.\n *\n * @param permission - The device permission to request.\n * @returns `ok(true)` if the host granted the permission, `ok(false)` if denied,\n * or `err(HostUnavailableError | HostCallFailedError)`.\n *\n * @example\n * ```ts\n * const r = await requestDevicePermission(\"Camera\");\n * if (!r.ok || !r.value) {\n * showCameraDeniedMessage();\n * }\n * ```\n */\nexport async function requestDevicePermission(\n permission: DevicePermissionKind,\n): Promise<Result<boolean, HostError>> {\n const truApi = await getTruApi();\n if (!truApi) {\n return err(new HostUnavailableError(\"requestDevicePermission: TruAPI unavailable\"));\n }\n log.debug(\"requestDevicePermission\", { permission });\n\n return mapHostResult(\n truApi.permissions.requestDevicePermission(permission),\n (response) => response.granted,\n \"requestDevicePermission failed\",\n );\n}\n\nif (import.meta.vitest) {\n const { test, expect, describe, vi } = import.meta.vitest;\n\n function okAsync<T>(value: T) {\n return { match: async (onOk: (v: T) => unknown) => onOk(value) };\n }\n function errAsync<E>(error: E) {\n return {\n match: async (_onOk: (v: unknown) => unknown, onErr: (e: E) => unknown) => onErr(error),\n };\n }\n\n async function withMockedTruApi<T>(\n client: unknown,\n fn: (mod: typeof import(\"./permissions.js\")) => Promise<T>,\n ): Promise<T> {\n vi.resetModules();\n vi.doMock(\"./truapi.js\", async (importOriginal) => {\n const original = await importOriginal<typeof import(\"./truapi.js\")>();\n return { ...original, getTruApi: async () => client };\n });\n try {\n const mod = await import(\"./permissions.js\");\n return await fn(mod);\n } finally {\n vi.doUnmock(\"./truapi.js\");\n vi.resetModules();\n }\n }\n\n describe(\"requestPermission\", () => {\n test(\"returns err(HostUnavailableError) when TruAPI is unavailable\", async () => {\n await withMockedTruApi(null, async (mod) => {\n const result = await mod.requestPermission({\n tag: \"ChainSubmit\",\n value: undefined,\n });\n expect(result.ok).toBe(false);\n if (!result.ok) {\n expect(result.error.name).toBe(\"HostUnavailableError\");\n }\n });\n });\n\n test(\"returns ok with the granted flag\", async () => {\n await withMockedTruApi(\n {\n permissions: {\n requestRemotePermission: vi.fn(() => okAsync({ granted: true })),\n },\n },\n async (mod) => {\n const result = await mod.requestPermission({\n tag: \"ChainSubmit\",\n value: undefined,\n });\n expect(result).toEqual({ ok: true, value: true });\n },\n );\n });\n\n test(\"wraps host errors in err(HostCallFailedError) with a diagnostic message\", async () => {\n await withMockedTruApi(\n {\n permissions: {\n requestRemotePermission: vi.fn(() => errAsync({ reason: \"boom\" })),\n },\n },\n async (mod) => {\n const result = await mod.requestPermission({\n tag: \"ChainSubmit\",\n value: undefined,\n });\n expect(result.ok).toBe(false);\n if (!result.ok) {\n expect(result.error.name).toBe(\"HostCallFailedError\");\n expect(result.error.message).toMatch(/requestPermission failed: boom/);\n }\n },\n );\n });\n });\n\n describe(\"requestDevicePermission\", () => {\n test(\"returns err(HostUnavailableError) when TruAPI is unavailable\", async () => {\n await withMockedTruApi(null, async (mod) => {\n const result = await mod.requestDevicePermission(\"Camera\");\n expect(result.ok).toBe(false);\n if (!result.ok) {\n expect(result.error.name).toBe(\"HostUnavailableError\");\n }\n });\n });\n\n test(\"returns ok with the granted flag\", async () => {\n await withMockedTruApi(\n {\n permissions: {\n requestDevicePermission: vi.fn(() => okAsync({ granted: true })),\n },\n },\n async (mod) => {\n expect(await mod.requestDevicePermission(\"Camera\")).toEqual({\n ok: true,\n value: true,\n });\n },\n );\n });\n\n test(\"wraps host errors in err(HostCallFailedError) with a diagnostic message\", async () => {\n await withMockedTruApi(\n {\n permissions: {\n requestDevicePermission: vi.fn(() => errAsync({ reason: \"boom\" })),\n },\n },\n async (mod) => {\n const result = await mod.requestDevicePermission(\"Camera\");\n expect(result.ok).toBe(false);\n if (!result.ok) {\n expect(result.error.name).toBe(\"HostCallFailedError\");\n expect(result.error.message).toMatch(\n /requestDevicePermission failed: boom/,\n );\n }\n },\n );\n });\n });\n}\n","// Copyright 2026 Parity Technologies (UK) Ltd.\n// SPDX-License-Identifier: Apache-2.0\n/**\n * Higher-level wrapper for the host's theme subscription, backed by\n * `truApi.theme.subscribe`.\n *\n * `getThemeProvider` returns a handle whose `subscribeTheme(cb)` delivers a\n * typed {@link ThemeMode} — a `{ name, variant }` struct where `variant` is\n * `\"Light\" | \"Dark\"` and `name` is `{ tag: \"Default\" }` or\n * `{ tag: \"Custom\", value }` — and yields a {@link HostSubscription}\n * (`unsubscribe` + `onInterrupt`).\n *\n * @module\n */\n\nimport type { HostThemeSubscribeItem, TrUApiClient } from \"@parity/truapi\";\n\nimport { getClient, subscribeWithInterrupt } from \"./transport.js\";\nimport type { HostSubscription } from \"./types.js\";\n\n/**\n * Host theme value. A `{ name, variant }` struct re-exported from\n * `@parity/truapi`.\n */\nexport type ThemeMode = HostThemeSubscribeItem;\n\n/** Light/dark variant of the active theme (`\"Light\" | \"Dark\"`) and the active theme name. Re-exported from `@parity/truapi`. */\nexport type { ThemeName, ThemeVariant } from \"@parity/truapi\";\n\n/**\n * Host theme provider handle. `subscribeTheme(callback)` receives a typed\n * {@link ThemeMode} on every change and returns a {@link HostSubscription}.\n */\nexport interface ThemeProvider {\n subscribeTheme(callback: (theme: ThemeMode) => void): HostSubscription;\n}\n\n/** Build a {@link ThemeProvider} over a TruAPI client's `theme` domain. */\nfunction adaptThemeProvider(client: TrUApiClient): ThemeProvider {\n return {\n subscribeTheme(callback) {\n return subscribeWithInterrupt(client.theme.subscribe(), callback);\n },\n };\n}\n\n/**\n * Get the host theme provider, backed by `truApi.theme.*`. Returns `null` when\n * running outside a host container.\n *\n * @returns The theme provider, or `null` if unavailable.\n *\n * @example\n * ```ts\n * import { getThemeProvider } from \"@parity/product-sdk-host\";\n *\n * const provider = await getThemeProvider();\n * if (provider) {\n * const sub = provider.subscribeTheme((theme) => {\n * document.documentElement.dataset.theme = theme.variant.toLowerCase();\n * if (theme.name.tag === \"Custom\") loadCustomTheme(theme.name.value);\n * });\n * // sub.unsubscribe() to stop listening\n * }\n * ```\n */\nexport async function getThemeProvider(): Promise<ThemeProvider | null> {\n const client = await getClient();\n return client ? adaptThemeProvider(client) : null;\n}\n\nif (import.meta.vitest) {\n const { test, expect } = import.meta.vitest;\n\n test(\"getThemeProvider returns null outside a container\", async () => {\n expect(await getThemeProvider()).toBeNull();\n });\n}\n","// Copyright 2026 Parity Technologies (UK) Ltd.\n// SPDX-License-Identifier: Apache-2.0\n/**\n * Higher-level wrapper for the host's entropy derivation (RFC-0007).\n *\n * `truApi.entropy.derive` takes a hex `context` and returns a hex `entropy`\n * payload wrapped in a neverthrow `ResultAsync`. `deriveEntropy` keeps the\n * ergonomic `Uint8Array → Result<Uint8Array, HostError>` signature: it\n * hex-encodes the context on the way in and decodes the entropy on the way out.\n *\n * @module\n */\n\nimport { createLogger } from \"@parity/product-sdk-logger\";\n\nimport { type HostError, HostUnavailableError } from \"./errors.js\";\nimport { type Result, err } from \"./result.js\";\nimport { fromHex, getTruApi, mapHostResult, toHex } from \"./truapi.js\";\n\nconst log = createLogger(\"host:entropy\");\n\n/**\n * Derive deterministic entropy from a context key (RFC-0007).\n *\n * The host derives entropy from the user's wallet + the provided context\n * key. Calling with the same key on the same wallet yields the same bytes;\n * different keys (or different wallets) yield uncorrelated entropy.\n *\n * @param key - Context key bytes (typically a SCALE-encoded discriminator).\n * @returns `ok` with the derived entropy bytes, or\n * `err(HostUnavailableError | HostCallFailedError)`.\n *\n * @example\n * ```ts\n * import { deriveEntropy } from \"@parity/product-sdk-host\";\n *\n * const r = await deriveEntropy(new TextEncoder().encode(\"my-app:seed-v1\"));\n * if (r.ok) { const seed = r.value; }\n * ```\n */\nexport async function deriveEntropy(key: Uint8Array): Promise<Result<Uint8Array, HostError>> {\n const truApi = await getTruApi();\n if (!truApi) {\n return err(new HostUnavailableError(\"deriveEntropy: TruAPI unavailable\"));\n }\n log.debug(\"deriveEntropy\", { keyLen: key.length });\n\n return mapHostResult(\n truApi.entropy.derive({ context: toHex(key) }),\n (response) => fromHex(response.entropy),\n \"deriveEntropy failed\",\n );\n}\n\nif (import.meta.vitest) {\n const { test, expect, describe, vi } = import.meta.vitest;\n\n function okAsync<T>(value: T) {\n return { match: async (onOk: (v: T) => unknown) => onOk(value) };\n }\n\n async function withMockedTruApi<T>(\n client: unknown,\n fn: (mod: typeof import(\"./entropy.js\")) => Promise<T>,\n ): Promise<T> {\n vi.resetModules();\n vi.doMock(\"./truapi.js\", async (importOriginal) => {\n const original = await importOriginal<typeof import(\"./truapi.js\")>();\n return { ...original, getTruApi: async () => client };\n });\n try {\n const mod = await import(\"./entropy.js\");\n return await fn(mod);\n } finally {\n vi.doUnmock(\"./truapi.js\");\n vi.resetModules();\n }\n }\n\n // Tests live inside `describe` so the re-import in `withMockedTruApi`\n // (via `vi.resetModules`) doesn't re-register top-level `test()` calls.\n describe(\"deriveEntropy\", () => {\n test(\"returns err(HostUnavailableError) when TruAPI is unavailable\", async () => {\n await withMockedTruApi(null, async (mod) => {\n const result = await mod.deriveEntropy(new Uint8Array([1, 2, 3]));\n expect(result.ok).toBe(false);\n if (!result.ok) {\n expect(result.error.name).toBe(\"HostUnavailableError\");\n }\n });\n });\n\n test(\"hex-encodes the context and decodes the entropy bytes\", async () => {\n const derive = vi.fn(() => okAsync({ entropy: \"0xc0ffee\" }));\n await withMockedTruApi({ entropy: { derive } }, async (mod) => {\n const result = await mod.deriveEntropy(new Uint8Array([0xab, 0xcd]));\n expect(derive).toHaveBeenCalledWith({ context: \"0xabcd\" });\n expect(result.ok).toBe(true);\n if (result.ok) {\n expect(Array.from(result.value)).toEqual([0xc0, 0xff, 0xee]);\n }\n });\n });\n });\n}\n","// Copyright 2026 Parity Technologies (UK) Ltd.\n// SPDX-License-Identifier: Apache-2.0\n/**\n * Wrapper for the host's chat surface, backed by `truApi.chat.*`.\n *\n * `getChatManager()` returns a manager for room/bot registration, message\n * sending, and subscription to the room list and incoming actions.\n *\n * @module\n */\n\nimport type {\n ChatBotRegistrationStatus,\n ChatRoomRegistrationStatus,\n HostChatActionSubscribeItem,\n HostChatCreateRoomRequest,\n HostChatRegisterBotRequest,\n TrUApiClient,\n} from \"@parity/truapi\";\n\nimport { getClient, subscribeWithInterrupt } from \"./transport.js\";\nimport { unwrapHostResult } from \"./truapi.js\";\nimport type { HostSubscription } from \"./types.js\";\n\n/** Chat message payload variants and room metadata. Re-exported from `@parity/truapi`. */\nexport type { ChatMessageContent, ChatRoom } from \"@parity/truapi\";\nimport type { ChatMessageContent, ChatRoom } from \"@parity/truapi\";\n\n/** Action received via {@link ChatManager.subscribeAction} (`{ roomId, peer, payload }`). Re-exported from `@parity/truapi`. */\nexport type ChatReceivedAction = HostChatActionSubscribeItem;\n\n/** Result of registering a chat room (`\"New\" | \"Exists\"`). Re-exported from `@parity/truapi`. */\nexport type ChatRoomRegistrationResult = ChatRoomRegistrationStatus;\n\n/** Result of registering a bot (`\"New\" | \"Exists\"`). Re-exported from `@parity/truapi`. */\nexport type ChatBotRegistrationResult = ChatBotRegistrationStatus;\n\n/**\n * Chat manager handle. Exposes room/bot registration, message sending, and\n * subscription to the room list and incoming actions.\n */\nexport interface ChatManager {\n registerRoom(request: HostChatCreateRoomRequest): Promise<ChatRoomRegistrationResult>;\n registerBot(request: HostChatRegisterBotRequest): Promise<ChatBotRegistrationResult>;\n sendMessage(roomId: string, payload: ChatMessageContent): Promise<{ messageId: string }>;\n subscribeChatList(callback: (rooms: ChatRoom[]) => void): HostSubscription;\n subscribeAction(callback: (action: ChatReceivedAction) => void): HostSubscription;\n}\n\n/** Build a {@link ChatManager} over a TruAPI client's `chat` domain. */\nfunction adaptChatManager(client: TrUApiClient): ChatManager {\n const chat = client.chat;\n // Cache registration status by id so repeat calls don't re-prompt the host.\n const roomStatus = new Map<string, ChatRoomRegistrationResult>();\n const botStatus = new Map<string, ChatBotRegistrationResult>();\n\n return {\n async registerRoom(request) {\n const cached = roomStatus.get(request.roomId);\n if (cached) return cached;\n const response = await unwrapHostResult(\n chat.createRoom(request),\n \"chat registerRoom failed\",\n );\n roomStatus.set(request.roomId, response.status);\n return response.status;\n },\n async registerBot(request) {\n const cached = botStatus.get(request.botId);\n if (cached) return cached;\n const response = await unwrapHostResult(\n chat.registerBot(request),\n \"chat registerBot failed\",\n );\n botStatus.set(request.botId, response.status);\n return response.status;\n },\n async sendMessage(roomId, payload) {\n const response = await unwrapHostResult(\n chat.postMessage({ roomId, payload }),\n \"chat sendMessage failed\",\n );\n return { messageId: response.messageId };\n },\n subscribeChatList(callback) {\n return subscribeWithInterrupt(chat.listSubscribe(), (item) => callback(item.rooms));\n },\n subscribeAction(callback) {\n return subscribeWithInterrupt(chat.actionSubscribe(), callback);\n },\n };\n}\n\n/**\n * Get the host chat manager, backed by `truApi.chat.*`. Returns `null` when\n * running outside a host container.\n *\n * @returns The chat manager, or `null` if unavailable.\n *\n * @example\n * ```ts\n * import { getChatManager } from \"@parity/product-sdk-host\";\n *\n * const chat = await getChatManager();\n * if (chat) {\n * await chat.registerBot({ botId: \"echo\", name: \"Echo Bot\", icon: \"\" });\n * chat.subscribeAction((action) => { ... });\n * }\n * ```\n */\nexport async function getChatManager(): Promise<ChatManager | null> {\n const client = await getClient();\n return client ? adaptChatManager(client) : null;\n}\n\nif (import.meta.vitest) {\n const { test, expect } = import.meta.vitest;\n\n test(\"getChatManager returns null outside a container\", async () => {\n expect(await getChatManager()).toBeNull();\n });\n}\n","// Copyright 2026 Parity Technologies (UK) Ltd.\n// SPDX-License-Identifier: Apache-2.0\n/**\n * Wrapper for the host's payment manager (RFC-0006), backed by\n * `truApi.payment.*`.\n *\n * Exposes balance subscription, top-up, payment requests, and payment-status\n * subscription. Distinct from the CoinPayment / merchant-payments surface\n * (RFC-0017): RFC-0006 is the user-initiated balance / top-up / payment-request\n * flow.\n *\n * @module\n */\n\nimport type {\n Balance,\n PaymentPurseId,\n HexString,\n HostPaymentBalanceSubscribeItem,\n HostPaymentStatusSubscribeItem,\n PaymentTopUpSource,\n TrUApiClient,\n} from \"@parity/truapi\";\n\nimport { getClient, subscribeWithInterrupt } from \"./transport.js\";\nimport { unwrapHostResult } from \"./truapi.js\";\nimport type { HostSubscription } from \"./types.js\";\n\n/**\n * Payment manager handle. Exposes balance subscription, top-up, payment\n * requests, and payment-status subscription.\n *\n * The balance / status / top-up-source shapes are `@parity/truapi`'s\n * `HostPaymentBalanceSubscribeItem`, `HostPaymentStatusSubscribeItem`, and\n * `PaymentTopUpSource` — used directly rather than re-aliased.\n */\nexport interface PaymentManager {\n subscribeBalance(\n callback: (balance: HostPaymentBalanceSubscribeItem) => void,\n purse?: PaymentPurseId,\n ): HostSubscription;\n topUp(amount: Balance, source: PaymentTopUpSource, into?: PaymentPurseId): Promise<void>;\n requestPayment(\n amount: Balance,\n destination: HexString,\n from?: PaymentPurseId,\n ): Promise<{ id: string }>;\n subscribePaymentStatus(\n paymentId: string,\n callback: (status: HostPaymentStatusSubscribeItem) => void,\n ): HostSubscription;\n}\n\n/** Build a {@link PaymentManager} over a TruAPI client's `payment` domain. */\nfunction adaptPaymentManager(client: TrUApiClient): PaymentManager {\n const payment = client.payment;\n return {\n subscribeBalance(callback, purse) {\n return subscribeWithInterrupt(\n payment.balanceSubscribe({ request: { purse } }),\n callback,\n );\n },\n topUp(amount, source, into) {\n return unwrapHostResult(\n payment.topUp({ into, amount, source }),\n \"payment topUp failed\",\n );\n },\n async requestPayment(amount, destination, from) {\n const response = await unwrapHostResult(\n payment.request({ from, amount, destination }),\n \"payment requestPayment failed\",\n );\n return { id: response.id };\n },\n subscribePaymentStatus(paymentId, callback) {\n return subscribeWithInterrupt(\n payment.statusSubscribe({ request: { paymentId } }),\n callback,\n );\n },\n };\n}\n\n/**\n * Get the host payment manager, backed by `truApi.payment.*`. Returns `null`\n * when running outside a host container.\n *\n * @returns The payment manager, or `null` if unavailable.\n *\n * @example\n * ```ts\n * import { getPaymentManager } from \"@parity/product-sdk-host\";\n *\n * const payments = await getPaymentManager();\n * if (payments) {\n * const sub = payments.subscribeBalance((b) => { ... });\n * await payments.topUp(1_000_000n, { tag: \"ProductAccount\", value: { derivationIndex: 0 } });\n * const { id } = await payments.requestPayment(500n, \"0x…\");\n * sub.unsubscribe();\n * }\n * ```\n */\nexport async function getPaymentManager(): Promise<PaymentManager | null> {\n const client = await getClient();\n return client ? adaptPaymentManager(client) : null;\n}\n\nif (import.meta.vitest) {\n const { test, expect } = import.meta.vitest;\n\n test(\"getPaymentManager returns null outside a container\", async () => {\n expect(await getPaymentManager()).toBeNull();\n });\n}\n","// Copyright 2026 Parity Technologies (UK) Ltd.\n// SPDX-License-Identifier: Apache-2.0\n/**\n * Wrapper for the host's scheduled push-notification surface (RFC-0019),\n * backed by `truApi.notifications.*`.\n *\n * `getNotificationManager()` returns a handle exposing `push(input)` (resolves\n * to a {@link NotificationId}) and `cancel(id)`, matching the singleton\n * pattern already used by {@link getPaymentManager}, {@link getPreimageManager},\n * and {@link getHostLocalStorage}.\n *\n * @module\n */\n\nimport type { HostPushNotificationRequest, NotificationId, TrUApiClient } from \"@parity/truapi\";\n\nimport { getClient } from \"./transport.js\";\nimport { unwrapHostResult } from \"./truapi.js\";\n\n/**\n * Error variants the host raises when scheduling a push notification.\n *\n * A `{ tag }` tagged union re-exported from `@parity/truapi`:\n * `{ tag: \"ScheduleLimitReached\" }` (the host-wide pending-notification cap) or\n * `{ tag: \"Unknown\"; value: { reason } }`. {@link NotificationManager.push} /\n * {@link NotificationManager.cancel} reject with an `Error` whose `cause`\n * carries this value, so branch on it — e.g.\n * `(err as Error).cause?.tag === \"ScheduleLimitReached\"`.\n */\nexport type { HostPushNotificationError as PushNotificationError } from \"@parity/truapi\";\n\n/**\n * Host-assigned id for a scheduled notification — pass to\n * {@link NotificationManager.cancel}. Re-exported from `@parity/truapi`.\n */\nexport type { NotificationId };\n\n/**\n * Push payload: `text`, an optional `deeplink`, and an optional `scheduledAt`\n * (Unix timestamp in milliseconds; omit for immediate delivery). Re-exported\n * from the truapi wire request type so the shape stays in lockstep with the\n * protocol.\n */\nexport type PushNotificationInput = HostPushNotificationRequest;\n\n/**\n * Host notification manager handle. Exposes `push(input)` (resolves to a\n * {@link NotificationId}) and `cancel(id)`.\n */\nexport interface NotificationManager {\n push(input: PushNotificationInput): Promise<NotificationId>;\n cancel(id: NotificationId): Promise<void>;\n}\n\n/** Build a {@link NotificationManager} over a TruAPI client's `notifications` domain. */\nfunction adaptNotificationManager(client: TrUApiClient): NotificationManager {\n const notifications = client.notifications;\n return {\n async push(input) {\n const response = await unwrapHostResult(\n notifications.sendPushNotification(input),\n \"notification push failed\",\n );\n return response.id;\n },\n async cancel(id) {\n await unwrapHostResult(\n notifications.cancelPushNotification({ id }),\n \"notification cancel failed\",\n );\n },\n };\n}\n\n/**\n * Get the host notification manager, backed by `truApi.notifications.*`.\n * Returns `null` when running outside a host container.\n *\n * @returns The notification manager, or `null` if unavailable.\n *\n * @example\n * ```ts\n * import { getNotificationManager, type PushNotificationError } from \"@parity/product-sdk-host\";\n *\n * const notifications = await getNotificationManager();\n * if (notifications) {\n * try {\n * const id = await notifications.push({\n * text: \"Doors open in 1h\",\n * scheduledAt: someUnixMs,\n * });\n * // later: await notifications.cancel(id);\n * } catch (err) {\n * const cause = (err as Error).cause as PushNotificationError | undefined;\n * if (cause?.tag === \"ScheduleLimitReached\") {\n * // host hit its pending-notification cap — surface to the user\n * }\n * }\n * }\n * ```\n */\nexport async function getNotificationManager(): Promise<NotificationManager | null> {\n const client = await getClient();\n return client ? adaptNotificationManager(client) : null;\n}\n\nif (import.meta.vitest) {\n const { test, expect } = import.meta.vitest;\n\n test(\"getNotificationManager returns null outside a container\", async () => {\n expect(await getNotificationManager()).toBeNull();\n });\n}\n","// Copyright 2026 Parity Technologies (UK) Ltd.\n// SPDX-License-Identifier: Apache-2.0\n/**\n * Higher-level wrapper for the host's deep-link navigation.\n *\n * `truApi.system.navigateTo` returns a neverthrow `ResultAsync`; consumers\n * still have to unwrap it themselves. {@link navigateTo} collapses that to a\n * `Result<void, HostError>`-returning Promise.\n *\n * @module\n */\n\nimport { createLogger } from \"@parity/product-sdk-logger\";\n\nimport { type HostError, HostUnavailableError } from \"./errors.js\";\nimport { type Result, err } from \"./result.js\";\nimport { getTruApi, mapHostResult } from \"./truapi.js\";\n\nconst log = createLogger(\"host:navigation\");\n\n/**\n * Ask the host to navigate to a URL (deep link or external link).\n *\n * Calls `truApi.system.navigateTo` and unwraps the response. The host resolves\n * the destination itself — a `dot`-suffixed deep link (e.g.\n * `\"https://search.dot\"`) routes to another app/route inside the container, an\n * `https://` URL opens externally.\n *\n * @param url - The URL to navigate to.\n * @returns `ok` on success, or `err`: {@link HostUnavailableError} if the host\n * is unavailable, or {@link HostCallFailedError} if it denies the navigation\n * (`NavigateToErr::PermissionDenied`) or fails otherwise (`NavigateToErr::Unknown`).\n *\n * @example\n * ```ts\n * import { navigateTo } from \"@parity/product-sdk-host\";\n *\n * const r = await navigateTo(\"https://search.dot\");\n * if (!r.ok) handle(r.error);\n * ```\n */\nexport async function navigateTo(url: string): Promise<Result<void, HostError>> {\n const truApi = await getTruApi();\n if (!truApi) {\n return err(new HostUnavailableError(\"navigateTo: TruAPI unavailable\"));\n }\n log.debug(\"navigateTo\", { url });\n\n return mapHostResult(truApi.system.navigateTo({ url }), () => undefined, \"navigateTo failed\");\n}\n\nif (import.meta.vitest) {\n const { test, expect, describe, vi } = import.meta.vitest;\n\n async function withMockedTruApi<T>(\n bridge: { system?: { navigateTo?: (req: unknown) => unknown } } | null,\n fn: (mod: typeof import(\"./navigation.js\")) => Promise<T>,\n ): Promise<T> {\n vi.resetModules();\n vi.doMock(\"./truapi.js\", async (importOriginal) => {\n const original = await importOriginal<typeof import(\"./truapi.js\")>();\n return {\n ...original,\n getTruApi: async () => bridge,\n };\n });\n try {\n const mod = await import(\"./navigation.js\");\n return await fn(mod);\n } finally {\n vi.doUnmock(\"./truapi.js\");\n vi.resetModules();\n }\n }\n\n describe(\"navigateTo\", () => {\n test(\"returns err(HostUnavailableError) when TruAPI is unavailable\", async () => {\n await withMockedTruApi(null, async (mod) => {\n const result = await mod.navigateTo(\"https://search.dot\");\n expect(result.ok).toBe(false);\n if (!result.ok) {\n expect(result.error.name).toBe(\"HostUnavailableError\");\n }\n });\n });\n\n test(\"returns ok on success\", async () => {\n await withMockedTruApi(\n {\n system: {\n navigateTo: vi.fn().mockReturnValue({\n match: async (onOk: (v: unknown) => unknown) => onOk(undefined),\n }),\n },\n },\n async (mod) => {\n expect(await mod.navigateTo(\"https://search.dot\")).toEqual({\n ok: true,\n value: undefined,\n });\n },\n );\n });\n\n test(\"wraps host errors in err(HostCallFailedError) with a diagnostic message\", async () => {\n await withMockedTruApi(\n {\n system: {\n navigateTo: vi.fn().mockReturnValue({\n match: async (\n _onOk: (v: unknown) => unknown,\n onErr: (e: unknown) => unknown,\n ) => onErr({ tag: \"PermissionDenied\" }),\n }),\n },\n },\n async (mod) => {\n const result = await mod.navigateTo(\"https://search.dot\");\n expect(result.ok).toBe(false);\n if (!result.ok) {\n expect(result.error.name).toBe(\"HostCallFailedError\");\n expect(result.error.message).toMatch(/navigateTo failed: PermissionDenied/);\n }\n },\n );\n });\n });\n}\n","// Copyright 2026 Parity Technologies (UK) Ltd.\n// SPDX-License-Identifier: Apache-2.0\n/**\n * Higher-level wrappers for the host's feature-support probe.\n *\n * `truApi.system.featureSupported` returns a neverthrow `ResultAsync`;\n * {@link featureSupported} collapses that to a `Result` carrying the host's\n * boolean answer. {@link isChainSupported} is a convenience over the only\n * feature variant the host exposes today (`Chain`).\n *\n * @module\n */\n\nimport { createLogger } from \"@parity/product-sdk-logger\";\n\nimport { type HostError, HostUnavailableError } from \"./errors.js\";\nimport { type Result, err } from \"./result.js\";\nimport { getTruApi, type HexString, mapHostResult } from \"./truapi.js\";\n\nconst log = createLogger(\"host:features\");\n\n/**\n * A feature the host can be probed for via {@link featureSupported}.\n *\n * The only variant today is `Chain`, carrying the chain's `0x`-prefixed genesis\n * hash. This is a flattened form of truapi's `HostFeatureSupportedRequest`,\n * which nests the hash as `{ tag: \"Chain\"; value: { genesisHash } }` — we\n * inline `value` as the `HexString` for ergonomics and re-nest it at the call\n * site. New variants surface here as a widening of the union.\n */\nexport type Feature = { tag: \"Chain\"; value: HexString };\n\n/**\n * Probe the host for support of a specific feature.\n *\n * Calls `truApi.system.featureSupported`, unwraps the response, and returns the\n * host's boolean answer.\n *\n * @param feature - The feature to probe for.\n * @returns `ok(true)` if the host supports the feature, `ok(false)` otherwise,\n * or `err(HostUnavailableError | HostCallFailedError)`.\n *\n * @example\n * ```ts\n * import { featureSupported } from \"@parity/product-sdk-host\";\n *\n * const r = await featureSupported({ tag: \"Chain\", value: genesisHash });\n * if (r.ok && r.value) { ... }\n * ```\n */\nexport async function featureSupported(feature: Feature): Promise<Result<boolean, HostError>> {\n const truApi = await getTruApi();\n if (!truApi) {\n return err(new HostUnavailableError(\"featureSupported: TruAPI unavailable\"));\n }\n log.debug(\"featureSupported\", { tag: feature.tag });\n\n return mapHostResult(\n truApi.system.featureSupported({ tag: feature.tag, value: { genesisHash: feature.value } }),\n (response) => response.supported,\n \"featureSupported failed\",\n );\n}\n\n/**\n * Convenience probe: is the chain with the given genesis hash supported by the\n * host? Wraps {@link featureSupported} for the `Chain` feature variant.\n *\n * @param genesisHash - The chain's `0x`-prefixed genesis hash.\n * @returns `ok(true)` if the host supports the chain, `ok(false)` otherwise, or\n * `err(HostUnavailableError | HostCallFailedError)`.\n *\n * @example\n * ```ts\n * import { isChainSupported } from \"@parity/product-sdk-host\";\n *\n * const r = await isChainSupported(genesisHash);\n * if (!r.ok || !r.value) {\n * tellUserChainUnavailable();\n * }\n * ```\n */\nexport async function isChainSupported(\n genesisHash: HexString,\n): Promise<Result<boolean, HostError>> {\n return featureSupported({ tag: \"Chain\", value: genesisHash });\n}\n\nif (import.meta.vitest) {\n const { test, expect, describe, vi } = import.meta.vitest;\n\n async function withMockedTruApi<T>(\n bridge: { system?: { featureSupported?: (req: unknown) => unknown } } | null,\n fn: (mod: typeof import(\"./features.js\")) => Promise<T>,\n ): Promise<T> {\n vi.resetModules();\n vi.doMock(\"./truapi.js\", async (importOriginal) => {\n const original = await importOriginal<typeof import(\"./truapi.js\")>();\n return {\n ...original,\n getTruApi: async () => bridge,\n };\n });\n try {\n const mod = await import(\"./features.js\");\n return await fn(mod);\n } finally {\n vi.doUnmock(\"./truapi.js\");\n vi.resetModules();\n }\n }\n\n const okBridge = (supported: boolean) => ({\n system: {\n featureSupported: vi.fn().mockReturnValue({\n match: async (onOk: (v: unknown) => unknown) => onOk({ supported }),\n }),\n },\n });\n\n describe(\"featureSupported\", () => {\n test(\"returns err(HostUnavailableError) when TruAPI is unavailable\", async () => {\n await withMockedTruApi(null, async (mod) => {\n const result = await mod.featureSupported({ tag: \"Chain\", value: \"0x00\" });\n expect(result.ok).toBe(false);\n if (!result.ok) {\n expect(result.error.name).toBe(\"HostUnavailableError\");\n }\n });\n });\n\n test(\"returns ok with the boolean outcome\", async () => {\n await withMockedTruApi(okBridge(true), async (mod) => {\n expect(await mod.featureSupported({ tag: \"Chain\", value: \"0x00\" })).toEqual({\n ok: true,\n value: true,\n });\n });\n });\n\n test(\"wraps host errors in err(HostCallFailedError) with a diagnostic message\", async () => {\n await withMockedTruApi(\n {\n system: {\n featureSupported: vi.fn().mockReturnValue({\n match: async (\n _onOk: (v: unknown) => unknown,\n onErr: (e: unknown) => unknown,\n ) => onErr({ reason: \"boom\" }),\n }),\n },\n },\n async (mod) => {\n const result = await mod.featureSupported({ tag: \"Chain\", value: \"0x00\" });\n expect(result.ok).toBe(false);\n if (!result.ok) {\n expect(result.error.name).toBe(\"HostCallFailedError\");\n expect(result.error.message).toMatch(/featureSupported failed: boom/);\n }\n },\n );\n });\n });\n\n describe(\"isChainSupported\", () => {\n test(\"delegates to featureSupported with the Chain variant\", async () => {\n await withMockedTruApi(okBridge(false), async (mod) => {\n expect(await mod.isChainSupported(\"0x1234\")).toEqual({ ok: true, value: false });\n });\n });\n });\n}\n","// Copyright 2026 Parity Technologies (UK) Ltd.\n// SPDX-License-Identifier: Apache-2.0\n/**\n * Higher-level wrapper for the host's chain-spec lookups.\n *\n * The host exposes three separate chain-spec calls — `chain.getSpecGenesisHash`,\n * `chain.getSpecChainName`, and `chain.getSpecProperties` — each reachable via\n * {@link getTruApi} and each returning a neverthrow `ResultAsync`.\n * {@link getChainSpec} fetches all three in one call and returns a single\n * struct so callers read whichever field they need, matching the JSON-RPC\n * `chainSpec_v1_*` family they mirror.\n *\n * @module\n */\n\nimport { createLogger } from \"@parity/product-sdk-logger\";\n\nimport type { HostError } from \"./errors.js\";\nimport { type Result, ok } from \"./result.js\";\nimport { getTruApi, type HexString, mapHostResult } from \"./truapi.js\";\n\nconst log = createLogger(\"host:chain-spec\");\n\n/**\n * Chain SS58/token properties as reported by the host's\n * `chainSpecProperties` call.\n *\n * The host returns this as a JSON string (mirroring the substrate\n * `chainSpec_v1_properties` JSON-RPC, whose payload is an open-ended object).\n * {@link getChainSpec} parses it into {@link ChainSpec.properties} and also\n * surfaces the untouched JSON as {@link ChainSpec.propertiesRaw}. The well-known substrate fields are\n * typed for convenience; the index signature keeps any chain-specific extras\n * reachable without `any` at the call site.\n */\nexport interface ChainProperties {\n /** Address prefix used for SS58 encoding (e.g. `0` for Polkadot). */\n ss58Format?: number;\n /** Decimal places of the chain's native token(s). */\n tokenDecimals?: number | number[];\n /** Ticker symbol(s) of the chain's native token(s). */\n tokenSymbol?: string | string[];\n /** Chain-specific extras passed through verbatim from the JSON payload. */\n [key: string]: unknown;\n}\n\n/**\n * Combined chain-spec view returned by {@link getChainSpec}.\n */\nexport interface ChainSpec {\n /** The chain's `0x`-prefixed genesis hash, as reported by the host. */\n genesisHash: HexString;\n /** Human-readable chain name (e.g. `\"Polkadot\"`). */\n name: string;\n /**\n * Parsed chain properties, or `null` if the host's JSON payload couldn't\n * be parsed. Inspect {@link propertiesRaw} for the original string.\n */\n properties: ChainProperties | null;\n /** The untouched JSON string the host returned for properties. */\n propertiesRaw: string;\n}\n\n/**\n * Fetch a chain's full spec (genesis hash, name, and properties) from the host\n * in one call.\n *\n * Issues the three underlying `chain.getSpec*` requests concurrently, unwraps\n * each response, and parses the properties JSON. Note the `genesisHash` in the\n * result is the value the host echoes back from `getSpecGenesisHash` for the\n * looked-up chain — pass the chain's known genesis hash as the lookup key.\n *\n * `null` (outside a container) is preserved as an `ok` value — it is an\n * expected state, not a failure — so callers branch on `r.ok && r.value`. A\n * real host-call failure surfaces on the `err` channel.\n *\n * @param genesisHash - The `0x`-prefixed genesis hash identifying the chain.\n * @returns `ok(spec)` with the combined {@link ChainSpec}, `ok(null)` if the\n * host is unavailable (running outside a container), or\n * `err(HostCallFailedError)` if any underlying host call fails.\n *\n * @example\n * ```ts\n * import { getChainSpec } from \"@parity/product-sdk-host\";\n *\n * const r = await getChainSpec(genesisHash);\n * if (r.ok && r.value) {\n * console.log(r.value.name, r.value.properties?.tokenSymbol);\n * }\n * ```\n */\nexport async function getChainSpec(\n genesisHash: HexString,\n): Promise<Result<ChainSpec | null, HostError>> {\n const truApi = await getTruApi();\n if (!truApi) {\n log.debug(\"getChainSpec: TruAPI unavailable\");\n return ok(null);\n }\n log.debug(\"getChainSpec\", { genesisHash });\n\n const [genesisHashResult, nameResult, propertiesResult] = await Promise.all([\n mapHostResult(\n truApi.chain.getSpecGenesisHash({ genesisHash }),\n (response) => response.genesisHash,\n \"getChainSpec (genesisHash) failed\",\n ),\n mapHostResult(\n truApi.chain.getSpecChainName({ genesisHash }),\n (response) => response.chainName,\n \"getChainSpec (chainName) failed\",\n ),\n mapHostResult(\n truApi.chain.getSpecProperties({ genesisHash }),\n (response) => response.properties,\n \"getChainSpec (properties) failed\",\n ),\n ]);\n\n // Short-circuit on the first failing call.\n if (!genesisHashResult.ok) return genesisHashResult;\n if (!nameResult.ok) return nameResult;\n if (!propertiesResult.ok) return propertiesResult;\n\n const propertiesRaw = propertiesResult.value;\n let properties: ChainProperties | null;\n try {\n properties = JSON.parse(propertiesRaw) as ChainProperties;\n } catch (parseError) {\n log.debug(\"getChainSpec: properties JSON parse failed\", parseError);\n properties = null;\n }\n\n return ok({\n genesisHash: genesisHashResult.value,\n name: nameResult.value,\n properties,\n propertiesRaw,\n });\n}\n\nif (import.meta.vitest) {\n const { test, expect, describe, vi } = import.meta.vitest;\n\n async function withMockedTruApi<T>(\n bridge: {\n chain?: {\n getSpecGenesisHash?: (req: unknown) => unknown;\n getSpecChainName?: (req: unknown) => unknown;\n getSpecProperties?: (req: unknown) => unknown;\n };\n } | null,\n fn: (mod: typeof import(\"./chain-spec.js\")) => Promise<T>,\n ): Promise<T> {\n vi.resetModules();\n vi.doMock(\"./truapi.js\", async (importOriginal) => {\n const original = await importOriginal<typeof import(\"./truapi.js\")>();\n return {\n ...original,\n getTruApi: async () => bridge,\n };\n });\n try {\n const mod = await import(\"./chain-spec.js\");\n return await fn(mod);\n } finally {\n vi.doUnmock(\"./truapi.js\");\n vi.resetModules();\n }\n }\n\n /** A resolved ResultAsync stub yielding the given response object. */\n const okAsync = (response: unknown) => ({\n match: async (onOk: (v: unknown) => unknown) => onOk(response),\n });\n\n describe(\"getChainSpec\", () => {\n test(\"returns ok(null) when TruAPI is unavailable\", async () => {\n await withMockedTruApi(null, async (mod) => {\n expect(await mod.getChainSpec(\"0x00\")).toEqual({ ok: true, value: null });\n });\n });\n\n test(\"combines the three calls and parses properties JSON\", async () => {\n await withMockedTruApi(\n {\n chain: {\n getSpecGenesisHash: vi\n .fn()\n .mockReturnValue(okAsync({ genesisHash: \"0xabcd\" })),\n getSpecChainName: vi\n .fn()\n .mockReturnValue(okAsync({ chainName: \"Polkadot\" })),\n getSpecProperties: vi.fn().mockReturnValue(\n okAsync({\n properties:\n '{\"ss58Format\":0,\"tokenDecimals\":10,\"tokenSymbol\":\"DOT\"}',\n }),\n ),\n },\n },\n async (mod) => {\n const result = await mod.getChainSpec(\"0xabcd\");\n expect(result).toEqual({\n ok: true,\n value: {\n genesisHash: \"0xabcd\",\n name: \"Polkadot\",\n properties: { ss58Format: 0, tokenDecimals: 10, tokenSymbol: \"DOT\" },\n propertiesRaw:\n '{\"ss58Format\":0,\"tokenDecimals\":10,\"tokenSymbol\":\"DOT\"}',\n },\n });\n },\n );\n });\n\n test(\"leaves properties null when the JSON is malformed\", async () => {\n await withMockedTruApi(\n {\n chain: {\n getSpecGenesisHash: vi\n .fn()\n .mockReturnValue(okAsync({ genesisHash: \"0xabcd\" })),\n getSpecChainName: vi\n .fn()\n .mockReturnValue(okAsync({ chainName: \"Polkadot\" })),\n getSpecProperties: vi\n .fn()\n .mockReturnValue(okAsync({ properties: \"not json\" })),\n },\n },\n async (mod) => {\n const result = await mod.getChainSpec(\"0xabcd\");\n expect(result.ok).toBe(true);\n if (result.ok) {\n expect(result.value?.properties).toBeNull();\n expect(result.value?.propertiesRaw).toBe(\"not json\");\n }\n },\n );\n });\n\n test(\"returns err(HostCallFailedError) when a host call fails\", async () => {\n await withMockedTruApi(\n {\n chain: {\n getSpecGenesisHash: vi.fn().mockReturnValue({\n match: async (\n _onOk: (v: unknown) => unknown,\n onErr: (e: unknown) => unknown,\n ) => onErr({ reason: \"boom\" }),\n }),\n getSpecChainName: vi\n .fn()\n .mockReturnValue(okAsync({ chainName: \"Polkadot\" })),\n getSpecProperties: vi.fn().mockReturnValue(okAsync({ properties: \"{}\" })),\n },\n },\n async (mod) => {\n const result = await mod.getChainSpec(\"0xabcd\");\n expect(result.ok).toBe(false);\n if (!result.ok) {\n expect(result.error.name).toBe(\"HostCallFailedError\");\n expect(result.error.message).toMatch(\n /getChainSpec \\(genesisHash\\) failed: boom/,\n );\n }\n },\n );\n });\n });\n}\n","// Copyright 2026 Parity Technologies (UK) Ltd.\n// SPDX-License-Identifier: Apache-2.0\n/**\n * Higher-level wrappers for the host's transaction broadcast lifecycle.\n *\n * `truApi.chain.broadcastTransaction` / `truApi.chain.stopTransaction` are\n * reachable via {@link getTruApi}, but consumers have to unwrap the neverthrow\n * `ResultAsync` themselves. {@link broadcastTransaction} and\n * {@link stopTransaction} collapse that to `Result`-returning Promises, mirroring\n * the JSON-RPC `transaction_v1_broadcast` / `transaction_v1_stop` pair they\n * wrap.\n *\n * @module\n */\n\nimport { createLogger } from \"@parity/product-sdk-logger\";\n\nimport { type HostError, HostUnavailableError } from \"./errors.js\";\nimport { type Result, err } from \"./result.js\";\nimport { getTruApi, type HexString, mapHostResult } from \"./truapi.js\";\n\nconst log = createLogger(\"host:chain-transaction\");\n\n/**\n * Broadcast a signed transaction to the network via the host.\n *\n * Calls `truApi.chain.broadcastTransaction` and unwraps the response. The host\n * keeps re-broadcasting until the transaction is finalized/dropped or\n * {@link stopTransaction} is called with the returned operation id.\n *\n * @param genesisHash - The `0x`-prefixed genesis hash of the target chain.\n * @param transaction - The `0x`-prefixed SCALE-encoded signed transaction.\n * @returns `ok` with the operation id to pass to {@link stopTransaction} (or\n * `null` if the host accepted the broadcast without issuing one), or\n * `err(HostUnavailableError | HostCallFailedError)`.\n *\n * @example\n * ```ts\n * import { broadcastTransaction, stopTransaction } from \"@parity/product-sdk-host\";\n *\n * const r = await broadcastTransaction(genesisHash, signedTx);\n * // later, to stop re-broadcasting:\n * if (r.ok && r.value) await stopTransaction(genesisHash, r.value);\n * ```\n */\nexport async function broadcastTransaction(\n genesisHash: HexString,\n transaction: HexString,\n): Promise<Result<string | null, HostError>> {\n const truApi = await getTruApi();\n if (!truApi) {\n return err(new HostUnavailableError(\"broadcastTransaction: TruAPI unavailable\"));\n }\n log.debug(\"broadcastTransaction\", { genesisHash });\n\n return mapHostResult(\n truApi.chain.broadcastTransaction({ genesisHash, transaction }),\n (response) => response.operationId ?? null,\n \"broadcastTransaction failed\",\n );\n}\n\n/**\n * Stop an in-flight broadcast started by {@link broadcastTransaction}.\n *\n * Calls `truApi.chain.stopTransaction` and unwraps the response.\n *\n * @param genesisHash - The `0x`-prefixed genesis hash of the target chain.\n * @param operationId - The operation id returned by\n * {@link broadcastTransaction}.\n * @returns `ok` on success, or `err(HostUnavailableError | HostCallFailedError)`.\n *\n * @example\n * ```ts\n * await stopTransaction(genesisHash, operationId);\n * ```\n */\nexport async function stopTransaction(\n genesisHash: HexString,\n operationId: string,\n): Promise<Result<void, HostError>> {\n const truApi = await getTruApi();\n if (!truApi) {\n return err(new HostUnavailableError(\"stopTransaction: TruAPI unavailable\"));\n }\n log.debug(\"stopTransaction\", { genesisHash, operationId });\n\n return mapHostResult(\n truApi.chain.stopTransaction({ genesisHash, operationId }),\n () => undefined,\n \"stopTransaction failed\",\n );\n}\n\nif (import.meta.vitest) {\n const { test, expect, describe, vi } = import.meta.vitest;\n\n async function withMockedTruApi<T>(\n bridge: {\n chain?: {\n broadcastTransaction?: (req: unknown) => unknown;\n stopTransaction?: (req: unknown) => unknown;\n };\n } | null,\n fn: (mod: typeof import(\"./chain-transaction.js\")) => Promise<T>,\n ): Promise<T> {\n vi.resetModules();\n vi.doMock(\"./truapi.js\", async (importOriginal) => {\n const original = await importOriginal<typeof import(\"./truapi.js\")>();\n return {\n ...original,\n getTruApi: async () => bridge,\n };\n });\n try {\n const mod = await import(\"./chain-transaction.js\");\n return await fn(mod);\n } finally {\n vi.doUnmock(\"./truapi.js\");\n vi.resetModules();\n }\n }\n\n /** A resolved ResultAsync stub yielding the given response object. */\n const ok = (response: unknown) => ({\n match: async (onOk: (v: unknown) => unknown) => onOk(response),\n });\n /** A rejected ResultAsync stub yielding a truapi `GenericError` (`{ reason }`). */\n const errResult = (reason: string) => ({\n match: async (_onOk: (v: unknown) => unknown, onErr: (e: unknown) => unknown) =>\n onErr({ reason }),\n });\n\n describe(\"broadcastTransaction\", () => {\n test(\"returns err(HostUnavailableError) when TruAPI is unavailable\", async () => {\n await withMockedTruApi(null, async (mod) => {\n const result = await mod.broadcastTransaction(\"0x00\", \"0x01\");\n expect(result.ok).toBe(false);\n if (!result.ok) {\n expect(result.error.name).toBe(\"HostUnavailableError\");\n }\n });\n });\n\n test(\"returns ok with the operation id\", async () => {\n await withMockedTruApi(\n {\n chain: {\n broadcastTransaction: vi.fn().mockReturnValue(ok({ operationId: \"op-1\" })),\n },\n },\n async (mod) => {\n expect(await mod.broadcastTransaction(\"0x00\", \"0x01\")).toEqual({\n ok: true,\n value: \"op-1\",\n });\n },\n );\n });\n\n test(\"passes through a missing operation id as ok(null)\", async () => {\n await withMockedTruApi(\n {\n chain: {\n broadcastTransaction: vi.fn().mockReturnValue(ok({})),\n },\n },\n async (mod) => {\n expect(await mod.broadcastTransaction(\"0x00\", \"0x01\")).toEqual({\n ok: true,\n value: null,\n });\n },\n );\n });\n\n test(\"wraps host errors in err(HostCallFailedError) with a diagnostic message\", async () => {\n await withMockedTruApi(\n {\n chain: {\n broadcastTransaction: vi.fn().mockReturnValue(errResult(\"boom\")),\n },\n },\n async (mod) => {\n const result = await mod.broadcastTransaction(\"0x00\", \"0x01\");\n expect(result.ok).toBe(false);\n if (!result.ok) {\n expect(result.error.name).toBe(\"HostCallFailedError\");\n expect(result.error.message).toMatch(/broadcastTransaction failed: boom/);\n }\n },\n );\n });\n });\n\n describe(\"stopTransaction\", () => {\n test(\"returns err(HostUnavailableError) when TruAPI is unavailable\", async () => {\n await withMockedTruApi(null, async (mod) => {\n const result = await mod.stopTransaction(\"0x00\", \"op-1\");\n expect(result.ok).toBe(false);\n if (!result.ok) {\n expect(result.error.name).toBe(\"HostUnavailableError\");\n }\n });\n });\n\n test(\"returns ok on success\", async () => {\n await withMockedTruApi(\n {\n chain: {\n stopTransaction: vi.fn().mockReturnValue(ok(undefined)),\n },\n },\n async (mod) => {\n expect(await mod.stopTransaction(\"0x00\", \"op-1\")).toEqual({\n ok: true,\n value: undefined,\n });\n },\n );\n });\n\n test(\"wraps host errors in err(HostCallFailedError) with a diagnostic message\", async () => {\n await withMockedTruApi(\n {\n chain: {\n stopTransaction: vi.fn().mockReturnValue(errResult(\"boom\")),\n },\n },\n async (mod) => {\n const result = await mod.stopTransaction(\"0x00\", \"op-1\");\n expect(result.ok).toBe(false);\n if (!result.ok) {\n expect(result.error.name).toBe(\"HostCallFailedError\");\n expect(result.error.message).toMatch(/stopTransaction failed: boom/);\n }\n },\n );\n });\n });\n}\n"]}
|