@parity/product-sdk 0.25.0 → 0.27.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.
@@ -1,4 +1,5 @@
1
1
  import { resolvePeopleUsernameOwner, accountIdBytesToHex } from './chunk-7GM3HUZF.js';
2
+ import { createLocalStorageApi } from './chunk-TUXU2VPS.js';
2
3
  import { createLogger, configure } from '@parity/product-sdk-logger';
3
4
  import { createLocalKvStore } from '@parity/product-sdk-local-storage';
4
5
  import { SignerManager } from '@parity/product-sdk-signer';
@@ -31,16 +32,7 @@ async function createApp(config) {
31
32
  } else {
32
33
  log.debug("Cloud Storage client disabled");
33
34
  }
34
- const localStorageApi = {
35
- get: (key) => localKvStore.get(key),
36
- set: (key, value) => localKvStore.set(key, value),
37
- getJSON: (key) => localKvStore.getJSON(key),
38
- setJSON: (key, value) => localKvStore.setJSON(key, value),
39
- remove: (key) => localKvStore.remove(key),
40
- clear: async () => {
41
- log.debug("clear() is not supported in container storage mode");
42
- }
43
- };
35
+ const localStorageApi = createLocalStorageApi(localKvStore);
44
36
  const walletApi = createWalletApi(signerManager);
