@solana/kit-plugin-wallet 0.13.0 → 0.13.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,14 @@
1
- import { isWalletWarmingUp } from '../chunk-MSZ5MCI7.browser.mjs';
2
1
  import { useAction, useClientCapability } from '@solana/react';
3
2
  import { useSyncExternalStore } from 'react';
4
3
 
4
+ // src/react/index.ts
5
+
6
+ // src/status.ts
7
+ function isWalletWarmingUp(status) {
8
+ return status === "pending" || status === "reconnecting";
9
+ }
10
+
11
+ // src/react/index.ts
5
12
  function useWalletNamespace(hookName) {
6
13
  return useClientCapability({
7
14
  capability: "wallet",
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/react/index.ts"],"names":[],"mappings":";;;;AAeA,SAAS,mBAAmB,QAAA,EAAmC;AAC3D,EAAA,OAAO,mBAAA,CAAsC;AAAA,IACzC,UAAA,EAAY,QAAA;AAAA,IACZ,QAAA;AAAA,IACA,YAAA,EAAc;AAAA,GACjB,CAAA,CAAE,MAAA;AACP;AAsBO,SAAS,eAAA,GAAgC;AAC5C,EAAA,MAAM,MAAA,GAAS,mBAAmB,iBAAiB,CAAA;AACnD,EAAA,MAAM,SAAA,GAAY,MAAM,MAAA,CAAO,QAAA,EAAS,CAAE,MAAA;AAC1C,EAAA,OAAO,oBAAA,CAAqB,MAAA,CAAO,SAAA,EAAW,SAAA,EAAW,SAAS,CAAA;AACtE;AAqBO,SAAS,kBAAA,GAA+C;AAC3D,EAAA,MAAM,MAAA,GAAS,mBAAmB,oBAAoB,CAAA;AACtD,EAAA,MAAM,YAAA,GAAe,MAAM,MAAA,CAAO,QAAA,EAAS,CAAE,SAAA;AAC7C,EAAA,OAAO,oBAAA,CAAqB,MAAA,CAAO,SAAA,EAAW,YAAA,EAAc,YAAY,CAAA;AAC5E;AAsBO,SAAS,UAAA,GAAkC;AAC9C,EAAA,MAAM,MAAA,GAAS,mBAAmB,YAAY,CAAA;AAC9C,EAAA,MAAM,UAAA,GAAa,MAAM,MAAA,CAAO,QAAA,EAAS,CAAE,OAAA;AAC3C,EAAA,OAAO,oBAAA,CAAqB,MAAA,CAAO,SAAA,EAAW,UAAA,EAAY,UAAU,CAAA;AACxE;AA2BO,SAAS,gBAAA,GAA4B;AACxC,EAAA,MAAM,MAAA,GAAS,mBAAmB,kBAAkB,CAAA;AACpD,EAAA,MAAM,aAAa,MAAM,CAAC,kBAAkB,MAAA,CAAO,QAAA,GAAW,MAAM,CAAA;AACpE,EAAA,OAAO,oBAAA,CAAqB,MAAA,CAAO,SAAA,EAAW,UAAA,EAAY,UAAU,CAAA;AACxE;AAuBO,SAAS,UAAA,GAA2E;AACvF,EAAA,MAAM,MAAA,GAAS,mBAAmB,YAAY,CAAA;AAC9C,EAAA,OAAO,SAAA,CAAU,CAAC,WAAA,EAAa,QAAA,KAAuB,MAAA,CAAO,QAAQ,QAAA,EAAU,EAAE,WAAA,EAAa,CAAC,CAAA;AACnG;AAuBO,SAAS,aAAA,GAAyD;AACrE,EAAA,MAAM,MAAA,GAAS,mBAAmB,eAAe,CAAA;AACjD,EAAA,OAAO,SAAA,CAAU,CAAC,WAAA,EAAa,QAAA,KAAwB,MAAA,CAAO,WAAW,QAAA,EAAU,EAAE,WAAA,EAAa,CAAC,CAAA;AACvG;AAqBO,SAAS,SAAA,GAA4F;AACxG,EAAA,MAAM,MAAA,GAAS,mBAAmB,WAAW,CAAA;AAC7C,EAAA,OAAO,SAAA;AAAA,IAAU,CAAC,WAAA,EAAa,QAAA,EAAoB,KAAA,KAC/C,MAAA,CAAO,OAAO,QAAA,EAAU,KAAA,EAAO,EAAE,WAAA,EAAa;AAAA,GAClD;AACJ;AAoBO,SAAS,cAAA,GAA8E;AAC1F,EAAA,MAAM,MAAA,GAAS,mBAAmB,gBAAgB,CAAA;AAClD,EAAA,OAAO,SAAA,CAAU,CAAC,WAAA,EAAa,OAAA,KAAgC,MAAA,CAAO,YAAY,OAAA,EAAS,EAAE,WAAA,EAAa,CAAC,CAAA;AAC/G;AA0BO,SAAS,gBAAA,GAAqD;AACjE,EAAA,OAAO,kBAAA,CAAmB,kBAAkB,CAAA,CAAE,aAAA;AAClD;AAwCO,SAAS,eAAA,CAAgB,EAAE,QAAA,EAAU,QAAA,EAAS,EAAoC;AACrF,EAAA,OAAO,gBAAA,KAAqB,QAAA,GAAW,QAAA;AAC3C","file":"index.browser.mjs","sourcesContent":["import type { ReadonlyUint8Array, SignatureBytes } from '@solana/kit';\nimport { type ActionResult, useAction, useClientCapability } from '@solana/react';\nimport type { SolanaSignInInput, SolanaSignInOutput } from '@solana/wallet-standard-features';\nimport type { UiWallet, UiWalletAccount } from '@wallet-standard/ui';\nimport type { ReactNode } from 'react';\nimport { useSyncExternalStore } from 'react';\n\nimport { isWalletWarmingUp } from '../status';\nimport type { ClientWithWallet, WalletNamespace, WalletState, WalletStatus } from '../types';\n\n/**\n * Reads the wallet namespace from the nearest `ClientProvider`, asserting at mount that a wallet\n * plugin is installed. Shared by every hook in this module so the capability check, error hook\n * name, and provider hint stay in one place.\n */\nfunction useWalletNamespace(hookName: string): WalletNamespace {\n return useClientCapability<ClientWithWallet>({\n capability: 'wallet',\n hookName,\n providerHint: 'Install a wallet plugin on the client, e.g. `walletSigner()`.',\n }).wallet;\n}\n\n// -- State hooks ------------------------------------------------------------\n\n/**\n * Subscribes to the wallet connection status from a React component.\n *\n * Wraps `client.wallet.subscribe` / `getState` with `useSyncExternalStore`, re-rendering only when\n * the status changes. Requires a `ClientProvider` whose client has a wallet plugin installed;\n * asserts this at mount with {@link useClientCapability}.\n *\n * @returns The current {@link WalletStatus} (`'pending'` on the server and in React Native).\n *\n * @example\n * ```tsx\n * const status = useWalletStatus();\n * if (status === 'pending') return null; // avoid flashing UI before auto-reconnect resolves\n * ```\n *\n * @see {@link useConnectedWallet}\n * @see {@link useWallets}\n */\nexport function useWalletStatus(): WalletStatus {\n const wallet = useWalletNamespace('useWalletStatus');\n const getStatus = () => wallet.getState().status;\n return useSyncExternalStore(wallet.subscribe, getStatus, getStatus);\n}\n\n/**\n * Subscribes to the active wallet connection from a React component.\n *\n * Wraps `client.wallet.subscribe` / `getState` with `useSyncExternalStore`, re-rendering only when\n * the connection changes (connect, disconnect, or account switch). Requires a `ClientProvider`\n * whose client has a wallet plugin installed; asserts this at mount with {@link useClientCapability}.\n *\n * @returns The active connection — `{ account, signer, wallet }` — or `null` when disconnected.\n * `signer` is `null` for read-only wallets.\n *\n * @example\n * ```tsx\n * const connected = useConnectedWallet();\n * return connected ? <p>{connected.account.address}</p> : <p>Not connected</p>;\n * ```\n *\n * @see {@link useWalletStatus}\n * @see {@link useWallets}\n */\nexport function useConnectedWallet(): WalletState['connected'] {\n const wallet = useWalletNamespace('useConnectedWallet');\n const getConnected = () => wallet.getState().connected;\n return useSyncExternalStore(wallet.subscribe, getConnected, getConnected);\n}\n\n/**\n * Subscribes to the list of discovered wallets from a React component.\n *\n * Wraps `client.wallet.subscribe` / `getState` with `useSyncExternalStore`, re-rendering only when\n * the discovered-wallet list changes. Requires a `ClientProvider` whose client has a wallet plugin\n * installed; asserts this at mount with {@link useClientCapability}.\n *\n * @returns All discovered wallets matching the client's configured chain and filter (empty on the\n * server and in React Native).\n *\n * @example\n * ```tsx\n * const wallets = useWallets();\n * const connect = useConnect();\n * return wallets.map(w => <button key={w.name} onClick={() => connect.dispatch(w)}>{w.name}</button>);\n * ```\n *\n * @see {@link useWalletStatus}\n * @see {@link useConnectedWallet}\n */\nexport function useWallets(): readonly UiWallet[] {\n const wallet = useWalletNamespace('useWallets');\n const getWallets = () => wallet.getState().wallets;\n return useSyncExternalStore(wallet.subscribe, getWallets, getWallets);\n}\n\n/**\n * Reports whether the wallet client has finished its initial auto-reconnect \"warm-up\".\n *\n * Returns `false` while the status is `'pending'` or `'reconnecting'` — the transient states a\n * freshly built client passes through while it silently re-establishes a persisted connection — and\n * `true` once it settles into any stable state (`'connected'`, `'disconnected'`, or a user-initiated\n * `'connecting'` / `'disconnecting'`). This is the declarative, render-time counterpart to the\n * store's imperative `whenReady()`, gating on the exact same set of statuses.\n *\n * Use it to hold back wallet-dependent UI until the connection has settled, so a persisted wallet\n * never flashes \"disconnected\" before it reconnects. Re-renders only when this readiness boolean\n * flips, not on every status change. Requires a `ClientProvider` whose client has a wallet plugin\n * installed; asserts this at mount with {@link useClientCapability}.\n *\n * @returns `true` once the warm-up has settled, otherwise `false`.\n *\n * @example\n * ```tsx\n * const isReady = useIsWalletReady();\n * return <button disabled={!isReady}>Connect</button>;\n * ```\n *\n * @see {@link WalletReadyGate}\n * @see {@link useWalletStatus}\n */\nexport function useIsWalletReady(): boolean {\n const wallet = useWalletNamespace('useIsWalletReady');\n const getIsReady = () => !isWalletWarmingUp(wallet.getState().status);\n return useSyncExternalStore(wallet.subscribe, getIsReady, getIsReady);\n}\n\n// -- Action hooks -----------------------------------------------------------\n\n/**\n * Connects to a wallet from a React component.\n *\n * Wraps `client.wallet.connect` with {@link useAction}. Requires a `ClientProvider` whose client\n * has a wallet plugin installed; asserts this at mount with {@link useClientCapability}. An\n * in-flight connect is superseded when a newer one is dispatched.\n *\n * `dispatch`/`dispatchAsync` take the {@link UiWallet} to connect to and resolve with the wallet's\n * accounts once connected.\n *\n * @example\n * ```tsx\n * const { dispatch, isRunning } = useConnect();\n * <button disabled={isRunning} onClick={() => dispatch(wallet)}>Connect</button>\n * ```\n *\n * @see {@link useDisconnect}\n * @see {@link useSignIn}\n */\nexport function useConnect(): ActionResult<[wallet: UiWallet], readonly UiWalletAccount[]> {\n const wallet = useWalletNamespace('useConnect');\n return useAction((abortSignal, uiWallet: UiWallet) => wallet.connect(uiWallet, { abortSignal }));\n}\n\n/**\n * Disconnects a wallet from a React component.\n *\n * Wraps `client.wallet.disconnect` with {@link useAction}. Requires a `ClientProvider` whose client\n * has a wallet plugin installed; asserts this at mount with {@link useClientCapability}.\n *\n * `dispatch`/`dispatchAsync` take an optional {@link UiWallet}. Called with no argument they\n * disconnect the active wallet; passing a non-active, currently authorized wallet deauthorizes that\n * wallet while leaving the active connection in place.\n *\n * @example\n * ```tsx\n * const { dispatch, isRunning } = useDisconnect();\n * // Disconnect the active wallet:\n * <button disabled={isRunning} onClick={() => dispatch()}>Disconnect</button>\n * // Or deauthorize a specific wallet:\n * <button disabled={isRunning} onClick={() => dispatch(wallet)}>Forget this wallet</button>\n * ```\n *\n * @see {@link useConnect}\n */\nexport function useDisconnect(): ActionResult<[wallet?: UiWallet], void> {\n const wallet = useWalletNamespace('useDisconnect');\n return useAction((abortSignal, uiWallet?: UiWallet) => wallet.disconnect(uiWallet, { abortSignal }));\n}\n\n/**\n * Signs in with a wallet (Sign In With Solana) from a React component.\n *\n * Wraps `client.wallet.signIn` with {@link useAction}. Requires a `ClientProvider` whose client has\n * a wallet plugin installed; asserts this at mount with {@link useClientCapability}. Like\n * {@link useConnect}, this establishes a full connection. An in-flight connect is superseded when a\n * newer one is dispatched.\n *\n * `dispatch`/`dispatchAsync` take the {@link UiWallet} and a `SolanaSignInInput` (pass `{}` for no\n * customization) and resolve with the wallet's sign-in output.\n *\n * @example\n * ```tsx\n * const { dispatch } = useSignIn();\n * dispatch(wallet, { domain: window.location.host });\n * ```\n *\n * @see {@link useConnect}\n */\nexport function useSignIn(): ActionResult<[wallet: UiWallet, input: SolanaSignInInput], SolanaSignInOutput> {\n const wallet = useWalletNamespace('useSignIn');\n return useAction((abortSignal, uiWallet: UiWallet, input: SolanaSignInInput) =>\n wallet.signIn(uiWallet, input, { abortSignal }),\n );\n}\n\n/**\n * Signs an arbitrary message with the connected account from a React component.\n *\n * Wraps `client.wallet.signMessage` with {@link useAction}. Requires a `ClientProvider` whose\n * client has a wallet plugin installed; asserts this at mount with {@link useClientCapability}.\n * A stale in-flight dispatch is dropped from the hook's state when a newer one starts (though\n * the wallet may still complete the older request in the background).\n *\n * `dispatch`/`dispatchAsync` take the message bytes and resolve with the signature. The bytes are\n * only read, never mutated, so a {@link ReadonlyUint8Array} (e.g. the output of a `@solana/kit`\n * codec) is accepted directly without a cast.\n *\n * @example\n * ```tsx\n * const { dispatch } = useSignMessage();\n * dispatch(new TextEncoder().encode('Hello'));\n * ```\n */\nexport function useSignMessage(): ActionResult<[message: ReadonlyUint8Array], SignatureBytes> {\n const wallet = useWalletNamespace('useSignMessage');\n return useAction((abortSignal, message: ReadonlyUint8Array) => wallet.signMessage(message, { abortSignal }));\n}\n\n/**\n * Returns a callback that selects a wallet account.\n *\n * Requires a `ClientProvider` whose client has a wallet plugin installed; asserts this at mount\n * with {@link useClientCapability}. Unlike the other action hooks, `selectAccount` is synchronous —\n * this hook returns the bound function directly rather than an `ActionResult`.\n *\n * The account may belong to any currently authorized wallet, not only the active one. Selecting an\n * account from a different authorized wallet switches the active connection to that wallet\n * synchronously (no prompt), leaving the previously active wallet authorized.\n *\n * @returns The `selectAccount` function — call it with a {@link UiWalletAccount} belonging to any\n * currently authorized wallet. It throws if the account's handle can't be resolved to an\n * authorized wallet (or isn't among that wallet's accounts), or that wallet is currently\n * disconnecting.\n *\n * @example\n * ```tsx\n * const selectAccount = useSelectAccount();\n * <button onClick={() => selectAccount(account)}>Use this account</button>\n * ```\n *\n * @see {@link useConnectedWallet}\n */\nexport function useSelectAccount(): WalletNamespace['selectAccount'] {\n return useWalletNamespace('useSelectAccount').selectAccount;\n}\n\n// -- Components -------------------------------------------------------------\n\n/**\n * Props for {@link WalletReadyGate}.\n */\nexport type WalletReadyGateProps = Readonly<{\n /** Rendered once the wallet client has finished its initial auto-reconnect warm-up. */\n children: ReactNode;\n /** Rendered while the client is still warming up (`'pending'` / `'reconnecting'`). */\n fallback: ReactNode;\n}>;\n\n/**\n * Renders `fallback` while the wallet client is still in its initial auto-reconnect warm-up\n * (`'pending'` / `'reconnecting'`), then reveals `children` once the connection has settled.\n *\n * A declarative wrapper over {@link useIsWalletReady} for the common case of holding back a whole\n * wallet-dependent subtree. Its props are shaped like React's `<Suspense fallback>`, but it is\n * synchronous — it renders `fallback` directly off the reactive status rather than suspending, so it\n * needs no `<Suspense>` ancestor. It lets an app paint its chrome immediately while gating only the\n * wallet-dependent UI, so a persisted wallet never flashes \"disconnected\" before it silently\n * reconnects. For finer control (disabling a button, showing an inline spinner) read the boolean from\n * {@link useIsWalletReady} directly. To actually suspend a subtree instead, throw the Suspense-safe\n * {@link WalletNamespace.whenReady} promise from your own component.\n *\n * Requires a `ClientProvider` whose client has a wallet plugin installed; asserts this at mount with\n * {@link useClientCapability}.\n *\n * @example\n * ```tsx\n * <WalletReadyGate fallback={<Spinner />}>\n * <WalletDependentApp />\n * </WalletReadyGate>\n * ```\n *\n * @see {@link useIsWalletReady}\n * @see {@link useWalletStatus}\n */\nexport function WalletReadyGate({ children, fallback }: WalletReadyGateProps): ReactNode {\n return useIsWalletReady() ? children : fallback;\n}\n"]}
1
+ {"version":3,"sources":["../../src/status.ts","../../src/react/index.ts"],"names":[],"mappings":";;;;;;AA8BO,SAAS,kBAAkB,MAAA,EAA+B;AAC7D,EAAA,OAAO,MAAA,KAAW,aAAa,MAAA,KAAW,cAAA;AAC9C;;;ACjBA,SAAS,mBAAmB,QAAA,EAAmC;AAC3D,EAAA,OAAO,mBAAA,CAAsC;AAAA,IACzC,UAAA,EAAY,QAAA;AAAA,IACZ,QAAA;AAAA,IACA,YAAA,EAAc;AAAA,GACjB,CAAA,CAAE,MAAA;AACP;AAsBO,SAAS,eAAA,GAAgC;AAC5C,EAAA,MAAM,MAAA,GAAS,mBAAmB,iBAAiB,CAAA;AACnD,EAAA,MAAM,SAAA,GAAY,MAAM,MAAA,CAAO,QAAA,EAAS,CAAE,MAAA;AAC1C,EAAA,OAAO,oBAAA,CAAqB,MAAA,CAAO,SAAA,EAAW,SAAA,EAAW,SAAS,CAAA;AACtE;AAqBO,SAAS,kBAAA,GAA+C;AAC3D,EAAA,MAAM,MAAA,GAAS,mBAAmB,oBAAoB,CAAA;AACtD,EAAA,MAAM,YAAA,GAAe,MAAM,MAAA,CAAO,QAAA,EAAS,CAAE,SAAA;AAC7C,EAAA,OAAO,oBAAA,CAAqB,MAAA,CAAO,SAAA,EAAW,YAAA,EAAc,YAAY,CAAA;AAC5E;AAsBO,SAAS,UAAA,GAAkC;AAC9C,EAAA,MAAM,MAAA,GAAS,mBAAmB,YAAY,CAAA;AAC9C,EAAA,MAAM,UAAA,GAAa,MAAM,MAAA,CAAO,QAAA,EAAS,CAAE,OAAA;AAC3C,EAAA,OAAO,oBAAA,CAAqB,MAAA,CAAO,SAAA,EAAW,UAAA,EAAY,UAAU,CAAA;AACxE;AA2BO,SAAS,gBAAA,GAA4B;AACxC,EAAA,MAAM,MAAA,GAAS,mBAAmB,kBAAkB,CAAA;AACpD,EAAA,MAAM,aAAa,MAAM,CAAC,kBAAkB,MAAA,CAAO,QAAA,GAAW,MAAM,CAAA;AACpE,EAAA,OAAO,oBAAA,CAAqB,MAAA,CAAO,SAAA,EAAW,UAAA,EAAY,UAAU,CAAA;AACxE;AAuBO,SAAS,UAAA,GAA2E;AACvF,EAAA,MAAM,MAAA,GAAS,mBAAmB,YAAY,CAAA;AAC9C,EAAA,OAAO,SAAA,CAAU,CAAC,WAAA,EAAa,QAAA,KAAuB,MAAA,CAAO,QAAQ,QAAA,EAAU,EAAE,WAAA,EAAa,CAAC,CAAA;AACnG;AAuBO,SAAS,aAAA,GAAyD;AACrE,EAAA,MAAM,MAAA,GAAS,mBAAmB,eAAe,CAAA;AACjD,EAAA,OAAO,SAAA,CAAU,CAAC,WAAA,EAAa,QAAA,KAAwB,MAAA,CAAO,WAAW,QAAA,EAAU,EAAE,WAAA,EAAa,CAAC,CAAA;AACvG;AAqBO,SAAS,SAAA,GAA4F;AACxG,EAAA,MAAM,MAAA,GAAS,mBAAmB,WAAW,CAAA;AAC7C,EAAA,OAAO,SAAA;AAAA,IAAU,CAAC,WAAA,EAAa,QAAA,EAAoB,KAAA,KAC/C,MAAA,CAAO,OAAO,QAAA,EAAU,KAAA,EAAO,EAAE,WAAA,EAAa;AAAA,GAClD;AACJ;AAoBO,SAAS,cAAA,GAA8E;AAC1F,EAAA,MAAM,MAAA,GAAS,mBAAmB,gBAAgB,CAAA;AAClD,EAAA,OAAO,SAAA,CAAU,CAAC,WAAA,EAAa,OAAA,KAAgC,MAAA,CAAO,YAAY,OAAA,EAAS,EAAE,WAAA,EAAa,CAAC,CAAA;AAC/G;AA0BO,SAAS,gBAAA,GAAqD;AACjE,EAAA,OAAO,kBAAA,CAAmB,kBAAkB,CAAA,CAAE,aAAA;AAClD;AAwCO,SAAS,eAAA,CAAgB,EAAE,QAAA,EAAU,QAAA,EAAS,EAAoC;AACrF,EAAA,OAAO,gBAAA,KAAqB,QAAA,GAAW,QAAA;AAC3C","file":"index.browser.mjs","sourcesContent":["import type { WalletStatus } from './types';\n\n/**\n * Reports whether a {@link WalletStatus} belongs to the initial auto-reconnect\n * \"warm-up\": `'pending'` (before auto-reconnect has resolved) and\n * `'reconnecting'` (silently re-establishing a persisted connection). During\n * this window a persisted wallet is on its way back, so wallet-dependent UI\n * should usually hold rather than flash a \"disconnected\" state.\n *\n * User-initiated `'connecting'` / `'disconnecting'` are deliberately excluded —\n * a normal connect or disconnect is not a warm-up and must stay interactive.\n *\n * This is the single source of truth for \"warm-up\" across the plugin: the\n * store's {@link WalletNamespace.whenReady} waits for the status to leave this set, and the\n * React `useIsWalletReady` hook and `WalletReadyGate` component gate on its\n * negation. Use it directly to gate on warm-up from a non-React consumer.\n *\n * @param status - A status from `client.wallet.getState().status`.\n * @returns `true` while the client is warming up, otherwise `false`.\n *\n * @example\n * ```ts\n * client.wallet.subscribe(() => {\n * const { status } = client.wallet.getState();\n * setLoading(isWalletWarmingUp(status));\n * });\n * ```\n *\n * @see {@link WalletNamespace.whenReady} — the imperative (promise-based) counterpart.\n */\nexport function isWalletWarmingUp(status: WalletStatus): boolean {\n return status === 'pending' || status === 'reconnecting';\n}\n","import type { ReadonlyUint8Array, SignatureBytes } from '@solana/kit';\nimport { type ActionResult, useAction, useClientCapability } from '@solana/react';\nimport type { SolanaSignInInput, SolanaSignInOutput } from '@solana/wallet-standard-features';\nimport type { UiWallet, UiWalletAccount } from '@wallet-standard/ui';\nimport type { ReactNode } from 'react';\nimport { useSyncExternalStore } from 'react';\n\nimport { isWalletWarmingUp } from '../status';\nimport type { ClientWithWallet, WalletNamespace, WalletState, WalletStatus } from '../types';\n\n/**\n * Reads the wallet namespace from the nearest `ClientProvider`, asserting at mount that a wallet\n * plugin is installed. Shared by every hook in this module so the capability check, error hook\n * name, and provider hint stay in one place.\n */\nfunction useWalletNamespace(hookName: string): WalletNamespace {\n return useClientCapability<ClientWithWallet>({\n capability: 'wallet',\n hookName,\n providerHint: 'Install a wallet plugin on the client, e.g. `walletSigner()`.',\n }).wallet;\n}\n\n// -- State hooks ------------------------------------------------------------\n\n/**\n * Subscribes to the wallet connection status from a React component.\n *\n * Wraps `client.wallet.subscribe` / `getState` with `useSyncExternalStore`, re-rendering only when\n * the status changes. Requires a `ClientProvider` whose client has a wallet plugin installed;\n * asserts this at mount with {@link useClientCapability}.\n *\n * @returns The current {@link WalletStatus} (`'pending'` on the server and in React Native).\n *\n * @example\n * ```tsx\n * const status = useWalletStatus();\n * if (status === 'pending') return null; // avoid flashing UI before auto-reconnect resolves\n * ```\n *\n * @see {@link useConnectedWallet}\n * @see {@link useWallets}\n */\nexport function useWalletStatus(): WalletStatus {\n const wallet = useWalletNamespace('useWalletStatus');\n const getStatus = () => wallet.getState().status;\n return useSyncExternalStore(wallet.subscribe, getStatus, getStatus);\n}\n\n/**\n * Subscribes to the active wallet connection from a React component.\n *\n * Wraps `client.wallet.subscribe` / `getState` with `useSyncExternalStore`, re-rendering only when\n * the connection changes (connect, disconnect, or account switch). Requires a `ClientProvider`\n * whose client has a wallet plugin installed; asserts this at mount with {@link useClientCapability}.\n *\n * @returns The active connection — `{ account, signer, wallet }` — or `null` when disconnected.\n * `signer` is `null` for read-only wallets.\n *\n * @example\n * ```tsx\n * const connected = useConnectedWallet();\n * return connected ? <p>{connected.account.address}</p> : <p>Not connected</p>;\n * ```\n *\n * @see {@link useWalletStatus}\n * @see {@link useWallets}\n */\nexport function useConnectedWallet(): WalletState['connected'] {\n const wallet = useWalletNamespace('useConnectedWallet');\n const getConnected = () => wallet.getState().connected;\n return useSyncExternalStore(wallet.subscribe, getConnected, getConnected);\n}\n\n/**\n * Subscribes to the list of discovered wallets from a React component.\n *\n * Wraps `client.wallet.subscribe` / `getState` with `useSyncExternalStore`, re-rendering only when\n * the discovered-wallet list changes. Requires a `ClientProvider` whose client has a wallet plugin\n * installed; asserts this at mount with {@link useClientCapability}.\n *\n * @returns All discovered wallets matching the client's configured chain and filter (empty on the\n * server and in React Native).\n *\n * @example\n * ```tsx\n * const wallets = useWallets();\n * const connect = useConnect();\n * return wallets.map(w => <button key={w.name} onClick={() => connect.dispatch(w)}>{w.name}</button>);\n * ```\n *\n * @see {@link useWalletStatus}\n * @see {@link useConnectedWallet}\n */\nexport function useWallets(): readonly UiWallet[] {\n const wallet = useWalletNamespace('useWallets');\n const getWallets = () => wallet.getState().wallets;\n return useSyncExternalStore(wallet.subscribe, getWallets, getWallets);\n}\n\n/**\n * Reports whether the wallet client has finished its initial auto-reconnect \"warm-up\".\n *\n * Returns `false` while the status is `'pending'` or `'reconnecting'` — the transient states a\n * freshly built client passes through while it silently re-establishes a persisted connection — and\n * `true` once it settles into any stable state (`'connected'`, `'disconnected'`, or a user-initiated\n * `'connecting'` / `'disconnecting'`). This is the declarative, render-time counterpart to the\n * store's imperative `whenReady()`, gating on the exact same set of statuses.\n *\n * Use it to hold back wallet-dependent UI until the connection has settled, so a persisted wallet\n * never flashes \"disconnected\" before it reconnects. Re-renders only when this readiness boolean\n * flips, not on every status change. Requires a `ClientProvider` whose client has a wallet plugin\n * installed; asserts this at mount with {@link useClientCapability}.\n *\n * @returns `true` once the warm-up has settled, otherwise `false`.\n *\n * @example\n * ```tsx\n * const isReady = useIsWalletReady();\n * return <button disabled={!isReady}>Connect</button>;\n * ```\n *\n * @see {@link WalletReadyGate}\n * @see {@link useWalletStatus}\n */\nexport function useIsWalletReady(): boolean {\n const wallet = useWalletNamespace('useIsWalletReady');\n const getIsReady = () => !isWalletWarmingUp(wallet.getState().status);\n return useSyncExternalStore(wallet.subscribe, getIsReady, getIsReady);\n}\n\n// -- Action hooks -----------------------------------------------------------\n\n/**\n * Connects to a wallet from a React component.\n *\n * Wraps `client.wallet.connect` with {@link useAction}. Requires a `ClientProvider` whose client\n * has a wallet plugin installed; asserts this at mount with {@link useClientCapability}. An\n * in-flight connect is superseded when a newer one is dispatched.\n *\n * `dispatch`/`dispatchAsync` take the {@link UiWallet} to connect to and resolve with the wallet's\n * accounts once connected.\n *\n * @example\n * ```tsx\n * const { dispatch, isRunning } = useConnect();\n * <button disabled={isRunning} onClick={() => dispatch(wallet)}>Connect</button>\n * ```\n *\n * @see {@link useDisconnect}\n * @see {@link useSignIn}\n */\nexport function useConnect(): ActionResult<[wallet: UiWallet], readonly UiWalletAccount[]> {\n const wallet = useWalletNamespace('useConnect');\n return useAction((abortSignal, uiWallet: UiWallet) => wallet.connect(uiWallet, { abortSignal }));\n}\n\n/**\n * Disconnects a wallet from a React component.\n *\n * Wraps `client.wallet.disconnect` with {@link useAction}. Requires a `ClientProvider` whose client\n * has a wallet plugin installed; asserts this at mount with {@link useClientCapability}.\n *\n * `dispatch`/`dispatchAsync` take an optional {@link UiWallet}. Called with no argument they\n * disconnect the active wallet; passing a non-active, currently authorized wallet deauthorizes that\n * wallet while leaving the active connection in place.\n *\n * @example\n * ```tsx\n * const { dispatch, isRunning } = useDisconnect();\n * // Disconnect the active wallet:\n * <button disabled={isRunning} onClick={() => dispatch()}>Disconnect</button>\n * // Or deauthorize a specific wallet:\n * <button disabled={isRunning} onClick={() => dispatch(wallet)}>Forget this wallet</button>\n * ```\n *\n * @see {@link useConnect}\n */\nexport function useDisconnect(): ActionResult<[wallet?: UiWallet], void> {\n const wallet = useWalletNamespace('useDisconnect');\n return useAction((abortSignal, uiWallet?: UiWallet) => wallet.disconnect(uiWallet, { abortSignal }));\n}\n\n/**\n * Signs in with a wallet (Sign In With Solana) from a React component.\n *\n * Wraps `client.wallet.signIn` with {@link useAction}. Requires a `ClientProvider` whose client has\n * a wallet plugin installed; asserts this at mount with {@link useClientCapability}. Like\n * {@link useConnect}, this establishes a full connection. An in-flight connect is superseded when a\n * newer one is dispatched.\n *\n * `dispatch`/`dispatchAsync` take the {@link UiWallet} and a `SolanaSignInInput` (pass `{}` for no\n * customization) and resolve with the wallet's sign-in output.\n *\n * @example\n * ```tsx\n * const { dispatch } = useSignIn();\n * dispatch(wallet, { domain: window.location.host });\n * ```\n *\n * @see {@link useConnect}\n */\nexport function useSignIn(): ActionResult<[wallet: UiWallet, input: SolanaSignInInput], SolanaSignInOutput> {\n const wallet = useWalletNamespace('useSignIn');\n return useAction((abortSignal, uiWallet: UiWallet, input: SolanaSignInInput) =>\n wallet.signIn(uiWallet, input, { abortSignal }),\n );\n}\n\n/**\n * Signs an arbitrary message with the connected account from a React component.\n *\n * Wraps `client.wallet.signMessage` with {@link useAction}. Requires a `ClientProvider` whose\n * client has a wallet plugin installed; asserts this at mount with {@link useClientCapability}.\n * A stale in-flight dispatch is dropped from the hook's state when a newer one starts (though\n * the wallet may still complete the older request in the background).\n *\n * `dispatch`/`dispatchAsync` take the message bytes and resolve with the signature. The bytes are\n * only read, never mutated, so a {@link ReadonlyUint8Array} (e.g. the output of a `@solana/kit`\n * codec) is accepted directly without a cast.\n *\n * @example\n * ```tsx\n * const { dispatch } = useSignMessage();\n * dispatch(new TextEncoder().encode('Hello'));\n * ```\n */\nexport function useSignMessage(): ActionResult<[message: ReadonlyUint8Array], SignatureBytes> {\n const wallet = useWalletNamespace('useSignMessage');\n return useAction((abortSignal, message: ReadonlyUint8Array) => wallet.signMessage(message, { abortSignal }));\n}\n\n/**\n * Returns a callback that selects a wallet account.\n *\n * Requires a `ClientProvider` whose client has a wallet plugin installed; asserts this at mount\n * with {@link useClientCapability}. Unlike the other action hooks, `selectAccount` is synchronous —\n * this hook returns the bound function directly rather than an `ActionResult`.\n *\n * The account may belong to any currently authorized wallet, not only the active one. Selecting an\n * account from a different authorized wallet switches the active connection to that wallet\n * synchronously (no prompt), leaving the previously active wallet authorized.\n *\n * @returns The `selectAccount` function — call it with a {@link UiWalletAccount} belonging to any\n * currently authorized wallet. It throws if the account's handle can't be resolved to an\n * authorized wallet (or isn't among that wallet's accounts), or that wallet is currently\n * disconnecting.\n *\n * @example\n * ```tsx\n * const selectAccount = useSelectAccount();\n * <button onClick={() => selectAccount(account)}>Use this account</button>\n * ```\n *\n * @see {@link useConnectedWallet}\n */\nexport function useSelectAccount(): WalletNamespace['selectAccount'] {\n return useWalletNamespace('useSelectAccount').selectAccount;\n}\n\n// -- Components -------------------------------------------------------------\n\n/**\n * Props for {@link WalletReadyGate}.\n */\nexport type WalletReadyGateProps = Readonly<{\n /** Rendered once the wallet client has finished its initial auto-reconnect warm-up. */\n children: ReactNode;\n /** Rendered while the client is still warming up (`'pending'` / `'reconnecting'`). */\n fallback: ReactNode;\n}>;\n\n/**\n * Renders `fallback` while the wallet client is still in its initial auto-reconnect warm-up\n * (`'pending'` / `'reconnecting'`), then reveals `children` once the connection has settled.\n *\n * A declarative wrapper over {@link useIsWalletReady} for the common case of holding back a whole\n * wallet-dependent subtree. Its props are shaped like React's `<Suspense fallback>`, but it is\n * synchronous — it renders `fallback` directly off the reactive status rather than suspending, so it\n * needs no `<Suspense>` ancestor. It lets an app paint its chrome immediately while gating only the\n * wallet-dependent UI, so a persisted wallet never flashes \"disconnected\" before it silently\n * reconnects. For finer control (disabling a button, showing an inline spinner) read the boolean from\n * {@link useIsWalletReady} directly. To actually suspend a subtree instead, throw the Suspense-safe\n * {@link WalletNamespace.whenReady} promise from your own component.\n *\n * Requires a `ClientProvider` whose client has a wallet plugin installed; asserts this at mount with\n * {@link useClientCapability}.\n *\n * @example\n * ```tsx\n * <WalletReadyGate fallback={<Spinner />}>\n * <WalletDependentApp />\n * </WalletReadyGate>\n * ```\n *\n * @see {@link useIsWalletReady}\n * @see {@link useWalletStatus}\n */\nexport function WalletReadyGate({ children, fallback }: WalletReadyGateProps): ReactNode {\n return useIsWalletReady() ? children : fallback;\n}\n"]}
@@ -1,7 +1,14 @@
1
- import { isWalletWarmingUp } from '../chunk-PBPOWD6O.node.mjs';
2
1
  import { useAction, useClientCapability } from '@solana/react';
3
2
  import { useSyncExternalStore } from 'react';
4
3
 
4
+ // src/react/index.ts
5
+
6
+ // src/status.ts
7
+ function isWalletWarmingUp(status) {
8
+ return status === "pending" || status === "reconnecting";
9
+ }
10
+
11
+ // src/react/index.ts
5
12
  function useWalletNamespace(hookName) {
6
13
  return useClientCapability({
7
14
  capability: "wallet",
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/react/index.ts"],"names":[],"mappings":";;;;AAeA,SAAS,mBAAmB,QAAA,EAAmC;AAC3D,EAAA,OAAO,mBAAA,CAAsC;AAAA,IACzC,UAAA,EAAY,QAAA;AAAA,IACZ,QAAA;AAAA,IACA,YAAA,EAAc;AAAA,GACjB,CAAA,CAAE,MAAA;AACP;AAsBO,SAAS,eAAA,GAAgC;AAC5C,EAAA,MAAM,MAAA,GAAS,mBAAmB,iBAAiB,CAAA;AACnD,EAAA,MAAM,SAAA,GAAY,MAAM,MAAA,CAAO,QAAA,EAAS,CAAE,MAAA;AAC1C,EAAA,OAAO,oBAAA,CAAqB,MAAA,CAAO,SAAA,EAAW,SAAA,EAAW,SAAS,CAAA;AACtE;AAqBO,SAAS,kBAAA,GAA+C;AAC3D,EAAA,MAAM,MAAA,GAAS,mBAAmB,oBAAoB,CAAA;AACtD,EAAA,MAAM,YAAA,GAAe,MAAM,MAAA,CAAO,QAAA,EAAS,CAAE,SAAA;AAC7C,EAAA,OAAO,oBAAA,CAAqB,MAAA,CAAO,SAAA,EAAW,YAAA,EAAc,YAAY,CAAA;AAC5E;AAsBO,SAAS,UAAA,GAAkC;AAC9C,EAAA,MAAM,MAAA,GAAS,mBAAmB,YAAY,CAAA;AAC9C,EAAA,MAAM,UAAA,GAAa,MAAM,MAAA,CAAO,QAAA,EAAS,CAAE,OAAA;AAC3C,EAAA,OAAO,oBAAA,CAAqB,MAAA,CAAO,SAAA,EAAW,UAAA,EAAY,UAAU,CAAA;AACxE;AA2BO,SAAS,gBAAA,GAA4B;AACxC,EAAA,MAAM,MAAA,GAAS,mBAAmB,kBAAkB,CAAA;AACpD,EAAA,MAAM,aAAa,MAAM,CAAC,kBAAkB,MAAA,CAAO,QAAA,GAAW,MAAM,CAAA;AACpE,EAAA,OAAO,oBAAA,CAAqB,MAAA,CAAO,SAAA,EAAW,UAAA,EAAY,UAAU,CAAA;AACxE;AAuBO,SAAS,UAAA,GAA2E;AACvF,EAAA,MAAM,MAAA,GAAS,mBAAmB,YAAY,CAAA;AAC9C,EAAA,OAAO,SAAA,CAAU,CAAC,WAAA,EAAa,QAAA,KAAuB,MAAA,CAAO,QAAQ,QAAA,EAAU,EAAE,WAAA,EAAa,CAAC,CAAA;AACnG;AAuBO,SAAS,aAAA,GAAyD;AACrE,EAAA,MAAM,MAAA,GAAS,mBAAmB,eAAe,CAAA;AACjD,EAAA,OAAO,SAAA,CAAU,CAAC,WAAA,EAAa,QAAA,KAAwB,MAAA,CAAO,WAAW,QAAA,EAAU,EAAE,WAAA,EAAa,CAAC,CAAA;AACvG;AAqBO,SAAS,SAAA,GAA4F;AACxG,EAAA,MAAM,MAAA,GAAS,mBAAmB,WAAW,CAAA;AAC7C,EAAA,OAAO,SAAA;AAAA,IAAU,CAAC,WAAA,EAAa,QAAA,EAAoB,KAAA,KAC/C,MAAA,CAAO,OAAO,QAAA,EAAU,KAAA,EAAO,EAAE,WAAA,EAAa;AAAA,GAClD;AACJ;AAoBO,SAAS,cAAA,GAA8E;AAC1F,EAAA,MAAM,MAAA,GAAS,mBAAmB,gBAAgB,CAAA;AAClD,EAAA,OAAO,SAAA,CAAU,CAAC,WAAA,EAAa,OAAA,KAAgC,MAAA,CAAO,YAAY,OAAA,EAAS,EAAE,WAAA,EAAa,CAAC,CAAA;AAC/G;AA0BO,SAAS,gBAAA,GAAqD;AACjE,EAAA,OAAO,kBAAA,CAAmB,kBAAkB,CAAA,CAAE,aAAA;AAClD;AAwCO,SAAS,eAAA,CAAgB,EAAE,QAAA,EAAU,QAAA,EAAS,EAAoC;AACrF,EAAA,OAAO,gBAAA,KAAqB,QAAA,GAAW,QAAA;AAC3C","file":"index.node.mjs","sourcesContent":["import type { ReadonlyUint8Array, SignatureBytes } from '@solana/kit';\nimport { type ActionResult, useAction, useClientCapability } from '@solana/react';\nimport type { SolanaSignInInput, SolanaSignInOutput } from '@solana/wallet-standard-features';\nimport type { UiWallet, UiWalletAccount } from '@wallet-standard/ui';\nimport type { ReactNode } from 'react';\nimport { useSyncExternalStore } from 'react';\n\nimport { isWalletWarmingUp } from '../status';\nimport type { ClientWithWallet, WalletNamespace, WalletState, WalletStatus } from '../types';\n\n/**\n * Reads the wallet namespace from the nearest `ClientProvider`, asserting at mount that a wallet\n * plugin is installed. Shared by every hook in this module so the capability check, error hook\n * name, and provider hint stay in one place.\n */\nfunction useWalletNamespace(hookName: string): WalletNamespace {\n return useClientCapability<ClientWithWallet>({\n capability: 'wallet',\n hookName,\n providerHint: 'Install a wallet plugin on the client, e.g. `walletSigner()`.',\n }).wallet;\n}\n\n// -- State hooks ------------------------------------------------------------\n\n/**\n * Subscribes to the wallet connection status from a React component.\n *\n * Wraps `client.wallet.subscribe` / `getState` with `useSyncExternalStore`, re-rendering only when\n * the status changes. Requires a `ClientProvider` whose client has a wallet plugin installed;\n * asserts this at mount with {@link useClientCapability}.\n *\n * @returns The current {@link WalletStatus} (`'pending'` on the server and in React Native).\n *\n * @example\n * ```tsx\n * const status = useWalletStatus();\n * if (status === 'pending') return null; // avoid flashing UI before auto-reconnect resolves\n * ```\n *\n * @see {@link useConnectedWallet}\n * @see {@link useWallets}\n */\nexport function useWalletStatus(): WalletStatus {\n const wallet = useWalletNamespace('useWalletStatus');\n const getStatus = () => wallet.getState().status;\n return useSyncExternalStore(wallet.subscribe, getStatus, getStatus);\n}\n\n/**\n * Subscribes to the active wallet connection from a React component.\n *\n * Wraps `client.wallet.subscribe` / `getState` with `useSyncExternalStore`, re-rendering only when\n * the connection changes (connect, disconnect, or account switch). Requires a `ClientProvider`\n * whose client has a wallet plugin installed; asserts this at mount with {@link useClientCapability}.\n *\n * @returns The active connection — `{ account, signer, wallet }` — or `null` when disconnected.\n * `signer` is `null` for read-only wallets.\n *\n * @example\n * ```tsx\n * const connected = useConnectedWallet();\n * return connected ? <p>{connected.account.address}</p> : <p>Not connected</p>;\n * ```\n *\n * @see {@link useWalletStatus}\n * @see {@link useWallets}\n */\nexport function useConnectedWallet(): WalletState['connected'] {\n const wallet = useWalletNamespace('useConnectedWallet');\n const getConnected = () => wallet.getState().connected;\n return useSyncExternalStore(wallet.subscribe, getConnected, getConnected);\n}\n\n/**\n * Subscribes to the list of discovered wallets from a React component.\n *\n * Wraps `client.wallet.subscribe` / `getState` with `useSyncExternalStore`, re-rendering only when\n * the discovered-wallet list changes. Requires a `ClientProvider` whose client has a wallet plugin\n * installed; asserts this at mount with {@link useClientCapability}.\n *\n * @returns All discovered wallets matching the client's configured chain and filter (empty on the\n * server and in React Native).\n *\n * @example\n * ```tsx\n * const wallets = useWallets();\n * const connect = useConnect();\n * return wallets.map(w => <button key={w.name} onClick={() => connect.dispatch(w)}>{w.name}</button>);\n * ```\n *\n * @see {@link useWalletStatus}\n * @see {@link useConnectedWallet}\n */\nexport function useWallets(): readonly UiWallet[] {\n const wallet = useWalletNamespace('useWallets');\n const getWallets = () => wallet.getState().wallets;\n return useSyncExternalStore(wallet.subscribe, getWallets, getWallets);\n}\n\n/**\n * Reports whether the wallet client has finished its initial auto-reconnect \"warm-up\".\n *\n * Returns `false` while the status is `'pending'` or `'reconnecting'` — the transient states a\n * freshly built client passes through while it silently re-establishes a persisted connection — and\n * `true` once it settles into any stable state (`'connected'`, `'disconnected'`, or a user-initiated\n * `'connecting'` / `'disconnecting'`). This is the declarative, render-time counterpart to the\n * store's imperative `whenReady()`, gating on the exact same set of statuses.\n *\n * Use it to hold back wallet-dependent UI until the connection has settled, so a persisted wallet\n * never flashes \"disconnected\" before it reconnects. Re-renders only when this readiness boolean\n * flips, not on every status change. Requires a `ClientProvider` whose client has a wallet plugin\n * installed; asserts this at mount with {@link useClientCapability}.\n *\n * @returns `true` once the warm-up has settled, otherwise `false`.\n *\n * @example\n * ```tsx\n * const isReady = useIsWalletReady();\n * return <button disabled={!isReady}>Connect</button>;\n * ```\n *\n * @see {@link WalletReadyGate}\n * @see {@link useWalletStatus}\n */\nexport function useIsWalletReady(): boolean {\n const wallet = useWalletNamespace('useIsWalletReady');\n const getIsReady = () => !isWalletWarmingUp(wallet.getState().status);\n return useSyncExternalStore(wallet.subscribe, getIsReady, getIsReady);\n}\n\n// -- Action hooks -----------------------------------------------------------\n\n/**\n * Connects to a wallet from a React component.\n *\n * Wraps `client.wallet.connect` with {@link useAction}. Requires a `ClientProvider` whose client\n * has a wallet plugin installed; asserts this at mount with {@link useClientCapability}. An\n * in-flight connect is superseded when a newer one is dispatched.\n *\n * `dispatch`/`dispatchAsync` take the {@link UiWallet} to connect to and resolve with the wallet's\n * accounts once connected.\n *\n * @example\n * ```tsx\n * const { dispatch, isRunning } = useConnect();\n * <button disabled={isRunning} onClick={() => dispatch(wallet)}>Connect</button>\n * ```\n *\n * @see {@link useDisconnect}\n * @see {@link useSignIn}\n */\nexport function useConnect(): ActionResult<[wallet: UiWallet], readonly UiWalletAccount[]> {\n const wallet = useWalletNamespace('useConnect');\n return useAction((abortSignal, uiWallet: UiWallet) => wallet.connect(uiWallet, { abortSignal }));\n}\n\n/**\n * Disconnects a wallet from a React component.\n *\n * Wraps `client.wallet.disconnect` with {@link useAction}. Requires a `ClientProvider` whose client\n * has a wallet plugin installed; asserts this at mount with {@link useClientCapability}.\n *\n * `dispatch`/`dispatchAsync` take an optional {@link UiWallet}. Called with no argument they\n * disconnect the active wallet; passing a non-active, currently authorized wallet deauthorizes that\n * wallet while leaving the active connection in place.\n *\n * @example\n * ```tsx\n * const { dispatch, isRunning } = useDisconnect();\n * // Disconnect the active wallet:\n * <button disabled={isRunning} onClick={() => dispatch()}>Disconnect</button>\n * // Or deauthorize a specific wallet:\n * <button disabled={isRunning} onClick={() => dispatch(wallet)}>Forget this wallet</button>\n * ```\n *\n * @see {@link useConnect}\n */\nexport function useDisconnect(): ActionResult<[wallet?: UiWallet], void> {\n const wallet = useWalletNamespace('useDisconnect');\n return useAction((abortSignal, uiWallet?: UiWallet) => wallet.disconnect(uiWallet, { abortSignal }));\n}\n\n/**\n * Signs in with a wallet (Sign In With Solana) from a React component.\n *\n * Wraps `client.wallet.signIn` with {@link useAction}. Requires a `ClientProvider` whose client has\n * a wallet plugin installed; asserts this at mount with {@link useClientCapability}. Like\n * {@link useConnect}, this establishes a full connection. An in-flight connect is superseded when a\n * newer one is dispatched.\n *\n * `dispatch`/`dispatchAsync` take the {@link UiWallet} and a `SolanaSignInInput` (pass `{}` for no\n * customization) and resolve with the wallet's sign-in output.\n *\n * @example\n * ```tsx\n * const { dispatch } = useSignIn();\n * dispatch(wallet, { domain: window.location.host });\n * ```\n *\n * @see {@link useConnect}\n */\nexport function useSignIn(): ActionResult<[wallet: UiWallet, input: SolanaSignInInput], SolanaSignInOutput> {\n const wallet = useWalletNamespace('useSignIn');\n return useAction((abortSignal, uiWallet: UiWallet, input: SolanaSignInInput) =>\n wallet.signIn(uiWallet, input, { abortSignal }),\n );\n}\n\n/**\n * Signs an arbitrary message with the connected account from a React component.\n *\n * Wraps `client.wallet.signMessage` with {@link useAction}. Requires a `ClientProvider` whose\n * client has a wallet plugin installed; asserts this at mount with {@link useClientCapability}.\n * A stale in-flight dispatch is dropped from the hook's state when a newer one starts (though\n * the wallet may still complete the older request in the background).\n *\n * `dispatch`/`dispatchAsync` take the message bytes and resolve with the signature. The bytes are\n * only read, never mutated, so a {@link ReadonlyUint8Array} (e.g. the output of a `@solana/kit`\n * codec) is accepted directly without a cast.\n *\n * @example\n * ```tsx\n * const { dispatch } = useSignMessage();\n * dispatch(new TextEncoder().encode('Hello'));\n * ```\n */\nexport function useSignMessage(): ActionResult<[message: ReadonlyUint8Array], SignatureBytes> {\n const wallet = useWalletNamespace('useSignMessage');\n return useAction((abortSignal, message: ReadonlyUint8Array) => wallet.signMessage(message, { abortSignal }));\n}\n\n/**\n * Returns a callback that selects a wallet account.\n *\n * Requires a `ClientProvider` whose client has a wallet plugin installed; asserts this at mount\n * with {@link useClientCapability}. Unlike the other action hooks, `selectAccount` is synchronous —\n * this hook returns the bound function directly rather than an `ActionResult`.\n *\n * The account may belong to any currently authorized wallet, not only the active one. Selecting an\n * account from a different authorized wallet switches the active connection to that wallet\n * synchronously (no prompt), leaving the previously active wallet authorized.\n *\n * @returns The `selectAccount` function — call it with a {@link UiWalletAccount} belonging to any\n * currently authorized wallet. It throws if the account's handle can't be resolved to an\n * authorized wallet (or isn't among that wallet's accounts), or that wallet is currently\n * disconnecting.\n *\n * @example\n * ```tsx\n * const selectAccount = useSelectAccount();\n * <button onClick={() => selectAccount(account)}>Use this account</button>\n * ```\n *\n * @see {@link useConnectedWallet}\n */\nexport function useSelectAccount(): WalletNamespace['selectAccount'] {\n return useWalletNamespace('useSelectAccount').selectAccount;\n}\n\n// -- Components -------------------------------------------------------------\n\n/**\n * Props for {@link WalletReadyGate}.\n */\nexport type WalletReadyGateProps = Readonly<{\n /** Rendered once the wallet client has finished its initial auto-reconnect warm-up. */\n children: ReactNode;\n /** Rendered while the client is still warming up (`'pending'` / `'reconnecting'`). */\n fallback: ReactNode;\n}>;\n\n/**\n * Renders `fallback` while the wallet client is still in its initial auto-reconnect warm-up\n * (`'pending'` / `'reconnecting'`), then reveals `children` once the connection has settled.\n *\n * A declarative wrapper over {@link useIsWalletReady} for the common case of holding back a whole\n * wallet-dependent subtree. Its props are shaped like React's `<Suspense fallback>`, but it is\n * synchronous — it renders `fallback` directly off the reactive status rather than suspending, so it\n * needs no `<Suspense>` ancestor. It lets an app paint its chrome immediately while gating only the\n * wallet-dependent UI, so a persisted wallet never flashes \"disconnected\" before it silently\n * reconnects. For finer control (disabling a button, showing an inline spinner) read the boolean from\n * {@link useIsWalletReady} directly. To actually suspend a subtree instead, throw the Suspense-safe\n * {@link WalletNamespace.whenReady} promise from your own component.\n *\n * Requires a `ClientProvider` whose client has a wallet plugin installed; asserts this at mount with\n * {@link useClientCapability}.\n *\n * @example\n * ```tsx\n * <WalletReadyGate fallback={<Spinner />}>\n * <WalletDependentApp />\n * </WalletReadyGate>\n * ```\n *\n * @see {@link useIsWalletReady}\n * @see {@link useWalletStatus}\n */\nexport function WalletReadyGate({ children, fallback }: WalletReadyGateProps): ReactNode {\n return useIsWalletReady() ? children : fallback;\n}\n"]}
1
+ {"version":3,"sources":["../../src/status.ts","../../src/react/index.ts"],"names":[],"mappings":";;;;;;AA8BO,SAAS,kBAAkB,MAAA,EAA+B;AAC7D,EAAA,OAAO,MAAA,KAAW,aAAa,MAAA,KAAW,cAAA;AAC9C;;;ACjBA,SAAS,mBAAmB,QAAA,EAAmC;AAC3D,EAAA,OAAO,mBAAA,CAAsC;AAAA,IACzC,UAAA,EAAY,QAAA;AAAA,IACZ,QAAA;AAAA,IACA,YAAA,EAAc;AAAA,GACjB,CAAA,CAAE,MAAA;AACP;AAsBO,SAAS,eAAA,GAAgC;AAC5C,EAAA,MAAM,MAAA,GAAS,mBAAmB,iBAAiB,CAAA;AACnD,EAAA,MAAM,SAAA,GAAY,MAAM,MAAA,CAAO,QAAA,EAAS,CAAE,MAAA;AAC1C,EAAA,OAAO,oBAAA,CAAqB,MAAA,CAAO,SAAA,EAAW,SAAA,EAAW,SAAS,CAAA;AACtE;AAqBO,SAAS,kBAAA,GAA+C;AAC3D,EAAA,MAAM,MAAA,GAAS,mBAAmB,oBAAoB,CAAA;AACtD,EAAA,MAAM,YAAA,GAAe,MAAM,MAAA,CAAO,QAAA,EAAS,CAAE,SAAA;AAC7C,EAAA,OAAO,oBAAA,CAAqB,MAAA,CAAO,SAAA,EAAW,YAAA,EAAc,YAAY,CAAA;AAC5E;AAsBO,SAAS,UAAA,GAAkC;AAC9C,EAAA,MAAM,MAAA,GAAS,mBAAmB,YAAY,CAAA;AAC9C,EAAA,MAAM,UAAA,GAAa,MAAM,MAAA,CAAO,QAAA,EAAS,CAAE,OAAA;AAC3C,EAAA,OAAO,oBAAA,CAAqB,MAAA,CAAO,SAAA,EAAW,UAAA,EAAY,UAAU,CAAA;AACxE;AA2BO,SAAS,gBAAA,GAA4B;AACxC,EAAA,MAAM,MAAA,GAAS,mBAAmB,kBAAkB,CAAA;AACpD,EAAA,MAAM,aAAa,MAAM,CAAC,kBAAkB,MAAA,CAAO,QAAA,GAAW,MAAM,CAAA;AACpE,EAAA,OAAO,oBAAA,CAAqB,MAAA,CAAO,SAAA,EAAW,UAAA,EAAY,UAAU,CAAA;AACxE;AAuBO,SAAS,UAAA,GAA2E;AACvF,EAAA,MAAM,MAAA,GAAS,mBAAmB,YAAY,CAAA;AAC9C,EAAA,OAAO,SAAA,CAAU,CAAC,WAAA,EAAa,QAAA,KAAuB,MAAA,CAAO,QAAQ,QAAA,EAAU,EAAE,WAAA,EAAa,CAAC,CAAA;AACnG;AAuBO,SAAS,aAAA,GAAyD;AACrE,EAAA,MAAM,MAAA,GAAS,mBAAmB,eAAe,CAAA;AACjD,EAAA,OAAO,SAAA,CAAU,CAAC,WAAA,EAAa,QAAA,KAAwB,MAAA,CAAO,WAAW,QAAA,EAAU,EAAE,WAAA,EAAa,CAAC,CAAA;AACvG;AAqBO,SAAS,SAAA,GAA4F;AACxG,EAAA,MAAM,MAAA,GAAS,mBAAmB,WAAW,CAAA;AAC7C,EAAA,OAAO,SAAA;AAAA,IAAU,CAAC,WAAA,EAAa,QAAA,EAAoB,KAAA,KAC/C,MAAA,CAAO,OAAO,QAAA,EAAU,KAAA,EAAO,EAAE,WAAA,EAAa;AAAA,GAClD;AACJ;AAoBO,SAAS,cAAA,GAA8E;AAC1F,EAAA,MAAM,MAAA,GAAS,mBAAmB,gBAAgB,CAAA;AAClD,EAAA,OAAO,SAAA,CAAU,CAAC,WAAA,EAAa,OAAA,KAAgC,MAAA,CAAO,YAAY,OAAA,EAAS,EAAE,WAAA,EAAa,CAAC,CAAA;AAC/G;AA0BO,SAAS,gBAAA,GAAqD;AACjE,EAAA,OAAO,kBAAA,CAAmB,kBAAkB,CAAA,CAAE,aAAA;AAClD;AAwCO,SAAS,eAAA,CAAgB,EAAE,QAAA,EAAU,QAAA,EAAS,EAAoC;AACrF,EAAA,OAAO,gBAAA,KAAqB,QAAA,GAAW,QAAA;AAC3C","file":"index.node.mjs","sourcesContent":["import type { WalletStatus } from './types';\n\n/**\n * Reports whether a {@link WalletStatus} belongs to the initial auto-reconnect\n * \"warm-up\": `'pending'` (before auto-reconnect has resolved) and\n * `'reconnecting'` (silently re-establishing a persisted connection). During\n * this window a persisted wallet is on its way back, so wallet-dependent UI\n * should usually hold rather than flash a \"disconnected\" state.\n *\n * User-initiated `'connecting'` / `'disconnecting'` are deliberately excluded —\n * a normal connect or disconnect is not a warm-up and must stay interactive.\n *\n * This is the single source of truth for \"warm-up\" across the plugin: the\n * store's {@link WalletNamespace.whenReady} waits for the status to leave this set, and the\n * React `useIsWalletReady` hook and `WalletReadyGate` component gate on its\n * negation. Use it directly to gate on warm-up from a non-React consumer.\n *\n * @param status - A status from `client.wallet.getState().status`.\n * @returns `true` while the client is warming up, otherwise `false`.\n *\n * @example\n * ```ts\n * client.wallet.subscribe(() => {\n * const { status } = client.wallet.getState();\n * setLoading(isWalletWarmingUp(status));\n * });\n * ```\n *\n * @see {@link WalletNamespace.whenReady} — the imperative (promise-based) counterpart.\n */\nexport function isWalletWarmingUp(status: WalletStatus): boolean {\n return status === 'pending' || status === 'reconnecting';\n}\n","import type { ReadonlyUint8Array, SignatureBytes } from '@solana/kit';\nimport { type ActionResult, useAction, useClientCapability } from '@solana/react';\nimport type { SolanaSignInInput, SolanaSignInOutput } from '@solana/wallet-standard-features';\nimport type { UiWallet, UiWalletAccount } from '@wallet-standard/ui';\nimport type { ReactNode } from 'react';\nimport { useSyncExternalStore } from 'react';\n\nimport { isWalletWarmingUp } from '../status';\nimport type { ClientWithWallet, WalletNamespace, WalletState, WalletStatus } from '../types';\n\n/**\n * Reads the wallet namespace from the nearest `ClientProvider`, asserting at mount that a wallet\n * plugin is installed. Shared by every hook in this module so the capability check, error hook\n * name, and provider hint stay in one place.\n */\nfunction useWalletNamespace(hookName: string): WalletNamespace {\n return useClientCapability<ClientWithWallet>({\n capability: 'wallet',\n hookName,\n providerHint: 'Install a wallet plugin on the client, e.g. `walletSigner()`.',\n }).wallet;\n}\n\n// -- State hooks ------------------------------------------------------------\n\n/**\n * Subscribes to the wallet connection status from a React component.\n *\n * Wraps `client.wallet.subscribe` / `getState` with `useSyncExternalStore`, re-rendering only when\n * the status changes. Requires a `ClientProvider` whose client has a wallet plugin installed;\n * asserts this at mount with {@link useClientCapability}.\n *\n * @returns The current {@link WalletStatus} (`'pending'` on the server and in React Native).\n *\n * @example\n * ```tsx\n * const status = useWalletStatus();\n * if (status === 'pending') return null; // avoid flashing UI before auto-reconnect resolves\n * ```\n *\n * @see {@link useConnectedWallet}\n * @see {@link useWallets}\n */\nexport function useWalletStatus(): WalletStatus {\n const wallet = useWalletNamespace('useWalletStatus');\n const getStatus = () => wallet.getState().status;\n return useSyncExternalStore(wallet.subscribe, getStatus, getStatus);\n}\n\n/**\n * Subscribes to the active wallet connection from a React component.\n *\n * Wraps `client.wallet.subscribe` / `getState` with `useSyncExternalStore`, re-rendering only when\n * the connection changes (connect, disconnect, or account switch). Requires a `ClientProvider`\n * whose client has a wallet plugin installed; asserts this at mount with {@link useClientCapability}.\n *\n * @returns The active connection — `{ account, signer, wallet }` — or `null` when disconnected.\n * `signer` is `null` for read-only wallets.\n *\n * @example\n * ```tsx\n * const connected = useConnectedWallet();\n * return connected ? <p>{connected.account.address}</p> : <p>Not connected</p>;\n * ```\n *\n * @see {@link useWalletStatus}\n * @see {@link useWallets}\n */\nexport function useConnectedWallet(): WalletState['connected'] {\n const wallet = useWalletNamespace('useConnectedWallet');\n const getConnected = () => wallet.getState().connected;\n return useSyncExternalStore(wallet.subscribe, getConnected, getConnected);\n}\n\n/**\n * Subscribes to the list of discovered wallets from a React component.\n *\n * Wraps `client.wallet.subscribe` / `getState` with `useSyncExternalStore`, re-rendering only when\n * the discovered-wallet list changes. Requires a `ClientProvider` whose client has a wallet plugin\n * installed; asserts this at mount with {@link useClientCapability}.\n *\n * @returns All discovered wallets matching the client's configured chain and filter (empty on the\n * server and in React Native).\n *\n * @example\n * ```tsx\n * const wallets = useWallets();\n * const connect = useConnect();\n * return wallets.map(w => <button key={w.name} onClick={() => connect.dispatch(w)}>{w.name}</button>);\n * ```\n *\n * @see {@link useWalletStatus}\n * @see {@link useConnectedWallet}\n */\nexport function useWallets(): readonly UiWallet[] {\n const wallet = useWalletNamespace('useWallets');\n const getWallets = () => wallet.getState().wallets;\n return useSyncExternalStore(wallet.subscribe, getWallets, getWallets);\n}\n\n/**\n * Reports whether the wallet client has finished its initial auto-reconnect \"warm-up\".\n *\n * Returns `false` while the status is `'pending'` or `'reconnecting'` — the transient states a\n * freshly built client passes through while it silently re-establishes a persisted connection — and\n * `true` once it settles into any stable state (`'connected'`, `'disconnected'`, or a user-initiated\n * `'connecting'` / `'disconnecting'`). This is the declarative, render-time counterpart to the\n * store's imperative `whenReady()`, gating on the exact same set of statuses.\n *\n * Use it to hold back wallet-dependent UI until the connection has settled, so a persisted wallet\n * never flashes \"disconnected\" before it reconnects. Re-renders only when this readiness boolean\n * flips, not on every status change. Requires a `ClientProvider` whose client has a wallet plugin\n * installed; asserts this at mount with {@link useClientCapability}.\n *\n * @returns `true` once the warm-up has settled, otherwise `false`.\n *\n * @example\n * ```tsx\n * const isReady = useIsWalletReady();\n * return <button disabled={!isReady}>Connect</button>;\n * ```\n *\n * @see {@link WalletReadyGate}\n * @see {@link useWalletStatus}\n */\nexport function useIsWalletReady(): boolean {\n const wallet = useWalletNamespace('useIsWalletReady');\n const getIsReady = () => !isWalletWarmingUp(wallet.getState().status);\n return useSyncExternalStore(wallet.subscribe, getIsReady, getIsReady);\n}\n\n// -- Action hooks -----------------------------------------------------------\n\n/**\n * Connects to a wallet from a React component.\n *\n * Wraps `client.wallet.connect` with {@link useAction}. Requires a `ClientProvider` whose client\n * has a wallet plugin installed; asserts this at mount with {@link useClientCapability}. An\n * in-flight connect is superseded when a newer one is dispatched.\n *\n * `dispatch`/`dispatchAsync` take the {@link UiWallet} to connect to and resolve with the wallet's\n * accounts once connected.\n *\n * @example\n * ```tsx\n * const { dispatch, isRunning } = useConnect();\n * <button disabled={isRunning} onClick={() => dispatch(wallet)}>Connect</button>\n * ```\n *\n * @see {@link useDisconnect}\n * @see {@link useSignIn}\n */\nexport function useConnect(): ActionResult<[wallet: UiWallet], readonly UiWalletAccount[]> {\n const wallet = useWalletNamespace('useConnect');\n return useAction((abortSignal, uiWallet: UiWallet) => wallet.connect(uiWallet, { abortSignal }));\n}\n\n/**\n * Disconnects a wallet from a React component.\n *\n * Wraps `client.wallet.disconnect` with {@link useAction}. Requires a `ClientProvider` whose client\n * has a wallet plugin installed; asserts this at mount with {@link useClientCapability}.\n *\n * `dispatch`/`dispatchAsync` take an optional {@link UiWallet}. Called with no argument they\n * disconnect the active wallet; passing a non-active, currently authorized wallet deauthorizes that\n * wallet while leaving the active connection in place.\n *\n * @example\n * ```tsx\n * const { dispatch, isRunning } = useDisconnect();\n * // Disconnect the active wallet:\n * <button disabled={isRunning} onClick={() => dispatch()}>Disconnect</button>\n * // Or deauthorize a specific wallet:\n * <button disabled={isRunning} onClick={() => dispatch(wallet)}>Forget this wallet</button>\n * ```\n *\n * @see {@link useConnect}\n */\nexport function useDisconnect(): ActionResult<[wallet?: UiWallet], void> {\n const wallet = useWalletNamespace('useDisconnect');\n return useAction((abortSignal, uiWallet?: UiWallet) => wallet.disconnect(uiWallet, { abortSignal }));\n}\n\n/**\n * Signs in with a wallet (Sign In With Solana) from a React component.\n *\n * Wraps `client.wallet.signIn` with {@link useAction}. Requires a `ClientProvider` whose client has\n * a wallet plugin installed; asserts this at mount with {@link useClientCapability}. Like\n * {@link useConnect}, this establishes a full connection. An in-flight connect is superseded when a\n * newer one is dispatched.\n *\n * `dispatch`/`dispatchAsync` take the {@link UiWallet} and a `SolanaSignInInput` (pass `{}` for no\n * customization) and resolve with the wallet's sign-in output.\n *\n * @example\n * ```tsx\n * const { dispatch } = useSignIn();\n * dispatch(wallet, { domain: window.location.host });\n * ```\n *\n * @see {@link useConnect}\n */\nexport function useSignIn(): ActionResult<[wallet: UiWallet, input: SolanaSignInInput], SolanaSignInOutput> {\n const wallet = useWalletNamespace('useSignIn');\n return useAction((abortSignal, uiWallet: UiWallet, input: SolanaSignInInput) =>\n wallet.signIn(uiWallet, input, { abortSignal }),\n );\n}\n\n/**\n * Signs an arbitrary message with the connected account from a React component.\n *\n * Wraps `client.wallet.signMessage` with {@link useAction}. Requires a `ClientProvider` whose\n * client has a wallet plugin installed; asserts this at mount with {@link useClientCapability}.\n * A stale in-flight dispatch is dropped from the hook's state when a newer one starts (though\n * the wallet may still complete the older request in the background).\n *\n * `dispatch`/`dispatchAsync` take the message bytes and resolve with the signature. The bytes are\n * only read, never mutated, so a {@link ReadonlyUint8Array} (e.g. the output of a `@solana/kit`\n * codec) is accepted directly without a cast.\n *\n * @example\n * ```tsx\n * const { dispatch } = useSignMessage();\n * dispatch(new TextEncoder().encode('Hello'));\n * ```\n */\nexport function useSignMessage(): ActionResult<[message: ReadonlyUint8Array], SignatureBytes> {\n const wallet = useWalletNamespace('useSignMessage');\n return useAction((abortSignal, message: ReadonlyUint8Array) => wallet.signMessage(message, { abortSignal }));\n}\n\n/**\n * Returns a callback that selects a wallet account.\n *\n * Requires a `ClientProvider` whose client has a wallet plugin installed; asserts this at mount\n * with {@link useClientCapability}. Unlike the other action hooks, `selectAccount` is synchronous —\n * this hook returns the bound function directly rather than an `ActionResult`.\n *\n * The account may belong to any currently authorized wallet, not only the active one. Selecting an\n * account from a different authorized wallet switches the active connection to that wallet\n * synchronously (no prompt), leaving the previously active wallet authorized.\n *\n * @returns The `selectAccount` function — call it with a {@link UiWalletAccount} belonging to any\n * currently authorized wallet. It throws if the account's handle can't be resolved to an\n * authorized wallet (or isn't among that wallet's accounts), or that wallet is currently\n * disconnecting.\n *\n * @example\n * ```tsx\n * const selectAccount = useSelectAccount();\n * <button onClick={() => selectAccount(account)}>Use this account</button>\n * ```\n *\n * @see {@link useConnectedWallet}\n */\nexport function useSelectAccount(): WalletNamespace['selectAccount'] {\n return useWalletNamespace('useSelectAccount').selectAccount;\n}\n\n// -- Components -------------------------------------------------------------\n\n/**\n * Props for {@link WalletReadyGate}.\n */\nexport type WalletReadyGateProps = Readonly<{\n /** Rendered once the wallet client has finished its initial auto-reconnect warm-up. */\n children: ReactNode;\n /** Rendered while the client is still warming up (`'pending'` / `'reconnecting'`). */\n fallback: ReactNode;\n}>;\n\n/**\n * Renders `fallback` while the wallet client is still in its initial auto-reconnect warm-up\n * (`'pending'` / `'reconnecting'`), then reveals `children` once the connection has settled.\n *\n * A declarative wrapper over {@link useIsWalletReady} for the common case of holding back a whole\n * wallet-dependent subtree. Its props are shaped like React's `<Suspense fallback>`, but it is\n * synchronous — it renders `fallback` directly off the reactive status rather than suspending, so it\n * needs no `<Suspense>` ancestor. It lets an app paint its chrome immediately while gating only the\n * wallet-dependent UI, so a persisted wallet never flashes \"disconnected\" before it silently\n * reconnects. For finer control (disabling a button, showing an inline spinner) read the boolean from\n * {@link useIsWalletReady} directly. To actually suspend a subtree instead, throw the Suspense-safe\n * {@link WalletNamespace.whenReady} promise from your own component.\n *\n * Requires a `ClientProvider` whose client has a wallet plugin installed; asserts this at mount with\n * {@link useClientCapability}.\n *\n * @example\n * ```tsx\n * <WalletReadyGate fallback={<Spinner />}>\n * <WalletDependentApp />\n * </WalletReadyGate>\n * ```\n *\n * @see {@link useIsWalletReady}\n * @see {@link useWalletStatus}\n */\nexport function WalletReadyGate({ children, fallback }: WalletReadyGateProps): ReactNode {\n return useIsWalletReady() ? children : fallback;\n}\n"]}
@@ -1,7 +1,14 @@
1
- import { isWalletWarmingUp } from '../chunk-Z7IRL62Z.react-native.mjs';
2
1
  import { useAction, useClientCapability } from '@solana/react';
3
2
  import { useSyncExternalStore } from 'react';
4
3
 
4
+ // src/react/index.ts
5
+
6
+ // src/status.ts
7
+ function isWalletWarmingUp(status) {
8
+ return status === "pending" || status === "reconnecting";
9
+ }
10
+
11
+ // src/react/index.ts
5
12
  function useWalletNamespace(hookName) {
6
13
  return useClientCapability({
7
14
  capability: "wallet",
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/react/index.ts"],"names":[],"mappings":";;;;AAeA,SAAS,mBAAmB,QAAA,EAAmC;AAC3D,EAAA,OAAO,mBAAA,CAAsC;AAAA,IACzC,UAAA,EAAY,QAAA;AAAA,IACZ,QAAA;AAAA,IACA,YAAA,EAAc;AAAA,GACjB,CAAA,CAAE,MAAA;AACP;AAsBO,SAAS,eAAA,GAAgC;AAC5C,EAAA,MAAM,MAAA,GAAS,mBAAmB,iBAAiB,CAAA;AACnD,EAAA,MAAM,SAAA,GAAY,MAAM,MAAA,CAAO,QAAA,EAAS,CAAE,MAAA;AAC1C,EAAA,OAAO,oBAAA,CAAqB,MAAA,CAAO,SAAA,EAAW,SAAA,EAAW,SAAS,CAAA;AACtE;AAqBO,SAAS,kBAAA,GAA+C;AAC3D,EAAA,MAAM,MAAA,GAAS,mBAAmB,oBAAoB,CAAA;AACtD,EAAA,MAAM,YAAA,GAAe,MAAM,MAAA,CAAO,QAAA,EAAS,CAAE,SAAA;AAC7C,EAAA,OAAO,oBAAA,CAAqB,MAAA,CAAO,SAAA,EAAW,YAAA,EAAc,YAAY,CAAA;AAC5E;AAsBO,SAAS,UAAA,GAAkC;AAC9C,EAAA,MAAM,MAAA,GAAS,mBAAmB,YAAY,CAAA;AAC9C,EAAA,MAAM,UAAA,GAAa,MAAM,MAAA,CAAO,QAAA,EAAS,CAAE,OAAA;AAC3C,EAAA,OAAO,oBAAA,CAAqB,MAAA,CAAO,SAAA,EAAW,UAAA,EAAY,UAAU,CAAA;AACxE;AA2BO,SAAS,gBAAA,GAA4B;AACxC,EAAA,MAAM,MAAA,GAAS,mBAAmB,kBAAkB,CAAA;AACpD,EAAA,MAAM,aAAa,MAAM,CAAC,kBAAkB,MAAA,CAAO,QAAA,GAAW,MAAM,CAAA;AACpE,EAAA,OAAO,oBAAA,CAAqB,MAAA,CAAO,SAAA,EAAW,UAAA,EAAY,UAAU,CAAA;AACxE;AAuBO,SAAS,UAAA,GAA2E;AACvF,EAAA,MAAM,MAAA,GAAS,mBAAmB,YAAY,CAAA;AAC9C,EAAA,OAAO,SAAA,CAAU,CAAC,WAAA,EAAa,QAAA,KAAuB,MAAA,CAAO,QAAQ,QAAA,EAAU,EAAE,WAAA,EAAa,CAAC,CAAA;AACnG;AAuBO,SAAS,aAAA,GAAyD;AACrE,EAAA,MAAM,MAAA,GAAS,mBAAmB,eAAe,CAAA;AACjD,EAAA,OAAO,SAAA,CAAU,CAAC,WAAA,EAAa,QAAA,KAAwB,MAAA,CAAO,WAAW,QAAA,EAAU,EAAE,WAAA,EAAa,CAAC,CAAA;AACvG;AAqBO,SAAS,SAAA,GAA4F;AACxG,EAAA,MAAM,MAAA,GAAS,mBAAmB,WAAW,CAAA;AAC7C,EAAA,OAAO,SAAA;AAAA,IAAU,CAAC,WAAA,EAAa,QAAA,EAAoB,KAAA,KAC/C,MAAA,CAAO,OAAO,QAAA,EAAU,KAAA,EAAO,EAAE,WAAA,EAAa;AAAA,GAClD;AACJ;AAoBO,SAAS,cAAA,GAA8E;AAC1F,EAAA,MAAM,MAAA,GAAS,mBAAmB,gBAAgB,CAAA;AAClD,EAAA,OAAO,SAAA,CAAU,CAAC,WAAA,EAAa,OAAA,KAAgC,MAAA,CAAO,YAAY,OAAA,EAAS,EAAE,WAAA,EAAa,CAAC,CAAA;AAC/G;AA0BO,SAAS,gBAAA,GAAqD;AACjE,EAAA,OAAO,kBAAA,CAAmB,kBAAkB,CAAA,CAAE,aAAA;AAClD;AAwCO,SAAS,eAAA,CAAgB,EAAE,QAAA,EAAU,QAAA,EAAS,EAAoC;AACrF,EAAA,OAAO,gBAAA,KAAqB,QAAA,GAAW,QAAA;AAC3C","file":"index.react-native.mjs","sourcesContent":["import type { ReadonlyUint8Array, SignatureBytes } from '@solana/kit';\nimport { type ActionResult, useAction, useClientCapability } from '@solana/react';\nimport type { SolanaSignInInput, SolanaSignInOutput } from '@solana/wallet-standard-features';\nimport type { UiWallet, UiWalletAccount } from '@wallet-standard/ui';\nimport type { ReactNode } from 'react';\nimport { useSyncExternalStore } from 'react';\n\nimport { isWalletWarmingUp } from '../status';\nimport type { ClientWithWallet, WalletNamespace, WalletState, WalletStatus } from '../types';\n\n/**\n * Reads the wallet namespace from the nearest `ClientProvider`, asserting at mount that a wallet\n * plugin is installed. Shared by every hook in this module so the capability check, error hook\n * name, and provider hint stay in one place.\n */\nfunction useWalletNamespace(hookName: string): WalletNamespace {\n return useClientCapability<ClientWithWallet>({\n capability: 'wallet',\n hookName,\n providerHint: 'Install a wallet plugin on the client, e.g. `walletSigner()`.',\n }).wallet;\n}\n\n// -- State hooks ------------------------------------------------------------\n\n/**\n * Subscribes to the wallet connection status from a React component.\n *\n * Wraps `client.wallet.subscribe` / `getState` with `useSyncExternalStore`, re-rendering only when\n * the status changes. Requires a `ClientProvider` whose client has a wallet plugin installed;\n * asserts this at mount with {@link useClientCapability}.\n *\n * @returns The current {@link WalletStatus} (`'pending'` on the server and in React Native).\n *\n * @example\n * ```tsx\n * const status = useWalletStatus();\n * if (status === 'pending') return null; // avoid flashing UI before auto-reconnect resolves\n * ```\n *\n * @see {@link useConnectedWallet}\n * @see {@link useWallets}\n */\nexport function useWalletStatus(): WalletStatus {\n const wallet = useWalletNamespace('useWalletStatus');\n const getStatus = () => wallet.getState().status;\n return useSyncExternalStore(wallet.subscribe, getStatus, getStatus);\n}\n\n/**\n * Subscribes to the active wallet connection from a React component.\n *\n * Wraps `client.wallet.subscribe` / `getState` with `useSyncExternalStore`, re-rendering only when\n * the connection changes (connect, disconnect, or account switch). Requires a `ClientProvider`\n * whose client has a wallet plugin installed; asserts this at mount with {@link useClientCapability}.\n *\n * @returns The active connection — `{ account, signer, wallet }` — or `null` when disconnected.\n * `signer` is `null` for read-only wallets.\n *\n * @example\n * ```tsx\n * const connected = useConnectedWallet();\n * return connected ? <p>{connected.account.address}</p> : <p>Not connected</p>;\n * ```\n *\n * @see {@link useWalletStatus}\n * @see {@link useWallets}\n */\nexport function useConnectedWallet(): WalletState['connected'] {\n const wallet = useWalletNamespace('useConnectedWallet');\n const getConnected = () => wallet.getState().connected;\n return useSyncExternalStore(wallet.subscribe, getConnected, getConnected);\n}\n\n/**\n * Subscribes to the list of discovered wallets from a React component.\n *\n * Wraps `client.wallet.subscribe` / `getState` with `useSyncExternalStore`, re-rendering only when\n * the discovered-wallet list changes. Requires a `ClientProvider` whose client has a wallet plugin\n * installed; asserts this at mount with {@link useClientCapability}.\n *\n * @returns All discovered wallets matching the client's configured chain and filter (empty on the\n * server and in React Native).\n *\n * @example\n * ```tsx\n * const wallets = useWallets();\n * const connect = useConnect();\n * return wallets.map(w => <button key={w.name} onClick={() => connect.dispatch(w)}>{w.name}</button>);\n * ```\n *\n * @see {@link useWalletStatus}\n * @see {@link useConnectedWallet}\n */\nexport function useWallets(): readonly UiWallet[] {\n const wallet = useWalletNamespace('useWallets');\n const getWallets = () => wallet.getState().wallets;\n return useSyncExternalStore(wallet.subscribe, getWallets, getWallets);\n}\n\n/**\n * Reports whether the wallet client has finished its initial auto-reconnect \"warm-up\".\n *\n * Returns `false` while the status is `'pending'` or `'reconnecting'` — the transient states a\n * freshly built client passes through while it silently re-establishes a persisted connection — and\n * `true` once it settles into any stable state (`'connected'`, `'disconnected'`, or a user-initiated\n * `'connecting'` / `'disconnecting'`). This is the declarative, render-time counterpart to the\n * store's imperative `whenReady()`, gating on the exact same set of statuses.\n *\n * Use it to hold back wallet-dependent UI until the connection has settled, so a persisted wallet\n * never flashes \"disconnected\" before it reconnects. Re-renders only when this readiness boolean\n * flips, not on every status change. Requires a `ClientProvider` whose client has a wallet plugin\n * installed; asserts this at mount with {@link useClientCapability}.\n *\n * @returns `true` once the warm-up has settled, otherwise `false`.\n *\n * @example\n * ```tsx\n * const isReady = useIsWalletReady();\n * return <button disabled={!isReady}>Connect</button>;\n * ```\n *\n * @see {@link WalletReadyGate}\n * @see {@link useWalletStatus}\n */\nexport function useIsWalletReady(): boolean {\n const wallet = useWalletNamespace('useIsWalletReady');\n const getIsReady = () => !isWalletWarmingUp(wallet.getState().status);\n return useSyncExternalStore(wallet.subscribe, getIsReady, getIsReady);\n}\n\n// -- Action hooks -----------------------------------------------------------\n\n/**\n * Connects to a wallet from a React component.\n *\n * Wraps `client.wallet.connect` with {@link useAction}. Requires a `ClientProvider` whose client\n * has a wallet plugin installed; asserts this at mount with {@link useClientCapability}. An\n * in-flight connect is superseded when a newer one is dispatched.\n *\n * `dispatch`/`dispatchAsync` take the {@link UiWallet} to connect to and resolve with the wallet's\n * accounts once connected.\n *\n * @example\n * ```tsx\n * const { dispatch, isRunning } = useConnect();\n * <button disabled={isRunning} onClick={() => dispatch(wallet)}>Connect</button>\n * ```\n *\n * @see {@link useDisconnect}\n * @see {@link useSignIn}\n */\nexport function useConnect(): ActionResult<[wallet: UiWallet], readonly UiWalletAccount[]> {\n const wallet = useWalletNamespace('useConnect');\n return useAction((abortSignal, uiWallet: UiWallet) => wallet.connect(uiWallet, { abortSignal }));\n}\n\n/**\n * Disconnects a wallet from a React component.\n *\n * Wraps `client.wallet.disconnect` with {@link useAction}. Requires a `ClientProvider` whose client\n * has a wallet plugin installed; asserts this at mount with {@link useClientCapability}.\n *\n * `dispatch`/`dispatchAsync` take an optional {@link UiWallet}. Called with no argument they\n * disconnect the active wallet; passing a non-active, currently authorized wallet deauthorizes that\n * wallet while leaving the active connection in place.\n *\n * @example\n * ```tsx\n * const { dispatch, isRunning } = useDisconnect();\n * // Disconnect the active wallet:\n * <button disabled={isRunning} onClick={() => dispatch()}>Disconnect</button>\n * // Or deauthorize a specific wallet:\n * <button disabled={isRunning} onClick={() => dispatch(wallet)}>Forget this wallet</button>\n * ```\n *\n * @see {@link useConnect}\n */\nexport function useDisconnect(): ActionResult<[wallet?: UiWallet], void> {\n const wallet = useWalletNamespace('useDisconnect');\n return useAction((abortSignal, uiWallet?: UiWallet) => wallet.disconnect(uiWallet, { abortSignal }));\n}\n\n/**\n * Signs in with a wallet (Sign In With Solana) from a React component.\n *\n * Wraps `client.wallet.signIn` with {@link useAction}. Requires a `ClientProvider` whose client has\n * a wallet plugin installed; asserts this at mount with {@link useClientCapability}. Like\n * {@link useConnect}, this establishes a full connection. An in-flight connect is superseded when a\n * newer one is dispatched.\n *\n * `dispatch`/`dispatchAsync` take the {@link UiWallet} and a `SolanaSignInInput` (pass `{}` for no\n * customization) and resolve with the wallet's sign-in output.\n *\n * @example\n * ```tsx\n * const { dispatch } = useSignIn();\n * dispatch(wallet, { domain: window.location.host });\n * ```\n *\n * @see {@link useConnect}\n */\nexport function useSignIn(): ActionResult<[wallet: UiWallet, input: SolanaSignInInput], SolanaSignInOutput> {\n const wallet = useWalletNamespace('useSignIn');\n return useAction((abortSignal, uiWallet: UiWallet, input: SolanaSignInInput) =>\n wallet.signIn(uiWallet, input, { abortSignal }),\n );\n}\n\n/**\n * Signs an arbitrary message with the connected account from a React component.\n *\n * Wraps `client.wallet.signMessage` with {@link useAction}. Requires a `ClientProvider` whose\n * client has a wallet plugin installed; asserts this at mount with {@link useClientCapability}.\n * A stale in-flight dispatch is dropped from the hook's state when a newer one starts (though\n * the wallet may still complete the older request in the background).\n *\n * `dispatch`/`dispatchAsync` take the message bytes and resolve with the signature. The bytes are\n * only read, never mutated, so a {@link ReadonlyUint8Array} (e.g. the output of a `@solana/kit`\n * codec) is accepted directly without a cast.\n *\n * @example\n * ```tsx\n * const { dispatch } = useSignMessage();\n * dispatch(new TextEncoder().encode('Hello'));\n * ```\n */\nexport function useSignMessage(): ActionResult<[message: ReadonlyUint8Array], SignatureBytes> {\n const wallet = useWalletNamespace('useSignMessage');\n return useAction((abortSignal, message: ReadonlyUint8Array) => wallet.signMessage(message, { abortSignal }));\n}\n\n/**\n * Returns a callback that selects a wallet account.\n *\n * Requires a `ClientProvider` whose client has a wallet plugin installed; asserts this at mount\n * with {@link useClientCapability}. Unlike the other action hooks, `selectAccount` is synchronous —\n * this hook returns the bound function directly rather than an `ActionResult`.\n *\n * The account may belong to any currently authorized wallet, not only the active one. Selecting an\n * account from a different authorized wallet switches the active connection to that wallet\n * synchronously (no prompt), leaving the previously active wallet authorized.\n *\n * @returns The `selectAccount` function — call it with a {@link UiWalletAccount} belonging to any\n * currently authorized wallet. It throws if the account's handle can't be resolved to an\n * authorized wallet (or isn't among that wallet's accounts), or that wallet is currently\n * disconnecting.\n *\n * @example\n * ```tsx\n * const selectAccount = useSelectAccount();\n * <button onClick={() => selectAccount(account)}>Use this account</button>\n * ```\n *\n * @see {@link useConnectedWallet}\n */\nexport function useSelectAccount(): WalletNamespace['selectAccount'] {\n return useWalletNamespace('useSelectAccount').selectAccount;\n}\n\n// -- Components -------------------------------------------------------------\n\n/**\n * Props for {@link WalletReadyGate}.\n */\nexport type WalletReadyGateProps = Readonly<{\n /** Rendered once the wallet client has finished its initial auto-reconnect warm-up. */\n children: ReactNode;\n /** Rendered while the client is still warming up (`'pending'` / `'reconnecting'`). */\n fallback: ReactNode;\n}>;\n\n/**\n * Renders `fallback` while the wallet client is still in its initial auto-reconnect warm-up\n * (`'pending'` / `'reconnecting'`), then reveals `children` once the connection has settled.\n *\n * A declarative wrapper over {@link useIsWalletReady} for the common case of holding back a whole\n * wallet-dependent subtree. Its props are shaped like React's `<Suspense fallback>`, but it is\n * synchronous — it renders `fallback` directly off the reactive status rather than suspending, so it\n * needs no `<Suspense>` ancestor. It lets an app paint its chrome immediately while gating only the\n * wallet-dependent UI, so a persisted wallet never flashes \"disconnected\" before it silently\n * reconnects. For finer control (disabling a button, showing an inline spinner) read the boolean from\n * {@link useIsWalletReady} directly. To actually suspend a subtree instead, throw the Suspense-safe\n * {@link WalletNamespace.whenReady} promise from your own component.\n *\n * Requires a `ClientProvider` whose client has a wallet plugin installed; asserts this at mount with\n * {@link useClientCapability}.\n *\n * @example\n * ```tsx\n * <WalletReadyGate fallback={<Spinner />}>\n * <WalletDependentApp />\n * </WalletReadyGate>\n * ```\n *\n * @see {@link useIsWalletReady}\n * @see {@link useWalletStatus}\n */\nexport function WalletReadyGate({ children, fallback }: WalletReadyGateProps): ReactNode {\n return useIsWalletReady() ? children : fallback;\n}\n"]}
1
+ {"version":3,"sources":["../../src/status.ts","../../src/react/index.ts"],"names":[],"mappings":";;;;;;AA8BO,SAAS,kBAAkB,MAAA,EAA+B;AAC7D,EAAA,OAAO,MAAA,KAAW,aAAa,MAAA,KAAW,cAAA;AAC9C;;;ACjBA,SAAS,mBAAmB,QAAA,EAAmC;AAC3D,EAAA,OAAO,mBAAA,CAAsC;AAAA,IACzC,UAAA,EAAY,QAAA;AAAA,IACZ,QAAA;AAAA,IACA,YAAA,EAAc;AAAA,GACjB,CAAA,CAAE,MAAA;AACP;AAsBO,SAAS,eAAA,GAAgC;AAC5C,EAAA,MAAM,MAAA,GAAS,mBAAmB,iBAAiB,CAAA;AACnD,EAAA,MAAM,SAAA,GAAY,MAAM,MAAA,CAAO,QAAA,EAAS,CAAE,MAAA;AAC1C,EAAA,OAAO,oBAAA,CAAqB,MAAA,CAAO,SAAA,EAAW,SAAA,EAAW,SAAS,CAAA;AACtE;AAqBO,SAAS,kBAAA,GAA+C;AAC3D,EAAA,MAAM,MAAA,GAAS,mBAAmB,oBAAoB,CAAA;AACtD,EAAA,MAAM,YAAA,GAAe,MAAM,MAAA,CAAO,QAAA,EAAS,CAAE,SAAA;AAC7C,EAAA,OAAO,oBAAA,CAAqB,MAAA,CAAO,SAAA,EAAW,YAAA,EAAc,YAAY,CAAA;AAC5E;AAsBO,SAAS,UAAA,GAAkC;AAC9C,EAAA,MAAM,MAAA,GAAS,mBAAmB,YAAY,CAAA;AAC9C,EAAA,MAAM,UAAA,GAAa,MAAM,MAAA,CAAO,QAAA,EAAS,CAAE,OAAA;AAC3C,EAAA,OAAO,oBAAA,CAAqB,MAAA,CAAO,SAAA,EAAW,UAAA,EAAY,UAAU,CAAA;AACxE;AA2BO,SAAS,gBAAA,GAA4B;AACxC,EAAA,MAAM,MAAA,GAAS,mBAAmB,kBAAkB,CAAA;AACpD,EAAA,MAAM,aAAa,MAAM,CAAC,kBAAkB,MAAA,CAAO,QAAA,GAAW,MAAM,CAAA;AACpE,EAAA,OAAO,oBAAA,CAAqB,MAAA,CAAO,SAAA,EAAW,UAAA,EAAY,UAAU,CAAA;AACxE;AAuBO,SAAS,UAAA,GAA2E;AACvF,EAAA,MAAM,MAAA,GAAS,mBAAmB,YAAY,CAAA;AAC9C,EAAA,OAAO,SAAA,CAAU,CAAC,WAAA,EAAa,QAAA,KAAuB,MAAA,CAAO,QAAQ,QAAA,EAAU,EAAE,WAAA,EAAa,CAAC,CAAA;AACnG;AAuBO,SAAS,aAAA,GAAyD;AACrE,EAAA,MAAM,MAAA,GAAS,mBAAmB,eAAe,CAAA;AACjD,EAAA,OAAO,SAAA,CAAU,CAAC,WAAA,EAAa,QAAA,KAAwB,MAAA,CAAO,WAAW,QAAA,EAAU,EAAE,WAAA,EAAa,CAAC,CAAA;AACvG;AAqBO,SAAS,SAAA,GAA4F;AACxG,EAAA,MAAM,MAAA,GAAS,mBAAmB,WAAW,CAAA;AAC7C,EAAA,OAAO,SAAA;AAAA,IAAU,CAAC,WAAA,EAAa,QAAA,EAAoB,KAAA,KAC/C,MAAA,CAAO,OAAO,QAAA,EAAU,KAAA,EAAO,EAAE,WAAA,EAAa;AAAA,GAClD;AACJ;AAoBO,SAAS,cAAA,GAA8E;AAC1F,EAAA,MAAM,MAAA,GAAS,mBAAmB,gBAAgB,CAAA;AAClD,EAAA,OAAO,SAAA,CAAU,CAAC,WAAA,EAAa,OAAA,KAAgC,MAAA,CAAO,YAAY,OAAA,EAAS,EAAE,WAAA,EAAa,CAAC,CAAA;AAC/G;AA0BO,SAAS,gBAAA,GAAqD;AACjE,EAAA,OAAO,kBAAA,CAAmB,kBAAkB,CAAA,CAAE,aAAA;AAClD;AAwCO,SAAS,eAAA,CAAgB,EAAE,QAAA,EAAU,QAAA,EAAS,EAAoC;AACrF,EAAA,OAAO,gBAAA,KAAqB,QAAA,GAAW,QAAA;AAC3C","file":"index.react-native.mjs","sourcesContent":["import type { WalletStatus } from './types';\n\n/**\n * Reports whether a {@link WalletStatus} belongs to the initial auto-reconnect\n * \"warm-up\": `'pending'` (before auto-reconnect has resolved) and\n * `'reconnecting'` (silently re-establishing a persisted connection). During\n * this window a persisted wallet is on its way back, so wallet-dependent UI\n * should usually hold rather than flash a \"disconnected\" state.\n *\n * User-initiated `'connecting'` / `'disconnecting'` are deliberately excluded —\n * a normal connect or disconnect is not a warm-up and must stay interactive.\n *\n * This is the single source of truth for \"warm-up\" across the plugin: the\n * store's {@link WalletNamespace.whenReady} waits for the status to leave this set, and the\n * React `useIsWalletReady` hook and `WalletReadyGate` component gate on its\n * negation. Use it directly to gate on warm-up from a non-React consumer.\n *\n * @param status - A status from `client.wallet.getState().status`.\n * @returns `true` while the client is warming up, otherwise `false`.\n *\n * @example\n * ```ts\n * client.wallet.subscribe(() => {\n * const { status } = client.wallet.getState();\n * setLoading(isWalletWarmingUp(status));\n * });\n * ```\n *\n * @see {@link WalletNamespace.whenReady} — the imperative (promise-based) counterpart.\n */\nexport function isWalletWarmingUp(status: WalletStatus): boolean {\n return status === 'pending' || status === 'reconnecting';\n}\n","import type { ReadonlyUint8Array, SignatureBytes } from '@solana/kit';\nimport { type ActionResult, useAction, useClientCapability } from '@solana/react';\nimport type { SolanaSignInInput, SolanaSignInOutput } from '@solana/wallet-standard-features';\nimport type { UiWallet, UiWalletAccount } from '@wallet-standard/ui';\nimport type { ReactNode } from 'react';\nimport { useSyncExternalStore } from 'react';\n\nimport { isWalletWarmingUp } from '../status';\nimport type { ClientWithWallet, WalletNamespace, WalletState, WalletStatus } from '../types';\n\n/**\n * Reads the wallet namespace from the nearest `ClientProvider`, asserting at mount that a wallet\n * plugin is installed. Shared by every hook in this module so the capability check, error hook\n * name, and provider hint stay in one place.\n */\nfunction useWalletNamespace(hookName: string): WalletNamespace {\n return useClientCapability<ClientWithWallet>({\n capability: 'wallet',\n hookName,\n providerHint: 'Install a wallet plugin on the client, e.g. `walletSigner()`.',\n }).wallet;\n}\n\n// -- State hooks ------------------------------------------------------------\n\n/**\n * Subscribes to the wallet connection status from a React component.\n *\n * Wraps `client.wallet.subscribe` / `getState` with `useSyncExternalStore`, re-rendering only when\n * the status changes. Requires a `ClientProvider` whose client has a wallet plugin installed;\n * asserts this at mount with {@link useClientCapability}.\n *\n * @returns The current {@link WalletStatus} (`'pending'` on the server and in React Native).\n *\n * @example\n * ```tsx\n * const status = useWalletStatus();\n * if (status === 'pending') return null; // avoid flashing UI before auto-reconnect resolves\n * ```\n *\n * @see {@link useConnectedWallet}\n * @see {@link useWallets}\n */\nexport function useWalletStatus(): WalletStatus {\n const wallet = useWalletNamespace('useWalletStatus');\n const getStatus = () => wallet.getState().status;\n return useSyncExternalStore(wallet.subscribe, getStatus, getStatus);\n}\n\n/**\n * Subscribes to the active wallet connection from a React component.\n *\n * Wraps `client.wallet.subscribe` / `getState` with `useSyncExternalStore`, re-rendering only when\n * the connection changes (connect, disconnect, or account switch). Requires a `ClientProvider`\n * whose client has a wallet plugin installed; asserts this at mount with {@link useClientCapability}.\n *\n * @returns The active connection — `{ account, signer, wallet }` — or `null` when disconnected.\n * `signer` is `null` for read-only wallets.\n *\n * @example\n * ```tsx\n * const connected = useConnectedWallet();\n * return connected ? <p>{connected.account.address}</p> : <p>Not connected</p>;\n * ```\n *\n * @see {@link useWalletStatus}\n * @see {@link useWallets}\n */\nexport function useConnectedWallet(): WalletState['connected'] {\n const wallet = useWalletNamespace('useConnectedWallet');\n const getConnected = () => wallet.getState().connected;\n return useSyncExternalStore(wallet.subscribe, getConnected, getConnected);\n}\n\n/**\n * Subscribes to the list of discovered wallets from a React component.\n *\n * Wraps `client.wallet.subscribe` / `getState` with `useSyncExternalStore`, re-rendering only when\n * the discovered-wallet list changes. Requires a `ClientProvider` whose client has a wallet plugin\n * installed; asserts this at mount with {@link useClientCapability}.\n *\n * @returns All discovered wallets matching the client's configured chain and filter (empty on the\n * server and in React Native).\n *\n * @example\n * ```tsx\n * const wallets = useWallets();\n * const connect = useConnect();\n * return wallets.map(w => <button key={w.name} onClick={() => connect.dispatch(w)}>{w.name}</button>);\n * ```\n *\n * @see {@link useWalletStatus}\n * @see {@link useConnectedWallet}\n */\nexport function useWallets(): readonly UiWallet[] {\n const wallet = useWalletNamespace('useWallets');\n const getWallets = () => wallet.getState().wallets;\n return useSyncExternalStore(wallet.subscribe, getWallets, getWallets);\n}\n\n/**\n * Reports whether the wallet client has finished its initial auto-reconnect \"warm-up\".\n *\n * Returns `false` while the status is `'pending'` or `'reconnecting'` — the transient states a\n * freshly built client passes through while it silently re-establishes a persisted connection — and\n * `true` once it settles into any stable state (`'connected'`, `'disconnected'`, or a user-initiated\n * `'connecting'` / `'disconnecting'`). This is the declarative, render-time counterpart to the\n * store's imperative `whenReady()`, gating on the exact same set of statuses.\n *\n * Use it to hold back wallet-dependent UI until the connection has settled, so a persisted wallet\n * never flashes \"disconnected\" before it reconnects. Re-renders only when this readiness boolean\n * flips, not on every status change. Requires a `ClientProvider` whose client has a wallet plugin\n * installed; asserts this at mount with {@link useClientCapability}.\n *\n * @returns `true` once the warm-up has settled, otherwise `false`.\n *\n * @example\n * ```tsx\n * const isReady = useIsWalletReady();\n * return <button disabled={!isReady}>Connect</button>;\n * ```\n *\n * @see {@link WalletReadyGate}\n * @see {@link useWalletStatus}\n */\nexport function useIsWalletReady(): boolean {\n const wallet = useWalletNamespace('useIsWalletReady');\n const getIsReady = () => !isWalletWarmingUp(wallet.getState().status);\n return useSyncExternalStore(wallet.subscribe, getIsReady, getIsReady);\n}\n\n// -- Action hooks -----------------------------------------------------------\n\n/**\n * Connects to a wallet from a React component.\n *\n * Wraps `client.wallet.connect` with {@link useAction}. Requires a `ClientProvider` whose client\n * has a wallet plugin installed; asserts this at mount with {@link useClientCapability}. An\n * in-flight connect is superseded when a newer one is dispatched.\n *\n * `dispatch`/`dispatchAsync` take the {@link UiWallet} to connect to and resolve with the wallet's\n * accounts once connected.\n *\n * @example\n * ```tsx\n * const { dispatch, isRunning } = useConnect();\n * <button disabled={isRunning} onClick={() => dispatch(wallet)}>Connect</button>\n * ```\n *\n * @see {@link useDisconnect}\n * @see {@link useSignIn}\n */\nexport function useConnect(): ActionResult<[wallet: UiWallet], readonly UiWalletAccount[]> {\n const wallet = useWalletNamespace('useConnect');\n return useAction((abortSignal, uiWallet: UiWallet) => wallet.connect(uiWallet, { abortSignal }));\n}\n\n/**\n * Disconnects a wallet from a React component.\n *\n * Wraps `client.wallet.disconnect` with {@link useAction}. Requires a `ClientProvider` whose client\n * has a wallet plugin installed; asserts this at mount with {@link useClientCapability}.\n *\n * `dispatch`/`dispatchAsync` take an optional {@link UiWallet}. Called with no argument they\n * disconnect the active wallet; passing a non-active, currently authorized wallet deauthorizes that\n * wallet while leaving the active connection in place.\n *\n * @example\n * ```tsx\n * const { dispatch, isRunning } = useDisconnect();\n * // Disconnect the active wallet:\n * <button disabled={isRunning} onClick={() => dispatch()}>Disconnect</button>\n * // Or deauthorize a specific wallet:\n * <button disabled={isRunning} onClick={() => dispatch(wallet)}>Forget this wallet</button>\n * ```\n *\n * @see {@link useConnect}\n */\nexport function useDisconnect(): ActionResult<[wallet?: UiWallet], void> {\n const wallet = useWalletNamespace('useDisconnect');\n return useAction((abortSignal, uiWallet?: UiWallet) => wallet.disconnect(uiWallet, { abortSignal }));\n}\n\n/**\n * Signs in with a wallet (Sign In With Solana) from a React component.\n *\n * Wraps `client.wallet.signIn` with {@link useAction}. Requires a `ClientProvider` whose client has\n * a wallet plugin installed; asserts this at mount with {@link useClientCapability}. Like\n * {@link useConnect}, this establishes a full connection. An in-flight connect is superseded when a\n * newer one is dispatched.\n *\n * `dispatch`/`dispatchAsync` take the {@link UiWallet} and a `SolanaSignInInput` (pass `{}` for no\n * customization) and resolve with the wallet's sign-in output.\n *\n * @example\n * ```tsx\n * const { dispatch } = useSignIn();\n * dispatch(wallet, { domain: window.location.host });\n * ```\n *\n * @see {@link useConnect}\n */\nexport function useSignIn(): ActionResult<[wallet: UiWallet, input: SolanaSignInInput], SolanaSignInOutput> {\n const wallet = useWalletNamespace('useSignIn');\n return useAction((abortSignal, uiWallet: UiWallet, input: SolanaSignInInput) =>\n wallet.signIn(uiWallet, input, { abortSignal }),\n );\n}\n\n/**\n * Signs an arbitrary message with the connected account from a React component.\n *\n * Wraps `client.wallet.signMessage` with {@link useAction}. Requires a `ClientProvider` whose\n * client has a wallet plugin installed; asserts this at mount with {@link useClientCapability}.\n * A stale in-flight dispatch is dropped from the hook's state when a newer one starts (though\n * the wallet may still complete the older request in the background).\n *\n * `dispatch`/`dispatchAsync` take the message bytes and resolve with the signature. The bytes are\n * only read, never mutated, so a {@link ReadonlyUint8Array} (e.g. the output of a `@solana/kit`\n * codec) is accepted directly without a cast.\n *\n * @example\n * ```tsx\n * const { dispatch } = useSignMessage();\n * dispatch(new TextEncoder().encode('Hello'));\n * ```\n */\nexport function useSignMessage(): ActionResult<[message: ReadonlyUint8Array], SignatureBytes> {\n const wallet = useWalletNamespace('useSignMessage');\n return useAction((abortSignal, message: ReadonlyUint8Array) => wallet.signMessage(message, { abortSignal }));\n}\n\n/**\n * Returns a callback that selects a wallet account.\n *\n * Requires a `ClientProvider` whose client has a wallet plugin installed; asserts this at mount\n * with {@link useClientCapability}. Unlike the other action hooks, `selectAccount` is synchronous —\n * this hook returns the bound function directly rather than an `ActionResult`.\n *\n * The account may belong to any currently authorized wallet, not only the active one. Selecting an\n * account from a different authorized wallet switches the active connection to that wallet\n * synchronously (no prompt), leaving the previously active wallet authorized.\n *\n * @returns The `selectAccount` function — call it with a {@link UiWalletAccount} belonging to any\n * currently authorized wallet. It throws if the account's handle can't be resolved to an\n * authorized wallet (or isn't among that wallet's accounts), or that wallet is currently\n * disconnecting.\n *\n * @example\n * ```tsx\n * const selectAccount = useSelectAccount();\n * <button onClick={() => selectAccount(account)}>Use this account</button>\n * ```\n *\n * @see {@link useConnectedWallet}\n */\nexport function useSelectAccount(): WalletNamespace['selectAccount'] {\n return useWalletNamespace('useSelectAccount').selectAccount;\n}\n\n// -- Components -------------------------------------------------------------\n\n/**\n * Props for {@link WalletReadyGate}.\n */\nexport type WalletReadyGateProps = Readonly<{\n /** Rendered once the wallet client has finished its initial auto-reconnect warm-up. */\n children: ReactNode;\n /** Rendered while the client is still warming up (`'pending'` / `'reconnecting'`). */\n fallback: ReactNode;\n}>;\n\n/**\n * Renders `fallback` while the wallet client is still in its initial auto-reconnect warm-up\n * (`'pending'` / `'reconnecting'`), then reveals `children` once the connection has settled.\n *\n * A declarative wrapper over {@link useIsWalletReady} for the common case of holding back a whole\n * wallet-dependent subtree. Its props are shaped like React's `<Suspense fallback>`, but it is\n * synchronous — it renders `fallback` directly off the reactive status rather than suspending, so it\n * needs no `<Suspense>` ancestor. It lets an app paint its chrome immediately while gating only the\n * wallet-dependent UI, so a persisted wallet never flashes \"disconnected\" before it silently\n * reconnects. For finer control (disabling a button, showing an inline spinner) read the boolean from\n * {@link useIsWalletReady} directly. To actually suspend a subtree instead, throw the Suspense-safe\n * {@link WalletNamespace.whenReady} promise from your own component.\n *\n * Requires a `ClientProvider` whose client has a wallet plugin installed; asserts this at mount with\n * {@link useClientCapability}.\n *\n * @example\n * ```tsx\n * <WalletReadyGate fallback={<Spinner />}>\n * <WalletDependentApp />\n * </WalletReadyGate>\n * ```\n *\n * @see {@link useIsWalletReady}\n * @see {@link useWalletStatus}\n */\nexport function WalletReadyGate({ children, fallback }: WalletReadyGateProps): ReactNode {\n return useIsWalletReady() ? children : fallback;\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solana/kit-plugin-wallet",
3
- "version": "0.13.0",
3
+ "version": "0.13.1",
4
4
  "description": "Wallet connection plugin for Kit clients",
5
5
  "keywords": [
6
6
  "kit",
@@ -100,7 +100,8 @@
100
100
  "scripts": {
101
101
  "build": "rimraf dist && tsup && tsc -p ./tsconfig.declarations.json",
102
102
  "dev": "vitest --project node",
103
- "test": "pnpm test:types && pnpm test:treeshakability && pnpm test:unit",
103
+ "test": "pnpm test:types && pnpm test:treeshakability && pnpm test:packaging && pnpm test:unit",
104
+ "test:packaging": "node ../../scripts/assert-publishable-dist.mjs",
104
105
  "test:treeshakability": "for file in dist/index.*.mjs dist/react/index.*.mjs; do agadoo $file; done",
105
106
  "test:types": "tsc --noEmit",
106
107
  "test:unit": "vitest run"