@kheopskit/core 6.0.0-rc.0 → 6.0.0-rc.2

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.
Files changed (41) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/MIGRATING_TO_V6.md +56 -39
  3. package/README.md +48 -35
  4. package/dist/ethereum.d.mts +2 -1
  5. package/dist/ethereum.d.ts +2 -1
  6. package/dist/index.d.mts +1 -1
  7. package/dist/index.d.ts +1 -1
  8. package/dist/index.js +21 -16
  9. package/dist/index.js.map +1 -1
  10. package/dist/index.mjs +22 -17
  11. package/dist/index.mjs.map +1 -1
  12. package/dist/internal.d.mts +1 -1
  13. package/dist/internal.d.ts +1 -1
  14. package/dist/polkadot.d.mts +1 -1
  15. package/dist/polkadot.d.ts +1 -1
  16. package/dist/polkadot.js +24 -41
  17. package/dist/polkadot.js.map +1 -1
  18. package/dist/polkadot.mjs +25 -42
  19. package/dist/polkadot.mjs.map +1 -1
  20. package/dist/registryReads-BpHsWLd7.js +38 -0
  21. package/dist/registryReads-BpHsWLd7.js.map +1 -0
  22. package/dist/{pageLoad-BZ7o8p5R.mjs → registryReads-BvtznSo3.mjs} +12 -3
  23. package/dist/registryReads-BvtznSo3.mjs.map +1 -0
  24. package/dist/solana.d.mts +2 -1
  25. package/dist/solana.d.ts +2 -1
  26. package/dist/solana.js +1 -0
  27. package/dist/solana.js.map +1 -1
  28. package/dist/solana.mjs +1 -0
  29. package/dist/solana.mjs.map +1 -1
  30. package/dist/testing.d.mts +1 -1
  31. package/dist/testing.d.ts +1 -1
  32. package/dist/types-BWHvNFlN.js.map +1 -1
  33. package/dist/{types-Dkyt9bf4.d.ts → types-DCaL9bIc.d.mts} +45 -29
  34. package/dist/{types-Dkyt9bf4.d.mts → types-DCaL9bIc.d.ts} +45 -29
  35. package/dist/types-Yor672md.mjs.map +1 -1
  36. package/dist/walletconnect.d.mts +1 -1
  37. package/dist/walletconnect.d.ts +1 -1
  38. package/package.json +1 -1
  39. package/dist/pageLoad-BZ7o8p5R.mjs.map +0 -1
  40. package/dist/pageLoad-D1wLeuFG.js +0 -28
  41. package/dist/pageLoad-D1wLeuFG.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"solana.mjs","names":[],"sources":["../src/api/solana/chains.ts","../src/api/solana/signer.ts","../src/api/solana/accounts.ts","../src/api/solana/wallets.ts","../src/api/solana/plugin.ts"],"sourcesContent":["import { KheopskitError } from \"../errors\";\n\n/**\n * Wallet Standard chain identifiers for Solana clusters.\n *\n * These are the values wallets advertise in their `chains` arrays and the\n * values kheopskit accepts in `solana({ chain })`.\n *\n * @see https://github.com/anza-xyz/wallet-standard\n */\nexport type SolanaChainId =\n\t| \"solana:mainnet\"\n\t| \"solana:devnet\"\n\t| \"solana:testnet\"\n\t| \"solana:localnet\";\n\nexport const DEFAULT_SOLANA_CHAIN: SolanaChainId = \"solana:mainnet\";\n\nconst SOLANA_CHAIN_IDS = [\n\t\"solana:mainnet\",\n\t\"solana:devnet\",\n\t\"solana:testnet\",\n\t\"solana:localnet\",\n] as const satisfies SolanaChainId[];\n\nexport const isSolanaChainId = (value: unknown): value is SolanaChainId =>\n\ttypeof value === \"string\" &&\n\t(SOLANA_CHAIN_IDS as readonly string[]).includes(value);\n\n/**\n * Maps Wallet Standard chain ids to the CAIP-2 chain ids used by WalletConnect.\n *\n * CAIP-2 ids use the first 32 characters of the cluster's genesis hash as the\n * reference. The values below match `@reown/appkit/networks` (solana,\n * solanaDevnet, solanaTestnet), which is what AppKit puts in session namespaces.\n *\n * `localnet` has no canonical CAIP-2 id and cannot be used over WalletConnect.\n */\nconst SOLANA_CHAIN_TO_CAIP2 = {\n\t\"solana:mainnet\": \"solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp\",\n\t\"solana:devnet\": \"solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1\",\n\t\"solana:testnet\": \"solana:4uhcVJyU9pJkvQyS88uRDiswHXSCkY3z\",\n} as const satisfies Partial<Record<SolanaChainId, string>>;\n\n/**\n * Returns the CAIP-2 chain id for a Solana chain, for use in WalletConnect\n * requests and when reading WalletConnect session namespaces.\n *\n * @throws if the chain has no canonical CAIP-2 id (e.g. localnet)\n */\nexport const getSolanaCaip2 = (chain: SolanaChainId): string => {\n\tconst caip2 = (SOLANA_CHAIN_TO_CAIP2 as Record<string, string | undefined>)[\n\t\tchain\n\t];\n\tif (!caip2)\n\t\tthrow new KheopskitError(\n\t\t\t\"UNSUPPORTED_CHAIN\",\n\t\t\t`Solana chain \"${chain}\" cannot be used over WalletConnect (no CAIP-2 id).`,\n\t\t);\n\treturn caip2;\n};\n\n/**\n * AppKit's *deprecated* CAIP-2 cluster ids (its `deprecatedCaipNetworkId`\n * values). Some wallets still place accounts in the WC session under these, so\n * map them back to a {@link SolanaChainId} instead of silently falling back to\n * the configured chain. Requests always use the current ids from\n * {@link SOLANA_CHAIN_TO_CAIP2}; testnet has no deprecated id.\n */\nconst DEPRECATED_CAIP2_TO_SOLANA_CHAIN: Record<string, SolanaChainId> = {\n\t\"solana:4sGjMW1sUnHzSxGspuhpqLDx6wiyjNtZ\": \"solana:mainnet\",\n\t\"solana:8E9rvCKLFQia2Y35HXjjpWzj8weVo44K\": \"solana:devnet\",\n};\n\nconst CAIP2_TO_SOLANA_CHAIN: Record<string, SolanaChainId> = {\n\t...Object.fromEntries(\n\t\tObject.entries(SOLANA_CHAIN_TO_CAIP2).map(([chain, caip2]) => [\n\t\t\tcaip2,\n\t\t\tchain as SolanaChainId,\n\t\t]),\n\t),\n\t...DEPRECATED_CAIP2_TO_SOLANA_CHAIN,\n};\n\n/**\n * Maps a CAIP-2 chain id (as found in WalletConnect session namespaces) back to\n * its {@link SolanaChainId}, or `undefined` if it isn't a recognised cluster.\n */\nexport const getSolanaChainIdFromCaip2 = (\n\tcaip2: string,\n): SolanaChainId | undefined => CAIP2_TO_SOLANA_CHAIN[caip2];\n","import {\n\taddress,\n\tgetBase58Decoder,\n\tgetBase58Encoder,\n\tgetBase64Decoder,\n\tgetBase64Encoder,\n\tgetTransactionDecoder,\n\tgetTransactionEncoder,\n\ttype SignableMessage,\n\ttype SignatureBytes,\n\ttype TransactionModifyingSigner,\n} from \"@solana/kit\";\nimport type {\n\tSolanaSignAndSendTransactionFeature,\n\tSolanaSignMessageFeature,\n\tSolanaSignTransactionFeature,\n} from \"@solana/wallet-standard-features\";\nimport type {\n\tWalletAccount,\n\tWallet as WalletStandardWallet,\n} from \"@wallet-standard/base\";\nimport { KheopskitError } from \"../errors\";\nimport type { WalletConnectProvider } from \"../types\";\nimport { getSolanaCaip2, type SolanaChainId } from \"./chains\";\nimport type { SolanaSigner } from \"./types\";\n\nconst SOLANA_SIGN_MESSAGE = \"solana:signMessage\";\nconst SOLANA_SIGN_TRANSACTION = \"solana:signTransaction\";\nconst SOLANA_SIGN_AND_SEND_TRANSACTION = \"solana:signAndSendTransaction\";\n\ntype SignMessageApi = SolanaSignMessageFeature[typeof SOLANA_SIGN_MESSAGE];\ntype SignTransactionApi =\n\tSolanaSignTransactionFeature[typeof SOLANA_SIGN_TRANSACTION];\ntype SignAndSendApi =\n\tSolanaSignAndSendTransactionFeature[typeof SOLANA_SIGN_AND_SEND_TRANSACTION];\n\n/** The branded array type expected back from a transaction-modifying signer. */\ntype SignedTransactions = Awaited<\n\tReturnType<TransactionModifyingSigner[\"modifyAndSignTransactions\"]>\n>;\n\nconst requireFeature = <T>(wallet: WalletStandardWallet, name: string): T => {\n\tconst feature = (wallet.features as Record<string, unknown>)[name];\n\tif (!feature)\n\t\tthrow new KheopskitError(\n\t\t\t\"FEATURE_NOT_SUPPORTED\",\n\t\t\t`wallet \"${wallet.name}\" does not support ${name}`,\n\t\t);\n\treturn feature as T;\n};\n\n/**\n * Builds a {@link SolanaSigner} backed by a Wallet Standard wallet's\n * `solana:*` features (injected wallets).\n *\n * Transactions are exchanged as wire bytes; messages and signatures as raw\n * bytes. Calling a method whose feature the wallet does not advertise throws.\n */\nexport const createInjectedSolanaSigner = (\n\twallet: WalletStandardWallet,\n\taccount: WalletAccount,\n\tchain: SolanaChainId,\n): SolanaSigner => {\n\tconst signerAddress = address(account.address);\n\n\treturn {\n\t\taddress: signerAddress,\n\t\tmodifyAndSignMessages: async (messages) => {\n\t\t\tconst feature = requireFeature<SignMessageApi>(\n\t\t\t\twallet,\n\t\t\t\tSOLANA_SIGN_MESSAGE,\n\t\t\t);\n\t\t\tconst outputs = await feature.signMessage(\n\t\t\t\t...messages.map((m) => ({ account, message: m.content })),\n\t\t\t);\n\t\t\treturn messages.map((m, i) => {\n\t\t\t\tconst output = outputs[i];\n\t\t\t\tif (!output) throw new Error(\"[kheopskit] missing signMessage output\");\n\t\t\t\treturn {\n\t\t\t\t\tcontent: output.signedMessage,\n\t\t\t\t\tsignatures: {\n\t\t\t\t\t\t...m.signatures,\n\t\t\t\t\t\t[signerAddress]: output.signature as SignatureBytes,\n\t\t\t\t\t},\n\t\t\t\t} satisfies SignableMessage;\n\t\t\t});\n\t\t},\n\t\tmodifyAndSignTransactions: async (transactions) => {\n\t\t\tconst feature = requireFeature<SignTransactionApi>(\n\t\t\t\twallet,\n\t\t\t\tSOLANA_SIGN_TRANSACTION,\n\t\t\t);\n\t\t\tconst encoder = getTransactionEncoder();\n\t\t\tconst decoder = getTransactionDecoder();\n\t\t\tconst outputs = await feature.signTransaction(\n\t\t\t\t...transactions.map((tx) => ({\n\t\t\t\t\taccount,\n\t\t\t\t\ttransaction: new Uint8Array(encoder.encode(tx)),\n\t\t\t\t\tchain,\n\t\t\t\t})),\n\t\t\t);\n\t\t\treturn transactions.map((_tx, i) => {\n\t\t\t\tconst output = outputs[i];\n\t\t\t\tif (!output)\n\t\t\t\t\tthrow new Error(\"[kheopskit] missing signTransaction output\");\n\t\t\t\treturn decoder.decode(output.signedTransaction);\n\t\t\t}) as unknown as SignedTransactions;\n\t\t},\n\t\tsignAndSendTransactions: async (transactions) => {\n\t\t\tconst feature = requireFeature<SignAndSendApi>(\n\t\t\t\twallet,\n\t\t\t\tSOLANA_SIGN_AND_SEND_TRANSACTION,\n\t\t\t);\n\t\t\tconst encoder = getTransactionEncoder();\n\t\t\tconst outputs = await feature.signAndSendTransaction(\n\t\t\t\t...transactions.map((tx) => ({\n\t\t\t\t\taccount,\n\t\t\t\t\ttransaction: new Uint8Array(encoder.encode(tx)),\n\t\t\t\t\tchain,\n\t\t\t\t})),\n\t\t\t);\n\t\t\treturn outputs.map((output) => output.signature as SignatureBytes);\n\t\t},\n\t};\n};\n\n/**\n * Builds a {@link SolanaSigner} backed by a WalletConnect session, adapting the\n * `solana_*` RPC methods to the same interface as the injected signer.\n *\n * The CAIP-2 chain id is derived from {@link SolanaChainId}; transactions are\n * base64-encoded and messages/signatures base58-encoded, per the WalletConnect\n * Solana spec.\n */\nexport const createWalletConnectSolanaSigner = (\n\tprovider: WalletConnectProvider,\n\taccountAddress: string,\n\tchain: SolanaChainId,\n): SolanaSigner => {\n\tconst signerAddress = address(accountAddress);\n\n\tconst request = <T>(method: string, params: unknown): Promise<T> => {\n\t\tif (!provider.session)\n\t\t\tthrow new KheopskitError(\"NO_SESSION\", \"No session found\");\n\t\t// Resolved lazily (not at signer construction) so building a signer for a\n\t\t// cluster without a CAIP-2 id (e.g. localnet) doesn't throw until a request\n\t\t// is actually attempted.\n\t\treturn provider.client.request<T>({\n\t\t\ttopic: provider.session.topic,\n\t\t\tchainId: getSolanaCaip2(chain),\n\t\t\trequest: { method, params },\n\t\t});\n\t};\n\n\treturn {\n\t\taddress: signerAddress,\n\t\tmodifyAndSignMessages: async (messages) => {\n\t\t\t// @solana/kit naming is the inverse of the value direction here: a\n\t\t\t// Decoder turns bytes -> string (so `toBase58` produces a base58 string),\n\t\t\t// an Encoder turns string -> bytes (so `fromBase58` parses one).\n\t\t\tconst toBase58 = getBase58Decoder();\n\t\t\tconst fromBase58 = getBase58Encoder();\n\t\t\treturn Promise.all(\n\t\t\t\tmessages.map(async (m) => {\n\t\t\t\t\tconst { signature } = await request<{ signature: string }>(\n\t\t\t\t\t\t\"solana_signMessage\",\n\t\t\t\t\t\t{ pubkey: accountAddress, message: toBase58.decode(m.content) },\n\t\t\t\t\t);\n\t\t\t\t\treturn {\n\t\t\t\t\t\tcontent: m.content,\n\t\t\t\t\t\tsignatures: {\n\t\t\t\t\t\t\t...m.signatures,\n\t\t\t\t\t\t\t[signerAddress]: fromBase58.encode(signature) as SignatureBytes,\n\t\t\t\t\t\t},\n\t\t\t\t\t} satisfies SignableMessage;\n\t\t\t\t}),\n\t\t\t);\n\t\t},\n\t\tmodifyAndSignTransactions: async (transactions) => {\n\t\t\tconst encoder = getTransactionEncoder();\n\t\t\tconst decoder = getTransactionDecoder();\n\t\t\tconst toBase64 = getBase64Decoder();\n\t\t\tconst fromBase64 = getBase64Encoder();\n\t\t\tconst fromBase58 = getBase58Encoder();\n\t\t\tconst signed = await Promise.all(\n\t\t\t\ttransactions.map(async (tx) => {\n\t\t\t\t\tconst result = await request<{\n\t\t\t\t\t\ttransaction?: string;\n\t\t\t\t\t\tsignature?: string;\n\t\t\t\t\t}>(\"solana_signTransaction\", {\n\t\t\t\t\t\ttransaction: toBase64.decode(encoder.encode(tx)),\n\t\t\t\t\t});\n\t\t\t\t\t// Wallets may return either the full signed transaction (base64)\n\t\t\t\t\t// or just the signature (base58) to merge into the original.\n\t\t\t\t\tif (result.transaction)\n\t\t\t\t\t\treturn decoder.decode(fromBase64.encode(result.transaction));\n\t\t\t\t\tif (result.signature)\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t...tx,\n\t\t\t\t\t\t\tsignatures: {\n\t\t\t\t\t\t\t\t...tx.signatures,\n\t\t\t\t\t\t\t\t[signerAddress]: fromBase58.encode(\n\t\t\t\t\t\t\t\t\tresult.signature,\n\t\t\t\t\t\t\t\t) as SignatureBytes,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t};\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\"[kheopskit] solana_signTransaction returned no transaction or signature\",\n\t\t\t\t\t);\n\t\t\t\t}),\n\t\t\t);\n\t\t\treturn signed as unknown as SignedTransactions;\n\t\t},\n\t\tsignAndSendTransactions: async (transactions) => {\n\t\t\tconst encoder = getTransactionEncoder();\n\t\t\tconst toBase64 = getBase64Decoder();\n\t\t\tconst fromBase58 = getBase58Encoder();\n\t\t\treturn Promise.all(\n\t\t\t\ttransactions.map(async (tx) => {\n\t\t\t\t\tconst { signature } = await request<{ signature: string }>(\n\t\t\t\t\t\t\"solana_signAndSendTransaction\",\n\t\t\t\t\t\t{ transaction: toBase64.decode(encoder.encode(tx)) },\n\t\t\t\t\t);\n\t\t\t\t\treturn fromBase58.encode(signature) as SignatureBytes;\n\t\t\t\t}),\n\t\t\t);\n\t\t},\n\t};\n};\n","import type { StandardEventsFeature } from \"@wallet-standard/features\";\nimport { Observable, of, shareReplay } from \"rxjs\";\nimport { getWalletAccountId } from \"../../utils\";\nimport { getCachedObservable$ } from \"../../utils/getCachedObservable\";\nimport {\n\tcreatePlatformAccounts$,\n\tgetCaip10Addresses,\n\tgetSessionCaip10s,\n\tgetWalletConnectSessionAccounts$,\n\ttype LiveWalletConnectWallet,\n} from \"../platformAccounts\";\nimport type { WalletConnectWallet } from \"../types\";\nimport { getSolanaChainIdFromCaip2, type SolanaChainId } from \"./chains\";\nimport {\n\tcreateInjectedSolanaSigner,\n\tcreateWalletConnectSolanaSigner,\n} from \"./signer\";\nimport type {\n\tSolanaAccount,\n\tSolanaInjectedWallet,\n\tSolanaWallet,\n} from \"./types\";\n\ntype StandardEventsApi = StandardEventsFeature[\"standard:events\"];\n\nconst getInjectedWalletAccounts$ = (\n\twallet: SolanaInjectedWallet,\n\tchain: SolanaChainId,\n): Observable<SolanaAccount[]> => {\n\tif (!wallet.isConnected) return of([]);\n\n\treturn getCachedObservable$(`accounts:${wallet.id}:${chain}`, () =>\n\t\tnew Observable<SolanaAccount[]>((subscriber) => {\n\t\t\tconst standardWallet = wallet.wallet;\n\n\t\t\tconst buildAccounts = (): SolanaAccount[] =>\n\t\t\t\tstandardWallet.accounts.map(\n\t\t\t\t\t(account): SolanaAccount => ({\n\t\t\t\t\t\tid: getWalletAccountId(wallet.id, account.address),\n\t\t\t\t\t\tplatform: \"solana\",\n\t\t\t\t\t\taddress: account.address,\n\t\t\t\t\t\tchains: wallet.chains,\n\t\t\t\t\t\tsigner: createInjectedSolanaSigner(standardWallet, account, chain),\n\t\t\t\t\t\tgetSigner: (c) =>\n\t\t\t\t\t\t\tcreateInjectedSolanaSigner(standardWallet, account, c),\n\t\t\t\t\t\twalletName: wallet.name,\n\t\t\t\t\t\twalletId: wallet.id,\n\t\t\t\t\t}),\n\t\t\t\t);\n\n\t\t\tsubscriber.next(buildAccounts());\n\n\t\t\t// Re-emit when the wallet's authorized accounts change.\n\t\t\tconst eventsFeature = (\n\t\t\t\tstandardWallet.features as Record<string, unknown>\n\t\t\t)[\"standard:events\"] as StandardEventsApi | undefined;\n\t\t\tconst off = eventsFeature?.on(\"change\", () =>\n\t\t\t\tsubscriber.next(buildAccounts()),\n\t\t\t);\n\n\t\t\treturn () => {\n\t\t\t\toff?.();\n\t\t\t};\n\t\t}).pipe(shareReplay({ refCount: true, bufferSize: 1 })),\n\t);\n};\n\nconst getWalletConnectAccounts$ = (\n\twallet: LiveWalletConnectWallet,\n\tchain: SolanaChainId,\n): Observable<SolanaAccount[]> =>\n\tgetWalletConnectSessionAccounts$({\n\t\twallet,\n\t\tplatform: \"solana\",\n\t\tnamespace: \"solana\",\n\t\tcacheKey: `accounts:${wallet.id}:solana:${chain}`,\n\t\tbuildAccounts: (provider) => {\n\t\t\tconst session = provider.session;\n\t\t\tif (!session) return [];\n\n\t\t\tconst solanaCaip10s = getSessionCaip10s(session, \"solana\");\n\t\t\tconst addresses = getCaip10Addresses(solanaCaip10s);\n\n\t\t\t// Clusters the session actually advertises (\"solana:<chainRef>\" from\n\t\t\t// each CAIP-10 entry), mapped back to SolanaChainId. Falls back to the\n\t\t\t// configured chain when none are recognised.\n\t\t\tconst advertisedChains = [\n\t\t\t\t...new Set(\n\t\t\t\t\tsolanaCaip10s\n\t\t\t\t\t\t.map((account) => account.split(\":\").slice(0, 2).join(\":\"))\n\t\t\t\t\t\t.map(getSolanaChainIdFromCaip2)\n\t\t\t\t\t\t.filter((c): c is SolanaChainId => !!c),\n\t\t\t\t),\n\t\t\t];\n\t\t\tconst chains = advertisedChains.length ? advertisedChains : [chain];\n\n\t\t\treturn addresses.map(\n\t\t\t\t(accountAddress): SolanaAccount => ({\n\t\t\t\t\tid: getWalletAccountId(wallet.id, accountAddress),\n\t\t\t\t\tplatform: \"solana\",\n\t\t\t\t\taddress: accountAddress,\n\t\t\t\t\tchains,\n\t\t\t\t\tsigner: createWalletConnectSolanaSigner(\n\t\t\t\t\t\tprovider,\n\t\t\t\t\t\taccountAddress,\n\t\t\t\t\t\tchain,\n\t\t\t\t\t),\n\t\t\t\t\tgetSigner: (c) =>\n\t\t\t\t\t\tcreateWalletConnectSolanaSigner(provider, accountAddress, c),\n\t\t\t\t\twalletName: wallet.name,\n\t\t\t\t\twalletId: wallet.id,\n\t\t\t\t}),\n\t\t\t);\n\t\t},\n\t});\n\nexport const getSolanaAccounts$ = (\n\tsolanaWallets$: Observable<(SolanaWallet | WalletConnectWallet)[]>,\n\tsolanaChain: SolanaChainId,\n) =>\n\tcreatePlatformAccounts$({\n\t\twallets$: solanaWallets$,\n\t\tgetInjectedAccounts$: (wallet) =>\n\t\t\tgetInjectedWalletAccounts$(wallet, solanaChain),\n\t\tgetWalletConnectAccounts$: (wallet) =>\n\t\t\tgetWalletConnectAccounts$(wallet, solanaChain),\n\t\t// Re-emit when the advertised clusters change, not just on id changes.\n\t\taccountChangeKey: (account) => `${account.id}|${account.chains.join(\",\")}`,\n\t});\n","import { getWallets } from \"@wallet-standard/app\";\nimport type { Wallet as WalletStandardWallet } from \"@wallet-standard/base\";\nimport type {\n\tStandardConnectFeature,\n\tStandardDisconnectFeature,\n} from \"@wallet-standard/features\";\nimport { Observable, shareReplay } from \"rxjs\";\nimport { clearCachedObservablesByPrefix } from \"../../utils/getCachedObservable\";\nimport { getWalletId } from \"../../utils/WalletId\";\nimport {\n\tisEip1193UserRejection,\n\tisRefusalMessage,\n\tKheopskitError,\n} from \"../errors\";\nimport { createInjectedWallets$ } from \"../injectedWallets\";\nimport type { KheopskitStore } from \"../store\";\nimport { isSolanaChainId, type SolanaChainId } from \"./chains\";\nimport type { SolanaInjectedWallet } from \"./types\";\n\ntype ConnectApi = StandardConnectFeature[\"standard:connect\"];\ntype DisconnectApi = StandardDisconnectFeature[\"standard:disconnect\"];\n\nconst SOLANA_NAMESPACE_PREFIX = \"solana:\";\n\n/** A Wallet Standard wallet is Solana-capable if it advertises a solana chain or feature. */\nconst isSolanaWallet = (wallet: WalletStandardWallet): boolean =>\n\twallet.chains.some((chain) => chain.startsWith(SOLANA_NAMESPACE_PREFIX)) ||\n\tObject.keys(wallet.features).some((feature) =>\n\t\tfeature.startsWith(SOLANA_NAMESPACE_PREFIX),\n\t);\n\nconst getSolanaChains = (wallet: WalletStandardWallet): SolanaChainId[] =>\n\twallet.chains.filter((chain): chain is SolanaChainId =>\n\t\tisSolanaChainId(chain),\n\t);\n\n/**\n * Observable of Solana-capable Wallet Standard wallets, updated as wallets\n * register/unregister. Returns an empty array during SSR.\n */\nconst walletStandardWallets$ = new Observable<readonly WalletStandardWallet[]>(\n\t(subscriber) => {\n\t\t// Guard against SSR - the Wallet Standard registry requires browser APIs\n\t\tif (typeof window === \"undefined\") {\n\t\t\tsubscriber.next([]);\n\t\t\treturn () => {};\n\t\t}\n\n\t\tconst { get, on } = getWallets();\n\t\tconst emit = () => subscriber.next(get().filter(isSolanaWallet));\n\n\t\temit();\n\n\t\tconst offRegister = on(\"register\", emit);\n\t\tconst offUnregister = on(\"unregister\", emit);\n\n\t\treturn () => {\n\t\t\toffRegister();\n\t\t\toffUnregister();\n\t\t};\n\t},\n).pipe(shareReplay({ refCount: true, bufferSize: 1 }));\n\n// The shared WalletConnect connector is emitted once by core (see\n// `getWallets$`), not per platform — so this returns only injected wallets.\nexport const getSolanaWallets$ = (store: KheopskitStore) =>\n\tcreateInjectedWallets$<WalletStandardWallet, SolanaInjectedWallet>(store, {\n\t\tsources$: walletStandardWallets$,\n\t\tgetWalletId: (wallet) => getWalletId(\"solana\", wallet.name),\n\t\tconnect: async (wallet, walletId) => {\n\t\t\tconst feature = (wallet.features as Record<string, unknown>)[\n\t\t\t\t\"standard:connect\"\n\t\t\t] as ConnectApi | undefined;\n\t\t\tif (!feature)\n\t\t\t\tthrow new KheopskitError(\n\t\t\t\t\t\"FEATURE_NOT_SUPPORTED\",\n\t\t\t\t\t`wallet ${walletId} does not support standard:connect`,\n\t\t\t\t\t{ walletId },\n\t\t\t\t);\n\n\t\t\tawait feature.connect();\n\t\t},\n\t\tisUserRejection: (error) =>\n\t\t\tisEip1193UserRejection(error) || isRefusalMessage(error),\n\t\t// standard:disconnect is an optional feature. Await it when present so a\n\t\t// failed disconnect rejects the returned promise; if absent we still\n\t\t// clear local state.\n\t\tdisconnect: async (wallet) => {\n\t\t\tconst feature = (wallet.features as Record<string, unknown>)[\n\t\t\t\t\"standard:disconnect\"\n\t\t\t] as DisconnectApi | undefined;\n\t\t\tawait feature?.disconnect();\n\t\t},\n\t\t// Drop cached account observables for this wallet so a later reconnect\n\t\t// rebuilds them against the current wallet handle, not a stale closure.\n\t\tonDisconnected: (walletId) =>\n\t\t\tclearCachedObservablesByPrefix(`accounts:${walletId}:`),\n\t\tbuildWallet: ({\n\t\t\tsource: wallet,\n\t\t\twalletId,\n\t\t\tstatus,\n\t\t\terror,\n\t\t\tisConnected,\n\t\t\tconnect,\n\t\t\tdisconnect,\n\t\t}) => ({\n\t\t\tplatform: \"solana\",\n\t\t\ttype: \"injected\",\n\t\t\tid: walletId,\n\t\t\tsourceId: wallet.name,\n\t\t\twallet,\n\t\t\tchains: getSolanaChains(wallet),\n\t\t\tname: wallet.name,\n\t\t\ticon: wallet.icon,\n\t\t\tstatus,\n\t\t\terror,\n\t\t\tisConnected,\n\t\t\tconnect,\n\t\t\tdisconnect,\n\t\t}),\n\t});\n","import type { KheopskitPlatform, PlatformContext } from \"../types\";\nimport { getSolanaAccounts$ } from \"./accounts\";\nimport {\n\tDEFAULT_SOLANA_CHAIN,\n\tisSolanaChainId,\n\ttype SolanaChainId,\n} from \"./chains\";\nimport type { SolanaAccount, SolanaWallet } from \"./types\";\nimport { getSolanaWallets$ } from \"./wallets\";\n\nexport type SolanaPluginOptions = {\n\t/**\n\t * Solana cluster that account signers target. Each account also exposes a\n\t * `getSigner(chain)` factory for targeting another cluster.\n\t *\n\t * @default \"solana:mainnet\"\n\t */\n\tchain?: SolanaChainId;\n};\n\n/**\n * Solana platform plugin. Pass to `getKheopskit$({ platforms: [solana()] })`.\n *\n * @example\n * ```ts\n * import { solana } from \"@kheopskit/core/solana\";\n * solana({ chain: \"solana:mainnet\" });\n * ```\n */\nexport const solana = (\n\toptions: SolanaPluginOptions = {},\n): KheopskitPlatform<\"solana\", SolanaWallet, SolanaAccount> => {\n\tconst chain = options.chain ?? DEFAULT_SOLANA_CHAIN;\n\n\tif (!isSolanaChainId(chain)) {\n\t\tconsole.warn(\n\t\t\t`[kheopskit] Unknown solana chain: ${JSON.stringify(chain)}. Valid values: \"solana:mainnet\", \"solana:devnet\", \"solana:testnet\", \"solana:localnet\".`,\n\t\t);\n\t}\n\n\treturn {\n\t\tplatform: \"solana\",\n\t\tgetWallets$: (ctx: PlatformContext) => getSolanaWallets$(ctx.store),\n\t\tgetAccounts$: (wallets$) => getSolanaAccounts$(wallets$, chain),\n\t};\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"solana.mjs","names":[],"sources":["../src/api/solana/chains.ts","../src/api/solana/signer.ts","../src/api/solana/accounts.ts","../src/api/solana/wallets.ts","../src/api/solana/plugin.ts"],"sourcesContent":["import { KheopskitError } from \"../errors\";\n\n/**\n * Wallet Standard chain identifiers for Solana clusters.\n *\n * These are the values wallets advertise in their `chains` arrays and the\n * values kheopskit accepts in `solana({ chain })`.\n *\n * @see https://github.com/anza-xyz/wallet-standard\n */\nexport type SolanaChainId =\n\t| \"solana:mainnet\"\n\t| \"solana:devnet\"\n\t| \"solana:testnet\"\n\t| \"solana:localnet\";\n\nexport const DEFAULT_SOLANA_CHAIN: SolanaChainId = \"solana:mainnet\";\n\nconst SOLANA_CHAIN_IDS = [\n\t\"solana:mainnet\",\n\t\"solana:devnet\",\n\t\"solana:testnet\",\n\t\"solana:localnet\",\n] as const satisfies SolanaChainId[];\n\nexport const isSolanaChainId = (value: unknown): value is SolanaChainId =>\n\ttypeof value === \"string\" &&\n\t(SOLANA_CHAIN_IDS as readonly string[]).includes(value);\n\n/**\n * Maps Wallet Standard chain ids to the CAIP-2 chain ids used by WalletConnect.\n *\n * CAIP-2 ids use the first 32 characters of the cluster's genesis hash as the\n * reference. The values below match `@reown/appkit/networks` (solana,\n * solanaDevnet, solanaTestnet), which is what AppKit puts in session namespaces.\n *\n * `localnet` has no canonical CAIP-2 id and cannot be used over WalletConnect.\n */\nconst SOLANA_CHAIN_TO_CAIP2 = {\n\t\"solana:mainnet\": \"solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp\",\n\t\"solana:devnet\": \"solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1\",\n\t\"solana:testnet\": \"solana:4uhcVJyU9pJkvQyS88uRDiswHXSCkY3z\",\n} as const satisfies Partial<Record<SolanaChainId, string>>;\n\n/**\n * Returns the CAIP-2 chain id for a Solana chain, for use in WalletConnect\n * requests and when reading WalletConnect session namespaces.\n *\n * @throws if the chain has no canonical CAIP-2 id (e.g. localnet)\n */\nexport const getSolanaCaip2 = (chain: SolanaChainId): string => {\n\tconst caip2 = (SOLANA_CHAIN_TO_CAIP2 as Record<string, string | undefined>)[\n\t\tchain\n\t];\n\tif (!caip2)\n\t\tthrow new KheopskitError(\n\t\t\t\"UNSUPPORTED_CHAIN\",\n\t\t\t`Solana chain \"${chain}\" cannot be used over WalletConnect (no CAIP-2 id).`,\n\t\t);\n\treturn caip2;\n};\n\n/**\n * AppKit's *deprecated* CAIP-2 cluster ids (its `deprecatedCaipNetworkId`\n * values). Some wallets still place accounts in the WC session under these, so\n * map them back to a {@link SolanaChainId} instead of silently falling back to\n * the configured chain. Requests always use the current ids from\n * {@link SOLANA_CHAIN_TO_CAIP2}; testnet has no deprecated id.\n */\nconst DEPRECATED_CAIP2_TO_SOLANA_CHAIN: Record<string, SolanaChainId> = {\n\t\"solana:4sGjMW1sUnHzSxGspuhpqLDx6wiyjNtZ\": \"solana:mainnet\",\n\t\"solana:8E9rvCKLFQia2Y35HXjjpWzj8weVo44K\": \"solana:devnet\",\n};\n\nconst CAIP2_TO_SOLANA_CHAIN: Record<string, SolanaChainId> = {\n\t...Object.fromEntries(\n\t\tObject.entries(SOLANA_CHAIN_TO_CAIP2).map(([chain, caip2]) => [\n\t\t\tcaip2,\n\t\t\tchain as SolanaChainId,\n\t\t]),\n\t),\n\t...DEPRECATED_CAIP2_TO_SOLANA_CHAIN,\n};\n\n/**\n * Maps a CAIP-2 chain id (as found in WalletConnect session namespaces) back to\n * its {@link SolanaChainId}, or `undefined` if it isn't a recognised cluster.\n */\nexport const getSolanaChainIdFromCaip2 = (\n\tcaip2: string,\n): SolanaChainId | undefined => CAIP2_TO_SOLANA_CHAIN[caip2];\n","import {\n\taddress,\n\tgetBase58Decoder,\n\tgetBase58Encoder,\n\tgetBase64Decoder,\n\tgetBase64Encoder,\n\tgetTransactionDecoder,\n\tgetTransactionEncoder,\n\ttype SignableMessage,\n\ttype SignatureBytes,\n\ttype TransactionModifyingSigner,\n} from \"@solana/kit\";\nimport type {\n\tSolanaSignAndSendTransactionFeature,\n\tSolanaSignMessageFeature,\n\tSolanaSignTransactionFeature,\n} from \"@solana/wallet-standard-features\";\nimport type {\n\tWalletAccount,\n\tWallet as WalletStandardWallet,\n} from \"@wallet-standard/base\";\nimport { KheopskitError } from \"../errors\";\nimport type { WalletConnectProvider } from \"../types\";\nimport { getSolanaCaip2, type SolanaChainId } from \"./chains\";\nimport type { SolanaSigner } from \"./types\";\n\nconst SOLANA_SIGN_MESSAGE = \"solana:signMessage\";\nconst SOLANA_SIGN_TRANSACTION = \"solana:signTransaction\";\nconst SOLANA_SIGN_AND_SEND_TRANSACTION = \"solana:signAndSendTransaction\";\n\ntype SignMessageApi = SolanaSignMessageFeature[typeof SOLANA_SIGN_MESSAGE];\ntype SignTransactionApi =\n\tSolanaSignTransactionFeature[typeof SOLANA_SIGN_TRANSACTION];\ntype SignAndSendApi =\n\tSolanaSignAndSendTransactionFeature[typeof SOLANA_SIGN_AND_SEND_TRANSACTION];\n\n/** The branded array type expected back from a transaction-modifying signer. */\ntype SignedTransactions = Awaited<\n\tReturnType<TransactionModifyingSigner[\"modifyAndSignTransactions\"]>\n>;\n\nconst requireFeature = <T>(wallet: WalletStandardWallet, name: string): T => {\n\tconst feature = (wallet.features as Record<string, unknown>)[name];\n\tif (!feature)\n\t\tthrow new KheopskitError(\n\t\t\t\"FEATURE_NOT_SUPPORTED\",\n\t\t\t`wallet \"${wallet.name}\" does not support ${name}`,\n\t\t);\n\treturn feature as T;\n};\n\n/**\n * Builds a {@link SolanaSigner} backed by a Wallet Standard wallet's\n * `solana:*` features (injected wallets).\n *\n * Transactions are exchanged as wire bytes; messages and signatures as raw\n * bytes. Calling a method whose feature the wallet does not advertise throws.\n */\nexport const createInjectedSolanaSigner = (\n\twallet: WalletStandardWallet,\n\taccount: WalletAccount,\n\tchain: SolanaChainId,\n): SolanaSigner => {\n\tconst signerAddress = address(account.address);\n\n\treturn {\n\t\taddress: signerAddress,\n\t\tmodifyAndSignMessages: async (messages) => {\n\t\t\tconst feature = requireFeature<SignMessageApi>(\n\t\t\t\twallet,\n\t\t\t\tSOLANA_SIGN_MESSAGE,\n\t\t\t);\n\t\t\tconst outputs = await feature.signMessage(\n\t\t\t\t...messages.map((m) => ({ account, message: m.content })),\n\t\t\t);\n\t\t\treturn messages.map((m, i) => {\n\t\t\t\tconst output = outputs[i];\n\t\t\t\tif (!output) throw new Error(\"[kheopskit] missing signMessage output\");\n\t\t\t\treturn {\n\t\t\t\t\tcontent: output.signedMessage,\n\t\t\t\t\tsignatures: {\n\t\t\t\t\t\t...m.signatures,\n\t\t\t\t\t\t[signerAddress]: output.signature as SignatureBytes,\n\t\t\t\t\t},\n\t\t\t\t} satisfies SignableMessage;\n\t\t\t});\n\t\t},\n\t\tmodifyAndSignTransactions: async (transactions) => {\n\t\t\tconst feature = requireFeature<SignTransactionApi>(\n\t\t\t\twallet,\n\t\t\t\tSOLANA_SIGN_TRANSACTION,\n\t\t\t);\n\t\t\tconst encoder = getTransactionEncoder();\n\t\t\tconst decoder = getTransactionDecoder();\n\t\t\tconst outputs = await feature.signTransaction(\n\t\t\t\t...transactions.map((tx) => ({\n\t\t\t\t\taccount,\n\t\t\t\t\ttransaction: new Uint8Array(encoder.encode(tx)),\n\t\t\t\t\tchain,\n\t\t\t\t})),\n\t\t\t);\n\t\t\treturn transactions.map((_tx, i) => {\n\t\t\t\tconst output = outputs[i];\n\t\t\t\tif (!output)\n\t\t\t\t\tthrow new Error(\"[kheopskit] missing signTransaction output\");\n\t\t\t\treturn decoder.decode(output.signedTransaction);\n\t\t\t}) as unknown as SignedTransactions;\n\t\t},\n\t\tsignAndSendTransactions: async (transactions) => {\n\t\t\tconst feature = requireFeature<SignAndSendApi>(\n\t\t\t\twallet,\n\t\t\t\tSOLANA_SIGN_AND_SEND_TRANSACTION,\n\t\t\t);\n\t\t\tconst encoder = getTransactionEncoder();\n\t\t\tconst outputs = await feature.signAndSendTransaction(\n\t\t\t\t...transactions.map((tx) => ({\n\t\t\t\t\taccount,\n\t\t\t\t\ttransaction: new Uint8Array(encoder.encode(tx)),\n\t\t\t\t\tchain,\n\t\t\t\t})),\n\t\t\t);\n\t\t\treturn outputs.map((output) => output.signature as SignatureBytes);\n\t\t},\n\t};\n};\n\n/**\n * Builds a {@link SolanaSigner} backed by a WalletConnect session, adapting the\n * `solana_*` RPC methods to the same interface as the injected signer.\n *\n * The CAIP-2 chain id is derived from {@link SolanaChainId}; transactions are\n * base64-encoded and messages/signatures base58-encoded, per the WalletConnect\n * Solana spec.\n */\nexport const createWalletConnectSolanaSigner = (\n\tprovider: WalletConnectProvider,\n\taccountAddress: string,\n\tchain: SolanaChainId,\n): SolanaSigner => {\n\tconst signerAddress = address(accountAddress);\n\n\tconst request = <T>(method: string, params: unknown): Promise<T> => {\n\t\tif (!provider.session)\n\t\t\tthrow new KheopskitError(\"NO_SESSION\", \"No session found\");\n\t\t// Resolved lazily (not at signer construction) so building a signer for a\n\t\t// cluster without a CAIP-2 id (e.g. localnet) doesn't throw until a request\n\t\t// is actually attempted.\n\t\treturn provider.client.request<T>({\n\t\t\ttopic: provider.session.topic,\n\t\t\tchainId: getSolanaCaip2(chain),\n\t\t\trequest: { method, params },\n\t\t});\n\t};\n\n\treturn {\n\t\taddress: signerAddress,\n\t\tmodifyAndSignMessages: async (messages) => {\n\t\t\t// @solana/kit naming is the inverse of the value direction here: a\n\t\t\t// Decoder turns bytes -> string (so `toBase58` produces a base58 string),\n\t\t\t// an Encoder turns string -> bytes (so `fromBase58` parses one).\n\t\t\tconst toBase58 = getBase58Decoder();\n\t\t\tconst fromBase58 = getBase58Encoder();\n\t\t\treturn Promise.all(\n\t\t\t\tmessages.map(async (m) => {\n\t\t\t\t\tconst { signature } = await request<{ signature: string }>(\n\t\t\t\t\t\t\"solana_signMessage\",\n\t\t\t\t\t\t{ pubkey: accountAddress, message: toBase58.decode(m.content) },\n\t\t\t\t\t);\n\t\t\t\t\treturn {\n\t\t\t\t\t\tcontent: m.content,\n\t\t\t\t\t\tsignatures: {\n\t\t\t\t\t\t\t...m.signatures,\n\t\t\t\t\t\t\t[signerAddress]: fromBase58.encode(signature) as SignatureBytes,\n\t\t\t\t\t\t},\n\t\t\t\t\t} satisfies SignableMessage;\n\t\t\t\t}),\n\t\t\t);\n\t\t},\n\t\tmodifyAndSignTransactions: async (transactions) => {\n\t\t\tconst encoder = getTransactionEncoder();\n\t\t\tconst decoder = getTransactionDecoder();\n\t\t\tconst toBase64 = getBase64Decoder();\n\t\t\tconst fromBase64 = getBase64Encoder();\n\t\t\tconst fromBase58 = getBase58Encoder();\n\t\t\tconst signed = await Promise.all(\n\t\t\t\ttransactions.map(async (tx) => {\n\t\t\t\t\tconst result = await request<{\n\t\t\t\t\t\ttransaction?: string;\n\t\t\t\t\t\tsignature?: string;\n\t\t\t\t\t}>(\"solana_signTransaction\", {\n\t\t\t\t\t\ttransaction: toBase64.decode(encoder.encode(tx)),\n\t\t\t\t\t});\n\t\t\t\t\t// Wallets may return either the full signed transaction (base64)\n\t\t\t\t\t// or just the signature (base58) to merge into the original.\n\t\t\t\t\tif (result.transaction)\n\t\t\t\t\t\treturn decoder.decode(fromBase64.encode(result.transaction));\n\t\t\t\t\tif (result.signature)\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t...tx,\n\t\t\t\t\t\t\tsignatures: {\n\t\t\t\t\t\t\t\t...tx.signatures,\n\t\t\t\t\t\t\t\t[signerAddress]: fromBase58.encode(\n\t\t\t\t\t\t\t\t\tresult.signature,\n\t\t\t\t\t\t\t\t) as SignatureBytes,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t};\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\"[kheopskit] solana_signTransaction returned no transaction or signature\",\n\t\t\t\t\t);\n\t\t\t\t}),\n\t\t\t);\n\t\t\treturn signed as unknown as SignedTransactions;\n\t\t},\n\t\tsignAndSendTransactions: async (transactions) => {\n\t\t\tconst encoder = getTransactionEncoder();\n\t\t\tconst toBase64 = getBase64Decoder();\n\t\t\tconst fromBase58 = getBase58Encoder();\n\t\t\treturn Promise.all(\n\t\t\t\ttransactions.map(async (tx) => {\n\t\t\t\t\tconst { signature } = await request<{ signature: string }>(\n\t\t\t\t\t\t\"solana_signAndSendTransaction\",\n\t\t\t\t\t\t{ transaction: toBase64.decode(encoder.encode(tx)) },\n\t\t\t\t\t);\n\t\t\t\t\treturn fromBase58.encode(signature) as SignatureBytes;\n\t\t\t\t}),\n\t\t\t);\n\t\t},\n\t};\n};\n","import type { StandardEventsFeature } from \"@wallet-standard/features\";\nimport { Observable, of, shareReplay } from \"rxjs\";\nimport { getWalletAccountId } from \"../../utils\";\nimport { getCachedObservable$ } from \"../../utils/getCachedObservable\";\nimport {\n\tcreatePlatformAccounts$,\n\tgetCaip10Addresses,\n\tgetSessionCaip10s,\n\tgetWalletConnectSessionAccounts$,\n\ttype LiveWalletConnectWallet,\n} from \"../platformAccounts\";\nimport type { WalletConnectWallet } from \"../types\";\nimport { getSolanaChainIdFromCaip2, type SolanaChainId } from \"./chains\";\nimport {\n\tcreateInjectedSolanaSigner,\n\tcreateWalletConnectSolanaSigner,\n} from \"./signer\";\nimport type {\n\tSolanaAccount,\n\tSolanaInjectedWallet,\n\tSolanaWallet,\n} from \"./types\";\n\ntype StandardEventsApi = StandardEventsFeature[\"standard:events\"];\n\nconst getInjectedWalletAccounts$ = (\n\twallet: SolanaInjectedWallet,\n\tchain: SolanaChainId,\n): Observable<SolanaAccount[]> => {\n\tif (!wallet.isConnected) return of([]);\n\n\treturn getCachedObservable$(`accounts:${wallet.id}:${chain}`, () =>\n\t\tnew Observable<SolanaAccount[]>((subscriber) => {\n\t\t\tconst standardWallet = wallet.wallet;\n\n\t\t\tconst buildAccounts = (): SolanaAccount[] =>\n\t\t\t\tstandardWallet.accounts.map(\n\t\t\t\t\t(account): SolanaAccount => ({\n\t\t\t\t\t\tid: getWalletAccountId(wallet.id, account.address),\n\t\t\t\t\t\tplatform: \"solana\",\n\t\t\t\t\t\taddress: account.address,\n\t\t\t\t\t\tchains: wallet.chains,\n\t\t\t\t\t\tsigner: createInjectedSolanaSigner(standardWallet, account, chain),\n\t\t\t\t\t\tgetSigner: (c) =>\n\t\t\t\t\t\t\tcreateInjectedSolanaSigner(standardWallet, account, c),\n\t\t\t\t\t\tname: account.label,\n\t\t\t\t\t\twalletName: wallet.name,\n\t\t\t\t\t\twalletId: wallet.id,\n\t\t\t\t\t}),\n\t\t\t\t);\n\n\t\t\tsubscriber.next(buildAccounts());\n\n\t\t\t// Re-emit when the wallet's authorized accounts change.\n\t\t\tconst eventsFeature = (\n\t\t\t\tstandardWallet.features as Record<string, unknown>\n\t\t\t)[\"standard:events\"] as StandardEventsApi | undefined;\n\t\t\tconst off = eventsFeature?.on(\"change\", () =>\n\t\t\t\tsubscriber.next(buildAccounts()),\n\t\t\t);\n\n\t\t\treturn () => {\n\t\t\t\toff?.();\n\t\t\t};\n\t\t}).pipe(shareReplay({ refCount: true, bufferSize: 1 })),\n\t);\n};\n\nconst getWalletConnectAccounts$ = (\n\twallet: LiveWalletConnectWallet,\n\tchain: SolanaChainId,\n): Observable<SolanaAccount[]> =>\n\tgetWalletConnectSessionAccounts$({\n\t\twallet,\n\t\tplatform: \"solana\",\n\t\tnamespace: \"solana\",\n\t\tcacheKey: `accounts:${wallet.id}:solana:${chain}`,\n\t\tbuildAccounts: (provider) => {\n\t\t\tconst session = provider.session;\n\t\t\tif (!session) return [];\n\n\t\t\tconst solanaCaip10s = getSessionCaip10s(session, \"solana\");\n\t\t\tconst addresses = getCaip10Addresses(solanaCaip10s);\n\n\t\t\t// Clusters the session actually advertises (\"solana:<chainRef>\" from\n\t\t\t// each CAIP-10 entry), mapped back to SolanaChainId. Falls back to the\n\t\t\t// configured chain when none are recognised.\n\t\t\tconst advertisedChains = [\n\t\t\t\t...new Set(\n\t\t\t\t\tsolanaCaip10s\n\t\t\t\t\t\t.map((account) => account.split(\":\").slice(0, 2).join(\":\"))\n\t\t\t\t\t\t.map(getSolanaChainIdFromCaip2)\n\t\t\t\t\t\t.filter((c): c is SolanaChainId => !!c),\n\t\t\t\t),\n\t\t\t];\n\t\t\tconst chains = advertisedChains.length ? advertisedChains : [chain];\n\n\t\t\treturn addresses.map(\n\t\t\t\t(accountAddress): SolanaAccount => ({\n\t\t\t\t\tid: getWalletAccountId(wallet.id, accountAddress),\n\t\t\t\t\tplatform: \"solana\",\n\t\t\t\t\taddress: accountAddress,\n\t\t\t\t\tchains,\n\t\t\t\t\tsigner: createWalletConnectSolanaSigner(\n\t\t\t\t\t\tprovider,\n\t\t\t\t\t\taccountAddress,\n\t\t\t\t\t\tchain,\n\t\t\t\t\t),\n\t\t\t\t\tgetSigner: (c) =>\n\t\t\t\t\t\tcreateWalletConnectSolanaSigner(provider, accountAddress, c),\n\t\t\t\t\twalletName: wallet.name,\n\t\t\t\t\twalletId: wallet.id,\n\t\t\t\t}),\n\t\t\t);\n\t\t},\n\t});\n\nexport const getSolanaAccounts$ = (\n\tsolanaWallets$: Observable<(SolanaWallet | WalletConnectWallet)[]>,\n\tsolanaChain: SolanaChainId,\n) =>\n\tcreatePlatformAccounts$({\n\t\twallets$: solanaWallets$,\n\t\tgetInjectedAccounts$: (wallet) =>\n\t\t\tgetInjectedWalletAccounts$(wallet, solanaChain),\n\t\tgetWalletConnectAccounts$: (wallet) =>\n\t\t\tgetWalletConnectAccounts$(wallet, solanaChain),\n\t\t// Re-emit when the advertised clusters change, not just on id changes.\n\t\taccountChangeKey: (account) => `${account.id}|${account.chains.join(\",\")}`,\n\t});\n","import { getWallets } from \"@wallet-standard/app\";\nimport type { Wallet as WalletStandardWallet } from \"@wallet-standard/base\";\nimport type {\n\tStandardConnectFeature,\n\tStandardDisconnectFeature,\n} from \"@wallet-standard/features\";\nimport { Observable, shareReplay } from \"rxjs\";\nimport { clearCachedObservablesByPrefix } from \"../../utils/getCachedObservable\";\nimport { getWalletId } from \"../../utils/WalletId\";\nimport {\n\tisEip1193UserRejection,\n\tisRefusalMessage,\n\tKheopskitError,\n} from \"../errors\";\nimport { createInjectedWallets$ } from \"../injectedWallets\";\nimport type { KheopskitStore } from \"../store\";\nimport { isSolanaChainId, type SolanaChainId } from \"./chains\";\nimport type { SolanaInjectedWallet } from \"./types\";\n\ntype ConnectApi = StandardConnectFeature[\"standard:connect\"];\ntype DisconnectApi = StandardDisconnectFeature[\"standard:disconnect\"];\n\nconst SOLANA_NAMESPACE_PREFIX = \"solana:\";\n\n/** A Wallet Standard wallet is Solana-capable if it advertises a solana chain or feature. */\nconst isSolanaWallet = (wallet: WalletStandardWallet): boolean =>\n\twallet.chains.some((chain) => chain.startsWith(SOLANA_NAMESPACE_PREFIX)) ||\n\tObject.keys(wallet.features).some((feature) =>\n\t\tfeature.startsWith(SOLANA_NAMESPACE_PREFIX),\n\t);\n\nconst getSolanaChains = (wallet: WalletStandardWallet): SolanaChainId[] =>\n\twallet.chains.filter((chain): chain is SolanaChainId =>\n\t\tisSolanaChainId(chain),\n\t);\n\n/**\n * Observable of Solana-capable Wallet Standard wallets, updated as wallets\n * register/unregister. Returns an empty array during SSR.\n */\nconst walletStandardWallets$ = new Observable<readonly WalletStandardWallet[]>(\n\t(subscriber) => {\n\t\t// Guard against SSR - the Wallet Standard registry requires browser APIs\n\t\tif (typeof window === \"undefined\") {\n\t\t\tsubscriber.next([]);\n\t\t\treturn () => {};\n\t\t}\n\n\t\tconst { get, on } = getWallets();\n\t\tconst emit = () => subscriber.next(get().filter(isSolanaWallet));\n\n\t\temit();\n\n\t\tconst offRegister = on(\"register\", emit);\n\t\tconst offUnregister = on(\"unregister\", emit);\n\n\t\treturn () => {\n\t\t\toffRegister();\n\t\t\toffUnregister();\n\t\t};\n\t},\n).pipe(shareReplay({ refCount: true, bufferSize: 1 }));\n\n// The shared WalletConnect connector is emitted once by core (see\n// `getWallets$`), not per platform — so this returns only injected wallets.\nexport const getSolanaWallets$ = (store: KheopskitStore) =>\n\tcreateInjectedWallets$<WalletStandardWallet, SolanaInjectedWallet>(store, {\n\t\tsources$: walletStandardWallets$,\n\t\tgetWalletId: (wallet) => getWalletId(\"solana\", wallet.name),\n\t\tconnect: async (wallet, walletId) => {\n\t\t\tconst feature = (wallet.features as Record<string, unknown>)[\n\t\t\t\t\"standard:connect\"\n\t\t\t] as ConnectApi | undefined;\n\t\t\tif (!feature)\n\t\t\t\tthrow new KheopskitError(\n\t\t\t\t\t\"FEATURE_NOT_SUPPORTED\",\n\t\t\t\t\t`wallet ${walletId} does not support standard:connect`,\n\t\t\t\t\t{ walletId },\n\t\t\t\t);\n\n\t\t\tawait feature.connect();\n\t\t},\n\t\tisUserRejection: (error) =>\n\t\t\tisEip1193UserRejection(error) || isRefusalMessage(error),\n\t\t// standard:disconnect is an optional feature. Await it when present so a\n\t\t// failed disconnect rejects the returned promise; if absent we still\n\t\t// clear local state.\n\t\tdisconnect: async (wallet) => {\n\t\t\tconst feature = (wallet.features as Record<string, unknown>)[\n\t\t\t\t\"standard:disconnect\"\n\t\t\t] as DisconnectApi | undefined;\n\t\t\tawait feature?.disconnect();\n\t\t},\n\t\t// Drop cached account observables for this wallet so a later reconnect\n\t\t// rebuilds them against the current wallet handle, not a stale closure.\n\t\tonDisconnected: (walletId) =>\n\t\t\tclearCachedObservablesByPrefix(`accounts:${walletId}:`),\n\t\tbuildWallet: ({\n\t\t\tsource: wallet,\n\t\t\twalletId,\n\t\t\tstatus,\n\t\t\terror,\n\t\t\tisConnected,\n\t\t\tconnect,\n\t\t\tdisconnect,\n\t\t}) => ({\n\t\t\tplatform: \"solana\",\n\t\t\ttype: \"injected\",\n\t\t\tid: walletId,\n\t\t\tsourceId: wallet.name,\n\t\t\twallet,\n\t\t\tchains: getSolanaChains(wallet),\n\t\t\tname: wallet.name,\n\t\t\ticon: wallet.icon,\n\t\t\tstatus,\n\t\t\terror,\n\t\t\tisConnected,\n\t\t\tconnect,\n\t\t\tdisconnect,\n\t\t}),\n\t});\n","import type { KheopskitPlatform, PlatformContext } from \"../types\";\nimport { getSolanaAccounts$ } from \"./accounts\";\nimport {\n\tDEFAULT_SOLANA_CHAIN,\n\tisSolanaChainId,\n\ttype SolanaChainId,\n} from \"./chains\";\nimport type { SolanaAccount, SolanaWallet } from \"./types\";\nimport { getSolanaWallets$ } from \"./wallets\";\n\nexport type SolanaPluginOptions = {\n\t/**\n\t * Solana cluster that account signers target. Each account also exposes a\n\t * `getSigner(chain)` factory for targeting another cluster.\n\t *\n\t * @default \"solana:mainnet\"\n\t */\n\tchain?: SolanaChainId;\n};\n\n/**\n * Solana platform plugin. Pass to `getKheopskit$({ platforms: [solana()] })`.\n *\n * @example\n * ```ts\n * import { solana } from \"@kheopskit/core/solana\";\n * solana({ chain: \"solana:mainnet\" });\n * ```\n */\nexport const solana = (\n\toptions: SolanaPluginOptions = {},\n): KheopskitPlatform<\"solana\", SolanaWallet, SolanaAccount> => {\n\tconst chain = options.chain ?? DEFAULT_SOLANA_CHAIN;\n\n\tif (!isSolanaChainId(chain)) {\n\t\tconsole.warn(\n\t\t\t`[kheopskit] Unknown solana chain: ${JSON.stringify(chain)}. Valid values: \"solana:mainnet\", \"solana:devnet\", \"solana:testnet\", \"solana:localnet\".`,\n\t\t);\n\t}\n\n\treturn {\n\t\tplatform: \"solana\",\n\t\tgetWallets$: (ctx: PlatformContext) => getSolanaWallets$(ctx.store),\n\t\tgetAccounts$: (wallets$) => getSolanaAccounts$(wallets$, chain),\n\t};\n};\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { t as SyncableStorage } from "./storage-D3AwNgqc.mjs";
2
- import { S as WalletPlatform, a as CachedAccount, g as PolkadotAccountType, o as CachedWallet } from "./types-Dkyt9bf4.mjs";
2
+ import { S as WalletPlatform, a as CachedAccount, g as PolkadotAccountType, o as CachedWallet } from "./types-DCaL9bIc.mjs";
3
3
  //#region src/testing/cachedState.d.ts
4
4
  type InjectedWalletId = `${WalletPlatform}:${string}`;
5
5
  type CachedInjectedWallet = CachedWallet & {
package/dist/testing.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { t as SyncableStorage } from "./storage-D3AwNgqc.js";
2
- import { S as WalletPlatform, a as CachedAccount, g as PolkadotAccountType, o as CachedWallet } from "./types-Dkyt9bf4.js";
2
+ import { S as WalletPlatform, a as CachedAccount, g as PolkadotAccountType, o as CachedWallet } from "./types-DCaL9bIc.js";
3
3
  //#region src/testing/cachedState.d.ts
4
4
  type InjectedWalletId = `${WalletPlatform}:${string}`;
5
5
  type CachedInjectedWallet = CachedWallet & {
@@ -1 +1 @@
1
- {"version":3,"file":"types-BWHvNFlN.js","names":[],"sources":["../src/utils/getCachedObservable.ts","../src/utils/isWalletPlatform.ts","../src/utils/WalletId.ts","../src/api/errors.ts","../src/api/types.ts"],"sourcesContent":["import type { Observable } from \"rxjs\";\n\n// Anchored on globalThis so the cache stays a single instance even if the\n// module is duplicated across bundle chunks (e.g. CJS subpath entries).\nconst CACHE_SYMBOL = Symbol.for(\"kheopskit.observableCache\");\n\nconst getCache = (): Map<string, Observable<unknown>> => {\n\tconst g = globalThis as unknown as Record<\n\t\tsymbol,\n\t\tMap<string, Observable<unknown>> | undefined\n\t>;\n\tlet cache = g[CACHE_SYMBOL];\n\tif (!cache) {\n\t\tcache = new Map();\n\t\tg[CACHE_SYMBOL] = cache;\n\t}\n\treturn cache;\n};\n\nexport const getCachedObservable$ = <T, Obs = Observable<T>>(\n\tkey: string,\n\tcreate: () => Obs,\n): Obs => {\n\tconst cache = getCache();\n\tif (!cache.has(key)) cache.set(key, create() as Observable<unknown>);\n\n\treturn cache.get(key) as Obs;\n};\n\n/**\n * Clears an observable from the cache.\n * Use when a wallet disconnects or configuration changes.\n */\nexport const clearCachedObservable = (key: string): void => {\n\tgetCache().delete(key);\n};\n\n/**\n * Clears all cached observables whose key starts with `prefix`.\n *\n * Used to drop a wallet's account observables when it disconnects, so a later\n * reconnect rebuilds them against the current wallet handle instead of a stale\n * closure — and to keep the cache from growing unbounded across connect cycles.\n */\nexport const clearCachedObservablesByPrefix = (prefix: string): void => {\n\tconst cache = getCache();\n\tfor (const key of cache.keys()) {\n\t\tif (key.startsWith(prefix)) cache.delete(key);\n\t}\n};\n\n/**\n * Clears all cached observables.\n * Use when resetting the entire kheopskit state.\n */\nexport const clearAllCachedObservables = (): void => {\n\tgetCache().clear();\n};\n","import type { WalletPlatform } from \"../api/types\";\n\nexport const isWalletPlatform = (\n\tplatform: unknown,\n): platform is WalletPlatform =>\n\ttypeof platform === \"string\" &&\n\t[\"polkadot\", \"ethereum\", \"solana\"].includes(platform as WalletPlatform);\n","import type { WalletPlatform } from \"../api/types\";\nimport { isWalletPlatform } from \"./isWalletPlatform\";\n\nexport type WalletId = string;\n\n/**\n * Stable id of the single WalletConnect connector. It is platform-less: one WC\n * session spans whichever namespaces the wallet approves, so it isn't tied to a\n * platform (unlike injected wallet ids, which are `<platform>:<identifier>`).\n */\nexport const WALLET_CONNECT_WALLET_ID: WalletId = \"walletconnect\";\n\nexport const getWalletId = (\n\tplatform: WalletPlatform,\n\tidentifier: string,\n): WalletId => {\n\tif (!isWalletPlatform(platform)) throw new Error(\"Invalid platform\");\n\tif (!identifier) throw new Error(\"Invalid name\");\n\treturn `${platform}:${identifier}`;\n};\n\nexport const parseWalletId = (walletId: string) => {\n\tif (!walletId) throw new Error(\"Invalid walletId\");\n\tconst [platform, identifier] = walletId.split(\":\");\n\tif (!isWalletPlatform(platform)) throw new Error(\"Invalid platform\");\n\tif (!identifier) throw new Error(\"Invalid address\");\n\treturn { platform, identifier };\n};\n\n/**\n * Non-throwing variant of {@link parseWalletId}. Use when validating untrusted\n * input (e.g. cached state persisted by an older version) before parsing.\n */\nexport const isValidWalletId = (walletId: unknown): walletId is WalletId => {\n\tif (typeof walletId !== \"string\" || !walletId) return false;\n\tif (walletId === WALLET_CONNECT_WALLET_ID) return true;\n\tconst [platform, identifier] = walletId.split(\":\");\n\treturn isWalletPlatform(platform) && !!identifier;\n};\n","import type { WalletId } from \"../utils/WalletId\";\n\n/**\n * Stable error codes thrown by kheopskit. Catch a {@link KheopskitError} and\n * branch on `error.code` instead of matching message strings.\n */\nexport type KheopskitErrorCode =\n\t/**\n\t * This `connect()` or `disconnect()` call was abandoned. For `connect()`:\n\t * its `signal` fired before the platform answered, `disconnect()` was\n\t * called while it pended, its wallet disappeared, or the last `kheopskit$`\n\t * subscriber unsubscribed.\n\t * For either call on an injected wallet: the wallet object came from a\n\t * subscription that had already ended, in which case the call rejects\n\t * without asking the platform and without changing the remembered list. A\n\t * placeholder's `connect()` rejects the same way, its `disconnect()`\n\t * resolves, and the WalletConnect connector still calls AppKit.\n\t */\n\t| \"ABORTED\"\n\t/** The user refused the wallet's connect prompt. `cause` holds the platform error. */\n\t| \"USER_REJECTED\"\n\t/** The wallet's connect call failed for another reason. `cause` holds the platform error. */\n\t| \"CONNECT_FAILED\"\n\t/**\n\t * `connect()` was called on a placeholder wallet restored from the cache:\n\t * on `kheopskit$`, hydration ended without kheopskit discovering that\n\t * wallet; on `getHydratedSnapshot` and the React provider's initial\n\t * snapshot, always.\n\t */\n\t| \"WALLET_PENDING\"\n\t/** The wallet does not advertise a capability kheopskit needs to proceed. */\n\t| \"FEATURE_NOT_SUPPORTED\"\n\t/** No active WalletConnect session for the requested operation. */\n\t| \"NO_SESSION\"\n\t/** No provider available for the requested namespace. */\n\t| \"NO_PROVIDER\"\n\t/** The installed polkadot-api version exposes neither `getTxCreatorFromPjs` (v3) nor `getPolkadotSignerFromPjs` (v2). */\n\t| \"UNSUPPORTED_VERSION\"\n\t/** The requested chain cannot be used for this operation (e.g. a Solana cluster with no CAIP-2 id over WalletConnect). */\n\t| \"UNSUPPORTED_CHAIN\"\n\t/** `getKheopskit$` received a config value it cannot use. The message names the fix. */\n\t| \"INVALID_CONFIG\";\n\n/**\n * Error thrown by kheopskit wallet/account operations. Carries a stable\n * {@link KheopskitErrorCode} and, when relevant, the offending wallet id.\n *\n * @example\n * ```ts\n * try {\n * await wallet.connect();\n * } catch (error) {\n * if (error instanceof KheopskitError && error.code === \"USER_REJECTED\") {\n * // the user closed or refused the wallet prompt\n * } else throw error;\n * }\n * ```\n */\nexport class KheopskitError extends Error {\n\treadonly code: KheopskitErrorCode;\n\t/** The wallet id this error relates to, when applicable. */\n\treadonly walletId?: WalletId;\n\n\tconstructor(\n\t\tcode: KheopskitErrorCode,\n\t\tmessage: string,\n\t\toptions?: { walletId?: WalletId; cause?: unknown },\n\t) {\n\t\tsuper(`[kheopskit] ${message}`, { cause: options?.cause });\n\t\tthis.name = \"KheopskitError\";\n\t\tthis.code = code;\n\t\tthis.walletId = options?.walletId;\n\t}\n}\n\nexport const abortedError = (\n\twalletId: WalletId,\n\toperation: \"connect\" | \"disconnect\",\n): KheopskitError =>\n\tnew KheopskitError(\n\t\t\"ABORTED\",\n\t\t`${operation} of wallet ${walletId} was aborted`,\n\t\t{ walletId },\n\t);\n\nexport const walletPendingError = (walletId: WalletId): KheopskitError =>\n\tnew KheopskitError(\n\t\t\"WALLET_PENDING\",\n\t\t`wallet ${walletId} was not discovered before hydration ended`,\n\t\t{ walletId },\n\t);\n\nexport const toConnectError = (\n\terror: unknown,\n\twalletId: WalletId,\n\tisUserRejection: (error: unknown) => boolean,\n): KheopskitError => {\n\tif (error instanceof KheopskitError) return error;\n\treturn isUserRejection(error)\n\t\t? new KheopskitError(\n\t\t\t\t\"USER_REJECTED\",\n\t\t\t\t`the user rejected the connect request of wallet ${walletId}`,\n\t\t\t\t{ walletId, cause: error },\n\t\t\t)\n\t\t: new KheopskitError(\n\t\t\t\t\"CONNECT_FAILED\",\n\t\t\t\t`wallet ${walletId} failed to connect`,\n\t\t\t\t{ walletId, cause: error },\n\t\t\t);\n};\n\nconst EIP1193_USER_REJECTED_REQUEST = 4001;\n\nexport const isEip1193UserRejection = (error: unknown): boolean =>\n\ttypeof error === \"object\" &&\n\terror !== null &&\n\t(error as { code?: unknown }).code === EIP1193_USER_REJECTED_REQUEST;\n\ntype RefusalMessage = {\n\tpattern: RegExp;\n\twallet: string;\n\tevidence: \"live\" | \"inferred\";\n};\n\n/**\n * How Polkadot extensions and Wallet Standard wallets reject a refused connect\n * prompt. They send a plain `Error`, so the message is the only signal.\n * `evidence` records whether the row was observed live or inferred from the\n * wallet's source or documentation.\n */\nconst REFUSAL_MESSAGES: readonly RefusalMessage[] = [\n\t{\n\t\tpattern: /^Site .+ has not been authorised for Talisman yet$/,\n\t\twallet: \"Talisman 3.11.0, Reject button or prompt window closed\",\n\t\tevidence: \"live\",\n\t},\n\t{\n\t\tpattern: /^Connection request was (rejected|cancelled) by the user\\.$/,\n\t\twallet: \"polkadot-js extension (extension-base 0.64 State.ts)\",\n\t\tevidence: \"inferred\",\n\t},\n\t{\n\t\tpattern: /^(Rejected|Cancelled)$/,\n\t\twallet: \"SubWallet (Extension.ts)\",\n\t\tevidence: \"inferred\",\n\t},\n\t{\n\t\tpattern: /^User rejected the request\\.$/,\n\t\twallet: \"Phantom (Wallet Standard)\",\n\t\tevidence: \"inferred\",\n\t},\n];\n\nexport const isRefusalMessage = (error: unknown): boolean => {\n\tconst message = (error as { message?: unknown } | null | undefined)?.message;\n\treturn (\n\t\ttypeof message === \"string\" &&\n\t\tREFUSAL_MESSAGES.some((row) => row.pattern.test(message))\n\t);\n};\n","import type { Observable } from \"rxjs\";\nimport type { WalletAccountId } from \"../utils\";\nimport type { WalletId } from \"../utils/WalletId\";\nimport type { KheopskitError } from \"./errors\";\n\nexport type WalletPlatform = \"polkadot\" | \"ethereum\" | \"solana\";\n\n/**\n * Minimal structural view of a WalletConnect `UniversalProvider`, the subset\n * kheopskit reads. Declared locally so core never depends on\n * `@walletconnect/universal-provider`. The concrete instance comes from\n * `@reown/appkit` at runtime.\n */\nexport type WalletConnectProvider = {\n\tsession?: {\n\t\ttopic: string;\n\t\tnamespaces: Record<string, { accounts?: string[] }>;\n\t};\n\tclient: {\n\t\trequest<T = unknown>(args: {\n\t\t\ttopic: string;\n\t\t\tchainId: string;\n\t\t\trequest: { method: string; params: unknown };\n\t\t}): Promise<T>;\n\t};\n\trequest(args: { method: string; params?: unknown }): Promise<unknown>;\n\ton(event: string, listener: (...args: unknown[]) => void): void;\n\toff(event: string, listener: (...args: unknown[]) => void): void;\n};\n\n/**\n * Minimal structural view of the Reown AppKit instance, the subset kheopskit's\n * account factories use. Exposed as the `appKit` escape hatch on AppKit wallets;\n * cast it to `@reown/appkit`'s `AppKit` type for the full API. Declared locally\n * so core never depends on `@reown/appkit`'s types (it's an optional peer).\n */\nexport type AppKitInstance = {\n\tgetProvider<T = WalletConnectProvider>(namespace: string): T | undefined;\n\tgetAccount(\n\t\tnamespace: string,\n\t): { allAccounts: { address: string }[] } | undefined;\n\tgetCaipNetworks(namespace: string): { caipNetworkId?: string }[];\n};\n\nexport type WalletType = \"injected\" | \"walletconnect\";\n\n/**\n * Where a wallet stands with the dapp. `connecting` covers a `connect()` call\n * in flight and an auto-reconnect in flight alike.\n */\nexport type WalletStatus = \"disconnected\" | \"connecting\" | \"connected\";\n\nexport type PolkadotAccountType = \"sr25519\" | \"ed25519\" | \"ecdsa\" | \"ethereum\";\n\n/** Options accepted by {@link BaseWallet.connect}. */\nexport type ConnectOptions = {\n\t/**\n\t * Ends this caller's wait for the platform's answer: its promise rejects\n\t * with a `KheopskitError` of code `ABORTED`. An abort after the platform\n\t * has granted the connect changes nothing, and a Polkadot extension counts\n\t * as granted only once its account list has been read. Other callers\n\t * sharing the attempt keep waiting. Once no caller waits any more,\n\t * kheopskit drops a handle the platform hands back afterwards without\n\t * revoking the platform's grant, and the wallet is neither connected nor\n\t * remembered. The\n\t * extension's own prompt may stay open: nothing can withdraw it from the\n\t * page. Kheopskit sets no timeout of its own, because an extension prompt\n\t * pends only while the user has not answered it; pass\n\t * `AbortSignal.timeout(ms)` to cap the wait.\n\t */\n\tsignal?: AbortSignal;\n};\n\n/**\n * SDK-free fields common to every wallet, regardless of platform. Platform\n * packages (`@kheopskit/core/<platform>`) extend this with SDK-typed fields\n * (the injected provider/extension/standard-wallet handle).\n */\nexport type BaseWallet = {\n\t/** `<platform>:<identifier>`, e.g. `polkadot:talisman`. Stable across reloads. */\n\tid: WalletId;\n\t/** The platform whose registry listed the wallet. */\n\tplatform: WalletPlatform;\n\t/**\n\t * `\"injected\"`, for a browser extension or Wallet Standard wallet. The\n\t * WalletConnect connector is the separate {@link WalletConnectWallet}.\n\t */\n\ttype: WalletType;\n\t/** Display name, e.g. `Talisman`. */\n\tname: string;\n\t/** Icon URL or data URI. `\"\"` when the wallet exposes none. */\n\ticon: string;\n\t/**\n\t * `true` for a wallet restored from the cache that kheopskit has not\n\t * discovered yet: its SDK handle and `sourceId` are absent and it is never\n\t * connected. On `kheopskit$` its `connect()` waits for the live wallet and\n\t * connects that one, rejecting with `WALLET_PENDING` if hydration ends first\n\t * and `ABORTED` if its `signal` fires or the subscription ends. On\n\t * `getHydratedSnapshot` it rejects with `WALLET_PENDING`. Its `disconnect()`\n\t * resolves at once. Set on every wallet of `kheopskit$` and of the hydrated\n\t * snapshot; plugins leave it unset.\n\t */\n\tisPlaceholder?: boolean;\n\t/**\n\t * `connecting` while a `connect()` call or an auto-reconnect waits for the\n\t * platform, `connected` once the platform granted it, `disconnected`\n\t * otherwise. On a reload, a remembered wallet kheopskit will reconnect is\n\t * `connecting` from the first frame when the cache recorded it connected,\n\t * which the cache does 1 s after the state settles.\n\t */\n\tstatus: WalletStatus;\n\t/**\n\t * The last `connect()` or auto-reconnect that the platform refused or\n\t * failed (`USER_REJECTED` or `CONNECT_FAILED`, or `FEATURE_NOT_SUPPORTED`\n\t * for a Wallet Standard wallet without `standard:connect`). Cleared when\n\t * the next attempt starts. An aborted attempt sets nothing.\n\t */\n\terror?: KheopskitError;\n\t/** `status === \"connected\"`. */\n\tisConnected: boolean;\n\t/**\n\t * Connects the wallet.\n\t *\n\t * - **Shared in flight.** Concurrent calls on one wallet share a single\n\t * platform attempt and the platform is asked once. Each call gets its own\n\t * promise: aborting a caller's `signal` rejects only that caller with code\n\t * `ABORTED`. The attempt itself is abandoned only when every caller that\n\t * passed a signal has aborted and no caller without one joined, when\n\t * `disconnect()` is called, or when the last `kheopskit$` subscriber\n\t * unsubscribes; those reject every remaining caller with `ABORTED`.\n\t * - **Idempotent.** On a connected wallet it resolves at once without\n\t * touching the platform, unless a `disconnect()` still waits for the\n\t * platform. It then waits for that disconnect to settle and connects\n\t * again if the wallet ended up disconnected.\n\t * - **Abortable.** See {@link ConnectOptions.signal}. There is no default\n\t * timeout.\n\t * - **Settles with the state.** It resolves only after `kheopskit$` has\n\t * emitted a state in which this wallet has `isConnected: true`, so the\n\t * consumer-visible state already reflects the connect. On platforms that\n\t * list accounts asynchronously (Ethereum `eth_accounts`), the accounts may\n\t * arrive in a later emission. No emission shows a disconnected wallet\n\t * still listing live accounts, or a connected wallet listing the accounts\n\t * of a previous connection. During hydration a wallet's cached\n\t * placeholder accounts stay listed whatever its status. Another wallet's\n\t * pending `eth_accounts` request never delays them. A `disconnect()` that\n\t * completes while it waits for that state rejects it with `ABORTED`. If\n\t * the platform's disconnect is still pending or refuses, it resolves once\n\t * the state shows the wallet connected (today only Solana has a platform\n\t * disconnect). A `connect()`\n\t * whose wallet disappears before the platform grants it (an extension\n\t * removed, a Wallet Standard wallet unregistered) rejects `ABORTED`;\n\t * granted first, it resolves and the wallet stays remembered. Once the last `kheopskit$`\n\t * subscriber has unsubscribed, a call on a wallet object from that\n\t * subscription rejects with `ABORTED` without asking the platform,\n\t * placeholders included.\n\t * - **Typed rejections.** A platform rejection is wrapped in a\n\t * `KheopskitError` carrying the original error as `cause` and the\n\t * `walletId`: `USER_REJECTED` when the user refused the prompt,\n\t * `CONNECT_FAILED` otherwise. A Wallet Standard wallet without\n\t * `standard:connect` rejects with `FEATURE_NOT_SUPPORTED`.\n\t */\n\tconnect: (options?: ConnectOptions) => Promise<void>;\n\t/**\n\t * Disconnects the wallet.\n\t *\n\t * On a disconnected wallet it resolves at once. While a `connect()` waits\n\t * for the platform to grant it, it aborts that connect (its promise rejects\n\t * with `ABORTED`) and never waits for the extension prompt. Otherwise it\n\t * first waits for the platform's disconnect and rejects if that fails, so\n\t * callers can surface or retry. It then resolves once `kheopskit$` has\n\t * emitted a state in which this wallet has `isConnected: false`, or once a\n\t * later `connect()`, the wallet leaving the list or the end of the\n\t * subscription makes that emission impossible. Once the last `kheopskit$`\n\t * subscriber has unsubscribed, a call on a wallet object from that\n\t * subscription rejects with `ABORTED` without asking the platform and\n\t * without changing the remembered list, except on a placeholder, whose\n\t * `disconnect()` resolves. A `disconnect()` started before that point\n\t * settles once the platform has answered: the wallet was\n\t * forgotten when it was called, and nothing else is written once the\n\t * subscription has ended.\n\t */\n\tdisconnect: () => Promise<void>;\n};\n\n/**\n * SDK-free fields common to every account, regardless of platform. Platform\n * packages extend this with their SDK-typed signer/client.\n */\nexport type BaseWalletAccount = {\n\t/** `<walletId>::<address>`, e.g. `polkadot:talisman::5Grw…`. */\n\tid: WalletAccountId;\n\t/**\n\t * `true` for an account restored from the cache that the live wallet has\n\t * not listed yet: its signing surface is absent. Set on every account of\n\t * `kheopskit$` and of the hydrated snapshot; plugins leave it unset.\n\t */\n\tisPlaceholder?: boolean;\n\t/** The platform of the wallet that lists the account. */\n\tplatform: WalletPlatform;\n\t/** Base58 (Solana), SS58 (Polkadot) or 0x-hex (Ethereum) address. */\n\taddress: string;\n\t/** Friendly account name, when the wallet exposes one (e.g. Polkadot). */\n\tname?: string;\n\t/** `name` of the wallet that lists the account. */\n\twalletName: string;\n\t/** `id` of the wallet that lists the account. */\n\twalletId: WalletId;\n};\n\ntype LiveWalletConnectWallet = {\n\t/** See {@link BaseWallet.isPlaceholder}. */\n\tisPlaceholder?: false;\n\t/** `\"walletconnect\"`, the one connector shared by every platform. */\n\tid: WalletId;\n\t/** Narrow with {@link isWalletConnectWallet}. */\n\ttype: \"walletconnect\";\n\t/**\n\t * Platforms (namespaces) the live session currently carries. Empty until\n\t * connected; populated with whatever the wallet approved (e.g. just\n\t * `[\"ethereum\"]`, or `[\"polkadot\", \"ethereum\"]`).\n\t */\n\tplatforms: WalletPlatform[];\n\t/**\n\t * Raw Reown AppKit instance, exposed as an escape hatch for advanced use\n\t * (custom modal control, reading providers directly). Most consumers should\n\t * use `connect`/`disconnect` and the derived accounts instead.\n\t */\n\tappKit: AppKitInstance;\n\t/** The name AppKit reports for the paired wallet, `\"WalletConnect\"` when it reports none. */\n\tname: string;\n\t/** The icon AppKit reports for the paired wallet, the WalletConnect logo when it reports none. */\n\ticon: string;\n\t/**\n\t * `connecting` while `connect()` opens the modal, `connected` while a\n\t * session is live, `disconnected` otherwise. On a reload with a cached\n\t * session it is `connecting` until AppKit restores the session or hydration\n\t * ends.\n\t */\n\tstatus: WalletStatus;\n\t/** The last `connect()` whose modal failed to open. Cleared by the next call. */\n\terror?: KheopskitError;\n\t/** `status === \"connected\"`. */\n\tisConnected: boolean;\n\t/**\n\t * Opens the WalletConnect modal and resolves once it is open; `isConnected`\n\t * follows when the user approves a session. Unlike injected wallets, this\n\t * promise does not wait for the state to show the connector connected, and\n\t * `signal` is accepted for signature compatibility only.\n\t */\n\tconnect: (options?: ConnectOptions) => Promise<void>;\n\t/** Ends the live session when there is one; resolves at once otherwise. */\n\tdisconnect: () => Promise<void>;\n};\n\n/**\n * The single WalletConnect connector, shared across every platform.\n *\n * Unlike injected wallets, it is **not tied to a platform**: one WalletConnect\n * session spans whichever namespaces the remote wallet approves in a single\n * pairing (a namespace can't be added to a live session afterwards). So there is\n * exactly one of these in `wallets`, discriminated by `type: \"walletconnect\"`.\n * Its accounts appear in `accounts`, each carrying its own `platform`.\n *\n * A placeholder (see {@link BaseWallet.isPlaceholder}) has no `appKit`.\n */\nexport type WalletConnectWallet =\n\t| LiveWalletConnectWallet\n\t| PlaceholderWallet<LiveWalletConnectWallet, \"appKit\">;\n\n/**\n * The cached stand-in for a live item `T`: `isPlaceholder: true`, and the\n * fields `K`, which only the live item carries, absent. Platform account and\n * wallet types are `T | Placeholder<T, K>`, so narrow on `isPlaceholder`\n * before reading a field of `K`.\n */\nexport type Placeholder<T, K extends keyof T> = Omit<T, K | \"isPlaceholder\"> & {\n\t/** Restored from the cache and not yet replaced by the live item. */\n\tisPlaceholder: true;\n} & { [P in K]?: undefined };\n\n/** A {@link Placeholder} wallet, which is never connected. */\nexport type PlaceholderWallet<\n\tT extends { isConnected: boolean },\n\tK extends keyof T,\n> = Omit<Placeholder<T, K>, \"isConnected\"> & { isConnected: false };\n\n/** Narrows a wallet to the platform-less {@link WalletConnectWallet}. */\nexport const isWalletConnectWallet = (\n\twallet: BaseWallet | WalletConnectWallet,\n): wallet is WalletConnectWallet => wallet.type === \"walletconnect\";\n\n/**\n * Narrows a wallet to an injected (browser-extension / Wallet Standard) wallet,\n * the complement of {@link isWalletConnectWallet}. `state.wallets` is\n * `(InjectedWallet | WalletConnectWallet)[]`, so use this to recover the\n * injected-only fields when iterating: `platform`, `sourceId`, and the SDK\n * handle (`provider` on Ethereum, `extension` on Polkadot, `wallet` on Solana).\n *\n * @example\n * ```ts\n * for (const wallet of wallets.filter(isInjectedWallet)) {\n * console.log(wallet.platform, wallet.sourceId); // typed, no WC widening\n * }\n * ```\n */\nexport const isInjectedWallet = <W extends BaseWallet | WalletConnectWallet>(\n\twallet: W,\n): wallet is Exclude<W, WalletConnectWallet> => wallet.type === \"injected\";\n\n/**\n * Dapp metadata shown in the WalletConnect modal. Mirrors WalletConnect's\n * `Metadata`, declared locally so core doesn't depend on\n * `@walletconnect/universal-provider`.\n */\nexport type WalletConnectMetadata = {\n\tname: string;\n\tdescription: string;\n\turl: string;\n\ticons: string[];\n};\n\n/**\n * The WalletConnect connector for `config.walletConnect`. Create it with\n * `walletConnect()` from `@kheopskit/core/walletconnect`; that entry is the only\n * one that loads Reown AppKit.\n */\nexport type WalletConnectPlugin = {\n\treadonly type: \"walletconnect\";\n\treadonly metadata: WalletConnectMetadata;\n\tgetWallet$(ctx: PlatformContext): Observable<WalletConnectWallet | null>;\n};\n\n/**\n * Context passed to a platform plugin's `getWallets$`. Carries the shared store\n * and the resolved core config (for WalletConnect / debug).\n */\nexport type PlatformContext = {\n\tstore: KheopskitStore;\n\tconfig: KheopskitConfig;\n};\n\n/**\n * A platform plugin. Created by the per-platform factories exported from\n * `@kheopskit/core/polkadot`, `/ethereum`, `/solana`. Core iterates plugins\n * generically and never imports a platform SDK itself.\n *\n * @typeParam TPlatform - the platform discriminant\n * @typeParam TWallet - the platform's wallet type (extends {@link BaseWallet})\n * @typeParam TAccount - the platform's account type (extends {@link BaseWalletAccount})\n */\nexport type KheopskitPlatform<\n\tTPlatform extends WalletPlatform = WalletPlatform,\n\tTWallet extends BaseWallet = BaseWallet,\n\tTAccount extends BaseWalletAccount = BaseWalletAccount,\n> = {\n\treadonly platform: TPlatform;\n\t// Declared as methods (not arrow properties) so parameters are checked\n\t// bivariantly — this lets a specific `KheopskitPlatform<\"polkadot\", …>` be\n\t// assigned to the base `KheopskitPlatform` despite the contravariant\n\t// `wallets$` parameter.\n\tgetWallets$(ctx: PlatformContext): Observable<TWallet[]>;\n\t// Receives this platform's (injected) wallets plus the shared, platform-less\n\t// WalletConnect connector — the plugin derives its injected accounts and, if\n\t// the WC session carries its namespace, its WalletConnect accounts.\n\tgetAccounts$(\n\t\twallets$: Observable<(TWallet | WalletConnectWallet)[]>,\n\t): Observable<TAccount[]>;\n\t/**\n\t * Optional hydration filter. Cached accounts for which this returns false are\n\t * dropped during SSR hydration (Polkadot uses it to honour `accountTypes`).\n\t */\n\tacceptsCachedAccount?(cached: CachedAccount): boolean;\n};\n\ntype ElementOf<T> = T extends readonly (infer E)[] ? E : never;\n\n/** The account type produced by a plugin (inferred from its `getAccounts$`). */\nexport type AccountOf<T> = T extends {\n\tgetAccounts$: (wallets$: never) => Observable<infer R>;\n}\n\t? ElementOf<R>\n\t: never;\n\n/** The wallet type produced by a plugin (inferred from its `getWallets$`). */\nexport type WalletOf<T> = T extends {\n\tgetWallets$: (ctx: never) => Observable<infer R>;\n}\n\t? ElementOf<R>\n\t: never;\n\nexport type KheopskitConfig<\n\tP extends readonly KheopskitPlatform[] = readonly KheopskitPlatform[],\n> = {\n\t/**\n\t * Which remembered injected wallets kheopskit reconnects after a reload.\n\t *\n\t * A wallet is remembered on every successful `connect()` and forgotten on\n\t * `disconnect()`, whatever this option says; the list is the store's\n\t * `remembered`. `true` reconnects every remembered injected wallet, `false`\n\t * none, and a function is asked for each cached connected injected wallet\n\t * on the first frame, then for each remembered one with its live object\n\t * before the attempt. The function must be synchronous and pure. It runs on\n\t * the server and on the client for the same input and both must agree, or\n\t * the markup diverges. It receives the {@link BaseWallet} fields only, so\n\t * key on `id`.\n\t *\n\t * The WalletConnect row is not governed by this option: AppKit restores its\n\t * own session on load, and the row hydrates `connecting` while it does.\n\t *\n\t * A reconnect opens no connect prompt while the site stays authorised in\n\t * the extension. When the authorisation was revoked the extension prompts\n\t * once; a refusal forgets the wallet, so the next load stays silent. A manual\n\t * `connect()` or `disconnect()` on a remembered wallet ends its automatic\n\t * reconnect for the session. A `disconnect()` while the automatic reconnect\n\t * still waits for the extension aborts it without forgetting the wallet, so\n\t * the next load reconnects it.\n\t * Automatic reconnect also ends when the wallet disappears mid-attempt; the\n\t * wallet stays remembered for the next load.\n\t *\n\t * A cached connected wallet this accepts shows `status: \"connecting\"` from\n\t * the first frame until its attempt settles. A wallet this rejects shows\n\t * `disconnected` from the first frame, without its cached accounts, so\n\t * nothing flashes from connected to disconnected.\n\t *\n\t * @default true\n\t */\n\tautoReconnect: boolean | ((wallet: BaseWallet) => boolean);\n\t/**\n\t * Platform plugins to enable, e.g. `[polkadot(), solana({ chain })]`.\n\t * Import factories from `@kheopskit/core/<platform>`.\n\t */\n\tplatforms: P;\n\t/** Create with `walletConnect()` from `@kheopskit/core/walletconnect`. */\n\twalletConnect?: WalletConnectPlugin;\n\t/**\n\t * Dapp name sent to Polkadot injected extensions on connect. Some\n\t * extensions (e.g. polkadot.js) warn about a suspicious origin when it's\n\t * empty.\n\t *\n\t * @default walletConnect.metadata.name, then window.location.hostname\n\t */\n\tappName?: string;\n\t/**\n\t * Logs the resolved config, every state emission and the hydration and\n\t * persistence steps with `console.debug`.\n\t *\n\t * @default false\n\t */\n\tdebug: boolean;\n\t/**\n\t * Custom storage key for persisting wallet connection state.\n\t * Useful when running multiple kheopskit instances on the same domain\n\t * to prevent state conflicts between different dapps.\n\t *\n\t * @default \"kheopskit\"\n\t */\n\tstorageKey: string;\n\t/**\n\t * Minimum time in milliseconds the cached placeholders are shown after a\n\t * reload, so the wallet list does not flash while extensions inject and\n\t * remembered wallets reconnect. Hydration goes on past it until the rule\n\t * documented on {@link KheopskitConfig.hydrationTimeout} ends it, and that\n\t * timeout wins over a longer grace period. The old cap of six times this\n\t * value is gone, so an app that raises it sets `hydrationTimeout` as well.\n\t *\n\t * `0` disables placeholders: `isHydrating` is `false` from the first\n\t * emission.\n\t *\n\t * @default 500\n\t */\n\thydrationGracePeriod: number;\n\t/**\n\t * Upper bound in milliseconds on {@link KheopskitState.isHydrating}.\n\t *\n\t * `isHydrating` turns `false` once `hydrationGracePeriod` has elapsed,\n\t * discovery has ended ({@link KheopskitState.isDiscovering} is `false`),\n\t * and every wallet that hydrated as `connecting` has settled. A wallet has\n\t * settled when it is absent from the discovered wallets, connected once its\n\t * account list has been read (an empty list counts) or connected with no\n\t * cached accounts, disconnected with `error` set, or disconnected and not\n\t * remembered when hydration began. At `hydrationTimeout` hydration ends\n\t * whatever is pending, and a wallet whose reconnect is still pending stays\n\t * `connecting`.\n\t *\n\t * - Discovery ends only after the page fires `load`, so a page with a cache\n\t * hydrates until `load` or `hydrationTimeout`, whichever comes first,\n\t * even when no wallet reconnects.\n\t * - A wallet that reconnects without sharing any account with the site\n\t * settles as soon as it answers; its cached accounts leave the list when\n\t * hydration ends.\n\t * - A failed reconnect ends that wallet's wait, even when the reconnect\n\t * policy still permits a retry. A retry runs only when the wallet list next\n\t * changes (a wallet discovered or removed, or any wallet's status, error\n\t * or name changing), which may never happen, so waiting for it would\n\t * hold most failed reconnects until `hydrationTimeout`. A retry that\n\t * does run shows on the live wallet's `status`, not as a placeholder.\n\t * - A remembered wallet the user disconnects during or right after its\n\t * reconnect holds hydration, and shows its cached accounts, until\n\t * `hydrationTimeout`.\n\t * - A prompt the extension opens for a revoked site holds hydration until\n\t * the user answers or `hydrationTimeout` elapses.\n\t *\n\t * @default 3000\n\t */\n\thydrationTimeout: number;\n};\n\n/**\n * The current kheopskit state.\n *\n * @remarks\n * While {@link KheopskitState.isHydrating} is `true`, `wallets` and `accounts`\n * may contain placeholders restored from the cache, flagged\n * `isPlaceholder: true`. Each platform type is a union on that flag: a\n * placeholder lacks the SDK handles (`account.txCreator` or `polkadotSigner`\n * on Polkadot, `account.client` on Ethereum, `account.signer` / `getSigner` on\n * Solana, `wallet.extension` / `provider` / `wallet` and `sourceId`). Reading\n * one without narrowing yields a possibly-`undefined` value, so the compiler\n * rejects using it as present until you narrow:\n *\n * ```ts\n * if (account.isPlaceholder) return;\n * account.txCreator; // TxCreator\n * ```\n *\n * A placeholder wallet's `connect()` waits for the live wallet and connects\n * it, rejecting with `WALLET_PENDING` if hydration ends first. No item is a\n * placeholder once `isHydrating` is `false`.\n *\n * The plain, serializable platform data that is persisted in the cache IS\n * restored on the placeholders (Ethereum `chainId`, Polkadot key `type`), so it\n * renders immediately on reload without flashing. Solana `chains` is not\n * cached: a placeholder account's is empty.\n */\nexport type KheopskitState<\n\tP extends readonly KheopskitPlatform[] = readonly KheopskitPlatform[],\n> = {\n\t/**\n\t * Every discovered wallet, connected or not, then the WalletConnect\n\t * connector once AppKit has loaded, when `config.walletConnect` is set.\n\t * While `isHydrating` is `true`, also the cached placeholders. Sorted by\n\t * platform (Polkadot, Ethereum, Solana, WalletConnect), then Talisman\n\t * first, then by name.\n\t */\n\twallets: (WalletOf<P[number]> | WalletConnectWallet)[];\n\t/**\n\t * The accounts of every connected wallet. While `isHydrating` is `true`,\n\t * also the cached placeholders of every wallet that has no live account\n\t * yet. Grouped by platform, then by wallet (Talisman first, then by wallet\n\t * name); inside a wallet, in the order the wallet lists them.\n\t */\n\taccounts: AccountOf<P[number]>[];\n\t/** The resolved config, with every default filled in. */\n\tconfig: KheopskitConfig<P>;\n\t/**\n\t * Whether the state is still being hydrated from cache.\n\t *\n\t * During hydration, cached wallets/accounts may be displayed before the\n\t * actual wallet extensions have injected. See the type-level remarks: those\n\t * are flagged `isPlaceholder: true` and carry no SDK handle.\n\t */\n\tisHydrating: boolean;\n\t/**\n\t * Whether wallet discovery may still be incomplete.\n\t *\n\t * `true` when the subscription started before `document.readyState` was\n\t * `\"complete\"`. It turns `false` in the first emission after `window` fired\n\t * `load` and the Polkadot registry (`window.injectedWeb3`) was read again,\n\t * so that emission already lists every extension injected by `load`. When the\n\t * subscription starts after `load`, it is `false` from the first emission.\n\t * Always `true` on the server.\n\t *\n\t * This rests on wallets injecting `injectedWeb3` before `load`. It says\n\t * nothing about wallets that inject after `load`. Ethereum (EIP-6963) and\n\t * Solana (Wallet Standard) registries are event-driven and need no re-read.\n\t */\n\tisDiscovering: boolean;\n};\n\n/** Shape of the persisted store data (localStorage, or cookies in compact form). */\nexport type KheopskitStoreData = {\n\t/** Wallets the user connected and has not disconnected */\n\tremembered?: WalletId[];\n\t/** Cached wallet state for SSR hydration to prevent UI flash */\n\tcachedWallets?: CachedWallet[];\n\t/** Cached account state for SSR hydration to prevent UI flash */\n\tcachedAccounts?: CachedAccount[];\n};\n\n/**\n * The persisted settings store shared between core and framework bindings.\n * Declared structurally here (implemented by `createKheopskitStore`) so modules\n * can depend on the interface without importing the implementation.\n */\nexport type KheopskitStore = {\n\tobservable: Observable<KheopskitStoreData>;\n\trememberWallet: (walletId: WalletId) => void;\n\tforgetWallet: (walletId: WalletId) => void;\n\tgetCachedState: () => { wallets: CachedWallet[]; accounts: CachedAccount[] };\n\tsetCachedState: (wallets: CachedWallet[], accounts: CachedAccount[]) => void;\n\t/** Stops cross-tab storage sync and completes `observable`. */\n\tdestroy: () => void;\n};\n\n/**\n * Serializable wallet data for SSR hydration cache.\n * Contains only the data needed to render wallet UI without flash.\n * Note: icon is NOT stored to save cookie space - it's looked up at hydration time.\n */\nexport type CachedWallet = {\n\tid: WalletId;\n\t/** Absent for the platform-less WalletConnect connector. */\n\tplatform?: WalletPlatform;\n\ttype: WalletType;\n\tname: string;\n\tisConnected: boolean;\n};\n\n/**\n * Serializable account data for SSR hydration cache.\n * Contains only the data needed to render account UI without flash.\n */\nexport type CachedAccount = {\n\tid: string;\n\tplatform: WalletPlatform;\n\taddress: string;\n\tname?: string;\n\t/** Cached chain ID for Ethereum accounts. */\n\tchainId?: number;\n\t/** Cached key type for Polkadot accounts. */\n\tpolkadotAccountType?: PolkadotAccountType;\n\twalletId: WalletId;\n\twalletName: string;\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"types-BWHvNFlN.js","names":[],"sources":["../src/utils/getCachedObservable.ts","../src/utils/isWalletPlatform.ts","../src/utils/WalletId.ts","../src/api/errors.ts","../src/api/types.ts"],"sourcesContent":["import type { Observable } from \"rxjs\";\n\n// Anchored on globalThis so the cache stays a single instance even if the\n// module is duplicated across bundle chunks (e.g. CJS subpath entries).\nconst CACHE_SYMBOL = Symbol.for(\"kheopskit.observableCache\");\n\nconst getCache = (): Map<string, Observable<unknown>> => {\n\tconst g = globalThis as unknown as Record<\n\t\tsymbol,\n\t\tMap<string, Observable<unknown>> | undefined\n\t>;\n\tlet cache = g[CACHE_SYMBOL];\n\tif (!cache) {\n\t\tcache = new Map();\n\t\tg[CACHE_SYMBOL] = cache;\n\t}\n\treturn cache;\n};\n\nexport const getCachedObservable$ = <T, Obs = Observable<T>>(\n\tkey: string,\n\tcreate: () => Obs,\n): Obs => {\n\tconst cache = getCache();\n\tif (!cache.has(key)) cache.set(key, create() as Observable<unknown>);\n\n\treturn cache.get(key) as Obs;\n};\n\n/**\n * Clears an observable from the cache.\n * Use when a wallet disconnects or configuration changes.\n */\nexport const clearCachedObservable = (key: string): void => {\n\tgetCache().delete(key);\n};\n\n/**\n * Clears all cached observables whose key starts with `prefix`.\n *\n * Used to drop a wallet's account observables when it disconnects, so a later\n * reconnect rebuilds them against the current wallet handle instead of a stale\n * closure — and to keep the cache from growing unbounded across connect cycles.\n */\nexport const clearCachedObservablesByPrefix = (prefix: string): void => {\n\tconst cache = getCache();\n\tfor (const key of cache.keys()) {\n\t\tif (key.startsWith(prefix)) cache.delete(key);\n\t}\n};\n\n/**\n * Clears all cached observables.\n * Use when resetting the entire kheopskit state.\n */\nexport const clearAllCachedObservables = (): void => {\n\tgetCache().clear();\n};\n","import type { WalletPlatform } from \"../api/types\";\n\nexport const isWalletPlatform = (\n\tplatform: unknown,\n): platform is WalletPlatform =>\n\ttypeof platform === \"string\" &&\n\t[\"polkadot\", \"ethereum\", \"solana\"].includes(platform as WalletPlatform);\n","import type { WalletPlatform } from \"../api/types\";\nimport { isWalletPlatform } from \"./isWalletPlatform\";\n\nexport type WalletId = string;\n\n/**\n * Stable id of the single WalletConnect connector. It is platform-less: one WC\n * session spans whichever namespaces the wallet approves, so it isn't tied to a\n * platform (unlike injected wallet ids, which are `<platform>:<identifier>`).\n */\nexport const WALLET_CONNECT_WALLET_ID: WalletId = \"walletconnect\";\n\nexport const getWalletId = (\n\tplatform: WalletPlatform,\n\tidentifier: string,\n): WalletId => {\n\tif (!isWalletPlatform(platform)) throw new Error(\"Invalid platform\");\n\tif (!identifier) throw new Error(\"Invalid name\");\n\treturn `${platform}:${identifier}`;\n};\n\nexport const parseWalletId = (walletId: string) => {\n\tif (!walletId) throw new Error(\"Invalid walletId\");\n\tconst [platform, identifier] = walletId.split(\":\");\n\tif (!isWalletPlatform(platform)) throw new Error(\"Invalid platform\");\n\tif (!identifier) throw new Error(\"Invalid address\");\n\treturn { platform, identifier };\n};\n\n/**\n * Non-throwing variant of {@link parseWalletId}. Use when validating untrusted\n * input (e.g. cached state persisted by an older version) before parsing.\n */\nexport const isValidWalletId = (walletId: unknown): walletId is WalletId => {\n\tif (typeof walletId !== \"string\" || !walletId) return false;\n\tif (walletId === WALLET_CONNECT_WALLET_ID) return true;\n\tconst [platform, identifier] = walletId.split(\":\");\n\treturn isWalletPlatform(platform) && !!identifier;\n};\n","import type { WalletId } from \"../utils/WalletId\";\n\n/**\n * Stable error codes thrown by kheopskit. Catch a {@link KheopskitError} and\n * branch on `error.code` instead of matching message strings.\n */\nexport type KheopskitErrorCode =\n\t/**\n\t * This `connect()` or `disconnect()` call was abandoned. For `connect()`:\n\t * its `signal` fired before the platform answered, `disconnect()` was\n\t * called while it pended, its wallet disappeared, or the last `kheopskit$`\n\t * subscriber unsubscribed.\n\t * For either call on an injected wallet: the wallet object came from a\n\t * subscription that had already ended, in which case the call rejects\n\t * without asking the platform and without changing the remembered list. A\n\t * placeholder's `connect()` rejects the same way, its `disconnect()`\n\t * resolves, and the WalletConnect connector still calls AppKit.\n\t */\n\t| \"ABORTED\"\n\t/** The user refused the wallet's connect prompt. `cause` holds the platform error. */\n\t| \"USER_REJECTED\"\n\t/** The wallet's connect call failed for another reason. `cause` holds the platform error. */\n\t| \"CONNECT_FAILED\"\n\t/**\n\t * `connect()` was called on a placeholder wallet restored from the cache:\n\t * on `kheopskit$`, hydration ended without kheopskit discovering that\n\t * wallet; on `getHydratedSnapshot` and the React provider's initial\n\t * snapshot, always.\n\t */\n\t| \"WALLET_PENDING\"\n\t/** The wallet does not advertise a capability kheopskit needs to proceed. */\n\t| \"FEATURE_NOT_SUPPORTED\"\n\t/** No active WalletConnect session for the requested operation. */\n\t| \"NO_SESSION\"\n\t/** No provider available for the requested namespace. */\n\t| \"NO_PROVIDER\"\n\t/** The installed polkadot-api version exposes neither `getTxCreatorFromPjs` (v3) nor `getPolkadotSignerFromPjs` (v2). */\n\t| \"UNSUPPORTED_VERSION\"\n\t/** The requested chain cannot be used for this operation (e.g. a Solana cluster with no CAIP-2 id over WalletConnect). */\n\t| \"UNSUPPORTED_CHAIN\"\n\t/** `getKheopskit$` received a config value it cannot use. The message names the fix. */\n\t| \"INVALID_CONFIG\";\n\n/**\n * Error thrown by kheopskit wallet/account operations. Carries a stable\n * {@link KheopskitErrorCode} and, when relevant, the offending wallet id.\n *\n * @example\n * ```ts\n * try {\n * await wallet.connect();\n * } catch (error) {\n * if (error instanceof KheopskitError && error.code === \"USER_REJECTED\") {\n * // the user closed or refused the wallet prompt\n * } else throw error;\n * }\n * ```\n */\nexport class KheopskitError extends Error {\n\treadonly code: KheopskitErrorCode;\n\t/** The wallet id this error relates to, when applicable. */\n\treadonly walletId?: WalletId;\n\n\tconstructor(\n\t\tcode: KheopskitErrorCode,\n\t\tmessage: string,\n\t\toptions?: { walletId?: WalletId; cause?: unknown },\n\t) {\n\t\tsuper(`[kheopskit] ${message}`, { cause: options?.cause });\n\t\tthis.name = \"KheopskitError\";\n\t\tthis.code = code;\n\t\tthis.walletId = options?.walletId;\n\t}\n}\n\nexport const abortedError = (\n\twalletId: WalletId,\n\toperation: \"connect\" | \"disconnect\",\n): KheopskitError =>\n\tnew KheopskitError(\n\t\t\"ABORTED\",\n\t\t`${operation} of wallet ${walletId} was aborted`,\n\t\t{ walletId },\n\t);\n\nexport const walletPendingError = (walletId: WalletId): KheopskitError =>\n\tnew KheopskitError(\n\t\t\"WALLET_PENDING\",\n\t\t`wallet ${walletId} was not discovered before hydration ended`,\n\t\t{ walletId },\n\t);\n\nexport const toConnectError = (\n\terror: unknown,\n\twalletId: WalletId,\n\tisUserRejection: (error: unknown) => boolean,\n): KheopskitError => {\n\tif (error instanceof KheopskitError) return error;\n\treturn isUserRejection(error)\n\t\t? new KheopskitError(\n\t\t\t\t\"USER_REJECTED\",\n\t\t\t\t`the user rejected the connect request of wallet ${walletId}`,\n\t\t\t\t{ walletId, cause: error },\n\t\t\t)\n\t\t: new KheopskitError(\n\t\t\t\t\"CONNECT_FAILED\",\n\t\t\t\t`wallet ${walletId} failed to connect`,\n\t\t\t\t{ walletId, cause: error },\n\t\t\t);\n};\n\nconst EIP1193_USER_REJECTED_REQUEST = 4001;\n\nexport const isEip1193UserRejection = (error: unknown): boolean =>\n\ttypeof error === \"object\" &&\n\terror !== null &&\n\t(error as { code?: unknown }).code === EIP1193_USER_REJECTED_REQUEST;\n\ntype RefusalMessage = {\n\tpattern: RegExp;\n\twallet: string;\n\tevidence: \"live\" | \"inferred\";\n};\n\n/**\n * How Polkadot extensions and Wallet Standard wallets reject a refused connect\n * prompt. They send a plain `Error`, so the message is the only signal.\n * `evidence` records whether the row was observed live or inferred from the\n * wallet's source or documentation.\n */\nconst REFUSAL_MESSAGES: readonly RefusalMessage[] = [\n\t{\n\t\tpattern: /^Site .+ has not been authorised for Talisman yet$/,\n\t\twallet: \"Talisman 3.11.0, Reject button or prompt window closed\",\n\t\tevidence: \"live\",\n\t},\n\t{\n\t\tpattern: /^Connection request was (rejected|cancelled) by the user\\.$/,\n\t\twallet: \"polkadot-js extension (extension-base 0.64 State.ts)\",\n\t\tevidence: \"inferred\",\n\t},\n\t{\n\t\tpattern: /^(Rejected|Cancelled)$/,\n\t\twallet: \"SubWallet (Extension.ts)\",\n\t\tevidence: \"inferred\",\n\t},\n\t{\n\t\tpattern: /^User rejected the request\\.$/,\n\t\twallet: \"Phantom (Wallet Standard)\",\n\t\tevidence: \"inferred\",\n\t},\n];\n\nexport const isRefusalMessage = (error: unknown): boolean => {\n\tconst message = (error as { message?: unknown } | null | undefined)?.message;\n\treturn (\n\t\ttypeof message === \"string\" &&\n\t\tREFUSAL_MESSAGES.some((row) => row.pattern.test(message))\n\t);\n};\n","import type { Observable } from \"rxjs\";\nimport type { WalletAccountId } from \"../utils\";\nimport type { WalletId } from \"../utils/WalletId\";\nimport type { KheopskitError } from \"./errors\";\n\nexport type WalletPlatform = \"polkadot\" | \"ethereum\" | \"solana\";\n\n/**\n * Minimal structural view of a WalletConnect `UniversalProvider`, the subset\n * kheopskit reads. Declared locally so core never depends on\n * `@walletconnect/universal-provider`. The concrete instance comes from\n * `@reown/appkit` at runtime.\n */\nexport type WalletConnectProvider = {\n\tsession?: {\n\t\ttopic: string;\n\t\tnamespaces: Record<string, { accounts?: string[] }>;\n\t};\n\tclient: {\n\t\trequest<T = unknown>(args: {\n\t\t\ttopic: string;\n\t\t\tchainId: string;\n\t\t\trequest: { method: string; params: unknown };\n\t\t}): Promise<T>;\n\t};\n\trequest(args: { method: string; params?: unknown }): Promise<unknown>;\n\ton(event: string, listener: (...args: unknown[]) => void): void;\n\toff(event: string, listener: (...args: unknown[]) => void): void;\n};\n\n/**\n * Minimal structural view of the Reown AppKit instance, the subset kheopskit's\n * account factories use. Exposed as the `appKit` escape hatch on AppKit wallets;\n * cast it to `@reown/appkit`'s `AppKit` type for the full API. Declared locally\n * so core never depends on `@reown/appkit`'s types (it's an optional peer).\n */\nexport type AppKitInstance = {\n\tgetProvider<T = WalletConnectProvider>(namespace: string): T | undefined;\n\tgetAccount(\n\t\tnamespace: string,\n\t): { allAccounts: { address: string }[] } | undefined;\n\tgetCaipNetworks(namespace: string): { caipNetworkId?: string }[];\n};\n\nexport type WalletType = \"injected\" | \"walletconnect\";\n\n/**\n * Where a wallet stands with the dapp. `connecting` covers a `connect()` call\n * in flight and an auto-reconnect in flight alike.\n */\nexport type WalletStatus = \"disconnected\" | \"connecting\" | \"connected\";\n\nexport type PolkadotAccountType = \"sr25519\" | \"ed25519\" | \"ecdsa\" | \"ethereum\";\n\n/** Options accepted by {@link BaseWallet.connect}. */\nexport type ConnectOptions = {\n\t/**\n\t * Ends this caller's wait for the platform's answer: its promise rejects\n\t * with a `KheopskitError` of code `ABORTED`. An abort after the platform\n\t * has granted the connect changes nothing, and a Polkadot extension counts\n\t * as granted only once its account list has been read. Other callers\n\t * sharing the attempt keep waiting. The extension's own prompt may stay\n\t * open: nothing can withdraw it from the page. A Polkadot `connect()`\n\t * started while that prompt is open waits for it instead of asking again,\n\t * and connects with the handle it answers. Once no caller waits any more\n\t * and no later `connect()` joined that prompt, kheopskit drops a handle\n\t * the platform hands back afterwards without revoking the platform's\n\t * grant, and the wallet is neither connected nor remembered. Kheopskit\n\t * sets no timeout of its own, because an extension prompt\n\t * pends only while the user has not answered it; pass\n\t * `AbortSignal.timeout(ms)` to cap the wait.\n\t */\n\tsignal?: AbortSignal;\n};\n\n/**\n * SDK-free fields common to every wallet, regardless of platform. Platform\n * packages (`@kheopskit/core/<platform>`) extend this with SDK-typed fields\n * (the injected provider/extension/standard-wallet handle).\n */\nexport type BaseWallet = {\n\t/** `<platform>:<identifier>`, e.g. `polkadot:talisman`. Stable across reloads. */\n\tid: WalletId;\n\t/** The platform whose registry listed the wallet. */\n\tplatform: WalletPlatform;\n\t/**\n\t * `\"injected\"`, for a browser extension or Wallet Standard wallet. The\n\t * WalletConnect connector is the separate {@link WalletConnectWallet}.\n\t */\n\ttype: WalletType;\n\t/** Display name, e.g. `Talisman`. */\n\tname: string;\n\t/** Icon URL or data URI. `\"\"` when the wallet exposes none. */\n\ticon: string;\n\t/**\n\t * `true` for a wallet restored from the cache that kheopskit has not\n\t * discovered yet: its SDK handle and `sourceId` are absent and it is never\n\t * connected. On `kheopskit$` its `connect()` waits for the live wallet and\n\t * connects that one, rejecting with `WALLET_PENDING` if hydration ends first\n\t * and `ABORTED` if its `signal` fires or the subscription ends. On\n\t * `getHydratedSnapshot` it rejects with `WALLET_PENDING`. Its `disconnect()`\n\t * resolves at once. Set on every wallet of `kheopskit$` and of the hydrated\n\t * snapshot; plugins leave it unset.\n\t */\n\tisPlaceholder?: boolean;\n\t/**\n\t * `connecting` while a `connect()` call or an auto-reconnect waits for the\n\t * platform, `connected` once the platform granted it, `disconnected`\n\t * otherwise. On a reload, a remembered wallet kheopskit will reconnect is\n\t * `connecting` from the first frame when the cache recorded it connected,\n\t * which the cache does 1 s after the state settles.\n\t */\n\tstatus: WalletStatus;\n\t/**\n\t * The last `connect()` or auto-reconnect that the platform refused or\n\t * failed (`USER_REJECTED` or `CONNECT_FAILED`, or `FEATURE_NOT_SUPPORTED`\n\t * for a Wallet Standard wallet without `standard:connect`). Cleared when\n\t * the next attempt starts. An aborted attempt sets nothing.\n\t */\n\terror?: KheopskitError;\n\t/** `status === \"connected\"`. */\n\tisConnected: boolean;\n\t/**\n\t * Connects the wallet.\n\t *\n\t * - **Shared in flight.** Concurrent calls on one wallet share a single\n\t * platform attempt and the platform is asked once. Each call gets its own\n\t * promise: aborting a caller's `signal` rejects only that caller with code\n\t * `ABORTED`. The attempt itself is abandoned only when every caller that\n\t * passed a signal has aborted and no caller without one joined, when\n\t * `disconnect()` is called, or when the last `kheopskit$` subscriber\n\t * unsubscribes; those reject every remaining caller with `ABORTED`.\n\t * - **Idempotent.** On a connected wallet it resolves at once without\n\t * touching the platform, unless a `disconnect()` still waits for the\n\t * platform. It then waits for that disconnect to settle and connects\n\t * again if the wallet ended up disconnected.\n\t * - **Abortable.** See {@link ConnectOptions.signal}. There is no default\n\t * timeout.\n\t * - **Settles with the state.** It resolves only after `kheopskit$` has\n\t * emitted a state in which this wallet has `isConnected: true`, so the\n\t * consumer-visible state already reflects the connect. On platforms that\n\t * list accounts asynchronously (Ethereum `eth_accounts`), the accounts may\n\t * arrive in a later emission. No emission shows a disconnected wallet\n\t * still listing live accounts, or a connected wallet listing the accounts\n\t * of a previous connection. During hydration a wallet's cached\n\t * placeholder accounts stay listed whatever its status. Another wallet's\n\t * pending `eth_accounts` request never delays them. A `disconnect()` that\n\t * completes while it waits for that state rejects it with `ABORTED`. If\n\t * the platform's disconnect is still pending or refuses, it resolves once\n\t * the state shows the wallet connected (today only Solana has a platform\n\t * disconnect). A `connect()`\n\t * whose wallet disappears before the platform grants it (an extension\n\t * removed, a Wallet Standard wallet unregistered) rejects `ABORTED`;\n\t * granted first, it resolves and the wallet stays remembered. Once the last `kheopskit$`\n\t * subscriber has unsubscribed, a call on a wallet object from that\n\t * subscription rejects with `ABORTED` without asking the platform,\n\t * placeholders included.\n\t * - **Typed rejections.** A platform rejection is wrapped in a\n\t * `KheopskitError` carrying the original error as `cause` and the\n\t * `walletId`: `USER_REJECTED` when the user refused the prompt,\n\t * `CONNECT_FAILED` otherwise. A Wallet Standard wallet without\n\t * `standard:connect` rejects with `FEATURE_NOT_SUPPORTED`.\n\t */\n\tconnect: (options?: ConnectOptions) => Promise<void>;\n\t/**\n\t * Disconnects the wallet.\n\t *\n\t * On a disconnected wallet it resolves at once. While a `connect()` waits\n\t * for the platform to grant it, it aborts that connect (its promise rejects\n\t * with `ABORTED`) and never waits for the extension prompt. Otherwise it\n\t * first waits for the platform's disconnect and rejects if that fails, so\n\t * callers can surface or retry. It then resolves once `kheopskit$` has\n\t * emitted a state in which this wallet has `isConnected: false`, or once a\n\t * later `connect()`, the wallet leaving the list or the end of the\n\t * subscription makes that emission impossible. Once the last `kheopskit$`\n\t * subscriber has unsubscribed, a call on a wallet object from that\n\t * subscription rejects with `ABORTED` without asking the platform and\n\t * without changing the remembered list, except on a placeholder, whose\n\t * `disconnect()` resolves. A `disconnect()` started before that point\n\t * settles once the platform has answered: the wallet was\n\t * forgotten when it was called, and nothing else is written once the\n\t * subscription has ended.\n\t */\n\tdisconnect: () => Promise<void>;\n};\n\n/**\n * SDK-free fields common to every account, regardless of platform. Platform\n * packages extend this with their SDK-typed signer/client.\n */\nexport type BaseWalletAccount = {\n\t/** `<walletId>::<address>`, e.g. `polkadot:talisman::5Grw…`. */\n\tid: WalletAccountId;\n\t/**\n\t * `true` for an account restored from the cache that the live wallet has\n\t * not listed yet: its signing surface is absent. Set on every account of\n\t * `kheopskit$` and of the hydrated snapshot; plugins leave it unset.\n\t */\n\tisPlaceholder?: boolean;\n\t/** The platform of the wallet that lists the account. */\n\tplatform: WalletPlatform;\n\t/** Base58 (Solana), SS58 (Polkadot) or 0x-hex (Ethereum) address. */\n\taddress: string;\n\t/**\n\t * Friendly account name, when the wallet exposes one: the extension's\n\t * account name on Polkadot, the Wallet Standard `label` on Solana. Always\n\t * `undefined` on Ethereum.\n\t */\n\tname?: string;\n\t/** `name` of the wallet that lists the account. */\n\twalletName: string;\n\t/** `id` of the wallet that lists the account. */\n\twalletId: WalletId;\n};\n\ntype LiveWalletConnectWallet = {\n\t/** See {@link BaseWallet.isPlaceholder}. */\n\tisPlaceholder?: false;\n\t/** `\"walletconnect\"`, the one connector shared by every platform. */\n\tid: WalletId;\n\t/** Narrow with {@link isWalletConnectWallet}. */\n\ttype: \"walletconnect\";\n\t/**\n\t * Platforms (namespaces) the live session currently carries. Empty until\n\t * connected; populated with whatever the wallet approved (e.g. just\n\t * `[\"ethereum\"]`, or `[\"polkadot\", \"ethereum\"]`).\n\t */\n\tplatforms: WalletPlatform[];\n\t/**\n\t * Raw Reown AppKit instance, exposed as an escape hatch for advanced use\n\t * (custom modal control, reading providers directly). Most consumers should\n\t * use `connect`/`disconnect` and the derived accounts instead.\n\t */\n\tappKit: AppKitInstance;\n\t/** The name AppKit reports for the paired wallet, `\"WalletConnect\"` when it reports none. */\n\tname: string;\n\t/** The icon AppKit reports for the paired wallet, the WalletConnect logo when it reports none. */\n\ticon: string;\n\t/**\n\t * `connecting` while `connect()` opens the modal, `connected` while a\n\t * session is live, `disconnected` otherwise. On a reload with a cached\n\t * session it is `connecting` until AppKit restores the session or hydration\n\t * ends.\n\t */\n\tstatus: WalletStatus;\n\t/** The last `connect()` whose modal failed to open. Cleared by the next call. */\n\terror?: KheopskitError;\n\t/** `status === \"connected\"`. */\n\tisConnected: boolean;\n\t/**\n\t * Opens the WalletConnect modal and resolves once it is open; `isConnected`\n\t * follows when the user approves a session. Unlike injected wallets, this\n\t * promise does not wait for the state to show the connector connected, and\n\t * `signal` is accepted for signature compatibility only.\n\t */\n\tconnect: (options?: ConnectOptions) => Promise<void>;\n\t/** Ends the live session when there is one; resolves at once otherwise. */\n\tdisconnect: () => Promise<void>;\n};\n\n/**\n * The single WalletConnect connector, shared across every platform.\n *\n * Unlike injected wallets, it is **not tied to a platform**: one WalletConnect\n * session spans whichever namespaces the remote wallet approves in a single\n * pairing (a namespace can't be added to a live session afterwards). So there is\n * exactly one of these in `wallets`, discriminated by `type: \"walletconnect\"`.\n * Its accounts appear in `accounts`, each carrying its own `platform`.\n *\n * A placeholder (see {@link BaseWallet.isPlaceholder}) has no `appKit`.\n */\nexport type WalletConnectWallet =\n\t| LiveWalletConnectWallet\n\t| PlaceholderWallet<LiveWalletConnectWallet, \"appKit\">;\n\n/**\n * The cached stand-in for a live item `T`: `isPlaceholder: true`, and the\n * fields `K`, which only the live item carries, absent. Platform account and\n * wallet types are `T | Placeholder<T, K>`, so narrow on `isPlaceholder`\n * before reading a field of `K`.\n */\nexport type Placeholder<T, K extends keyof T> = Omit<T, K | \"isPlaceholder\"> & {\n\t/** Restored from the cache and not yet replaced by the live item. */\n\tisPlaceholder: true;\n} & { [P in K]?: undefined };\n\n/** A {@link Placeholder} wallet, which is never connected. */\nexport type PlaceholderWallet<\n\tT extends { isConnected: boolean },\n\tK extends keyof T,\n> = Omit<Placeholder<T, K>, \"isConnected\"> & { isConnected: false };\n\n/** Narrows a wallet to the platform-less {@link WalletConnectWallet}. */\nexport const isWalletConnectWallet = (\n\twallet: BaseWallet | WalletConnectWallet,\n): wallet is WalletConnectWallet => wallet.type === \"walletconnect\";\n\n/**\n * Narrows a wallet to an injected (browser-extension / Wallet Standard) wallet,\n * the complement of {@link isWalletConnectWallet}. `state.wallets` is\n * `(InjectedWallet | WalletConnectWallet)[]`, so use this to recover the\n * injected-only fields when iterating: `platform`, `sourceId`, and the SDK\n * handle (`provider` on Ethereum, `extension` on Polkadot, `wallet` on Solana).\n *\n * @example\n * ```ts\n * for (const wallet of wallets.filter(isInjectedWallet)) {\n * console.log(wallet.platform, wallet.sourceId); // typed, no WC widening\n * }\n * ```\n */\nexport const isInjectedWallet = <W extends BaseWallet | WalletConnectWallet>(\n\twallet: W,\n): wallet is Exclude<W, WalletConnectWallet> => wallet.type === \"injected\";\n\n/**\n * Dapp metadata shown in the WalletConnect modal. Mirrors WalletConnect's\n * `Metadata`, declared locally so core doesn't depend on\n * `@walletconnect/universal-provider`.\n */\nexport type WalletConnectMetadata = {\n\tname: string;\n\tdescription: string;\n\turl: string;\n\ticons: string[];\n};\n\n/**\n * The WalletConnect connector for `config.walletConnect`. Create it with\n * `walletConnect()` from `@kheopskit/core/walletconnect`; that entry is the only\n * one that loads Reown AppKit.\n */\nexport type WalletConnectPlugin = {\n\treadonly type: \"walletconnect\";\n\treadonly metadata: WalletConnectMetadata;\n\tgetWallet$(ctx: PlatformContext): Observable<WalletConnectWallet | null>;\n};\n\n/**\n * Context passed to a platform plugin's `getWallets$`. Carries the shared store\n * and the resolved core config (for WalletConnect / debug).\n */\nexport type PlatformContext = {\n\tstore: KheopskitStore;\n\tconfig: KheopskitConfig;\n\t/**\n\t * Moments to re-read a registry that has no announce event. Emits at\n\t * subscribe and on a tail restarted at window `load`, and completes after\n\t * the last read. {@link KheopskitState.isDiscovering} turns `false` on the\n\t * first wallet list after that, which waits for a WalletConnect connector\n\t * still loading.\n\t */\n\tregistryReads$?: Observable<void>;\n};\n\n/**\n * A platform plugin. Created by the per-platform factories exported from\n * `@kheopskit/core/polkadot`, `/ethereum`, `/solana`. Core iterates plugins\n * generically and never imports a platform SDK itself.\n *\n * @typeParam TPlatform - the platform discriminant\n * @typeParam TWallet - the platform's wallet type (extends {@link BaseWallet})\n * @typeParam TAccount - the platform's account type (extends {@link BaseWalletAccount})\n */\nexport type KheopskitPlatform<\n\tTPlatform extends WalletPlatform = WalletPlatform,\n\tTWallet extends BaseWallet = BaseWallet,\n\tTAccount extends BaseWalletAccount = BaseWalletAccount,\n> = {\n\treadonly platform: TPlatform;\n\t// Declared as methods (not arrow properties) so parameters are checked\n\t// bivariantly — this lets a specific `KheopskitPlatform<\"polkadot\", …>` be\n\t// assigned to the base `KheopskitPlatform` despite the contravariant\n\t// `wallets$` parameter.\n\tgetWallets$(ctx: PlatformContext): Observable<TWallet[]>;\n\t// Receives this platform's (injected) wallets plus the shared, platform-less\n\t// WalletConnect connector — the plugin derives its injected accounts and, if\n\t// the WC session carries its namespace, its WalletConnect accounts.\n\tgetAccounts$(\n\t\twallets$: Observable<(TWallet | WalletConnectWallet)[]>,\n\t): Observable<TAccount[]>;\n\t/**\n\t * Optional hydration filter. Cached accounts for which this returns false are\n\t * dropped during SSR hydration (Polkadot uses it to honour `accountTypes`).\n\t */\n\tacceptsCachedAccount?(cached: CachedAccount): boolean;\n};\n\ntype ElementOf<T> = T extends readonly (infer E)[] ? E : never;\n\n/** The account type produced by a plugin (inferred from its `getAccounts$`). */\nexport type AccountOf<T> = T extends {\n\tgetAccounts$: (wallets$: never) => Observable<infer R>;\n}\n\t? ElementOf<R>\n\t: never;\n\n/** The wallet type produced by a plugin (inferred from its `getWallets$`). */\nexport type WalletOf<T> = T extends {\n\tgetWallets$: (ctx: never) => Observable<infer R>;\n}\n\t? ElementOf<R>\n\t: never;\n\nexport type KheopskitConfig<\n\tP extends readonly KheopskitPlatform[] = readonly KheopskitPlatform[],\n> = {\n\t/**\n\t * Which remembered injected wallets kheopskit reconnects after a reload.\n\t *\n\t * A wallet is remembered on every successful `connect()` and forgotten on\n\t * `disconnect()`, whatever this option says; the list is the store's\n\t * `remembered`. `true` reconnects every remembered injected wallet, `false`\n\t * none, and a function is asked for each cached connected injected wallet\n\t * on the first frame, then for each remembered one with its live object\n\t * before the attempt. The function must be synchronous and pure. It runs on\n\t * the server and on the client for the same input and both must agree, or\n\t * the markup diverges. It receives the {@link BaseWallet} fields only, so\n\t * key on `id`.\n\t *\n\t * The WalletConnect row is not governed by this option: AppKit restores its\n\t * own session on load, and the row hydrates `connecting` while it does.\n\t *\n\t * A reconnect opens no connect prompt while the site stays authorised in\n\t * the extension. When the authorisation was revoked the extension prompts\n\t * once; a refusal forgets the wallet, so the next load stays silent. A manual\n\t * `connect()` or `disconnect()` on a remembered wallet ends its automatic\n\t * reconnect for the session. A `disconnect()` while the automatic reconnect\n\t * still waits for the extension aborts it without forgetting the wallet, so\n\t * the next load reconnects it.\n\t * Automatic reconnect also ends when the wallet disappears mid-attempt; the\n\t * wallet stays remembered for the next load.\n\t *\n\t * A cached connected wallet this accepts shows `status: \"connecting\"` from\n\t * the first frame until its attempt settles. A wallet this rejects shows\n\t * `disconnected` from the first frame, without its cached accounts, so\n\t * nothing flashes from connected to disconnected.\n\t *\n\t * @default true\n\t */\n\tautoReconnect: boolean | ((wallet: BaseWallet) => boolean);\n\t/**\n\t * Platform plugins to enable, e.g. `[polkadot(), solana({ chain })]`.\n\t * Import factories from `@kheopskit/core/<platform>`.\n\t */\n\tplatforms: P;\n\t/** Create with `walletConnect()` from `@kheopskit/core/walletconnect`. */\n\twalletConnect?: WalletConnectPlugin;\n\t/**\n\t * Dapp name sent to Polkadot injected extensions on connect. Some\n\t * extensions (e.g. polkadot.js) warn about a suspicious origin when it's\n\t * empty.\n\t *\n\t * @default walletConnect.metadata.name, then window.location.hostname\n\t */\n\tappName?: string;\n\t/**\n\t * Logs the resolved config, every state emission and the hydration and\n\t * persistence steps with `console.debug`.\n\t *\n\t * @default false\n\t */\n\tdebug: boolean;\n\t/**\n\t * Custom storage key for persisting wallet connection state.\n\t * Useful when running multiple kheopskit instances on the same domain\n\t * to prevent state conflicts between different dapps.\n\t *\n\t * @default \"kheopskit\"\n\t */\n\tstorageKey: string;\n\t/**\n\t * Minimum time in milliseconds the cached placeholders are shown after a\n\t * reload, so the wallet list does not flash while extensions inject and\n\t * remembered wallets reconnect. Hydration goes on past it until the rule\n\t * documented on {@link KheopskitConfig.hydrationTimeout} ends it, and that\n\t * timeout wins over a longer grace period. The old cap of six times this\n\t * value is gone, so an app that raises it sets `hydrationTimeout` as well.\n\t *\n\t * `0` disables placeholders: `isHydrating` is `false` from the first\n\t * emission.\n\t *\n\t * @default 500\n\t */\n\thydrationGracePeriod: number;\n\t/**\n\t * Upper bound in milliseconds on {@link KheopskitState.isHydrating}.\n\t *\n\t * `isHydrating` turns `false` once `hydrationGracePeriod` has elapsed and\n\t * every cached wallet has settled. A cached wallet missing from the\n\t * discovered wallets settles when discovery ends\n\t * ({@link KheopskitState.isDiscovering} is `false`). A discovered one\n\t * settles at once unless it hydrated as `connecting`. Then it settles when\n\t * connected once its account list has been read (an empty list counts) or\n\t * connected with no cached accounts, disconnected with `error` set, or\n\t * disconnected and not remembered when hydration began. At\n\t * `hydrationTimeout` hydration ends whatever is pending, and a wallet whose\n\t * reconnect is still pending stays `connecting`.\n\t *\n\t * - When every cached wallet is discovered and settled, hydration ends at\n\t * the grace period, without waiting for `load`. A cached wallet that has\n\t * not appeared holds hydration until discovery ends (1100 ms after `load`\n\t * or after subscribe, whichever is later, or later while a WalletConnect\n\t * connector loads), or until `hydrationTimeout`.\n\t * - A wallet that reconnects without sharing any account with the site\n\t * settles as soon as it answers; its cached accounts leave the list when\n\t * hydration ends.\n\t * - A failed reconnect ends that wallet's wait, even when the reconnect\n\t * policy still permits a retry. A retry runs only when the wallet list next\n\t * changes (a wallet discovered or removed, or any wallet's status, error\n\t * or name changing), which may never happen, so waiting for it would\n\t * hold most failed reconnects until `hydrationTimeout`. A retry that\n\t * does run shows on the live wallet's `status`, not as a placeholder.\n\t * - A remembered wallet the user disconnects during or right after its\n\t * reconnect holds hydration, and shows its cached accounts, until\n\t * `hydrationTimeout`.\n\t * - A prompt the extension opens for a revoked site holds hydration until\n\t * the user answers or `hydrationTimeout` elapses.\n\t *\n\t * @default 3000\n\t */\n\thydrationTimeout: number;\n};\n\n/**\n * The current kheopskit state.\n *\n * @remarks\n * While {@link KheopskitState.isHydrating} is `true`, `wallets` and `accounts`\n * may contain placeholders restored from the cache, flagged\n * `isPlaceholder: true`. Each platform type is a union on that flag: a\n * placeholder lacks the SDK handles (`account.txCreator` or `polkadotSigner`\n * on Polkadot, `account.client` on Ethereum, `account.signer` / `getSigner` on\n * Solana, `wallet.extension` / `provider` / `wallet` and `sourceId`). Reading\n * one without narrowing yields a possibly-`undefined` value, so the compiler\n * rejects using it as present until you narrow:\n *\n * ```ts\n * if (account.isPlaceholder) return;\n * account.txCreator; // TxCreator\n * ```\n *\n * A placeholder wallet's `connect()` waits for the live wallet and connects\n * it, rejecting with `WALLET_PENDING` if hydration ends first. No item is a\n * placeholder once `isHydrating` is `false`.\n *\n * The plain, serializable platform data that is persisted in the cache IS\n * restored on the placeholders (Ethereum `chainId`, Polkadot key `type`), so it\n * renders immediately on reload without flashing. Solana `chains` is not\n * cached: a placeholder account's is empty.\n */\nexport type KheopskitState<\n\tP extends readonly KheopskitPlatform[] = readonly KheopskitPlatform[],\n> = {\n\t/**\n\t * Every discovered wallet, connected or not, then the WalletConnect\n\t * connector once AppKit has loaded, when `config.walletConnect` is set.\n\t * While `isHydrating` is `true`, also the cached placeholders. Sorted by\n\t * platform (Polkadot, Ethereum, Solana, WalletConnect), then Talisman\n\t * first, then by name.\n\t */\n\twallets: (WalletOf<P[number]> | WalletConnectWallet)[];\n\t/**\n\t * The accounts of every connected wallet. While `isHydrating` is `true`,\n\t * also the cached placeholders of every wallet that has no live account\n\t * yet. Grouped by platform, then by wallet (Talisman first, then by wallet\n\t * name); inside a wallet, in the order the wallet lists them.\n\t */\n\taccounts: AccountOf<P[number]>[];\n\t/** The resolved config, with every default filled in. */\n\tconfig: KheopskitConfig<P>;\n\t/**\n\t * Whether the state is still being hydrated from cache.\n\t *\n\t * During hydration, cached wallets/accounts may be displayed before the\n\t * actual wallet extensions have injected. See the type-level remarks: those\n\t * are flagged `isPlaceholder: true` and carry no SDK handle.\n\t */\n\tisHydrating: boolean;\n\t/**\n\t * Whether wallet discovery may still be incomplete.\n\t *\n\t * `true` until the Polkadot registry (`window.injectedWeb3`) read window\n\t * ends, 1100 ms after `load` or after subscribe, whichever is later. With\n\t * `config.walletConnect`, it also waits for the connector to load. The\n\t * emission that turns it `false` lists every Polkadot extension injected\n\t * before the last read. A wallet that injects later is not listed until\n\t * the next subscription. Always `true` on the server.\n\t *\n\t * Ethereum (EIP-6963) and Solana (Wallet Standard) registries are\n\t * event-driven and need no re-read.\n\t */\n\tisDiscovering: boolean;\n};\n\n/** Shape of the persisted store data (localStorage, or cookies in compact form). */\nexport type KheopskitStoreData = {\n\t/** Wallets the user connected and has not disconnected */\n\tremembered?: WalletId[];\n\t/** Cached wallet state for SSR hydration to prevent UI flash */\n\tcachedWallets?: CachedWallet[];\n\t/** Cached account state for SSR hydration to prevent UI flash */\n\tcachedAccounts?: CachedAccount[];\n};\n\n/**\n * The persisted settings store shared between core and framework bindings.\n * Declared structurally here (implemented by `createKheopskitStore`) so modules\n * can depend on the interface without importing the implementation.\n */\nexport type KheopskitStore = {\n\tobservable: Observable<KheopskitStoreData>;\n\trememberWallet: (walletId: WalletId) => void;\n\tforgetWallet: (walletId: WalletId) => void;\n\tgetCachedState: () => { wallets: CachedWallet[]; accounts: CachedAccount[] };\n\tsetCachedState: (wallets: CachedWallet[], accounts: CachedAccount[]) => void;\n\t/** Stops cross-tab storage sync and completes `observable`. */\n\tdestroy: () => void;\n};\n\n/**\n * Serializable wallet data for SSR hydration cache.\n * Contains only the data needed to render wallet UI without flash.\n * Note: icon is NOT stored to save cookie space - it's looked up at hydration time.\n */\nexport type CachedWallet = {\n\tid: WalletId;\n\t/** Absent for the platform-less WalletConnect connector. */\n\tplatform?: WalletPlatform;\n\ttype: WalletType;\n\tname: string;\n\tisConnected: boolean;\n};\n\n/**\n * Serializable account data for SSR hydration cache.\n * Contains only the data needed to render account UI without flash.\n */\nexport type CachedAccount = {\n\tid: string;\n\tplatform: WalletPlatform;\n\taddress: string;\n\tname?: string;\n\t/** Cached chain ID for Ethereum accounts. */\n\tchainId?: number;\n\t/** Cached key type for Polkadot accounts. */\n\tpolkadotAccountType?: PolkadotAccountType;\n\twalletId: WalletId;\n\twalletName: string;\n};\n"],"mappings":"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"}
@@ -149,12 +149,14 @@ type ConnectOptions = {
149
149
  * with a `KheopskitError` of code `ABORTED`. An abort after the platform
150
150
  * has granted the connect changes nothing, and a Polkadot extension counts
151
151
  * as granted only once its account list has been read. Other callers
152
- * sharing the attempt keep waiting. Once no caller waits any more,
153
- * kheopskit drops a handle the platform hands back afterwards without
154
- * revoking the platform's grant, and the wallet is neither connected nor
155
- * remembered. The
156
- * extension's own prompt may stay open: nothing can withdraw it from the
157
- * page. Kheopskit sets no timeout of its own, because an extension prompt
152
+ * sharing the attempt keep waiting. The extension's own prompt may stay
153
+ * open: nothing can withdraw it from the page. A Polkadot `connect()`
154
+ * started while that prompt is open waits for it instead of asking again,
155
+ * and connects with the handle it answers. Once no caller waits any more
156
+ * and no later `connect()` joined that prompt, kheopskit drops a handle
157
+ * the platform hands back afterwards without revoking the platform's
158
+ * grant, and the wallet is neither connected nor remembered. Kheopskit
159
+ * sets no timeout of its own, because an extension prompt
158
160
  * pends only while the user has not answered it; pass
159
161
  * `AbortSignal.timeout(ms)` to cap the wait.
160
162
  */
@@ -287,7 +289,11 @@ type BaseWalletAccount = {
287
289
  platform: WalletPlatform;
288
290
  /** Base58 (Solana), SS58 (Polkadot) or 0x-hex (Ethereum) address. */
289
291
  address: string;
290
- /** Friendly account name, when the wallet exposes one (e.g. Polkadot). */
292
+ /**
293
+ * Friendly account name, when the wallet exposes one: the extension's
294
+ * account name on Polkadot, the Wallet Standard `label` on Solana. Always
295
+ * `undefined` on Ethereum.
296
+ */
291
297
  name?: string;
292
298
  /** `name` of the wallet that lists the account. */
293
299
  walletName: string;
@@ -411,6 +417,14 @@ type WalletConnectPlugin = {
411
417
  type PlatformContext = {
412
418
  store: KheopskitStore;
413
419
  config: KheopskitConfig;
420
+ /**
421
+ * Moments to re-read a registry that has no announce event. Emits at
422
+ * subscribe and on a tail restarted at window `load`, and completes after
423
+ * the last read. {@link KheopskitState.isDiscovering} turns `false` on the
424
+ * first wallet list after that, which waits for a WalletConnect connector
425
+ * still loading.
426
+ */
427
+ registryReads$?: Observable<void>;
414
428
  };
415
429
  /**
416
430
  * A platform plugin. Created by the per-platform factories exported from
@@ -522,19 +536,22 @@ type KheopskitConfig<P extends readonly KheopskitPlatform[] = readonly Kheopskit
522
536
  /**
523
537
  * Upper bound in milliseconds on {@link KheopskitState.isHydrating}.
524
538
  *
525
- * `isHydrating` turns `false` once `hydrationGracePeriod` has elapsed,
526
- * discovery has ended ({@link KheopskitState.isDiscovering} is `false`),
527
- * and every wallet that hydrated as `connecting` has settled. A wallet has
528
- * settled when it is absent from the discovered wallets, connected once its
529
- * account list has been read (an empty list counts) or connected with no
530
- * cached accounts, disconnected with `error` set, or disconnected and not
531
- * remembered when hydration began. At `hydrationTimeout` hydration ends
532
- * whatever is pending, and a wallet whose reconnect is still pending stays
533
- * `connecting`.
539
+ * `isHydrating` turns `false` once `hydrationGracePeriod` has elapsed and
540
+ * every cached wallet has settled. A cached wallet missing from the
541
+ * discovered wallets settles when discovery ends
542
+ * ({@link KheopskitState.isDiscovering} is `false`). A discovered one
543
+ * settles at once unless it hydrated as `connecting`. Then it settles when
544
+ * connected once its account list has been read (an empty list counts) or
545
+ * connected with no cached accounts, disconnected with `error` set, or
546
+ * disconnected and not remembered when hydration began. At
547
+ * `hydrationTimeout` hydration ends whatever is pending, and a wallet whose
548
+ * reconnect is still pending stays `connecting`.
534
549
  *
535
- * - Discovery ends only after the page fires `load`, so a page with a cache
536
- * hydrates until `load` or `hydrationTimeout`, whichever comes first,
537
- * even when no wallet reconnects.
550
+ * - When every cached wallet is discovered and settled, hydration ends at
551
+ * the grace period, without waiting for `load`. A cached wallet that has
552
+ * not appeared holds hydration until discovery ends (1100 ms after `load`
553
+ * or after subscribe, whichever is later, or later while a WalletConnect
554
+ * connector loads), or until `hydrationTimeout`.
538
555
  * - A wallet that reconnects without sharing any account with the site
539
556
  * settles as soon as it answers; its cached accounts leave the list when
540
557
  * hydration ends.
@@ -610,16 +627,15 @@ type KheopskitState<P extends readonly KheopskitPlatform[] = readonly KheopskitP
610
627
  /**
611
628
  * Whether wallet discovery may still be incomplete.
612
629
  *
613
- * `true` when the subscription started before `document.readyState` was
614
- * `"complete"`. It turns `false` in the first emission after `window` fired
615
- * `load` and the Polkadot registry (`window.injectedWeb3`) was read again,
616
- * so that emission already lists every extension injected by `load`. When the
617
- * subscription starts after `load`, it is `false` from the first emission.
618
- * Always `true` on the server.
630
+ * `true` until the Polkadot registry (`window.injectedWeb3`) read window
631
+ * ends, 1100 ms after `load` or after subscribe, whichever is later. With
632
+ * `config.walletConnect`, it also waits for the connector to load. The
633
+ * emission that turns it `false` lists every Polkadot extension injected
634
+ * before the last read. A wallet that injects later is not listed until
635
+ * the next subscription. Always `true` on the server.
619
636
  *
620
- * This rests on wallets injecting `injectedWeb3` before `load`. It says
621
- * nothing about wallets that inject after `load`. Ethereum (EIP-6963) and
622
- * Solana (Wallet Standard) registries are event-driven and need no re-read.
637
+ * Ethereum (EIP-6963) and Solana (Wallet Standard) registries are
638
+ * event-driven and need no re-read.
623
639
  */
624
640
  isDiscovering: boolean;
625
641
  };
@@ -680,4 +696,4 @@ type CachedAccount = {
680
696
  };
681
697
  //#endregion
682
698
  export { getWalletId as A, WalletStatus as C, KheopskitError as D, isWalletConnectWallet as E, parseWalletAccountId as F, parseWalletId as M, WalletAccountId as N, KheopskitErrorCode as O, getWalletAccountId as P, WalletPlatform as S, isInjectedWallet as T, WalletConnectMetadata as _, CachedAccount as a, WalletConnectWallet as b, KheopskitConfig as c, KheopskitStore as d, KheopskitStoreData as f, PolkadotAccountType as g, PlatformContext as h, BaseWalletAccount as i, isValidWalletId as j, WalletId as k, KheopskitPlatform as l, PlaceholderWallet as m, AppKitInstance as n, CachedWallet as o, Placeholder as p, BaseWallet as r, ConnectOptions as s, AccountOf as t, KheopskitState as u, WalletConnectPlugin as v, WalletType as w, WalletOf as x, WalletConnectProvider as y };
683
- //# sourceMappingURL=types-Dkyt9bf4.d.ts.map
699
+ //# sourceMappingURL=types-DCaL9bIc.d.mts.map
@@ -149,12 +149,14 @@ type ConnectOptions = {
149
149
  * with a `KheopskitError` of code `ABORTED`. An abort after the platform
150
150
  * has granted the connect changes nothing, and a Polkadot extension counts
151
151
  * as granted only once its account list has been read. Other callers
152
- * sharing the attempt keep waiting. Once no caller waits any more,
153
- * kheopskit drops a handle the platform hands back afterwards without
154
- * revoking the platform's grant, and the wallet is neither connected nor
155
- * remembered. The
156
- * extension's own prompt may stay open: nothing can withdraw it from the
157
- * page. Kheopskit sets no timeout of its own, because an extension prompt
152
+ * sharing the attempt keep waiting. The extension's own prompt may stay
153
+ * open: nothing can withdraw it from the page. A Polkadot `connect()`
154
+ * started while that prompt is open waits for it instead of asking again,
155
+ * and connects with the handle it answers. Once no caller waits any more
156
+ * and no later `connect()` joined that prompt, kheopskit drops a handle
157
+ * the platform hands back afterwards without revoking the platform's
158
+ * grant, and the wallet is neither connected nor remembered. Kheopskit
159
+ * sets no timeout of its own, because an extension prompt
158
160
  * pends only while the user has not answered it; pass
159
161
  * `AbortSignal.timeout(ms)` to cap the wait.
160
162
  */
@@ -287,7 +289,11 @@ type BaseWalletAccount = {
287
289
  platform: WalletPlatform;
288
290
  /** Base58 (Solana), SS58 (Polkadot) or 0x-hex (Ethereum) address. */
289
291
  address: string;
290
- /** Friendly account name, when the wallet exposes one (e.g. Polkadot). */
292
+ /**
293
+ * Friendly account name, when the wallet exposes one: the extension's
294
+ * account name on Polkadot, the Wallet Standard `label` on Solana. Always
295
+ * `undefined` on Ethereum.
296
+ */
291
297
  name?: string;
292
298
  /** `name` of the wallet that lists the account. */
293
299
  walletName: string;
@@ -411,6 +417,14 @@ type WalletConnectPlugin = {
411
417
  type PlatformContext = {
412
418
  store: KheopskitStore;
413
419
  config: KheopskitConfig;
420
+ /**
421
+ * Moments to re-read a registry that has no announce event. Emits at
422
+ * subscribe and on a tail restarted at window `load`, and completes after
423
+ * the last read. {@link KheopskitState.isDiscovering} turns `false` on the
424
+ * first wallet list after that, which waits for a WalletConnect connector
425
+ * still loading.
426
+ */
427
+ registryReads$?: Observable<void>;
414
428
  };
415
429
  /**
416
430
  * A platform plugin. Created by the per-platform factories exported from
@@ -522,19 +536,22 @@ type KheopskitConfig<P extends readonly KheopskitPlatform[] = readonly Kheopskit
522
536
  /**
523
537
  * Upper bound in milliseconds on {@link KheopskitState.isHydrating}.
524
538
  *
525
- * `isHydrating` turns `false` once `hydrationGracePeriod` has elapsed,
526
- * discovery has ended ({@link KheopskitState.isDiscovering} is `false`),
527
- * and every wallet that hydrated as `connecting` has settled. A wallet has
528
- * settled when it is absent from the discovered wallets, connected once its
529
- * account list has been read (an empty list counts) or connected with no
530
- * cached accounts, disconnected with `error` set, or disconnected and not
531
- * remembered when hydration began. At `hydrationTimeout` hydration ends
532
- * whatever is pending, and a wallet whose reconnect is still pending stays
533
- * `connecting`.
539
+ * `isHydrating` turns `false` once `hydrationGracePeriod` has elapsed and
540
+ * every cached wallet has settled. A cached wallet missing from the
541
+ * discovered wallets settles when discovery ends
542
+ * ({@link KheopskitState.isDiscovering} is `false`). A discovered one
543
+ * settles at once unless it hydrated as `connecting`. Then it settles when
544
+ * connected once its account list has been read (an empty list counts) or
545
+ * connected with no cached accounts, disconnected with `error` set, or
546
+ * disconnected and not remembered when hydration began. At
547
+ * `hydrationTimeout` hydration ends whatever is pending, and a wallet whose
548
+ * reconnect is still pending stays `connecting`.
534
549
  *
535
- * - Discovery ends only after the page fires `load`, so a page with a cache
536
- * hydrates until `load` or `hydrationTimeout`, whichever comes first,
537
- * even when no wallet reconnects.
550
+ * - When every cached wallet is discovered and settled, hydration ends at
551
+ * the grace period, without waiting for `load`. A cached wallet that has
552
+ * not appeared holds hydration until discovery ends (1100 ms after `load`
553
+ * or after subscribe, whichever is later, or later while a WalletConnect
554
+ * connector loads), or until `hydrationTimeout`.
538
555
  * - A wallet that reconnects without sharing any account with the site
539
556
  * settles as soon as it answers; its cached accounts leave the list when
540
557
  * hydration ends.
@@ -610,16 +627,15 @@ type KheopskitState<P extends readonly KheopskitPlatform[] = readonly KheopskitP
610
627
  /**
611
628
  * Whether wallet discovery may still be incomplete.
612
629
  *
613
- * `true` when the subscription started before `document.readyState` was
614
- * `"complete"`. It turns `false` in the first emission after `window` fired
615
- * `load` and the Polkadot registry (`window.injectedWeb3`) was read again,
616
- * so that emission already lists every extension injected by `load`. When the
617
- * subscription starts after `load`, it is `false` from the first emission.
618
- * Always `true` on the server.
630
+ * `true` until the Polkadot registry (`window.injectedWeb3`) read window
631
+ * ends, 1100 ms after `load` or after subscribe, whichever is later. With
632
+ * `config.walletConnect`, it also waits for the connector to load. The
633
+ * emission that turns it `false` lists every Polkadot extension injected
634
+ * before the last read. A wallet that injects later is not listed until
635
+ * the next subscription. Always `true` on the server.
619
636
  *
620
- * This rests on wallets injecting `injectedWeb3` before `load`. It says
621
- * nothing about wallets that inject after `load`. Ethereum (EIP-6963) and
622
- * Solana (Wallet Standard) registries are event-driven and need no re-read.
637
+ * Ethereum (EIP-6963) and Solana (Wallet Standard) registries are
638
+ * event-driven and need no re-read.
623
639
  */
624
640
  isDiscovering: boolean;
625
641
  };
@@ -680,4 +696,4 @@ type CachedAccount = {
680
696
  };
681
697
  //#endregion
682
698
  export { getWalletId as A, WalletStatus as C, KheopskitError as D, isWalletConnectWallet as E, parseWalletAccountId as F, parseWalletId as M, WalletAccountId as N, KheopskitErrorCode as O, getWalletAccountId as P, WalletPlatform as S, isInjectedWallet as T, WalletConnectMetadata as _, CachedAccount as a, WalletConnectWallet as b, KheopskitConfig as c, KheopskitStore as d, KheopskitStoreData as f, PolkadotAccountType as g, PlatformContext as h, BaseWalletAccount as i, isValidWalletId as j, WalletId as k, KheopskitPlatform as l, PlaceholderWallet as m, AppKitInstance as n, CachedWallet as o, Placeholder as p, BaseWallet as r, ConnectOptions as s, AccountOf as t, KheopskitState as u, WalletConnectPlugin as v, WalletType as w, WalletOf as x, WalletConnectProvider as y };
683
- //# sourceMappingURL=types-Dkyt9bf4.d.mts.map
699
+ //# sourceMappingURL=types-DCaL9bIc.d.ts.map