45
37
  const chainApi = {
46
38
  getClient(descriptor) {
@@ -236,5 +228,5 @@ async function getPrimaryUsername(accountsProvider) {
236
228
  }
237
229
 
238
230
  export { createApp };
239
- //# sourceMappingURL=chunk-JIO3OZSC.js.map
240
- //# sourceMappingURL=chunk-JIO3OZSC.js.map
231
+ //# sourceMappingURL=chunk-KXJT2BNL.js.map
232
+ //# sourceMappingURL=chunk-KXJT2BNL.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/createApp.ts"],"names":["err"],"mappings":"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","file":"chunk-KXJT2BNL.js","sourcesContent":["// Copyright 2026 Parity Technologies (UK) Ltd.\n// SPDX-License-Identifier: Apache-2.0\n/**\n * createApp - Main entry point for the Product SDK\n *\n * Creates an App instance with wallet, storage, chain, and cloud storage APIs.\n */\n\nimport type { ChainDefinition } from \"polkadot-api\";\nimport type {\n App,\n AppConfig,\n WalletApi,\n ChainApi,\n Account,\n CloudStorageApi,\n LocalStorageApi,\n} from \"./types.js\";\nimport { createLocalStorageApi } from \"./local-storage-api.js\";\nimport { configure, createLogger } from \"@parity/product-sdk-logger\";\nimport { createLocalKvStore } from \"@parity/product-sdk-local-storage\";\nimport { SignerManager } from \"@parity/product-sdk-signer\";\nimport {\n CloudStorageClient,\n ProductCloudStorageError,\n calculateCid,\n createLazySigner,\n} from \"@parity/product-sdk-cloud-storage\";\nimport { err, ok } from \"@parity/result\";\nimport {\n createChainClient,\n getClient,\n isConnected,\n destroyAll,\n} from \"@parity/product-sdk-chain-client\";\nimport { accountIdBytes } from \"@parity/product-sdk-address\";\nimport { getAccountsProvider } from \"@parity/product-sdk-host\";\nimport {\n accountIdBytesToHex,\n type PeopleUsernameQueryApi,\n resolvePeopleUsernameOwner,\n} from \"../identity/dotns.js\";\n\nconst log = createLogger(\"app\");\n\n/**\n * Create a new Product SDK app instance\n *\n * @param config - Application configuration\n * @returns App instance with all APIs\n *\n * @example\n * ```ts\n * import { createApp } from '@parity/product-sdk';\n *\n * // Default: cloud storage enabled with paseo environment\n * const app = await createApp({\n * name: 'my-app',\n * logLevel: 'info',\n * });\n *\n * // Custom cloud storage environment\n * const prodApp = await createApp({\n * name: 'my-app',\n * cloudStorage: { environment: 'polkadot' },\n * });\n *\n * // Disable cloud storage entirely\n * const noCloudStorageApp = await createApp({\n * name: 'my-app',\n * cloudStorage: false,\n * });\n *\n * // Connect wallet\n * const { accounts } = await app.wallet.connect();\n *\n * // Use storage\n * await app.localStorage.set('key', 'value');\n *\n * // Use cloud storage (check for null if it might be disabled)\n * if (app.cloudStorage) {\n * const cid = await app.cloudStorage.upload('hello world');\n * }\n * ```\n */\nexport async function createApp(config: AppConfig): Promise<App> {\n // Set log level if specified\n if (config.logLevel) {\n configure({ level: config.logLevel });\n }\n\n log.info(\"Creating Product SDK app\", { name: config.name });\n\n // Initialize storage (container-only - will throw if not in container)\n const localKvStore = await createLocalKvStore({ prefix: config.name });\n\n // Initialize signer manager\n const signerManager = new SignerManager({\n dappName: config.name,\n });\n\n // Initialize cloud storage client (configurable, defaults to paseo).\n //\n // The signer is wrapped lazily so the cloud storage client can be built before\n // an account is selected. Uploads will throw a clear error if no signer\n // is available at submission time. Reads (fetch / fetchJson) don't need\n // a signer, so they work regardless of whether an account is selected --\n // but they are container-only and throw CloudStorageHostUnavailableError\n // outside a host container (no IPFS-gateway fallback).\n const cloudStorageEnabled = config.cloudStorage !== false;\n const cloudStorageEnvironment =\n typeof config.cloudStorage === \"object\" ? config.cloudStorage.environment : \"paseo\";\n const cloudStorageClient = cloudStorageEnabled\n ? await CloudStorageClient.create({\n environment: cloudStorageEnvironment,\n signer: createLazySigner(() => signerManager.getSigner()),\n })\n : null;\n\n if (cloudStorageEnabled) {\n log.debug(\"Cloud Storage client (Bulletin) initialized\", {\n environment: cloudStorageEnvironment,\n });\n } else {\n log.debug(\"Cloud Storage client disabled\");\n }\n\n // Storage API adapter — shared with `createFakeApp` so the two cannot drift.\n const localStorageApi: LocalStorageApi = createLocalStorageApi(localKvStore);\n\n // Create wallet API adapter\n const walletApi = createWalletApi(signerManager);\n\n // Create chain API\n const chainApi: ChainApi = {\n getClient(descriptor) {\n log.debug(\"getClient called\", { genesis: descriptor.genesis });\n const client = getClient(descriptor);\n return client.getTypedApi(descriptor);\n },\n\n getRawClient(descriptor) {\n log.debug(\"getRawClient called\", { genesis: descriptor.genesis });\n return getClient(descriptor);\n },\n\n async connect<T extends Record<string, ChainDefinition>>(chains: T) {\n log.debug(\"connect called\", { chains: Object.keys(chains) });\n return createChainClient({ chains });\n },\n\n isConnected(descriptor) {\n return isConnected(descriptor);\n },\n\n destroyAll() {\n log.debug(\"destroyAll called\");\n destroyAll();\n },\n };\n\n // Create Cloud Storage API adapter (null if disabled)\n const cloudStorageApi: CloudStorageApi | null = cloudStorageClient\n ? {\n upload: async (data) => {\n const bytes = typeof data === \"string\" ? new TextEncoder().encode(data) : data;\n try {\n // Explicitly request a DAG-PB manifest so chunked uploads always\n // resolve to a single root CID. Without this, AsyncBulletinClient\n // can return `result.cid: undefined` for chunked-without-manifest\n // uploads — but app consumers expect a CID they can hand to\n // `fetch(cid)`, so treat a missing CID as an error.\n const uploaded = await cloudStorageClient\n .store(bytes)\n .withManifest(true)\n .send();\n if (!uploaded.cid) {\n return err(\n new ProductCloudStorageError(\n \"Cloud storage upload returned no CID despite .withManifest(true). Upstream contract may have shifted — file an issue.\",\n ),\n );\n }\n return ok(uploaded.cid.toString());\n } catch (cause) {\n const message = cause instanceof Error ? cause.message : String(cause);\n return err(new ProductCloudStorageError(message, { cause }));\n }\n },\n fetch: (cid) => cloudStorageClient.fetchBytes(cid),\n computeCid: async (data) => {\n const bytes = typeof data === \"string\" ? new TextEncoder().encode(data) : data;\n const cid = await calculateCid(bytes);\n return cid.toString();\n },\n }\n : null;\n\n log.info(\"Product SDK app created\", {\n name: config.name,\n cloudStorage: cloudStorageEnabled ? cloudStorageEnvironment : \"disabled\",\n });\n\n return {\n wallet: walletApi,\n localStorage: localStorageApi,\n chain: chainApi,\n cloudStorage: cloudStorageApi,\n getAppInfo: () => ({ ...config }),\n };\n}\n\n/**\n * Create wallet API adapter using SignerManager from leaf package\n */\nfunction createWalletApi(signerManager: SignerManager): WalletApi {\n // Track account change subscribers\n const accountChangeSubscribers = new Set<(account: Account | null) => void>();\n\n // Subscribe to signer manager state changes\n signerManager.subscribe((state) => {\n const account = state.selectedAccount\n ? {\n address: state.selectedAccount.address,\n name: state.selectedAccount.name ?? undefined,\n source: state.selectedAccount.source,\n }\n : null;\n for (const callback of accountChangeSubscribers) {\n try {\n callback(account);\n } catch (e) {\n log.warn(\"Account change callback threw\", { error: e });\n }\n }\n });\n\n return {\n async connect(): Promise<{ accounts: Account[] }> {\n const result = await signerManager.connect();\n if (!result.ok) {\n throw new Error(result.error.message);\n }\n return {\n accounts: result.value.map((a) => ({\n address: a.address,\n name: a.name ?? undefined,\n source: a.source,\n })),\n };\n },\n\n async disconnect(): Promise<void> {\n signerManager.disconnect();\n },\n\n getAccounts(): Account[] {\n return signerManager.getState().accounts.map((a) => ({\n address: a.address,\n name: a.name ?? undefined,\n source: a.source,\n }));\n },\n\n getSelectedAccount(): Account | null {\n const selected = signerManager.getState().selectedAccount;\n if (!selected) return null;\n return {\n address: selected.address,\n name: selected.name ?? undefined,\n source: selected.source,\n };\n },\n\n selectAccount(address: string): void {\n signerManager.selectAccount(address);\n },\n\n async signMessage(message: string | Uint8Array): Promise<Uint8Array> {\n const bytes = typeof message === \"string\" ? new TextEncoder().encode(message) : message;\n const result = await signerManager.signRaw(bytes);\n if (!result.ok) {\n throw new Error(result.error.message);\n }\n return result.value;\n },\n\n async signMessageWithDotNsIdentity(args) {\n const message =\n typeof args.message === \"string\"\n ? new TextEncoder().encode(args.message)\n : args.message;\n const accountsProvider = await getHostAccountsProvider();\n const username = args.username ?? (await getPrimaryUsername(accountsProvider));\n\n // Reuse an already-connected People chain when the caller has\n // wired one up via `app.chain.connect({ ..., <name>: peopleChain })`.\n // Fall back to opening a transient connection so first-time users\n // don't have to think about chain lifecycles. The chain-client\n // module caches by genesis fingerprint, so subsequent\n // signMessageWithDotNsIdentity calls share whichever connection\n // got established first.\n const peopleClient = isConnected(args.peopleChain)\n ? getClient(args.peopleChain)\n : await createChainClient({ chains: { people: args.peopleChain } }).then(\n (c) => c.raw.people,\n );\n // PAPI's `TypedApi.query.X.Y.getValue` is variadic\n // (`...args: [...WithCallOptions<Args>]`) so it's not assignable\n // to our deliberately-narrow `(key) => Promise<...>` shape. The\n // resolver only ever passes the storage key; the cast adapts the\n // wider PAPI surface to the minimum we depend on.\n const peopleApi = peopleClient.getTypedApi(\n args.peopleChain,\n ) as unknown as PeopleUsernameQueryApi;\n const owner = await resolvePeopleUsernameOwner(username, peopleApi);\n if (!owner) {\n throw new Error(`No account owns DotNS username \"${username}\"`);\n }\n\n const publicKey = accountIdBytes(owner);\n // This field is hex, not the SS58 the resolver now returns. Converted\n // here and not in the try, so an address failure is not reported as a\n // signing failure.\n const accountId = accountIdBytesToHex(publicKey);\n const signer = accountsProvider.getLegacyAccountSigner({ publicKey, name: username });\n\n try {\n return {\n username,\n accountId,\n signature: await signer.signBytes(message),\n };\n } catch (cause) {\n throw new Error(\n `Failed to sign with DotNS username \"${username}\": ${\n cause instanceof Error ? cause.message : String(cause)\n }`,\n { cause },\n );\n }\n },\n\n onAccountChange(callback: (account: Account | null) => void): () => void {\n accountChangeSubscribers.add(callback);\n return () => accountChangeSubscribers.delete(callback);\n },\n\n getProductAccount(): Account | null {\n // Product accounts require async call - this sync API can't support it properly\n // Users should use SignerManager.getProductAccount() directly\n log.warn(\n \"getProductAccount() is deprecated - use SignerManager.getProductAccount() directly\",\n );\n return null;\n },\n\n getAnonymousAlias(): string | null {\n // Anonymous aliases require async call - this sync API can't support it properly\n // Users should use SignerManager.getProductAccountAlias() directly\n log.warn(\n \"getAnonymousAlias() is deprecated - use SignerManager.getProductAccountAlias() directly\",\n );\n return null;\n },\n\n async createProof(_message: Uint8Array): Promise<Uint8Array> {\n // Ring VRF proofs require SignerManager.createRingVRFProof() directly\n throw new Error(\n \"createProof() is not implemented in the App API. \" +\n \"Use SignerManager.createRingVRFProof() directly.\",\n );\n },\n };\n}\n\ntype HostAccountsProvider = NonNullable<Awaited<ReturnType<typeof getAccountsProvider>>>;\n\nasync function getHostAccountsProvider(): Promise<HostAccountsProvider> {\n const provider = await getAccountsProvider();\n if (!provider) {\n throw new Error(\"Host accounts provider is not available\");\n }\n return provider;\n}\n\nasync function getPrimaryUsername(accountsProvider: HostAccountsProvider): Promise<string> {\n const result = await accountsProvider.getUserId().match(\n (value) => value,\n (err) => {\n throw err;\n },\n );\n const username = result.primaryUsername.trim();\n if (!username) {\n throw new Error(\"Host identity did not provide a primary DotNS username\");\n }\n return username;\n}\n"]}
@@ -0,0 +1,14 @@
1
+ // src/core/local-storage-api.ts
2
+ function createLocalStorageApi(kv) {
3
+ return {
4
+ get: (key) => kv.get(key),
5
+ set: (key, value) => kv.set(key, value),
6
+ getJSON: (key) => kv.getJSON(key),
7
+ setJSON: (key, value) => kv.setJSON(key, value),
8
+ remove: (key) => kv.remove(key)
9
+ };
10
+ }
11
+
12
+ export { createLocalStorageApi };
13
+ //# sourceMappingURL=chunk-TUXU2VPS.js.map
14
+ //# sourceMappingURL=chunk-TUXU2VPS.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/local-storage-api.ts"],"names":[],"mappings":";AAgBO,SAAS,sBAAsB,EAAA,EAAmC;AACrE,EAAA,OAAO;AAAA,IACH,GAAA,EAAK,CAAC,GAAA,KAAQ,EAAA,CAAG,IAAI,GAAG,CAAA;AAAA,IACxB,KAAK,CAAC,GAAA,EAAK,UAAU,EAAA,CAAG,GAAA,CAAI,KAAK,KAAK,CAAA;AAAA,IACtC,OAAA,EAAS,CAAI,GAAA,KAAgB,EAAA,CAAG,QAAW,GAAG,CAAA;AAAA,IAC9C,SAAS,CAAI,GAAA,EAAa,UAAa,EAAA,CAAG,OAAA,CAAQ,KAAK,KAAK,CAAA;AAAA,IAC5D,MAAA,EAAQ,CAAC,GAAA,KAAQ,EAAA,CAAG,OAAO,GAAG;AAAA,GAClC;AACJ","file":"chunk-TUXU2VPS.js","sourcesContent":["// Copyright 2026 Parity Technologies (UK) Ltd.\n// SPDX-License-Identifier: Apache-2.0\n/**\n * The one adapter from a {@link LocalKvStore} to the SDK's {@link LocalStorageApi}.\n *\n * Both `createApp` (host-backed store) and `createFakeApp` (in-memory store)\n * build their `localStorage` through here, so the shipped adapter and the test\n * fake cannot drift — which is exactly how `clear()` came to be a no-op in\n * production while the fake emptied (#344). The in-source test below drives one\n * assertion set through this factory over an in-memory store; anything the fake\n * exposes, the real path exposes the same way.\n */\nimport type { LocalKvStore } from \"@parity/product-sdk-local-storage\";\nimport type { LocalStorageApi } from \"./types.js\";\n\n/** Wrap a {@link LocalKvStore} as the SDK's {@link LocalStorageApi}. */\nexport function createLocalStorageApi(kv: LocalKvStore): LocalStorageApi {\n return {\n get: (key) => kv.get(key),\n set: (key, value) => kv.set(key, value),\n getJSON: <T>(key: string) => kv.getJSON<T>(key),\n setJSON: <T>(key: string, value: T) => kv.setJSON(key, value),\n remove: (key) => kv.remove(key),\n };\n}\n\nif (import.meta.vitest) {\n const { describe, test, expect } = import.meta.vitest;\n\n /** A minimal in-memory {@link LocalKvStore}, string- and JSON-aware. */\n function memoryKvStore(): LocalKvStore {\n const store = new Map<string, string>();\n return {\n async get(key) {\n return store.get(key) ?? null;\n },\n async set(key, value) {\n store.set(key, value);\n },\n async remove(key) {\n store.delete(key);\n },\n async getJSON<T>(key: string) {\n const raw = store.get(key);\n return raw === undefined ? null : (JSON.parse(raw) as T);\n },\n async setJSON(key, value) {\n store.set(key, JSON.stringify(value));\n },\n };\n }\n\n describe(\"createLocalStorageApi\", () => {\n test(\"round-trips strings and JSON, and remove deletes one key\", async () => {\n const api = createLocalStorageApi(memoryKvStore());\n\n await api.set(\"k\", \"v\");\n await api.setJSON(\"o\", { n: 1 });\n expect(await api.get(\"k\")).toBe(\"v\");\n expect(await api.getJSON(\"o\")).toEqual({ n: 1 });\n expect(await api.get(\"missing\")).toBeNull();\n\n await api.remove(\"k\");\n expect(await api.get(\"k\")).toBeNull();\n // No clear-all: an untouched key stays.\n expect(await api.getJSON(\"o\")).toEqual({ n: 1 });\n });\n\n test(\"exposes exactly the LocalStorageApi surface — no clear()\", () => {\n const api = createLocalStorageApi(memoryKvStore());\n expect(Object.keys(api).sort()).toEqual([\"get\", \"getJSON\", \"remove\", \"set\", \"setJSON\"]);\n });\n });\n}\n"]}
@@ -1,6 +1,6 @@
1
1
  export { createApp } from '../index.js';
2
2
  export { LogEntry, LogHandler, LogLevel, Logger, LoggerConfig, configure, createLogger } from '@parity/product-sdk-logger';
3
- export { A as Account, b as App, c as AppConfig, C as ChainApi, a as CloudStorageApi, d as CloudStorageConfig, D as DotNsIdentitySignature, L as LocalStorageApi, S as SignMessageWithDotNsIdentityArgs, W as WalletApi } from '../types-CRYPRNYk.js';
3
+ export { A as Account, b as App, c as AppConfig, C as ChainApi, a as CloudStorageApi, d as CloudStorageConfig, D as DotNsIdentitySignature, L as LocalStorageApi, S as SignMessageWithDotNsIdentityArgs, W as WalletApi } from '../types-C676_6Fp.js';
4
4
  export { ChainClient } from '@parity/product-sdk-chain-client';
5
5
  export { ChainDefinition, PolkadotClient, TypedApi } from 'polkadot-api';
6
6
  import '@parity/result';
@@ -1,5 +1,6 @@
1
1
  export { configure, createLogger } from '../chunk-JVJ552RU.js';
2
- export { createApp } from '../chunk-JIO3OZSC.js';
2
+ export { createApp } from '../chunk-KXJT2BNL.js';
3
3
  import '../chunk-7GM3HUZF.js';
4
+ import '../chunk-TUXU2VPS.js';
4
5
  //# sourceMappingURL=index.js.map
5
6
  //# sourceMappingURL=index.js.map
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export { LogEntry, LogHandler, LogLevel, Logger, LoggerConfig, configure, createLogger } from '@parity/product-sdk-logger';
2
- import { c as AppConfig, b as App } from './types-CRYPRNYk.js';
3
- export { A as Account, C as ChainApi, a as CloudStorageApi, D as DotNsIdentitySignature, L as LocalStorageApi, S as SignMessageWithDotNsIdentityArgs, W as WalletApi } from './types-CRYPRNYk.js';
2
+ import { c as AppConfig, b as App } from './types-C676_6Fp.js';
3
+ export { A as Account, C as ChainApi, a as CloudStorageApi, D as DotNsIdentitySignature, L as LocalStorageApi, S as SignMessageWithDotNsIdentityArgs, W as WalletApi } from './types-C676_6Fp.js';
4
4
  export { Result, err, isErrorOf, ok } from '@parity/result';
5
5
  export { SdkError, isSdkError } from '@parity/product-sdk-errors';
6
6
  export { isInsideContainer, isInsideContainerSync } from '@parity/product-sdk-host';
package/dist/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  export { configure, createLogger } from './chunk-JVJ552RU.js';
2
- export { createApp } from './chunk-JIO3OZSC.js';
2
+ export { createApp } from './chunk-KXJT2BNL.js';
3
3
  import './chunk-7GM3HUZF.js';
4
+ import './chunk-TUXU2VPS.js';
4
5
  export { err, isErrorOf, ok } from '@parity/result';
5
6
  export { isSdkError } from '@parity/product-sdk-errors';
6
7
  export { isInsideContainer, isInsideContainerSync } from '@parity/product-sdk-host';
@@ -2,7 +2,7 @@ import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import * as react from 'react';
3
3
  import { ReactNode } from 'react';
4
4
  import { LogLevel } from '@parity/product-sdk-logger';
5
- import { b as App, S as SignMessageWithDotNsIdentityArgs, D as DotNsIdentitySignature, A as Account } from '../types-CRYPRNYk.js';
5
+ import { b as App, S as SignMessageWithDotNsIdentityArgs, D as DotNsIdentitySignature, A as Account } from '../types-C676_6Fp.js';
6
6
  import { ChainDefinition, TypedApi } from 'polkadot-api';
7
7
  import '@parity/product-sdk-cloud-storage';
8
8
  import '@parity/result';
@@ -1,5 +1,6 @@
1
- import { createApp } from '../chunk-JIO3OZSC.js';
1
+ import { createApp } from '../chunk-KXJT2BNL.js';
2
2
  import '../chunk-7GM3HUZF.js';
3
+ import '../chunk-TUXU2VPS.js';
3
4
  import { createContext, useContext, useState, useEffect, useCallback, useMemo } from 'react';
4
5
  import { jsx, Fragment } from 'react/jsx-runtime';
5
6
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/react/context.ts","../../src/react/provider.tsx","../../src/react/useWallet.ts","../../src/react/useLocalStorage.ts","../../src/react/useChain.ts"],"names":["useState","useEffect","useCallback"],"mappings":"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","file":"index.js","sourcesContent":["// Copyright 2026 Parity Technologies (UK) Ltd.\n// SPDX-License-Identifier: Apache-2.0\n/**\n * React context for Product SDK\n */\n\nimport { createContext, useContext } from \"react\";\nimport type { App } from \"../core/types.js\";\n\n/** Context for the Product SDK app instance */\nexport const ProductSDKContext = createContext<App | null>(null);\n\n/**\n * Hook to access the Product SDK app instance\n *\n * @throws If used outside of ProductSDKProvider\n *\n * @example\n * ```tsx\n * function MyComponent() {\n * const app = useProductSDK();\n * // Use app.wallet, app.localStorage, etc.\n * }\n * ```\n */\nexport function useProductSDK(): App {\n const app = useContext(ProductSDKContext);\n if (!app) {\n throw new Error(\n \"useProductSDK must be used within a ProductSDKProvider. \" +\n 'Wrap your app with <ProductSDKProvider name=\"your-app\">.',\n );\n }\n return app;\n}\n","// Copyright 2026 Parity Technologies (UK) Ltd.\n// SPDX-License-Identifier: Apache-2.0\n/**\n * ProductSDKProvider component\n */\n\nimport React, { useEffect, useState, type ReactNode } from \"react\";\nimport { ProductSDKContext } from \"./context.js\";\nimport { createApp } from \"../core/createApp.js\";\nimport type { App, LogLevel } from \"../core/types.js\";\n\n/** Props for ProductSDKProvider */\nexport interface ProductSDKProviderProps {\n /** Application name - used for storage namespacing and product account derivation */\n name: string;\n /** Log level (default: 'info') */\n logLevel?: LogLevel;\n /** Child components */\n children: ReactNode;\n /** Fallback to show while loading */\n fallback?: ReactNode;\n}\n\n/**\n * Provider component that initializes the Product SDK\n *\n * @example\n * ```tsx\n * import { ProductSDKProvider } from '@parity/product-sdk/react';\n *\n * function App() {\n * return (\n * <ProductSDKProvider name=\"my-app\" fallback={<Loading />}>\n * <MyApp />\n * </ProductSDKProvider>\n * );\n * }\n * ```\n */\nexport function ProductSDKProvider({\n name,\n logLevel = \"info\",\n children,\n fallback = null,\n}: ProductSDKProviderProps) {\n const [app, setApp] = useState<App | null>(null);\n const [error, setError] = useState<Error | null>(null);\n\n useEffect(() => {\n let mounted = true;\n\n createApp({ name, logLevel })\n .then((createdApp) => {\n if (mounted) {\n setApp(createdApp);\n }\n })\n .catch((e) => {\n if (mounted) {\n setError(e instanceof Error ? e : new Error(String(e)));\n }\n });\n\n return () => {\n mounted = false;\n };\n }, [name, logLevel]);\n\n if (error) {\n throw error;\n }\n\n if (!app) {\n return <>{fallback}</>;\n }\n\n return <ProductSDKContext.Provider value={app}>{children}</ProductSDKContext.Provider>;\n}\n","// Copyright 2026 Parity Technologies (UK) Ltd.\n// SPDX-License-Identifier: Apache-2.0\n/**\n * useWallet hook\n */\n\nimport { useState, useEffect, useCallback } from \"react\";\nimport { useProductSDK } from \"./context.js\";\nimport type {\n Account,\n DotNsIdentitySignature,\n SignMessageWithDotNsIdentityArgs,\n} from \"../core/types.js\";\n\n/** Wallet hook state */\nexport interface UseWalletState {\n /** Whether wallet is connected */\n isConnected: boolean;\n /** Whether connection is in progress */\n isConnecting: boolean;\n /** Available accounts */\n accounts: Account[];\n /** Currently selected account */\n selectedAccount: Account | null;\n /** Last error */\n error: Error | null;\n}\n\n/** Wallet hook actions */\nexport interface UseWalletActions {\n /** Connect to wallet */\n connect: () => Promise<void>;\n /** Disconnect from wallet */\n disconnect: () => Promise<void>;\n /** Select an account */\n selectAccount: (address: string) => void;\n /** Sign a message */\n signMessage: (message: string | Uint8Array) => Promise<Uint8Array>;\n /** Sign a message with the account that owns a DotNS / People username identity. */\n signMessageWithDotNsIdentity: (\n args: SignMessageWithDotNsIdentityArgs,\n ) => Promise<DotNsIdentitySignature>;\n}\n\n/** Return type of useWallet */\nexport type UseWalletReturn = UseWalletState & UseWalletActions;\n\n/**\n * Hook for wallet connection and signing\n *\n * @example\n * ```tsx\n * function WalletButton() {\n * const { isConnected, accounts, connect, disconnect, selectAccount } = useWallet();\n *\n * if (!isConnected) {\n * return <button onClick={connect}>Connect Wallet</button>;\n * }\n *\n * return (\n * <div>\n * <select onChange={(e) => selectAccount(e.target.value)}>\n * {accounts.map((a) => (\n * <option key={a.address} value={a.address}>\n * {a.name || a.address}\n * </option>\n * ))}\n * </select>\n * <button onClick={disconnect}>Disconnect</button>\n * </div>\n * );\n * }\n * ```\n */\nexport function useWallet(): UseWalletReturn {\n const app = useProductSDK();\n\n const [state, setState] = useState<UseWalletState>({\n isConnected: false,\n isConnecting: false,\n accounts: [],\n selectedAccount: null,\n error: null,\n });\n\n const connect = useCallback(async () => {\n setState((s) => ({ ...s, isConnecting: true, error: null }));\n try {\n const result = await app.wallet.connect();\n setState((s) => ({\n ...s,\n isConnected: true,\n isConnecting: false,\n accounts: result.accounts,\n selectedAccount: result.accounts[0] || null,\n }));\n } catch (e) {\n setState((s) => ({\n ...s,\n isConnecting: false,\n error: e instanceof Error ? e : new Error(String(e)),\n }));\n }\n }, [app]);\n\n const disconnect = useCallback(async () => {\n await app.wallet.disconnect();\n setState({\n isConnected: false,\n isConnecting: false,\n accounts: [],\n selectedAccount: null,\n error: null,\n });\n }, [app]);\n\n const selectAccount = useCallback(\n (address: string) => {\n app.wallet.selectAccount(address);\n const account = state.accounts.find((a) => a.address === address) || null;\n setState((s) => ({ ...s, selectedAccount: account }));\n },\n [app, state.accounts],\n );\n\n const signMessage = useCallback(\n async (message: string | Uint8Array) => {\n return app.wallet.signMessage(message);\n },\n [app],\n );\n\n const signMessageWithDotNsIdentity = useCallback(\n async (args: SignMessageWithDotNsIdentityArgs) => {\n return app.wallet.signMessageWithDotNsIdentity(args);\n },\n [app],\n );\n\n // Subscribe to account changes\n useEffect(() => {\n const unsubscribe = app.wallet.onAccountChange((account) => {\n setState((s) => ({ ...s, selectedAccount: account }));\n });\n return unsubscribe;\n }, [app]);\n\n return {\n ...state,\n connect,\n disconnect,\n selectAccount,\n signMessage,\n signMessageWithDotNsIdentity,\n };\n}\n","// Copyright 2026 Parity Technologies (UK) Ltd.\n// SPDX-License-Identifier: Apache-2.0\n/**\n * useLocalStorage hook\n */\n\nimport { useState, useEffect, useCallback } from \"react\";\nimport { useProductSDK } from \"./context.js\";\n\n/**\n * Hook for key-value storage operations\n *\n * @param key - Storage key\n * @param defaultValue - Default value if key doesn't exist\n *\n * @example\n * ```tsx\n * function ThemeToggle() {\n * const [theme, setTheme, { loading }] = useLocalStorage('theme', 'light');\n *\n * if (loading) return <div>Loading...</div>;\n *\n * return (\n * <button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>\n * Current: {theme}\n * </button>\n * );\n * }\n * ```\n */\nexport function useLocalStorage<T = string>(\n key: string,\n defaultValue?: T,\n): [T | null, (value: T) => Promise<void>, { loading: boolean; error: Error | null }] {\n const app = useProductSDK();\n\n const [value, setValue] = useState<T | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n // Load initial value\n useEffect(() => {\n let mounted = true;\n\n const loadValue = async () => {\n try {\n setLoading(true);\n const stored = await app.localStorage.getJSON<T>(key);\n if (mounted) {\n setValue(stored ?? defaultValue ?? null);\n setLoading(false);\n }\n } catch (e) {\n if (mounted) {\n setError(e instanceof Error ? e : new Error(String(e)));\n setLoading(false);\n }\n }\n };\n\n loadValue();\n\n return () => {\n mounted = false;\n };\n }, [app, key, defaultValue]);\n\n const setStoredValue = useCallback(\n async (newValue: T) => {\n try {\n setError(null);\n await app.localStorage.setJSON(key, newValue);\n setValue(newValue);\n } catch (e) {\n setError(e instanceof Error ? e : new Error(String(e)));\n throw e;\n }\n },\n [app, key],\n );\n\n return [value, setStoredValue, { loading, error }];\n}\n\n/**\n * Hook for string storage (simpler API)\n *\n * @param key - Storage key\n * @param defaultValue - Default value\n */\nexport function useLocalStorageString(\n key: string,\n defaultValue?: string,\n): [string | null, (value: string) => Promise<void>, { loading: boolean }] {\n const app = useProductSDK();\n\n const [value, setValue] = useState<string | null>(null);\n const [loading, setLoading] = useState(true);\n\n useEffect(() => {\n let mounted = true;\n\n const loadValue = async () => {\n const stored = await app.localStorage.get(key);\n if (mounted) {\n setValue(stored ?? defaultValue ?? null);\n setLoading(false);\n }\n };\n\n loadValue();\n\n return () => {\n mounted = false;\n };\n }, [app, key, defaultValue]);\n\n const setStoredValue = useCallback(\n async (newValue: string) => {\n await app.localStorage.set(key, newValue);\n setValue(newValue);\n },\n [app, key],\n );\n\n return [value, setStoredValue, { loading }];\n}\n","// Copyright 2026 Parity Technologies (UK) Ltd.\n// SPDX-License-Identifier: Apache-2.0\n/**\n * useChain hook\n */\n\nimport { useMemo } from \"react\";\nimport { useProductSDK } from \"./context.js\";\nimport type { ChainDefinition, TypedApi } from \"../core/types.js\";\n\n/**\n * Hook to get a typed chain client\n *\n * @param chain - Chain descriptor (PAPI ChainDefinition)\n *\n * @example\n * ```tsx\n * import { paseo_asset_hub } from '@parity/product-sdk-descriptors/paseo-asset-hub';\n * import { useChain } from '@parity/product-sdk/react';\n *\n * function AssetHubBalance() {\n * const assetHub = useChain(paseo_asset_hub);\n *\n * // assetHub is typed for Asset Hub queries\n * const balance = await assetHub.query.System.Account.getValue(address);\n * }\n * ```\n */\nexport function useChain<T extends ChainDefinition>(chain: T): TypedApi<T> {\n const app = useProductSDK();\n\n return useMemo(() => {\n return app.chain.getClient(chain);\n }, [app, chain]);\n}\n"]}
1
+ {"version":3,"sources":["../../src/react/context.ts","../../src/react/provider.tsx","../../src/react/useWallet.ts","../../src/react/useLocalStorage.ts","../../src/react/useChain.ts"],"names":["useState","useEffect","useCallback"],"mappings":"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","file":"index.js","sourcesContent":["// Copyright 2026 Parity Technologies (UK) Ltd.\n// SPDX-License-Identifier: Apache-2.0\n/**\n * React context for Product SDK\n */\n\nimport { createContext, useContext } from \"react\";\nimport type { App } from \"../core/types.js\";\n\n/** Context for the Product SDK app instance */\nexport const ProductSDKContext = createContext<App | null>(null);\n\n/**\n * Hook to access the Product SDK app instance\n *\n * @throws If used outside of ProductSDKProvider\n *\n * @example\n * ```tsx\n * function MyComponent() {\n * const app = useProductSDK();\n * // Use app.wallet, app.localStorage, etc.\n * }\n * ```\n */\nexport function useProductSDK(): App {\n const app = useContext(ProductSDKContext);\n if (!app) {\n throw new Error(\n \"useProductSDK must be used within a ProductSDKProvider. \" +\n 'Wrap your app with <ProductSDKProvider name=\"your-app\">.',\n );\n }\n return app;\n}\n","// Copyright 2026 Parity Technologies (UK) Ltd.\n// SPDX-License-Identifier: Apache-2.0\n/**\n * ProductSDKProvider component\n */\n\nimport React, { useEffect, useState, type ReactNode } from \"react\";\nimport { ProductSDKContext } from \"./context.js\";\nimport { createApp } from \"../core/createApp.js\";\nimport type { App, LogLevel } from \"../core/types.js\";\n\n/** Props for ProductSDKProvider */\nexport interface ProductSDKProviderProps {\n /** Application name - used for storage namespacing and product account derivation */\n name: string;\n /** Log level (default: 'info') */\n logLevel?: LogLevel;\n /** Child components */\n children: ReactNode;\n /** Fallback to show while loading */\n fallback?: ReactNode;\n}\n\n/**\n * Provider component that initializes the Product SDK\n *\n * @example\n * ```tsx\n * import { ProductSDKProvider } from '@parity/product-sdk/react';\n *\n * function App() {\n * return (\n * <ProductSDKProvider name=\"my-app\" fallback={<Loading />}>\n * <MyApp />\n * </ProductSDKProvider>\n * );\n * }\n * ```\n */\nexport function ProductSDKProvider({\n name,\n logLevel = \"info\",\n children,\n fallback = null,\n}: ProductSDKProviderProps) {\n const [app, setApp] = useState<App | null>(null);\n const [error, setError] = useState<Error | null>(null);\n\n useEffect(() => {\n let mounted = true;\n\n createApp({ name, logLevel })\n .then((createdApp) => {\n if (mounted) {\n setApp(createdApp);\n }\n })\n .catch((e) => {\n if (mounted) {\n setError(e instanceof Error ? e : new Error(String(e)));\n }\n });\n\n return () => {\n mounted = false;\n };\n }, [name, logLevel]);\n\n if (error) {\n throw error;\n }\n\n if (!app) {\n return <>{fallback}</>;\n }\n\n return <ProductSDKContext.Provider value={app}>{children}</ProductSDKContext.Provider>;\n}\n","// Copyright 2026 Parity Technologies (UK) Ltd.\n// SPDX-License-Identifier: Apache-2.0\n/**\n * useWallet hook\n */\n\nimport { useState, useEffect, useCallback } from \"react\";\nimport { useProductSDK } from \"./context.js\";\nimport type {\n Account,\n DotNsIdentitySignature,\n SignMessageWithDotNsIdentityArgs,\n} from \"../core/types.js\";\n\n/** Wallet hook state */\nexport interface UseWalletState {\n /** Whether wallet is connected */\n isConnected: boolean;\n /** Whether connection is in progress */\n isConnecting: boolean;\n /** Available accounts */\n accounts: Account[];\n /** Currently selected account */\n selectedAccount: Account | null;\n /** Last error */\n error: Error | null;\n}\n\n/** Wallet hook actions */\nexport interface UseWalletActions {\n /** Connect to wallet */\n connect: () => Promise<void>;\n /** Disconnect from wallet */\n disconnect: () => Promise<void>;\n /** Select an account */\n selectAccount: (address: string) => void;\n /** Sign a message */\n signMessage: (message: string | Uint8Array) => Promise<Uint8Array>;\n /** Sign a message with the account that owns a DotNS / People username identity. */\n signMessageWithDotNsIdentity: (\n args: SignMessageWithDotNsIdentityArgs,\n ) => Promise<DotNsIdentitySignature>;\n}\n\n/** Return type of useWallet */\nexport type UseWalletReturn = UseWalletState & UseWalletActions;\n\n/**\n * Hook for wallet connection and signing\n *\n * @example\n * ```tsx\n * function WalletButton() {\n * const { isConnected, accounts, connect, disconnect, selectAccount } = useWallet();\n *\n * if (!isConnected) {\n * return <button onClick={connect}>Connect Wallet</button>;\n * }\n *\n * return (\n * <div>\n * <select onChange={(e) => selectAccount(e.target.value)}>\n * {accounts.map((a) => (\n * <option key={a.address} value={a.address}>\n * {a.name || a.address}\n * </option>\n * ))}\n * </select>\n * <button onClick={disconnect}>Disconnect</button>\n * </div>\n * );\n * }\n * ```\n */\nexport function useWallet(): UseWalletReturn {\n const app = useProductSDK();\n\n const [state, setState] = useState<UseWalletState>({\n isConnected: false,\n isConnecting: false,\n accounts: [],\n selectedAccount: null,\n error: null,\n });\n\n const connect = useCallback(async () => {\n setState((s) => ({ ...s, isConnecting: true, error: null }));\n try {\n const result = await app.wallet.connect();\n setState((s) => ({\n ...s,\n isConnected: true,\n isConnecting: false,\n accounts: result.accounts,\n selectedAccount: result.accounts[0] || null,\n }));\n } catch (e) {\n setState((s) => ({\n ...s,\n isConnecting: false,\n error: e instanceof Error ? e : new Error(String(e)),\n }));\n }\n }, [app]);\n\n const disconnect = useCallback(async () => {\n await app.wallet.disconnect();\n setState({\n isConnected: false,\n isConnecting: false,\n accounts: [],\n selectedAccount: null,\n error: null,\n });\n }, [app]);\n\n const selectAccount = useCallback(\n (address: string) => {\n app.wallet.selectAccount(address);\n const account = state.accounts.find((a) => a.address === address) || null;\n setState((s) => ({ ...s, selectedAccount: account }));\n },\n [app, state.accounts],\n );\n\n const signMessage = useCallback(\n async (message: string | Uint8Array) => {\n return app.wallet.signMessage(message);\n },\n [app],\n );\n\n const signMessageWithDotNsIdentity = useCallback(\n async (args: SignMessageWithDotNsIdentityArgs) => {\n return app.wallet.signMessageWithDotNsIdentity(args);\n },\n [app],\n );\n\n // Subscribe to account changes\n useEffect(() => {\n const unsubscribe = app.wallet.onAccountChange((account) => {\n setState((s) => ({ ...s, selectedAccount: account }));\n });\n return unsubscribe;\n }, [app]);\n\n return {\n ...state,\n connect,\n disconnect,\n selectAccount,\n signMessage,\n signMessageWithDotNsIdentity,\n };\n}\n","// Copyright 2026 Parity Technologies (UK) Ltd.\n// SPDX-License-Identifier: Apache-2.0\n/**\n * useLocalStorage hook\n */\n\nimport { useState, useEffect, useCallback } from \"react\";\nimport { useProductSDK } from \"./context.js\";\n\n/**\n * Hook for key-value storage operations\n *\n * @param key - Storage key\n * @param defaultValue - Default value if key doesn't exist\n *\n * @example\n * ```tsx\n * function ThemeToggle() {\n * const [theme, setTheme, { loading }] = useLocalStorage('theme', 'light');\n *\n * if (loading) return <div>Loading...</div>;\n *\n * return (\n * <button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>\n * Current: {theme}\n * </button>\n * );\n * }\n * ```\n */\nexport function useLocalStorage<T = string>(\n key: string,\n defaultValue?: T,\n): [T | null, (value: T) => Promise<void>, { loading: boolean; error: Error | null }] {\n const app = useProductSDK();\n\n const [value, setValue] = useState<T | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n // Load initial value\n useEffect(() => {\n let mounted = true;\n\n const loadValue = async () => {\n try {\n setLoading(true);\n const stored = await app.localStorage.getJSON<T>(key);\n if (mounted) {\n setValue(stored ?? defaultValue ?? null);\n setLoading(false);\n }\n } catch (e) {\n if (mounted) {\n setError(e instanceof Error ? e : new Error(String(e)));\n setLoading(false);\n }\n }\n };\n\n loadValue();\n\n return () => {\n mounted = false;\n };\n }, [app, key, defaultValue]);\n\n const setStoredValue = useCallback(\n async (newValue: T) => {\n try {\n setError(null);\n await app.localStorage.setJSON(key, newValue);\n setValue(newValue);\n } catch (e) {\n setError(e instanceof Error ? e : new Error(String(e)));\n throw e;\n }\n },\n [app, key],\n );\n\n return [value, setStoredValue, { loading, error }];\n}\n\n/**\n * Hook for string storage (simpler API)\n *\n * @param key - Storage key\n * @param defaultValue - Default value\n */\nexport function useLocalStorageString(\n key: string,\n defaultValue?: string,\n): [string | null, (value: string) => Promise<void>, { loading: boolean }] {\n const app = useProductSDK();\n\n const [value, setValue] = useState<string | null>(null);\n const [loading, setLoading] = useState(true);\n\n useEffect(() => {\n let mounted = true;\n\n const loadValue = async () => {\n const stored = await app.localStorage.get(key);\n if (mounted) {\n setValue(stored ?? defaultValue ?? null);\n setLoading(false);\n }\n };\n\n loadValue();\n\n return () => {\n mounted = false;\n };\n }, [app, key, defaultValue]);\n\n const setStoredValue = useCallback(\n async (newValue: string) => {\n await app.localStorage.set(key, newValue);\n setValue(newValue);\n },\n [app, key],\n );\n\n return [value, setStoredValue, { loading }];\n}\n","// Copyright 2026 Parity Technologies (UK) Ltd.\n// SPDX-License-Identifier: Apache-2.0\n/**\n * useChain hook\n */\n\nimport { useMemo } from \"react\";\nimport { useProductSDK } from \"./context.js\";\nimport type { ChainDefinition, TypedApi } from \"../core/types.js\";\n\n/**\n * Hook to get a typed chain client\n *\n * @param chain - Chain descriptor (PAPI ChainDefinition)\n *\n * @example\n * ```tsx\n * import { paseo_asset_hub } from '@parity/product-sdk-descriptors/paseo-asset-hub';\n * import { useChain } from '@parity/product-sdk/react';\n *\n * function AssetHubBalance() {\n * const assetHub = useChain(paseo_asset_hub);\n *\n * // assetHub is typed for Asset Hub queries\n * const balance = await assetHub.query.System.Account.getValue(address);\n * }\n * ```\n */\nexport function useChain<T extends ChainDefinition>(chain: T): TypedApi<T> {\n const app = useProductSDK();\n\n return useMemo(() => {\n return app.chain.getClient(chain);\n }, [app, chain]);\n}\n"]}
@@ -1,4 +1,4 @@
1
- import { A as Account, L as LocalStorageApi, C as ChainApi, a as CloudStorageApi, b as App } from '../types-CRYPRNYk.js';
1
+ import { A as Account, L as LocalStorageApi, C as ChainApi, a as CloudStorageApi, b as App } from '../types-C676_6Fp.js';
2
2
  export * from '@parity/product-sdk-local-storage/testing';
3
3
  export * from '@parity/product-sdk-signer/testing';
4
4
  export * from '@parity/product-sdk-contracts/testing';
@@ -1,3 +1,4 @@
1
+ import { createLocalStorageApi } from '../chunk-TUXU2VPS.js';
1
2
  import { ProductCloudStorageError } from '@parity/product-sdk-cloud-storage';
2
3
  import { createFakeHostLocalStorage } from '@parity/product-sdk-local-storage/testing';
3
4
  export * from '@parity/product-sdk-local-storage/testing';
@@ -6,7 +7,6 @@ export * from '@parity/product-sdk-signer/testing';
6
7
  export * from '@parity/product-sdk-contracts/testing';
7
8
  export * from '@parity/product-sdk-host/testing';
8
9
 
9
- // src/testing.ts
10
10
  var textEncoder = new TextEncoder();
11
11
  function makeFakeWallet(options) {
12
12
  const accounts = options?.accounts ?? [];
@@ -63,7 +63,7 @@ function makeFakeWallet(options) {
63
63
  }
64
64
  function makeFakeLocalStorage() {
65
65
  const host = createFakeHostLocalStorage();
66
- return {
66
+ const kv = {
67
67
  async get(key) {
68
68
  return await host.readString(key) || null;
69
69
  },
@@ -78,11 +78,9 @@ function makeFakeLocalStorage() {
78
78
  },
79
79
  async remove(key) {
80
80
  await host.clear(key);
81
- },
82
- async clear() {
83
- host.reset();
84
81
  }
85
82
  };
83
+ return createLocalStorageApi(kv);
86
84
  }
87
85
  function makeFakeCloudStorage() {
88
86
  const store = /* @__PURE__ */ new Map();
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/testing.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["// Copyright 2026 Parity Technologies (UK) Ltd.\n// SPDX-License-Identifier: Apache-2.0\n/**\n * Test fakes for `@parity/product-sdk` (umbrella).\n *\n * `createFakeApp` returns a fake {@link App} — use it directly in logic tests or\n * hand it to `ProductSDKContext.Provider` for React component tests, so code that\n * calls `useProductSDK()` / `useWallet()` runs with no host. This entry also\n * re-exports every package's fakes, for a single meta-package import.\n *\n * @packageDocumentation\n */\nimport { ProductCloudStorageError } from \"@parity/product-sdk-cloud-storage\";\nimport { createFakeHostLocalStorage } from \"@parity/product-sdk-local-storage/testing\";\nimport { err, ok, unwrapErr, unwrapOk } from \"@parity/result\";\n\nimport type {\n Account,\n App,\n ChainApi,\n CloudStorageApi,\n LocalStorageApi,\n WalletApi,\n} from \"./core/types.js\";\n\nconst textEncoder = new TextEncoder();\n\n// Re-export the per-package fakes so `@parity/product-sdk/testing` is a one-stop import.\n// statement-store is deliberately absent: the umbrella doesn't wrap that package, so apps\n// using it depend on it directly — import `@parity/product-sdk-statement-store/testing`\n// there instead. Re-exporting its fake here would add a dependency the umbrella otherwise\n// doesn't have and could skew against the version the app actually installs.\nexport * from \"@parity/product-sdk-local-storage/testing\";\nexport * from \"@parity/product-sdk-signer/testing\";\nexport * from \"@parity/product-sdk-contracts/testing\";\nexport * from \"@parity/product-sdk-host/testing\";\n\n/** Configurable behavior for the fake {@link WalletApi}. */\nexport interface FakeWalletOptions {\n /** Accounts `connect()` / `getAccounts()` expose. Default: none. */\n accounts?: Account[];\n /** Initially selected account. Default: the first account, or `null`. */\n selected?: Account | null;\n /** Bytes returned by `signMessage` / `createProof`. Default: 64 zero bytes. */\n signature?: Uint8Array;\n /** Value returned by `getProductAccount()`. Default: the selected account. */\n productAccount?: Account | null;\n /** Value returned by `getAnonymousAlias()`. Default: `null`. */\n anonymousAlias?: string | null;\n}\n\n/** Options for {@link createFakeApp}. */\nexport interface CreateFakeAppOptions {\n /** App name reported by `getAppInfo()`. Default: `\"fake-app\"`. */\n name?: string;\n /** Configure the fake wallet. */\n wallet?: FakeWalletOptions;\n /** Override the local-storage API. Default: in-memory (backed by `createFakeHostLocalStorage`). */\n localStorage?: LocalStorageApi;\n /** Override the chain API. Default: unconfigured (throws until you provide one). */\n chain?: ChainApi;\n /** Override cloud storage, or pass `null` to disable it. Default: an in-memory fake. */\n cloudStorage?: CloudStorageApi | null;\n}\n\nfunction makeFakeWallet(options?: FakeWalletOptions): WalletApi {\n const accounts = options?.accounts ?? [];\n let selected = options?.selected !== undefined ? options.selected : (accounts[0] ?? null);\n const signature = options?.signature ?? new Uint8Array(64);\n const listeners = new Set<(account: Account | null) => void>();\n const notify = () => {\n for (const l of listeners) l(selected);\n };\n\n return {\n async connect() {\n return { accounts };\n },\n async disconnect() {\n selected = null;\n notify();\n },\n getAccounts() {\n return accounts;\n },\n getSelectedAccount() {\n return selected;\n },\n selectAccount(address) {\n selected = accounts.find((a) => a.address === address) ?? null;\n notify();\n },\n async signMessage() {\n return signature;\n },\n async signMessageWithDotNsIdentity(args) {\n return {\n username: args.username ?? \"alice\",\n accountId: `0x${\"00\".repeat(32)}`,\n signature,\n };\n },\n onAccountChange(callback) {\n listeners.add(callback);\n return () => {\n listeners.delete(callback);\n };\n },\n getProductAccount() {\n return options?.productAccount !== undefined ? options.productAccount : selected;\n },\n getAnonymousAlias() {\n return options?.anonymousAlias ?? null;\n },\n async createProof() {\n return signature;\n },\n };\n}\n\nfunction makeFakeLocalStorage(): LocalStorageApi {\n // Compose the local-storage fake; mirror createApp's adapter semantics\n // (get normalizes \"\" -> null, getJSON normalizes undefined -> null).\n const host = createFakeHostLocalStorage();\n return {\n async get(key) {\n return (await host.readString(key)) || null;\n },\n async set(key, value) {\n await host.writeString(key, value);\n },\n async getJSON<T>(key: string) {\n return ((await host.readJSON(key)) ?? null) as T | null;\n },\n async setJSON<T>(key: string, value: T) {\n await host.writeJSON(key, value);\n },\n async remove(key) {\n await host.clear(key);\n },\n async clear() {\n // Unlike the production adapter (a no-op in container mode), the fake\n // actually empties, so tests get real isolation.\n host.reset();\n },\n };\n}\n\nfunction makeFakeCloudStorage(): CloudStorageApi {\n const store = new Map<string, Uint8Array>();\n const toBytes = (data: string | Uint8Array) =>\n typeof data === \"string\" ? textEncoder.encode(data) : data;\n // Deterministic content-addressed id so upload / computeCid / fetch line up.\n const cidOf = (bytes: Uint8Array) => {\n let h = 0x811c9dc5;\n for (const b of bytes) {\n h ^= b;\n h = Math.imul(h, 0x01000193);\n }\n return `bafyfake${(h >>> 0).toString(16).padStart(8, \"0\")}`;\n };\n return {\n async upload(data) {\n const bytes = toBytes(data);\n const cid = cidOf(bytes);\n store.set(cid, bytes);\n return ok(cid);\n },\n async fetch(cid) {\n const bytes = store.get(cid);\n if (!bytes) {\n return err(\n new ProductCloudStorageError(\n `fake cloud storage: no object stored for CID ${cid}`,\n ),\n );\n }\n return ok(bytes);\n },\n async computeCid(data) {\n return cidOf(toBytes(data));\n },\n };\n}\n\nfunction makeUnconfiguredChain(): ChainApi {\n const fail = (): never => {\n throw new Error(\n \"createFakeApp: `chain` is not configured. Pass a `chain` override in options. \" +\n \"There is no chain-read fake — the host owns RPC selection; test chain-reading \" +\n \"logic behind your own interface and cover the real wiring in e2e.\",\n );\n };\n // Reads throw (fail fast, no silent undefined); state queries stay quiet.\n return {\n getClient: fail,\n getRawClient: fail,\n connect: fail,\n isConnected: () => false,\n destroyAll: () => {},\n };\n}\n\n/**\n * Create a fake {@link App}. Wallet, local-storage, and cloud-storage are working\n * in-memory fakes; `chain` is unconfigured until you override it. Every sub-API\n * is overridable.\n *\n * @example\n * ```ts\n * import { createFakeApp } from \"@parity/product-sdk/testing\";\n *\n * const app = createFakeApp({ wallet: { accounts: [alice, bob], selected: alice } });\n * await app.wallet.connect();\n * expect(app.wallet.getAccounts()).toHaveLength(2);\n * ```\n *\n * For React, feed the fake to the exported context:\n * ```tsx\n * import { ProductSDKContext } from \"@parity/product-sdk/react\";\n * render(<ProductSDKContext.Provider value={app}><AccountPicker /></ProductSDKContext.Provider>);\n * ```\n */\nexport function createFakeApp(options?: CreateFakeAppOptions): App {\n const name = options?.name ?? \"fake-app\";\n return {\n wallet: makeFakeWallet(options?.wallet),\n localStorage: options?.localStorage ?? makeFakeLocalStorage(),\n chain: options?.chain ?? makeUnconfiguredChain(),\n cloudStorage:\n options?.cloudStorage !== undefined ? options.cloudStorage : makeFakeCloudStorage(),\n getAppInfo: () => ({ name }),\n };\n}\n\nif (import.meta.vitest) {\n const { describe, test, expect } = import.meta.vitest;\n\n const alice: Account = { address: \"5Alice\", name: \"Alice\", source: \"dev\" };\n const bob: Account = { address: \"5Bob\", name: \"Bob\", source: \"dev\" };\n\n describe(\"createFakeApp\", () => {\n test(\"wallet lists, selects, and notifies on account change\", async () => {\n const app = createFakeApp({ wallet: { accounts: [alice, bob], selected: alice } });\n expect(app.wallet.getAccounts()).toHaveLength(2);\n expect(app.wallet.getSelectedAccount()).toEqual(alice);\n\n let changed: Account | null = null;\n app.wallet.onAccountChange((a) => {\n changed = a;\n });\n app.wallet.selectAccount(\"5Bob\");\n expect(app.wallet.getSelectedAccount()).toEqual(bob);\n expect(changed).toEqual(bob);\n\n const { accounts } = await app.wallet.connect();\n expect(accounts).toHaveLength(2);\n });\n\n test(\"localStorage round-trips and clear empties it\", async () => {\n const app = createFakeApp();\n await app.localStorage.set(\"k\", \"v\");\n await app.localStorage.setJSON(\"o\", { n: 1 });\n expect(await app.localStorage.get(\"k\")).toBe(\"v\");\n expect(await app.localStorage.getJSON(\"o\")).toEqual({ n: 1 });\n expect(await app.localStorage.get(\"missing\")).toBeNull();\n\n await app.localStorage.clear();\n expect(await app.localStorage.get(\"k\")).toBeNull();\n });\n\n test(\"cloudStorage round-trips upload -> fetch; computeCid matches upload\", async () => {\n const cs = createFakeApp().cloudStorage;\n if (!cs) throw new Error(\"expected default cloud storage\");\n const cid = unwrapOk(await cs.upload(\"hello\"));\n expect(await cs.computeCid(\"hello\")).toBe(cid);\n expect(new TextDecoder().decode(unwrapOk(await cs.fetch(cid)))).toBe(\"hello\");\n expect(unwrapErr(await cs.fetch(\"bafyfakemissing\"))).toBeInstanceOf(\n ProductCloudStorageError,\n );\n });\n\n test(\"cloudStorage can be disabled with null\", () => {\n expect(createFakeApp({ cloudStorage: null }).cloudStorage).toBeNull();\n });\n\n test(\"chain is unconfigured by default\", () => {\n const app = createFakeApp();\n expect(app.chain.isConnected({} as never)).toBe(false);\n expect(() => app.chain.getClient({} as never)).toThrow(/not configured/);\n });\n\n test(\"getAppInfo reports the name\", () => {\n expect(createFakeApp({ name: \"my-app\" }).getAppInfo().name).toBe(\"my-app\");\n });\n });\n}\n"]}
1
+ {"version":3,"sources":["../../src/testing.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["// Copyright 2026 Parity Technologies (UK) Ltd.\n// SPDX-License-Identifier: Apache-2.0\n/**\n * Test fakes for `@parity/product-sdk` (umbrella).\n *\n * `createFakeApp` returns a fake {@link App} — use it directly in logic tests or\n * hand it to `ProductSDKContext.Provider` for React component tests, so code that\n * calls `useProductSDK()` / `useWallet()` runs with no host. This entry also\n * re-exports every package's fakes, for a single meta-package import.\n *\n * @packageDocumentation\n */\nimport { ProductCloudStorageError } from \"@parity/product-sdk-cloud-storage\";\nimport type { LocalKvStore } from \"@parity/product-sdk-local-storage\";\nimport { createFakeHostLocalStorage } from \"@parity/product-sdk-local-storage/testing\";\nimport { createLocalStorageApi } from \"./core/local-storage-api.js\";\nimport { err, ok, unwrapErr, unwrapOk } from \"@parity/result\";\n\nimport type {\n Account,\n App,\n ChainApi,\n CloudStorageApi,\n LocalStorageApi,\n WalletApi,\n} from \"./core/types.js\";\n\nconst textEncoder = new TextEncoder();\n\n// Re-export the per-package fakes so `@parity/product-sdk/testing` is a one-stop import.\n// statement-store is deliberately absent: the umbrella doesn't wrap that package, so apps\n// using it depend on it directly — import `@parity/product-sdk-statement-store/testing`\n// there instead. Re-exporting its fake here would add a dependency the umbrella otherwise\n// doesn't have and could skew against the version the app actually installs.\nexport * from \"@parity/product-sdk-local-storage/testing\";\nexport * from \"@parity/product-sdk-signer/testing\";\nexport * from \"@parity/product-sdk-contracts/testing\";\nexport * from \"@parity/product-sdk-host/testing\";\n\n/** Configurable behavior for the fake {@link WalletApi}. */\nexport interface FakeWalletOptions {\n /** Accounts `connect()` / `getAccounts()` expose. Default: none. */\n accounts?: Account[];\n /** Initially selected account. Default: the first account, or `null`. */\n selected?: Account | null;\n /** Bytes returned by `signMessage` / `createProof`. Default: 64 zero bytes. */\n signature?: Uint8Array;\n /** Value returned by `getProductAccount()`. Default: the selected account. */\n productAccount?: Account | null;\n /** Value returned by `getAnonymousAlias()`. Default: `null`. */\n anonymousAlias?: string | null;\n}\n\n/** Options for {@link createFakeApp}. */\nexport interface CreateFakeAppOptions {\n /** App name reported by `getAppInfo()`. Default: `\"fake-app\"`. */\n name?: string;\n /** Configure the fake wallet. */\n wallet?: FakeWalletOptions;\n /** Override the local-storage API. Default: in-memory (backed by `createFakeHostLocalStorage`). */\n localStorage?: LocalStorageApi;\n /** Override the chain API. Default: unconfigured (throws until you provide one). */\n chain?: ChainApi;\n /** Override cloud storage, or pass `null` to disable it. Default: an in-memory fake. */\n cloudStorage?: CloudStorageApi | null;\n}\n\nfunction makeFakeWallet(options?: FakeWalletOptions): WalletApi {\n const accounts = options?.accounts ?? [];\n let selected = options?.selected !== undefined ? options.selected : (accounts[0] ?? null);\n const signature = options?.signature ?? new Uint8Array(64);\n const listeners = new Set<(account: Account | null) => void>();\n const notify = () => {\n for (const l of listeners) l(selected);\n };\n\n return {\n async connect() {\n return { accounts };\n },\n async disconnect() {\n selected = null;\n notify();\n },\n getAccounts() {\n return accounts;\n },\n getSelectedAccount() {\n return selected;\n },\n selectAccount(address) {\n selected = accounts.find((a) => a.address === address) ?? null;\n notify();\n },\n async signMessage() {\n return signature;\n },\n async signMessageWithDotNsIdentity(args) {\n return {\n username: args.username ?? \"alice\",\n accountId: `0x${\"00\".repeat(32)}`,\n signature,\n };\n },\n onAccountChange(callback) {\n listeners.add(callback);\n return () => {\n listeners.delete(callback);\n };\n },\n getProductAccount() {\n return options?.productAccount !== undefined ? options.productAccount : selected;\n },\n getAnonymousAlias() {\n return options?.anonymousAlias ?? null;\n },\n async createProof() {\n return signature;\n },\n };\n}\n\nfunction makeFakeLocalStorage(): LocalStorageApi {\n // Route through the *same* `createLocalStorageApi` factory `createApp` uses,\n // so the fake and the shipped adapter cannot drift (#344). The fake's host\n // is adapted to a `LocalKvStore` here rather than via the async\n // `createLocalKvStore`, so `createFakeApp` stays synchronous; the semantics\n // (get \"\" -> null, getJSON undefined -> null) match `createHostBackend`.\n const host = createFakeHostLocalStorage();\n const kv: LocalKvStore = {\n async get(key) {\n return (await host.readString(key)) || null;\n },\n async set(key, value) {\n await host.writeString(key, value);\n },\n async getJSON<T>(key: string) {\n return ((await host.readJSON(key)) ?? null) as T | null;\n },\n async setJSON(key, value) {\n await host.writeJSON(key, value);\n },\n async remove(key) {\n await host.clear(key);\n },\n };\n return createLocalStorageApi(kv);\n}\n\nfunction makeFakeCloudStorage(): CloudStorageApi {\n const store = new Map<string, Uint8Array>();\n const toBytes = (data: string | Uint8Array) =>\n typeof data === \"string\" ? textEncoder.encode(data) : data;\n // Deterministic content-addressed id so upload / computeCid / fetch line up.\n const cidOf = (bytes: Uint8Array) => {\n let h = 0x811c9dc5;\n for (const b of bytes) {\n h ^= b;\n h = Math.imul(h, 0x01000193);\n }\n return `bafyfake${(h >>> 0).toString(16).padStart(8, \"0\")}`;\n };\n return {\n async upload(data) {\n const bytes = toBytes(data);\n const cid = cidOf(bytes);\n store.set(cid, bytes);\n return ok(cid);\n },\n async fetch(cid) {\n const bytes = store.get(cid);\n if (!bytes) {\n return err(\n new ProductCloudStorageError(\n `fake cloud storage: no object stored for CID ${cid}`,\n ),\n );\n }\n return ok(bytes);\n },\n async computeCid(data) {\n return cidOf(toBytes(data));\n },\n };\n}\n\nfunction makeUnconfiguredChain(): ChainApi {\n const fail = (): never => {\n throw new Error(\n \"createFakeApp: `chain` is not configured. Pass a `chain` override in options. \" +\n \"There is no chain-read fake — the host owns RPC selection; test chain-reading \" +\n \"logic behind your own interface and cover the real wiring in e2e.\",\n );\n };\n // Reads throw (fail fast, no silent undefined); state queries stay quiet.\n return {\n getClient: fail,\n getRawClient: fail,\n connect: fail,\n isConnected: () => false,\n destroyAll: () => {},\n };\n}\n\n/**\n * Create a fake {@link App}. Wallet, local-storage, and cloud-storage are working\n * in-memory fakes; `chain` is unconfigured until you override it. Every sub-API\n * is overridable.\n *\n * @example\n * ```ts\n * import { createFakeApp } from \"@parity/product-sdk/testing\";\n *\n * const app = createFakeApp({ wallet: { accounts: [alice, bob], selected: alice } });\n * await app.wallet.connect();\n * expect(app.wallet.getAccounts()).toHaveLength(2);\n * ```\n *\n * For React, feed the fake to the exported context:\n * ```tsx\n * import { ProductSDKContext } from \"@parity/product-sdk/react\";\n * render(<ProductSDKContext.Provider value={app}><AccountPicker /></ProductSDKContext.Provider>);\n * ```\n */\nexport function createFakeApp(options?: CreateFakeAppOptions): App {\n const name = options?.name ?? \"fake-app\";\n return {\n wallet: makeFakeWallet(options?.wallet),\n localStorage: options?.localStorage ?? makeFakeLocalStorage(),\n chain: options?.chain ?? makeUnconfiguredChain(),\n cloudStorage:\n options?.cloudStorage !== undefined ? options.cloudStorage : makeFakeCloudStorage(),\n getAppInfo: () => ({ name }),\n };\n}\n\nif (import.meta.vitest) {\n const { describe, test, expect } = import.meta.vitest;\n\n const alice: Account = { address: \"5Alice\", name: \"Alice\", source: \"dev\" };\n const bob: Account = { address: \"5Bob\", name: \"Bob\", source: \"dev\" };\n\n describe(\"createFakeApp\", () => {\n test(\"wallet lists, selects, and notifies on account change\", async () => {\n const app = createFakeApp({ wallet: { accounts: [alice, bob], selected: alice } });\n expect(app.wallet.getAccounts()).toHaveLength(2);\n expect(app.wallet.getSelectedAccount()).toEqual(alice);\n\n let changed: Account | null = null;\n app.wallet.onAccountChange((a) => {\n changed = a;\n });\n app.wallet.selectAccount(\"5Bob\");\n expect(app.wallet.getSelectedAccount()).toEqual(bob);\n expect(changed).toEqual(bob);\n\n const { accounts } = await app.wallet.connect();\n expect(accounts).toHaveLength(2);\n });\n\n test(\"localStorage round-trips and remove deletes a key\", async () => {\n const app = createFakeApp();\n await app.localStorage.set(\"k\", \"v\");\n await app.localStorage.setJSON(\"o\", { n: 1 });\n expect(await app.localStorage.get(\"k\")).toBe(\"v\");\n expect(await app.localStorage.getJSON(\"o\")).toEqual({ n: 1 });\n expect(await app.localStorage.get(\"missing\")).toBeNull();\n\n await app.localStorage.remove(\"k\");\n expect(await app.localStorage.get(\"k\")).toBeNull();\n // Untouched keys stay — there is no clear-all.\n expect(await app.localStorage.getJSON(\"o\")).toEqual({ n: 1 });\n });\n\n test(\"cloudStorage round-trips upload -> fetch; computeCid matches upload\", async () => {\n const cs = createFakeApp().cloudStorage;\n if (!cs) throw new Error(\"expected default cloud storage\");\n const cid = unwrapOk(await cs.upload(\"hello\"));\n expect(await cs.computeCid(\"hello\")).toBe(cid);\n expect(new TextDecoder().decode(unwrapOk(await cs.fetch(cid)))).toBe(\"hello\");\n expect(unwrapErr(await cs.fetch(\"bafyfakemissing\"))).toBeInstanceOf(\n ProductCloudStorageError,\n );\n });\n\n test(\"cloudStorage can be disabled with null\", () => {\n expect(createFakeApp({ cloudStorage: null }).cloudStorage).toBeNull();\n });\n\n test(\"chain is unconfigured by default\", () => {\n const app = createFakeApp();\n expect(app.chain.isConnected({} as never)).toBe(false);\n expect(() => app.chain.getClient({} as never)).toThrow(/not configured/);\n });\n\n test(\"getAppInfo reports the name\", () => {\n expect(createFakeApp({ name: \"my-app\" }).getAppInfo().name).toBe(\"my-app\");\n });\n });\n}\n"]}
@@ -61,7 +61,14 @@ interface WalletApi {
61
61
  /** Create Ring VRF proof (container mode only) */
62
62
  createProof(message: Uint8Array): Promise<Uint8Array>;
63
63
  }
64
- /** Storage API exposed by the SDK */
64
+ /**
65
+ * Storage API exposed by the SDK: per-key get / set / remove, string and JSON.
66
+ *
67
+ * There is no `clear()`. Host localStorage exposes no key enumeration (only
68
+ * per-key read / write / remove), so a clear-all cannot be implemented — it was
69
+ * a silent no-op in a host container. Remove keys individually with `remove`,
70
+ * or track the keys your app writes and remove them.
71
+ */
65
72
  interface LocalStorageApi {
66
73
  /** Get a value by key */
67
74
  get(key: string): Promise<string | null>;
@@ -73,8 +80,6 @@ interface LocalStorageApi {
73
80
  setJSON<T = unknown>(key: string, value: T): Promise<void>;
74
81
  /** Remove a value by key */
75
82
  remove(key: string): Promise<void>;
76
- /** Clear all values */
77
- clear(): Promise<void>;
78
83
  }
79
84
  /** Chain API exposed by the SDK */
80
85
  interface ChainApi {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@parity/product-sdk",
3
- "version": "0.25.0",
3
+ "version": "0.27.0",
4
4
  "description": "Unified SDK for building products in the Polkadot ecosystem - umbrella package",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -72,19 +72,19 @@
72
72
  "dependencies": {
73
73
  "polkadot-api": "^2.1.6",
74
74
  "@parity/product-sdk-address": "0.2.0",
75
- "@parity/product-sdk-cloud-storage": "0.11.1",
76
75
  "@parity/product-sdk-crypto": "0.1.1",
77
- "@parity/product-sdk-host": "0.18.0",
78
- "@parity/product-sdk-contracts": "0.10.5",
79
- "@parity/product-sdk-chain-client": "0.12.1",
80
- "@parity/product-sdk-individuality": "0.2.0",
76
+ "@parity/product-sdk-individuality": "0.4.0",
77
+ "@parity/product-sdk-contracts": "0.10.7",
78
+ "@parity/product-sdk-host": "0.19.1",
79
+ "@parity/product-sdk-chain-client": "0.12.3",
80
+ "@parity/product-sdk-keys": "0.3.24",
81
81
  "@parity/product-sdk-logger": "0.1.1",
82
- "@parity/product-sdk-keys": "0.3.22",
82
+ "@parity/product-sdk-signer": "0.14.4",
83
83
  "@parity/result": "0.2.0",
84
- "@parity/product-sdk-tx": "0.4.5",
85
- "@parity/product-sdk-local-storage": "0.3.7",
86
- "@parity/product-sdk-signer": "0.14.2",
87
- "@parity/product-sdk-errors": "0.2.0"
84
+ "@parity/product-sdk-errors": "0.2.0",
85
+ "@parity/product-sdk-tx": "0.4.7",
86
+ "@parity/product-sdk-local-storage": "0.3.9",
87
+ "@parity/product-sdk-cloud-storage": "0.11.3"
88
88
  },
89
89
  "peerDependencies": {
90
90
  "react": "^18.0.0 || ^19.0.0"
@@ -16,6 +16,7 @@ import type {
16
16
  CloudStorageApi,
17
17
  LocalStorageApi,
18
18
  } from "./types.js";
19
+ import { createLocalStorageApi } from "./local-storage-api.js";
19
20
  import { configure, createLogger } from "@parity/product-sdk-logger";
20
21
  import { createLocalKvStore } from "@parity/product-sdk-local-storage";
21
22
  import { SignerManager } from "@parity/product-sdk-signer";
@@ -124,18 +125,8 @@ export async function createApp(config: AppConfig): Promise<App> {
124
125
  log.debug("Cloud Storage client disabled");
125
126
  }
126
127
 
127
- // Create storage API adapter
128
- const localStorageApi: LocalStorageApi = {
129
- get: (key) => localKvStore.get(key),
130
- set: (key, value) => localKvStore.set(key, value),
131
- getJSON: <T>(key: string) => localKvStore.getJSON<T>(key),
132
- setJSON: <T>(key: string, value: T) => localKvStore.setJSON(key, value),
133
- remove: (key) => localKvStore.remove(key),
134
- clear: async () => {
135
- // LocalKvStore doesn't have clear - this is a no-op
136
- log.debug("clear() is not supported in container storage mode");
137
- },
138
- };
128
+ // Storage API adapter — shared with `createFakeApp` so the two cannot drift.
129
+ const localStorageApi: LocalStorageApi = createLocalStorageApi(localKvStore);
139
130
 
140
131
  // Create wallet API adapter
141
132
  const walletApi = createWalletApi(signerManager);
@@ -0,0 +1,74 @@
1
+ // Copyright 2026 Parity Technologies (UK) Ltd.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ /**
4
+ * The one adapter from a {@link LocalKvStore} to the SDK's {@link LocalStorageApi}.
5
+ *
6
+ * Both `createApp` (host-backed store) and `createFakeApp` (in-memory store)
7
+ * build their `localStorage` through here, so the shipped adapter and the test
8
+ * fake cannot drift — which is exactly how `clear()` came to be a no-op in
9
+ * production while the fake emptied (#344). The in-source test below drives one
10
+ * assertion set through this factory over an in-memory store; anything the fake
11
+ * exposes, the real path exposes the same way.
12
+ */
13
+ import type { LocalKvStore } from "@parity/product-sdk-local-storage";
14
+ import type { LocalStorageApi } from "./types.js";
15
+
16
+ /** Wrap a {@link LocalKvStore} as the SDK's {@link LocalStorageApi}. */
17
+ export function createLocalStorageApi(kv: LocalKvStore): LocalStorageApi {
18
+ return {
19
+ get: (key) => kv.get(key),
20
+ set: (key, value) => kv.set(key, value),
21
+ getJSON: <T>(key: string) => kv.getJSON<T>(key),
22
+ setJSON: <T>(key: string, value: T) => kv.setJSON(key, value),
23
+ remove: (key) => kv.remove(key),
24
+ };
25
+ }
26
+
27
+ if (import.meta.vitest) {
28
+ const { describe, test, expect } = import.meta.vitest;
29
+
30
+ /** A minimal in-memory {@link LocalKvStore}, string- and JSON-aware. */
31
+ function memoryKvStore(): LocalKvStore {
32
+ const store = new Map<string, string>();
33
+ return {
34
+ async get(key) {
35
+ return store.get(key) ?? null;
36
+ },
37
+ async set(key, value) {
38
+ store.set(key, value);
39
+ },
40
+ async remove(key) {
41
+ store.delete(key);
42
+ },
43
+ async getJSON<T>(key: string) {
44
+ const raw = store.get(key);
45
+ return raw === undefined ? null : (JSON.parse(raw) as T);
46
+ },
47
+ async setJSON(key, value) {
48
+ store.set(key, JSON.stringify(value));
49
+ },
50
+ };
51
+ }
52
+
53
+ describe("createLocalStorageApi", () => {
54
+ test("round-trips strings and JSON, and remove deletes one key", async () => {
55
+ const api = createLocalStorageApi(memoryKvStore());
56
+
57
+ await api.set("k", "v");
58
+ await api.setJSON("o", { n: 1 });
59
+ expect(await api.get("k")).toBe("v");
60
+ expect(await api.getJSON("o")).toEqual({ n: 1 });
61
+ expect(await api.get("missing")).toBeNull();
62
+
63
+ await api.remove("k");
64
+ expect(await api.get("k")).toBeNull();
65
+ // No clear-all: an untouched key stays.
66
+ expect(await api.getJSON("o")).toEqual({ n: 1 });
67
+ });
68
+
69
+ test("exposes exactly the LocalStorageApi surface — no clear()", () => {
70
+ const api = createLocalStorageApi(memoryKvStore());
71
+ expect(Object.keys(api).sort()).toEqual(["get", "getJSON", "remove", "set", "setJSON"]);
72
+ });
73
+ });
74
+ }
package/src/core/types.ts CHANGED
@@ -72,7 +72,14 @@ export interface WalletApi {
72
72
  createProof(message: Uint8Array): Promise<Uint8Array>;
73
73
  }
74
74
 
75
- /** Storage API exposed by the SDK */
75
+ /**
76
+ * Storage API exposed by the SDK: per-key get / set / remove, string and JSON.
77
+ *
78
+ * There is no `clear()`. Host localStorage exposes no key enumeration (only
79
+ * per-key read / write / remove), so a clear-all cannot be implemented — it was
80
+ * a silent no-op in a host container. Remove keys individually with `remove`,
81
+ * or track the keys your app writes and remove them.
82
+ */
76
83
  export interface LocalStorageApi {
77
84
  /** Get a value by key */
78
85
  get(key: string): Promise<string | null>;
@@ -84,8 +91,6 @@ export interface LocalStorageApi {
84
91
  setJSON<T = unknown>(key: string, value: T): Promise<void>;
85
92
  /** Remove a value by key */
86
93
  remove(key: string): Promise<void>;
87
- /** Clear all values */
88
- clear(): Promise<void>;
89
94
  }
90
95
 
91
96
  /** Chain API exposed by the SDK */
@@ -32,6 +32,7 @@
32
32
  */
33
33
  import type { getChainAPI } from "@parity/product-sdk-chain-client";
34
34
  import type {
35
+ RingLocation as HostRingLocation,
35
36
  RingVRFProof as HostRingVRFProof,
36
37
  VrfSignature as HostVrfSignature,
37
38
  VrfTranscriptItem as HostVrfTranscriptItem,
@@ -41,8 +42,17 @@ import type {
41
42
  ClaimChain,
42
43
  ConsumersChain,
43
44
  GameChain,
45
+ GamePlayersChain,
44
46
  IndividualityChain,
47
+ LegacySuffixChain,
48
+ LiteSignUpChain,
49
+ NetworkSuffixChain,
50
+ PapiIndividualityChain,
45
51
  PrizeStatusChain,
52
+ RegisterChain,
53
+ RegistrationEligibilityChain,
54
+ RingLocation as SdkRingLocation,
55
+ ScoreContextChain,
46
56
  SignUpChain,
47
57
  AccountVrfSignature,
48
58
  VrfTranscriptItem as IndividualityVrfTranscriptItem,
@@ -100,6 +110,53 @@ type PreviewnetSatisfiesGameContract = Assert<PreviewnetClient extends GameChain
100
110
  // prompt to flip it positive and check the read against it.
101
111
  type DevnetPredatesTheGameContract = Assert<DevnetClient extends GameChain ? false : true>;
102
112
 
113
+ // `GamePlayersChain` is the opt-in half of the game read (`players`), typed
114
+ // separately so a caller who never asks about a player keeps the narrower
115
+ // contract. Both game-capable chains carry `Game.Players`.
116
+ type PaseoSatisfiesPlayersContract = Assert<PaseoClient extends GamePlayersChain ? true : false>;
117
+ type PreviewnetSatisfiesPlayersContract = Assert<
118
+ PreviewnetClient extends GamePlayersChain ? true : false
119
+ >;
120
+
121
+ // `fromPapi` hands a caller's own typed API to the reads; it must land exactly
122
+ // where the chain-client shape puts it, or every contract above would be
123
+ // satisfied by `getChainAPI` and none by a self-connected product.
124
+ type FromPapiSatisfiesContracts = Assert<
125
+ PapiIndividualityChain<PaseoClient["individuality"]> extends IndividualityChain &
126
+ AirdropChain &
127
+ GameChain &
128
+ GamePlayersChain &
129
+ ScoreContextChain &
130
+ SignUpChain &
131
+ LiteSignUpChain
132
+ ? true
133
+ : false
134
+ >;
135
+
136
+ type PaseoSatisfiesScoreContext = Assert<PaseoClient extends ScoreContextChain ? true : false>;
137
+ type DevnetSatisfiesScoreContext = Assert<DevnetClient extends ScoreContextChain ? true : false>;
138
+ type PreviewnetSatisfiesScoreContext = Assert<
139
+ PreviewnetClient extends ScoreContextChain ? true : false
140
+ >;
141
+
142
+ // Negative on purpose: a re-pin that flips either is the prompt to re-run the
143
+ // read against that chain.
144
+ type PreviewnetSatisfiesLegacySuffix = Assert<
145
+ PreviewnetClient extends LegacySuffixChain ? true : false
146
+ >;
147
+ type PaseoPredatesTheLegacySuffix = Assert<PaseoClient extends LegacySuffixChain ? false : true>;
148
+ type DevnetPredatesTheLegacySuffix = Assert<DevnetClient extends LegacySuffixChain ? false : true>;
149
+ type PreviewnetHasNoSuffixStorage = Assert<
150
+ PreviewnetClient extends NetworkSuffixChain ? false : true
151
+ >;
152
+ type PaseoHasNoSuffixStorage = Assert<PaseoClient extends NetworkSuffixChain ? false : true>;
153
+ type DevnetHasNoSuffixStorage = Assert<DevnetClient extends NetworkSuffixChain ? false : true>;
154
+
155
+ // The two are declared separately, so a rename on either side would go
156
+ // unnoticed anywhere but here.
157
+ type LocalRingLocationFitsHost = Assert<SdkRingLocation extends HostRingLocation ? true : false>;
158
+ type HostRingLocationFitsLocal = Assert<HostRingLocation extends SdkRingLocation ? true : false>;
159
+
103
160
  // Negative control, kept type-only so it emits no runtime code. If
104
161
  // `IndividualityChain` ever stopped constraining, this flips to `false` and the
105
162
  // file fails to typecheck, which is what keeps the assertions above honest.
@@ -115,6 +172,9 @@ type RejectsBogusAirdropClient = Assert<
115
172
  ClientWithoutIndividuality extends AirdropChain ? false : true
116
173
  >;
117
174
  type RejectsBogusGameClient = Assert<ClientWithoutIndividuality extends GameChain ? false : true>;
175
+ type RejectsBogusPlayersClient = Assert<
176
+ ClientWithoutIndividuality extends GamePlayersChain ? false : true
177
+ >;
118
178
 
119
179
  // `Game.Game` being optional is what makes "between games" representable at all,
120
180
  // and `GameIndex` / `GameSchedules` being `ValueQuery` is what lets the read treat
@@ -328,6 +388,82 @@ type RejectsItemMissingValue = Assert<
328
388
  { label: Uint8Array } extends IndividualityVrfTranscriptItem ? false : true
329
389
  >;
330
390
 
391
+ // The lite sign-up surface: the alias binding, the lite-person marker, the
392
+ // invite pin, the ring membership entry and the free sign-up call. Paseo and
393
+ // previewnet carry all five; devnet predates `Game.LiteInvites` and the call,
394
+ // so it is asserted negatively, like the game surface it composes with.
395
+ type PaseoSatisfiesLiteSignUpContract = Assert<PaseoClient extends LiteSignUpChain ? true : false>;
396
+ type PreviewnetSatisfiesLiteSignUpContract = Assert<
397
+ PreviewnetClient extends LiteSignUpChain ? true : false
398
+ >;
399
+ type DevnetPredatesTheLiteSignUpContract = Assert<
400
+ DevnetClient extends LiteSignUpChain ? false : true
401
+ >;
402
+ type RejectsBogusLiteSignUpClient = Assert<
403
+ ClientWithoutIndividuality extends LiteSignUpChain ? false : true
404
+ >;
405
+
406
+ // `LiteSignUpChain` alone proves nothing about what the read accepts: it takes
407
+ // an intersection and resolves a suffix.
408
+ type LiteSignUpReadBase = GameChain & SignUpChain & ScoreContextChain & LiteSignUpChain;
409
+ type PaseoSatisfiesTheLiteReadWithATld = Assert<
410
+ PaseoClient extends LiteSignUpReadBase ? true : false
411
+ >;
412
+ type PaseoCannotResolveTheSuffixItself = Assert<
413
+ PaseoClient extends LiteSignUpReadBase & LegacySuffixChain ? false : true
414
+ >;
415
+ type PreviewnetSatisfiesTheLiteReadFromItsConstant = Assert<
416
+ PreviewnetClient extends LiteSignUpReadBase & LegacySuffixChain ? true : false
417
+ >;
418
+ type DevnetPredatesTheLiteRead = Assert<DevnetClient extends LiteSignUpReadBase ? false : true>;
419
+
420
+ // Pinned by name for the same reason `sign_up_with_account`'s are: PAPI encodes
421
+ // the object it is handed, so a renamed field silently encodes as `undefined` —
422
+ // and `account` here is a call argument, not implied by the signer.
423
+ type LiteInviteArgs = Parameters<GameTx["sign_up_with_account_lite_invite"]>[0];
424
+ type LiteInviteTakesTheDocumentedArgs = Assert<
425
+ "account" extends keyof LiteInviteArgs
426
+ ? "identifier_key" extends keyof LiteInviteArgs
427
+ ? "airdrops" extends keyof LiteInviteArgs
428
+ ? true
429
+ : false
430
+ : false
431
+ : false
432
+ >;
433
+ type LiteInviteAirdropsIsOptional = Assert<
434
+ undefined extends LiteInviteArgs["airdrops"] ? true : false
435
+ >;
436
+
437
+ // `PeopleLite.AccountToAlias`: the three fields the lite requirement read uses.
438
+ // The context matters as much as the alias — a binding outside the score
439
+ // context does not satisfy the game's origin check — so it is pinned by name.
440
+ type LiteBindingValue = NonNullable<
441
+ Awaited<
442
+ ReturnType<
443
+ PaseoClient["individuality"]["query"]["PeopleLite"]["AccountToAlias"]["getValue"]
444
+ >
445
+ >
446
+ >;
447
+ type LiteBindingHasTheDocumentedFields = Assert<
448
+ "revision" extends keyof LiteBindingValue
449
+ ? "ring" extends keyof LiteBindingValue
450
+ ? LiteBindingValue["ca"] extends { alias: string; context: string }
451
+ ? true
452
+ : false
453
+ : false
454
+ : false
455
+ >;
456
+
457
+ // `Members.Members` is a double map (collection id, member key), which is what
458
+ // lets the membership check be a point read. A collapsed key would silently
459
+ // turn the varargs call into a mistyped one, so the arity is pinned.
460
+ type MembersKey = Parameters<
461
+ PaseoClient["individuality"]["query"]["Members"]["Members"]["getValue"]
462
+ >;
463
+ type MembersTakesTwoKeys = Assert<
464
+ MembersKey extends [unknown, unknown, ...unknown[]] ? true : false
465
+ >;
466
+
331
467
  // `Game.Players`: the variant spelling the read keys by, and the one field it uses.
332
468
  // Sibling reads got exactly these pins.
333
469
  type PlayersEntry = PaseoClient["individuality"]["query"]["Game"]["Players"];
@@ -341,6 +477,44 @@ type RejectsAliasSpelling = Assert<
341
477
  type PlayersValue = NonNullable<Awaited<ReturnType<PlayersEntry["getValue"]>>>;
342
478
  type PlayersHasRegistered = Assert<"registered" extends keyof PlayersValue ? true : false>;
343
479
 
480
+ type PaseoSatisfiesRegisterContract = Assert<PaseoClient extends RegisterChain ? true : false>;
481
+ type PreviewnetSatisfiesRegisterContract = Assert<
482
+ PreviewnetClient extends RegisterChain ? true : false
483
+ >;
484
+ type DevnetSatisfiesRegisterContract = Assert<DevnetClient extends RegisterChain ? true : false>;
485
+ type RejectsBogusRegisterClient = Assert<
486
+ ClientWithoutIndividuality extends RegisterChain ? false : true
487
+ >;
488
+
489
+ type PaseoSatisfiesEligibilityContract = Assert<
490
+ PaseoClient extends RegistrationEligibilityChain ? true : false
491
+ >;
492
+ type PreviewnetSatisfiesEligibilityContract = Assert<
493
+ PreviewnetClient extends RegistrationEligibilityChain ? true : false
494
+ >;
495
+ type DevnetSatisfiesEligibilityContract = Assert<
496
+ DevnetClient extends RegistrationEligibilityChain ? true : false
497
+ >;
498
+ type RejectsBogusEligibilityClient = Assert<
499
+ ClientWithoutIndividuality extends RegistrationEligibilityChain ? false : true
500
+ >;
501
+
502
+ // The same trap as `claim_airdrop`'s pins, and worse: `key` is an `Option`, so a
503
+ // renamed argument encodes as `None` — the resume arm this builder never means.
504
+ type ScoreTx = PaseoClient["individuality"]["tx"]["Score"];
505
+ type RegisterExists = Assert<"register" extends keyof ScoreTx ? true : false>;
506
+ type RegisterArgs = Parameters<ScoreTx["register"]>[0];
507
+ type RegisterTakesKey = Assert<"key" extends keyof RegisterArgs ? true : false>;
508
+ type RegisterKeyIsOptional = Assert<undefined extends RegisterArgs["key"] ? true : false>;
509
+
510
+ // `ValueQuery` is what lets the read type the threshold as a plain number, and
511
+ // `Participants` being optional is what makes "never seen" a null, not a throw.
512
+ type ScoreStorage = PaseoClient["individuality"]["query"]["Score"];
513
+ type ThresholdValue = Awaited<ReturnType<ScoreStorage["PersonhoodThreshold"]["getValue"]>>;
514
+ type ThresholdIsNotOptional = Assert<undefined extends ThresholdValue ? false : true>;
515
+ type ParticipantsValue = Awaited<ReturnType<ScoreStorage["Participants"]["getValue"]>>;
516
+ type ParticipantsIsOptional = Assert<undefined extends ParticipantsValue ? true : false>;
517
+
344
518
  test("the individuality chain contract is asserted at compile time", () => {
345
519
  // The type assertions above are the test. This keeps vitest from reporting
346
520
  // the file as an empty suite.
package/src/testing.ts CHANGED
@@ -11,7 +11,9 @@
11
11
  * @packageDocumentation
12
12
  */
13
13
  import { ProductCloudStorageError } from "@parity/product-sdk-cloud-storage";
14
+ import type { LocalKvStore } from "@parity/product-sdk-local-storage";
14
15
  import { createFakeHostLocalStorage } from "@parity/product-sdk-local-storage/testing";
16
+ import { createLocalStorageApi } from "./core/local-storage-api.js";
15
17
  import { err, ok, unwrapErr, unwrapOk } from "@parity/result";
16
18
 
17
19
  import type {
@@ -119,10 +121,13 @@ function makeFakeWallet(options?: FakeWalletOptions): WalletApi {
119
121
  }
120
122
 
121
123
  function makeFakeLocalStorage(): LocalStorageApi {
122
- // Compose the local-storage fake; mirror createApp's adapter semantics
123
- // (get normalizes "" -> null, getJSON normalizes undefined -> null).
124
+ // Route through the *same* `createLocalStorageApi` factory `createApp` uses,
125
+ // so the fake and the shipped adapter cannot drift (#344). The fake's host
126
+ // is adapted to a `LocalKvStore` here rather than via the async
127
+ // `createLocalKvStore`, so `createFakeApp` stays synchronous; the semantics
128
+ // (get "" -> null, getJSON undefined -> null) match `createHostBackend`.
124
129
  const host = createFakeHostLocalStorage();
125
- return {
130
+ const kv: LocalKvStore = {
126
131
  async get(key) {
127
132
  return (await host.readString(key)) || null;
128
133
  },
@@ -132,18 +137,14 @@ function makeFakeLocalStorage(): LocalStorageApi {
132
137
  async getJSON<T>(key: string) {
133
138
  return ((await host.readJSON(key)) ?? null) as T | null;
134
139
  },
135
- async setJSON<T>(key: string, value: T) {
140
+ async setJSON(key, value) {
136
141
  await host.writeJSON(key, value);
137
142
  },
138
143
  async remove(key) {
139
144
  await host.clear(key);
140
145
  },
141
- async clear() {
142
- // Unlike the production adapter (a no-op in container mode), the fake
143
- // actually empties, so tests get real isolation.
144
- host.reset();
145
- },
146
146
  };
147
+ return createLocalStorageApi(kv);
147
148
  }
148
149
 
149
150
  function makeFakeCloudStorage(): CloudStorageApi {
@@ -257,7 +258,7 @@ if (import.meta.vitest) {
257
258
  expect(accounts).toHaveLength(2);
258
259
  });
259
260
 
260
- test("localStorage round-trips and clear empties it", async () => {
261
+ test("localStorage round-trips and remove deletes a key", async () => {
261
262
  const app = createFakeApp();
262
263
  await app.localStorage.set("k", "v");
263
264
  await app.localStorage.setJSON("o", { n: 1 });
@@ -265,8 +266,10 @@ if (import.meta.vitest) {
265
266
  expect(await app.localStorage.getJSON("o")).toEqual({ n: 1 });
266
267
  expect(await app.localStorage.get("missing")).toBeNull();
267
268
 
268
- await app.localStorage.clear();
269
+ await app.localStorage.remove("k");
269
270
  expect(await app.localStorage.get("k")).toBeNull();
271
+ // Untouched keys stay — there is no clear-all.
272
+ expect(await app.localStorage.getJSON("o")).toEqual({ n: 1 });
270
273
  });
271
274
 
272
275
  test("cloudStorage round-trips upload -> fetch; computeCid matches upload", async () => {
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/core/createApp.ts"],"names":["err"],"mappings":"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","file":"chunk-JIO3OZSC.js","sourcesContent":["// Copyright 2026 Parity Technologies (UK) Ltd.\n// SPDX-License-Identifier: Apache-2.0\n/**\n * createApp - Main entry point for the Product SDK\n *\n * Creates an App instance with wallet, storage, chain, and cloud storage APIs.\n */\n\nimport type { ChainDefinition } from \"polkadot-api\";\nimport type {\n App,\n AppConfig,\n WalletApi,\n ChainApi,\n Account,\n CloudStorageApi,\n LocalStorageApi,\n} from \"./types.js\";\nimport { configure, createLogger } from \"@parity/product-sdk-logger\";\nimport { createLocalKvStore } from \"@parity/product-sdk-local-storage\";\nimport { SignerManager } from \"@parity/product-sdk-signer\";\nimport {\n CloudStorageClient,\n ProductCloudStorageError,\n calculateCid,\n createLazySigner,\n} from \"@parity/product-sdk-cloud-storage\";\nimport { err, ok } from \"@parity/result\";\nimport {\n createChainClient,\n getClient,\n isConnected,\n destroyAll,\n} from \"@parity/product-sdk-chain-client\";\nimport { accountIdBytes } from \"@parity/product-sdk-address\";\nimport { getAccountsProvider } from \"@parity/product-sdk-host\";\nimport {\n accountIdBytesToHex,\n type PeopleUsernameQueryApi,\n resolvePeopleUsernameOwner,\n} from \"../identity/dotns.js\";\n\nconst log = createLogger(\"app\");\n\n/**\n * Create a new Product SDK app instance\n *\n * @param config - Application configuration\n * @returns App instance with all APIs\n *\n * @example\n * ```ts\n * import { createApp } from '@parity/product-sdk';\n *\n * // Default: cloud storage enabled with paseo environment\n * const app = await createApp({\n * name: 'my-app',\n * logLevel: 'info',\n * });\n *\n * // Custom cloud storage environment\n * const prodApp = await createApp({\n * name: 'my-app',\n * cloudStorage: { environment: 'polkadot' },\n * });\n *\n * // Disable cloud storage entirely\n * const noCloudStorageApp = await createApp({\n * name: 'my-app',\n * cloudStorage: false,\n * });\n *\n * // Connect wallet\n * const { accounts } = await app.wallet.connect();\n *\n * // Use storage\n * await app.localStorage.set('key', 'value');\n *\n * // Use cloud storage (check for null if it might be disabled)\n * if (app.cloudStorage) {\n * const cid = await app.cloudStorage.upload('hello world');\n * }\n * ```\n */\nexport async function createApp(config: AppConfig): Promise<App> {\n // Set log level if specified\n if (config.logLevel) {\n configure({ level: config.logLevel });\n }\n\n log.info(\"Creating Product SDK app\", { name: config.name });\n\n // Initialize storage (container-only - will throw if not in container)\n const localKvStore = await createLocalKvStore({ prefix: config.name });\n\n // Initialize signer manager\n const signerManager = new SignerManager({\n dappName: config.name,\n });\n\n // Initialize cloud storage client (configurable, defaults to paseo).\n //\n // The signer is wrapped lazily so the cloud storage client can be built before\n // an account is selected. Uploads will throw a clear error if no signer\n // is available at submission time. Reads (fetch / fetchJson) don't need\n // a signer, so they work regardless of whether an account is selected --\n // but they are container-only and throw CloudStorageHostUnavailableError\n // outside a host container (no IPFS-gateway fallback).\n const cloudStorageEnabled = config.cloudStorage !== false;\n const cloudStorageEnvironment =\n typeof config.cloudStorage === \"object\" ? config.cloudStorage.environment : \"paseo\";\n const cloudStorageClient = cloudStorageEnabled\n ? await CloudStorageClient.create({\n environment: cloudStorageEnvironment,\n signer: createLazySigner(() => signerManager.getSigner()),\n })\n : null;\n\n if (cloudStorageEnabled) {\n log.debug(\"Cloud Storage client (Bulletin) initialized\", {\n environment: cloudStorageEnvironment,\n });\n } else {\n log.debug(\"Cloud Storage client disabled\");\n }\n\n // Create storage API adapter\n const localStorageApi: LocalStorageApi = {\n get: (key) => localKvStore.get(key),\n set: (key, value) => localKvStore.set(key, value),\n getJSON: <T>(key: string) => localKvStore.getJSON<T>(key),\n setJSON: <T>(key: string, value: T) => localKvStore.setJSON(key, value),\n remove: (key) => localKvStore.remove(key),\n clear: async () => {\n // LocalKvStore doesn't have clear - this is a no-op\n log.debug(\"clear() is not supported in container storage mode\");\n },\n };\n\n // Create wallet API adapter\n const walletApi = createWalletApi(signerManager);\n\n // Create chain API\n const chainApi: ChainApi = {\n getClient(descriptor) {\n log.debug(\"getClient called\", { genesis: descriptor.genesis });\n const client = getClient(descriptor);\n return client.getTypedApi(descriptor);\n },\n\n getRawClient(descriptor) {\n log.debug(\"getRawClient called\", { genesis: descriptor.genesis });\n return getClient(descriptor);\n },\n\n async connect<T extends Record<string, ChainDefinition>>(chains: T) {\n log.debug(\"connect called\", { chains: Object.keys(chains) });\n return createChainClient({ chains });\n },\n\n isConnected(descriptor) {\n return isConnected(descriptor);\n },\n\n destroyAll() {\n log.debug(\"destroyAll called\");\n destroyAll();\n },\n };\n\n // Create Cloud Storage API adapter (null if disabled)\n const cloudStorageApi: CloudStorageApi | null = cloudStorageClient\n ? {\n upload: async (data) => {\n const bytes = typeof data === \"string\" ? new TextEncoder().encode(data) : data;\n try {\n // Explicitly request a DAG-PB manifest so chunked uploads always\n // resolve to a single root CID. Without this, AsyncBulletinClient\n // can return `result.cid: undefined` for chunked-without-manifest\n // uploads — but app consumers expect a CID they can hand to\n // `fetch(cid)`, so treat a missing CID as an error.\n const uploaded = await cloudStorageClient\n .store(bytes)\n .withManifest(true)\n .send();\n if (!uploaded.cid) {\n return err(\n new ProductCloudStorageError(\n \"Cloud storage upload returned no CID despite .withManifest(true). Upstream contract may have shifted — file an issue.\",\n ),\n );\n }\n return ok(uploaded.cid.toString());\n } catch (cause) {\n const message = cause instanceof Error ? cause.message : String(cause);\n return err(new ProductCloudStorageError(message, { cause }));\n }\n },\n fetch: (cid) => cloudStorageClient.fetchBytes(cid),\n computeCid: async (data) => {\n const bytes = typeof data === \"string\" ? new TextEncoder().encode(data) : data;\n const cid = await calculateCid(bytes);\n return cid.toString();\n },\n }\n : null;\n\n log.info(\"Product SDK app created\", {\n name: config.name,\n cloudStorage: cloudStorageEnabled ? cloudStorageEnvironment : \"disabled\",\n });\n\n return {\n wallet: walletApi,\n localStorage: localStorageApi,\n chain: chainApi,\n cloudStorage: cloudStorageApi,\n getAppInfo: () => ({ ...config }),\n };\n}\n\n/**\n * Create wallet API adapter using SignerManager from leaf package\n */\nfunction createWalletApi(signerManager: SignerManager): WalletApi {\n // Track account change subscribers\n const accountChangeSubscribers = new Set<(account: Account | null) => void>();\n\n // Subscribe to signer manager state changes\n signerManager.subscribe((state) => {\n const account = state.selectedAccount\n ? {\n address: state.selectedAccount.address,\n name: state.selectedAccount.name ?? undefined,\n source: state.selectedAccount.source,\n }\n : null;\n for (const callback of accountChangeSubscribers) {\n try {\n callback(account);\n } catch (e) {\n log.warn(\"Account change callback threw\", { error: e });\n }\n }\n });\n\n return {\n async connect(): Promise<{ accounts: Account[] }> {\n const result = await signerManager.connect();\n if (!result.ok) {\n throw new Error(result.error.message);\n }\n return {\n accounts: result.value.map((a) => ({\n address: a.address,\n name: a.name ?? undefined,\n source: a.source,\n })),\n };\n },\n\n async disconnect(): Promise<void> {\n signerManager.disconnect();\n },\n\n getAccounts(): Account[] {\n return signerManager.getState().accounts.map((a) => ({\n address: a.address,\n name: a.name ?? undefined,\n source: a.source,\n }));\n },\n\n getSelectedAccount(): Account | null {\n const selected = signerManager.getState().selectedAccount;\n if (!selected) return null;\n return {\n address: selected.address,\n name: selected.name ?? undefined,\n source: selected.source,\n };\n },\n\n selectAccount(address: string): void {\n signerManager.selectAccount(address);\n },\n\n async signMessage(message: string | Uint8Array): Promise<Uint8Array> {\n const bytes = typeof message === \"string\" ? new TextEncoder().encode(message) : message;\n const result = await signerManager.signRaw(bytes);\n if (!result.ok) {\n throw new Error(result.error.message);\n }\n return result.value;\n },\n\n async signMessageWithDotNsIdentity(args) {\n const message =\n typeof args.message === \"string\"\n ? new TextEncoder().encode(args.message)\n : args.message;\n const accountsProvider = await getHostAccountsProvider();\n const username = args.username ?? (await getPrimaryUsername(accountsProvider));\n\n // Reuse an already-connected People chain when the caller has\n // wired one up via `app.chain.connect({ ..., <name>: peopleChain })`.\n // Fall back to opening a transient connection so first-time users\n // don't have to think about chain lifecycles. The chain-client\n // module caches by genesis fingerprint, so subsequent\n // signMessageWithDotNsIdentity calls share whichever connection\n // got established first.\n const peopleClient = isConnected(args.peopleChain)\n ? getClient(args.peopleChain)\n : await createChainClient({ chains: { people: args.peopleChain } }).then(\n (c) => c.raw.people,\n );\n // PAPI's `TypedApi.query.X.Y.getValue` is variadic\n // (`...args: [...WithCallOptions<Args>]`) so it's not assignable\n // to our deliberately-narrow `(key) => Promise<...>` shape. The\n // resolver only ever passes the storage key; the cast adapts the\n // wider PAPI surface to the minimum we depend on.\n const peopleApi = peopleClient.getTypedApi(\n args.peopleChain,\n ) as unknown as PeopleUsernameQueryApi;\n const owner = await resolvePeopleUsernameOwner(username, peopleApi);\n if (!owner) {\n throw new Error(`No account owns DotNS username \"${username}\"`);\n }\n\n const publicKey = accountIdBytes(owner);\n // This field is hex, not the SS58 the resolver now returns. Converted\n // here and not in the try, so an address failure is not reported as a\n // signing failure.\n const accountId = accountIdBytesToHex(publicKey);\n const signer = accountsProvider.getLegacyAccountSigner({ publicKey, name: username });\n\n try {\n return {\n username,\n accountId,\n signature: await signer.signBytes(message),\n };\n } catch (cause) {\n throw new Error(\n `Failed to sign with DotNS username \"${username}\": ${\n cause instanceof Error ? cause.message : String(cause)\n }`,\n { cause },\n );\n }\n },\n\n onAccountChange(callback: (account: Account | null) => void): () => void {\n accountChangeSubscribers.add(callback);\n return () => accountChangeSubscribers.delete(callback);\n },\n\n getProductAccount(): Account | null {\n // Product accounts require async call - this sync API can't support it properly\n // Users should use SignerManager.getProductAccount() directly\n log.warn(\n \"getProductAccount() is deprecated - use SignerManager.getProductAccount() directly\",\n );\n return null;\n },\n\n getAnonymousAlias(): string | null {\n // Anonymous aliases require async call - this sync API can't support it properly\n // Users should use SignerManager.getProductAccountAlias() directly\n log.warn(\n \"getAnonymousAlias() is deprecated - use SignerManager.getProductAccountAlias() directly\",\n );\n return null;\n },\n\n async createProof(_message: Uint8Array): Promise<Uint8Array> {\n // Ring VRF proofs require SignerManager.createRingVRFProof() directly\n throw new Error(\n \"createProof() is not implemented in the App API. \" +\n \"Use SignerManager.createRingVRFProof() directly.\",\n );\n },\n };\n}\n\ntype HostAccountsProvider = NonNullable<Awaited<ReturnType<typeof getAccountsProvider>>>;\n\nasync function getHostAccountsProvider(): Promise<HostAccountsProvider> {\n const provider = await getAccountsProvider();\n if (!provider) {\n throw new Error(\"Host accounts provider is not available\");\n }\n return provider;\n}\n\nasync function getPrimaryUsername(accountsProvider: HostAccountsProvider): Promise<string> {\n const result = await accountsProvider.getUserId().match(\n (value) => value,\n (err) => {\n throw err;\n },\n );\n const username = result.primaryUsername.trim();\n if (!username) {\n throw new Error(\"Host identity did not provide a primary DotNS username\");\n }\n return username;\n}\n"]}