@solana/kit-plugin-wallet 0.0.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +22 -0
- package/README.md +303 -0
- package/dist/index.browser.cjs +538 -0
- package/dist/index.browser.cjs.map +1 -0
- package/dist/index.browser.mjs +533 -0
- package/dist/index.browser.mjs.map +1 -0
- package/dist/index.node.cjs +93 -0
- package/dist/index.node.cjs.map +1 -0
- package/dist/index.node.mjs +88 -0
- package/dist/index.node.mjs.map +1 -0
- package/dist/index.react-native.mjs +88 -0
- package/dist/index.react-native.mjs.map +1 -0
- package/dist/types/index.d.ts +3 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/store.d.ts +16 -0
- package/dist/types/store.d.ts.map +1 -0
- package/dist/types/types.d.ts +336 -0
- package/dist/types/types.d.ts.map +1 -0
- package/dist/types/wallet.d.ts +133 -0
- package/dist/types/wallet.d.ts.map +1 -0
- package/package.json +73 -7
- package/src/__typetests__/wallet-typetest.ts +114 -0
- package/src/index.ts +2 -0
- package/src/store.ts +863 -0
- package/src/types/global.d.ts +6 -0
- package/src/types.ts +358 -0
- package/src/wallet.ts +216 -0
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { withCleanup, extendClient, SolanaError, SOLANA_ERROR__WALLET__NO_SIGNER_CONNECTED, SOLANA_ERROR__WALLET__SIGNER_NOT_AVAILABLE, SOLANA_ERROR__WALLET__NOT_CONNECTED } from '@solana/kit';
|
|
2
|
+
import '@solana/wallet-account-signer';
|
|
3
|
+
import '@solana/wallet-standard-features';
|
|
4
|
+
import '@wallet-standard/app';
|
|
5
|
+
import '@wallet-standard/features';
|
|
6
|
+
import '@wallet-standard/ui-features';
|
|
7
|
+
import '@wallet-standard/ui-registry';
|
|
8
|
+
|
|
9
|
+
// src/wallet.ts
|
|
10
|
+
function createWalletStore(config) {
|
|
11
|
+
{
|
|
12
|
+
const ssrSnapshot = Object.freeze({
|
|
13
|
+
connected: null,
|
|
14
|
+
status: "pending",
|
|
15
|
+
wallets: Object.freeze([])
|
|
16
|
+
});
|
|
17
|
+
return {
|
|
18
|
+
connect: () => Promise.reject(new SolanaError(SOLANA_ERROR__WALLET__NOT_CONNECTED, { operation: "connect" })),
|
|
19
|
+
disconnect: () => Promise.resolve(),
|
|
20
|
+
getState: () => ssrSnapshot,
|
|
21
|
+
selectAccount: () => {
|
|
22
|
+
throw new SolanaError(SOLANA_ERROR__WALLET__NOT_CONNECTED, { operation: "selectAccount" });
|
|
23
|
+
},
|
|
24
|
+
signIn: () => Promise.reject(new SolanaError(SOLANA_ERROR__WALLET__NOT_CONNECTED, { operation: "signIn" })),
|
|
25
|
+
signMessage: () => Promise.reject(new SolanaError(SOLANA_ERROR__WALLET__NOT_CONNECTED, { operation: "signMessage" })),
|
|
26
|
+
subscribe: () => () => {
|
|
27
|
+
},
|
|
28
|
+
subscribeSigner: () => () => {
|
|
29
|
+
},
|
|
30
|
+
[Symbol.dispose]: () => {
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// src/wallet.ts
|
|
37
|
+
function defineSignerGetter(additions, property, store) {
|
|
38
|
+
Object.defineProperty(additions, property, {
|
|
39
|
+
configurable: true,
|
|
40
|
+
enumerable: true,
|
|
41
|
+
get() {
|
|
42
|
+
const state = store.getState();
|
|
43
|
+
if (!state.connected) {
|
|
44
|
+
throw new SolanaError(SOLANA_ERROR__WALLET__NO_SIGNER_CONNECTED, { status: state.status });
|
|
45
|
+
}
|
|
46
|
+
if (!state.connected.signer) {
|
|
47
|
+
throw new SolanaError(SOLANA_ERROR__WALLET__SIGNER_NOT_AVAILABLE);
|
|
48
|
+
}
|
|
49
|
+
return state.connected.signer;
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
var SUBSCRIBE_PROPERTY = {
|
|
54
|
+
identity: "subscribeToIdentity",
|
|
55
|
+
payer: "subscribeToPayer"
|
|
56
|
+
};
|
|
57
|
+
function createPlugin(config, signerProperties) {
|
|
58
|
+
return (client) => {
|
|
59
|
+
if ("wallet" in client) {
|
|
60
|
+
throw new Error(
|
|
61
|
+
"Only one wallet plugin can be used per client. Use walletSigner, walletPayer, walletIdentity, or walletWithoutSigner \u2014 not multiple."
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
const store = createWalletStore();
|
|
65
|
+
const additions = { wallet: store };
|
|
66
|
+
for (const prop of signerProperties) {
|
|
67
|
+
defineSignerGetter(additions, prop, store);
|
|
68
|
+
additions[SUBSCRIBE_PROPERTY[prop]] = store.subscribeSigner;
|
|
69
|
+
}
|
|
70
|
+
return withCleanup(extendClient(client, additions), () => store[Symbol.dispose]());
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
function walletSigner(config) {
|
|
74
|
+
return createPlugin(config, ["payer", "identity"]);
|
|
75
|
+
}
|
|
76
|
+
function walletIdentity(config) {
|
|
77
|
+
return createPlugin(config, ["identity"]);
|
|
78
|
+
}
|
|
79
|
+
function walletPayer(config) {
|
|
80
|
+
return createPlugin(config, ["payer"]);
|
|
81
|
+
}
|
|
82
|
+
function walletWithoutSigner(config) {
|
|
83
|
+
return createPlugin(config, []);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export { walletIdentity, walletPayer, walletSigner, walletWithoutSigner };
|
|
87
|
+
//# sourceMappingURL=index.react-native.mjs.map
|
|
88
|
+
//# sourceMappingURL=index.react-native.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/store.ts","../src/wallet.ts"],"names":["SolanaError"],"mappings":"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","file":"index.react-native.mjs","sourcesContent":["import {\n type SignatureBytes,\n SOLANA_ERROR__WALLET__ACCOUNT_NOT_AVAILABLE,\n SOLANA_ERROR__WALLET__NOT_CONNECTED,\n SolanaError,\n} from '@solana/kit';\nimport { createSignerFromWalletAccount } from '@solana/wallet-account-signer';\nimport {\n SolanaSignIn,\n type SolanaSignInFeature,\n type SolanaSignInInput,\n type SolanaSignInOutput,\n SolanaSignMessage,\n type SolanaSignMessageFeature,\n} from '@solana/wallet-standard-features';\nimport { getWallets } from '@wallet-standard/app';\nimport {\n StandardConnect,\n type StandardConnectFeature,\n StandardDisconnect,\n type StandardDisconnectFeature,\n StandardEvents,\n type StandardEventsFeature,\n} from '@wallet-standard/features';\nimport type { UiWallet, UiWalletAccount } from '@wallet-standard/ui';\nimport { getWalletAccountFeature, getWalletFeature } from '@wallet-standard/ui-features';\nimport { getOrCreateUiWalletForStandardWallet, getWalletForHandle } from '@wallet-standard/ui-registry';\n\nimport type {\n WalletActionOptions,\n WalletNamespace,\n WalletPluginConfig,\n WalletSigner,\n WalletState,\n WalletStatus,\n WalletStorage,\n} from './types';\n\n// -- Internal types ---------------------------------------------------------\n\n/** @internal */\nexport type WalletStore = WalletNamespace & {\n /**\n * Registers a listener notified only when the wallet signer may\n * have changed.\n * Returns an idempotent unsubscribe function.\n *\n * @internal\n */\n subscribeSigner: (listener: () => void) => () => void;\n [Symbol.dispose]: () => void;\n};\n\n// Flat internal state for easier updates\ntype WalletStoreState = {\n account: UiWalletAccount | null;\n connectedWallet: UiWallet | null;\n signer: WalletSigner | null;\n status: WalletStatus;\n wallets: readonly UiWallet[];\n};\n\n// -- Store ------------------------------------------------------------------\n\n/** @internal */\nexport function createWalletStore(config: WalletPluginConfig): WalletStore {\n // -- SSR guard: on the server, return an inert stub ---------------------\n\n if (!__BROWSER__ || __REACTNATIVE__) {\n const ssrSnapshot: WalletState = Object.freeze({\n connected: null,\n status: 'pending' as const,\n wallets: Object.freeze([]) as readonly UiWallet[],\n });\n return {\n connect: () =>\n Promise.reject(new SolanaError(SOLANA_ERROR__WALLET__NOT_CONNECTED, { operation: 'connect' })),\n disconnect: () => Promise.resolve(),\n getState: () => ssrSnapshot,\n selectAccount: () => {\n throw new SolanaError(SOLANA_ERROR__WALLET__NOT_CONNECTED, { operation: 'selectAccount' });\n },\n signIn: () => Promise.reject(new SolanaError(SOLANA_ERROR__WALLET__NOT_CONNECTED, { operation: 'signIn' })),\n signMessage: () =>\n Promise.reject(new SolanaError(SOLANA_ERROR__WALLET__NOT_CONNECTED, { operation: 'signMessage' })),\n subscribe: () => () => {},\n subscribeSigner: () => () => {},\n [Symbol.dispose]: () => {},\n };\n }\n\n // -- Browser-only initialization below this point ----------------------\n\n let state: WalletStoreState = {\n account: null,\n connectedWallet: null,\n signer: null,\n status: 'pending',\n wallets: [],\n };\n\n const listeners = new Set<() => void>();\n const signerListeners = new Set<() => void>();\n let walletEventsCleanup: (() => void) | null = null;\n let reconnectCleanup: (() => void) | null = null;\n let userHasSelected = false;\n let connectGeneration = 0;\n let disposed = false;\n // Wallet that is in the process of disconnecting, stored to guard against re-connecting to it if a future connect fails\n let disconnectingWalletName: string | null = null;\n\n // Resolve storage: default to localStorage in browser, null to disable.\n // Merely *accessing* `localStorage` throws a `SecurityError` in sandboxed\n // iframes or when third-party storage is blocked, so the default resolution\n // degrades to `null` rather than letting the whole plugin-install chain\n // throw. This matches the best-effort persistence applied to reads/writes.\n function resolveDefaultStorage(): WalletStorage | null {\n try {\n return localStorage;\n } catch {\n return null;\n }\n }\n\n const storage: WalletStorage | null = config.storage === null ? null : (config.storage ?? resolveDefaultStorage());\n const storageKey = config.storageKey ?? 'kit-wallet';\n\n // -- State management --------------------------------------------------\n\n function deriveSnapshot(s: WalletStoreState): WalletState {\n return Object.freeze({\n connected:\n s.connectedWallet && s.account\n ? Object.freeze({\n account: s.account,\n signer: s.signer,\n wallet: s.connectedWallet,\n })\n : null,\n status: s.status,\n wallets: s.wallets,\n });\n }\n\n let snapshot: WalletState = deriveSnapshot(state);\n\n // We set `client.{payer,identity}` to `getState().connected?.signer\n // This is `undefined` if disconnected, `null` if connected to a read-only wallet,\n // or the signer reference otherwise.\n function observableSigner(s: WalletStoreState): WalletSigner | null | undefined {\n return s.connectedWallet && s.account ? s.signer : undefined;\n }\n\n function updateState(updates: Partial<WalletStoreState>): void {\n const prev = state;\n state = { ...state, ...updates };\n\n // Signer subscribers (`subscribeToPayer` / `subscribeToIdentity`) fire\n // only when the observed signer changes.\n const signerChanged = observableSigner(state) !== observableSigner(prev);\n\n if (\n state.connectedWallet !== prev.connectedWallet ||\n state.account !== prev.account ||\n state.status !== prev.status ||\n state.signer !== prev.signer ||\n state.wallets !== prev.wallets\n ) {\n snapshot = deriveSnapshot(state);\n listeners.forEach(l => l());\n }\n\n if (signerChanged) {\n signerListeners.forEach(l => l());\n }\n }\n\n // -- Signer creation ---------------------------------------------------\n\n function tryCreateSigner(account: UiWalletAccount): WalletSigner | null {\n try {\n // `createSignerFromWalletAccount` throws for chains the wallet\n // doesn't support — which we catch below. Non-Solana chains\n // therefore degrade to `signer: null`, matching the read-only\n // wallet contract.\n return createSignerFromWalletAccount(account, config.chain);\n } catch {\n // Wallet doesn't support signing (read-only / watch wallet, or a\n // non-Solana chain).\n return null;\n }\n }\n\n // -- Wallet discovery --------------------------------------------------\n\n // `getWallets()` is a wallet-standard function that returns the `Wallets` API\n // This includes a `get()` function to get the current list of wallets, and\n // an `on()` function to subscribe to wallet registration/unregistration events.\n const registry = getWallets();\n\n function filterWallet(uiWallet: UiWallet): boolean {\n const supportsChain = uiWallet.chains.includes(config.chain);\n const supportsConnect = uiWallet.features.includes(StandardConnect);\n if (!supportsChain || !supportsConnect) return false;\n return config.filter ? config.filter(uiWallet) : true;\n }\n\n function buildWalletList(): readonly UiWallet[] {\n return Object.freeze(registry.get().map(getOrCreateUiWalletForStandardWallet).filter(filterWallet));\n }\n\n // Rebuilds the filtered wallet list but returns the *existing* `state.wallets`\n // reference when the contents are unchanged. `buildWalletList` always\n // allocates a fresh frozen array, so without this a registry event for a\n // wallet that gets filtered out (or any event that doesn't alter the visible\n // set) would still produce a new snapshot and notify every listener — a\n // spurious re-render. Wallets are compared by reference, in order: the\n // wallet-ui registry hands back a stable UiWallet for a given underlying\n // wallet until its accounts/features/chains change, so reference equality is\n // a sound same-contents check here.\n function reconcileWalletList(): readonly UiWallet[] {\n const next = buildWalletList();\n const prev = state.wallets;\n if (next.length === prev.length && next.every((w, i) => w === prev[i])) {\n return prev;\n }\n return next;\n }\n\n // True if a wallet matching `uiWallet` (by name) is currently registered and\n // passes the filter. Used to re-check membership after an awaited connect /\n // sign-in / silent reconnect resolves: the wallet may have unregistered (or\n // dropped a required feature/chain) while its prompt was open, and the\n // unregister handler can't tear down a connection that hasn't been\n // established yet. Compared by name — the same identity the unregister\n // handler uses — because the wallet-ui registry hands back a fresh UiWallet\n // reference once connect adds accounts, so reference equality would not hold.\n function isWalletStillAvailable(uiWallet: UiWallet): boolean {\n return buildWalletList().some(w => w.name === uiWallet.name);\n }\n\n updateState({ wallets: buildWalletList() });\n\n // Listen for new wallets being registered\n const unsubRegister = registry.on('register', () => {\n updateState({ wallets: reconcileWalletList() });\n });\n\n // Listen for wallets being unregistered — if the connected wallet is removed, disconnect\n const unsubUnregister = registry.on('unregister', () => {\n const newWallets = reconcileWalletList();\n const updates: Partial<WalletStoreState> = { wallets: newWallets };\n\n // If the connected wallet was unregistered, disconnect locally.\n if (state.connectedWallet && !newWallets.some(w => w.name === state.connectedWallet!.name)) {\n walletEventsCleanup?.();\n walletEventsCleanup = null;\n updates.connectedWallet = null;\n updates.account = null;\n updates.signer = null;\n updates.status = 'disconnected';\n clearPersistedAccount();\n }\n\n updateState(updates);\n });\n\n // -- Wallet-initiated events -------------------------------------------\n\n function cancelReconnect(): void {\n reconnectCleanup?.();\n reconnectCleanup = null;\n }\n\n function resubscribeToWalletEvents(uiWallet: UiWallet): void {\n walletEventsCleanup?.();\n walletEventsCleanup = subscribeToWalletEvents(uiWallet);\n }\n\n function setConnected(account: UiWalletAccount, wallet: UiWallet, options?: { persist?: boolean }): void {\n // The store may have been disposed while a connect / sign-in / silent\n // reconnect awaited. The `connectGeneration` guard at each call site\n // doesn't cover the auto-connect IIFE resuming from its storage read —\n // it captures a fresh generation in `attemptSilentReconnect` after\n // disposal. Bail here so a disposed store never re-subscribes to wallet\n // events (a listener the already-run disposer can't clean up) or reports\n // itself connected.\n if (disposed) return;\n const signer = tryCreateSigner(account);\n resubscribeToWalletEvents(wallet);\n disconnectingWalletName = null;\n updateState({ account, connectedWallet: wallet, signer, status: 'connected' });\n if (options?.persist !== false) {\n persistAccount(account, wallet);\n }\n }\n\n function refreshUiWallet(staleUiWallet: UiWallet): UiWallet {\n const rawWallet = getWalletForHandle(staleUiWallet);\n return getOrCreateUiWalletForStandardWallet(rawWallet);\n }\n\n function subscribeToWalletEvents(uiWallet: UiWallet): () => void {\n if (!uiWallet.features.includes(StandardEvents)) {\n return () => {};\n }\n\n const eventsFeature = getWalletFeature(\n uiWallet,\n StandardEvents,\n ) as StandardEventsFeature[typeof StandardEvents];\n\n // The handler ignores the changed-property flags (`accounts`, `chains`,\n // `features`) and instead reconciles the active account against the\n // refreshed wallet. The signer is derived entirely from the account's\n // features and chains, and the wallet-ui registry regenerates the\n // account reference precisely when one of those changes — so the\n // account reference is the single source of truth for whether the\n // signer is affected, regardless of which flag the wallet sets.\n return eventsFeature.on('change', () => {\n const refreshed = refreshUiWallet(uiWallet);\n\n // If the wallet no longer passes the filter (e.g. it dropped the\n // configured chain or a required feature), disconnect.\n if (!filterWallet(refreshed)) {\n disconnectLocally();\n return;\n }\n\n const result = reconcileActiveAccount(refreshed);\n if (result === null) {\n disconnectLocally();\n return;\n }\n\n // `connectedWallet` is always refreshed so consumers reading\n // `getState().connected.wallet` see current metadata; the signer\n // (in `result`) only churns when the active account changed, which\n // keeps the snapshot referentially stable on no-op change events.\n // `wallets` is reconciled too: the registry regenerates the wallet\n // handle on a change, so without this the list entry would keep the\n // stale handle (`wallets[i] !== connected.wallet`). `reconcileWalletList`\n // returns the existing reference on a no-op, preserving that stability.\n updateState({ connectedWallet: refreshed, wallets: reconcileWalletList(), ...result });\n\n if (result.account) {\n persistAccount(result.account, refreshed);\n }\n });\n }\n\n function reconcileActiveAccount(wallet: UiWallet): Partial<WalletStoreState> | null {\n const newAccounts = wallet.accounts;\n\n // No accounts left — the caller disconnects.\n if (newAccounts.length === 0) return null;\n\n const currentAddress = state.account?.address;\n const stillPresent = currentAddress ? newAccounts.find(a => a.address === currentAddress) : null;\n const activeAccount = stillPresent ?? newAccounts[0];\n\n // Same reference — nothing the signer depends on changed, so leave the\n // signer untouched to preserve referential stability.\n if (activeAccount === state.account) return {};\n\n // The active account changed (switched, removed, or regenerated because\n // its features/chains changed) — recreate the signer for it.\n return { account: activeAccount, signer: tryCreateSigner(activeAccount) };\n }\n\n // -- Connection lifecycle ----------------------------------------------\n\n async function connect(uiWallet: UiWallet, options?: WalletActionOptions): Promise<readonly UiWalletAccount[]> {\n options?.abortSignal?.throwIfAborted();\n\n // Resolve the feature before mutating any state. getWalletFeature throws\n // WalletStandardError synchronously when the wallet doesn't support\n // standard:connect — doing this first means an unsupported wallet is a\n // no-op that never tears down an existing connection or cancels a\n // pending reconnect.\n const connectFeature = getWalletFeature(\n uiWallet,\n StandardConnect,\n ) as StandardConnectFeature[typeof StandardConnect];\n\n userHasSelected = true;\n cancelReconnect();\n const generation = ++connectGeneration;\n updateState({ status: 'connecting' });\n\n try {\n // Snapshot existing accounts before connect.\n const existingAccounts = [...uiWallet.accounts];\n\n await connectFeature.connect();\n\n // A newer connect/signIn was started while we were awaiting. Reject\n // with an `AbortError` so this superseded call settles in the\n // standard, ignorable way (matching the abort-signal contract) while\n // the newer request owns the connection. The `catch` below leaves\n // that newer connection untouched, since `generation` no longer\n // matches `connectGeneration`.\n if (generation !== connectGeneration) {\n throw new DOMException('Wallet connect was superseded by a newer connect or sign-in', 'AbortError');\n }\n\n // The wallet may have unregistered (or dropped a required\n // feature/chain) while its connect prompt was open. The unregister\n // handler only disconnects an already-connected wallet, so without\n // this re-check the store would be left connected to a wallet absent\n // from `wallets`. Throw so the connection failure surfaces as a\n // rejection; the `catch` below reverts to any prior connection.\n if (!isWalletStillAvailable(uiWallet)) {\n throw new SolanaError(SOLANA_ERROR__WALLET__NOT_CONNECTED, { operation: 'connect' });\n }\n\n // Refresh UiWallet to get updated accounts after connect.\n const refreshedWallet = refreshUiWallet(uiWallet);\n const allAccounts = refreshedWallet.accounts;\n\n // No authorized accounts means there's nothing to connect to. Throw\n // rather than resolving so a caller can't mistake an empty result for\n // a successful connection; the `catch` reverts to any prior wallet.\n if (allAccounts.length === 0) {\n throw new SolanaError(SOLANA_ERROR__WALLET__NOT_CONNECTED, { operation: 'connect' });\n }\n\n // Prefer the first newly authorized account.\n const newAccount = allAccounts.find(a => !existingAccounts.some(e => e.address === a.address));\n const activeAccount = newAccount ?? allAccounts[0];\n\n setConnected(activeAccount, refreshedWallet);\n return allAccounts;\n } catch (error) {\n // Only act if we're still the active connection attempt; a\n // superseded attempt must leave the newer connection untouched.\n if (generation === connectGeneration) {\n revertToPreviousConnectionOrDisconnect();\n }\n throw error;\n }\n }\n\n async function disconnect(options?: WalletActionOptions): Promise<void> {\n options?.abortSignal?.throwIfAborted();\n\n // No connected wallet, but an auto-reconnect may still be in flight:\n // during 'reconnecting' a silent reconnect or the late-registration\n // listener can complete and connect, overriding this explicit\n // disconnect. Record the user's intent so auto-connect won't re-arm,\n // tear down the late-registration listener, and bump the generation so\n // any in-flight connect/signIn/silent-reconnect bails at its guard\n // instead of resuming.\n if (!state.connectedWallet) {\n userHasSelected = true;\n cancelReconnect();\n connectGeneration++;\n updateState({ status: 'disconnected' });\n clearPersistedAccount();\n return;\n }\n\n const currentWallet = state.connectedWallet;\n // Bump the generation so this disconnect supersedes any connect/signIn\n // that was already in flight (its prompt opened before the user chose to\n // disconnect): that attempt captured an earlier generation, so it will\n // bail at its guard rather than establishing a connection the user has\n // since dismissed. Mirrors the not-connected branch above — newest action\n // wins. A connect/signIn started *after* this disconnect bumps the\n // generation again, so the `finally` guard skips `disconnectLocally` and\n // leaves the newer connection intact.\n const generation = ++connectGeneration;\n // Mark this wallet as disconnecting\n disconnectingWalletName = currentWallet.name;\n updateState({ status: 'disconnecting' });\n\n try {\n if (currentWallet.features.includes(StandardDisconnect)) {\n const disconnectFeature = getWalletFeature(\n currentWallet,\n StandardDisconnect,\n ) as StandardDisconnectFeature[typeof StandardDisconnect];\n await disconnectFeature.disconnect();\n }\n } finally {\n // Only clean up if no new connect/signIn started while we were awaiting.\n if (generation === connectGeneration) {\n disconnectLocally();\n }\n }\n }\n\n function disconnectLocally(): void {\n walletEventsCleanup?.();\n walletEventsCleanup = null;\n disconnectingWalletName = null;\n\n updateState({\n account: null,\n connectedWallet: null,\n signer: null,\n status: 'disconnected',\n // Reconcile the list too: when this runs from the change handler\n // because the connected wallet dropped the configured chain or\n // failed the filter, that wallet must leave `wallets` immediately\n // rather than linger until an unrelated register/unregister event.\n // `reconcileWalletList` returns the existing reference when the\n // visible set is unchanged (the common case for a plain disconnect),\n // so this stays churn-free.\n wallets: reconcileWalletList(),\n });\n\n clearPersistedAccount();\n }\n\n // A `connect`/`signIn` attempt failed to establish a new connection. While\n // such an attempt is in flight only `status` is moved to 'connecting' — the\n // existing connection's account, signer, event subscription, and persisted\n // key are untouched until `setConnected` runs on success. So if a prior\n // connection is still intact, revert to it (declining the new wallet must not\n // log the user out of the one they had). Otherwise there's nothing to keep,\n // so disconnect cleanly. The active-attempt (`generation`) guard at each call\n // site ensures a superseded attempt never reverts the connection a newer one\n // owns.\n function revertToPreviousConnectionOrDisconnect(): void {\n // Don't revive a wallet with disconnect in progress\n if (state.connectedWallet && state.account && state.connectedWallet.name !== disconnectingWalletName) {\n updateState({ status: 'connected' });\n } else {\n disconnectLocally();\n }\n }\n\n function selectAccount(account: UiWalletAccount): void {\n if (!state.connectedWallet) {\n throw new SolanaError(SOLANA_ERROR__WALLET__NOT_CONNECTED, { operation: 'selectAccount' });\n }\n // Reject if this wallet is disconnecting\n if (state.connectedWallet.name === disconnectingWalletName) {\n throw new SolanaError(SOLANA_ERROR__WALLET__NOT_CONNECTED, { operation: 'selectAccount' });\n }\n const refreshed = refreshUiWallet(state.connectedWallet);\n const selectedAccount = refreshed.accounts.find(a => a.address === account.address);\n if (!selectedAccount) {\n throw new SolanaError(SOLANA_ERROR__WALLET__ACCOUNT_NOT_AVAILABLE, {\n account: account.address,\n operation: 'selectAccount',\n });\n }\n userHasSelected = true;\n // Bump the generation so this selection supersedes any in-flight\n // connect/signIn\n ++connectGeneration;\n // Change status back to `connected` if something in-flight changed it\n // But skip recreating the signer if account is the same as before\n if (selectedAccount === state.account && refreshed === state.connectedWallet) {\n updateState({ status: 'connected' });\n return;\n }\n const signer = tryCreateSigner(selectedAccount);\n updateState({ account: selectedAccount, connectedWallet: refreshed, signer, status: 'connected' });\n persistAccount(selectedAccount, refreshed);\n }\n\n // -- Message signing ---------------------------------------------------\n\n async function signMessage(message: Uint8Array, options?: WalletActionOptions): Promise<SignatureBytes> {\n options?.abortSignal?.throwIfAborted();\n const { connectedWallet, account } = state;\n if (!connectedWallet || !account) {\n throw new SolanaError(SOLANA_ERROR__WALLET__NOT_CONNECTED, { operation: 'signMessage' });\n }\n // Use the wallet feature directly rather than going through the cached\n // signer. This decouples message signing from transaction signing —\n // a wallet that supports solana:signMessage but not transaction signing\n // still works. solana:signMessage is an account-scoped feature, so this\n // resolves it against the active account: getWalletAccountFeature throws\n // a WalletStandardError when either the wallet or the account does not\n // support it.\n const signMessageFeature = getWalletAccountFeature(\n account,\n SolanaSignMessage,\n ) as SolanaSignMessageFeature[typeof SolanaSignMessage];\n const [output] = await signMessageFeature.signMessage({ account, message });\n return output.signature as SignatureBytes;\n }\n\n // -- Sign In With Solana (SIWS-as-connect) ------------------------------\n\n async function signIn(\n uiWallet: UiWallet,\n input: SolanaSignInInput,\n options?: WalletActionOptions,\n ): Promise<SolanaSignInOutput> {\n options?.abortSignal?.throwIfAborted();\n\n // Resolve the feature before mutating any state. getWalletFeature throws\n // WalletStandardError synchronously when the wallet doesn't support\n // solana:signIn — doing this first means an unsupported wallet is a\n // no-op that never tears down an existing connection.\n const signInFeature = getWalletFeature(uiWallet, SolanaSignIn) as SolanaSignInFeature[typeof SolanaSignIn];\n\n userHasSelected = true;\n cancelReconnect();\n const generation = ++connectGeneration;\n updateState({ status: 'connecting' });\n\n try {\n const [result] = await signInFeature.signIn(input);\n\n // A newer connect/signIn was started while we were awaiting. Reject\n // with an `AbortError` (matching the abort-signal contract) so the\n // superseded call settles in the standard, ignorable way while the\n // newer request owns the connection.\n if (generation !== connectGeneration) {\n throw new DOMException('Wallet sign-in was superseded by a newer connect or sign-in', 'AbortError');\n }\n\n // The wallet may have unregistered (or dropped a required\n // feature/chain) while its sign-in prompt was open. Throw rather than\n // resolving while the store is left connected to no wallet (or a\n // different one); the `catch` below reverts to any prior connection.\n if (!isWalletStillAvailable(uiWallet)) {\n throw new SolanaError(SOLANA_ERROR__WALLET__NOT_CONNECTED, { operation: 'signIn' });\n }\n\n // Set up full connection state using the account from the sign-in response.\n const refreshedWallet = refreshUiWallet(uiWallet);\n const activeAccount = refreshedWallet.accounts.find(a => a.address === result.account.address);\n\n if (!activeAccount) {\n // The wallet signed in with an account it doesn't expose — a\n // protocol violation that can't be mapped to a connection. Throw\n // so the failure surfaces as a rejection rather than resolving\n // with a sign-in result the store can't act on; the `catch`\n // reverts to any prior connection.\n throw new SolanaError(SOLANA_ERROR__WALLET__NOT_CONNECTED, { operation: 'signIn' });\n }\n\n setConnected(activeAccount, refreshedWallet);\n return result;\n } catch (error) {\n if (generation === connectGeneration) {\n revertToPreviousConnectionOrDisconnect();\n }\n throw error;\n }\n }\n\n // -- Persistence -------------------------------------------------------\n\n function ignoreStorageFailure(operation: () => Promise<void> | void): void {\n void (async () => {\n try {\n await operation();\n } catch {\n // Persistence is best-effort; wallet state is the source of truth.\n }\n })();\n }\n\n function persistAccount(account: UiWalletAccount, wallet: UiWallet): void {\n if (!storage) return;\n ignoreStorageFailure(() => storage.setItem(storageKey, `${wallet.name}:${account.address}`));\n }\n\n function clearPersistedAccount(): void {\n if (!storage) return;\n ignoreStorageFailure(() => storage.removeItem(storageKey));\n }\n\n // -- Auto-connect ------------------------------------------------------\n\n if (config.autoConnect !== false && storage) {\n const generation = connectGeneration;\n (async () => {\n const savedKey = await storage.getItem(storageKey);\n // Don't auto-connect if the user has selected a wallet, or if the\n // store was disposed while the storage read was in flight — the\n // disposer has already torn down its listeners and bumped the\n // generation, but this IIFE hasn't captured one yet, so without this\n // check it would resume and silently reconnect into a disposed store.\n if (userHasSelected || disposed) return;\n\n if (!savedKey) {\n updateState({ status: 'disconnected' });\n return;\n }\n\n const separatorIndex = savedKey.lastIndexOf(':');\n if (separatorIndex === -1) {\n // Malformed saved key.\n updateState({ status: 'disconnected' });\n clearPersistedAccount();\n return;\n }\n\n const walletName = savedKey.slice(0, separatorIndex);\n const savedAddress = savedKey.slice(separatorIndex + 1);\n const existing = state.wallets.find(w => w.name === walletName);\n\n if (existing) {\n await attemptSilentReconnect(savedAddress, existing);\n } else if (\n registry.get().some(w => {\n const ui = getOrCreateUiWalletForStandardWallet(w);\n return ui.name === walletName;\n })\n ) {\n // Wallet registered but doesn't pass the filter.\n updateState({ status: 'disconnected' });\n clearPersistedAccount();\n } else {\n // Wallet not registered yet — wait for it to appear.\n updateState({ status: 'reconnecting' });\n\n // Change state to disconnected after 3s\n // Note that the listener stays alive until reconnect is cancelled, so this is\n // just affecting how long the UI shows reconnecting\n const statusTimeout = setTimeout(() => {\n if (!userHasSelected && state.status === 'reconnecting') {\n updateState({ status: 'disconnected' });\n }\n }, 3000);\n\n const unsubRegisterForReconnect = registry.on('register', () => {\n const generation = connectGeneration;\n void (async () => {\n if (userHasSelected) {\n clearTimeout(statusTimeout);\n unsubRegisterForReconnect();\n reconnectCleanup = null;\n return;\n }\n const found = buildWalletList().find(w => w.name === walletName);\n if (found) {\n clearTimeout(statusTimeout);\n unsubRegisterForReconnect();\n reconnectCleanup = null;\n await attemptSilentReconnect(savedAddress, found);\n } else if (\n registry.get().some(w => {\n const ui = getOrCreateUiWalletForStandardWallet(w);\n return ui.name === walletName;\n })\n ) {\n // Wallet registered but filtered out.\n clearTimeout(statusTimeout);\n unsubRegisterForReconnect();\n reconnectCleanup = null;\n updateState({ status: 'disconnected' });\n clearPersistedAccount();\n }\n })().catch(() => {\n // Reconnect failed — fall back to disconnected.\n if (generation === connectGeneration && !userHasSelected) {\n updateState({ status: 'disconnected' });\n }\n });\n });\n\n reconnectCleanup = () => {\n clearTimeout(statusTimeout);\n unsubRegisterForReconnect();\n };\n }\n })().catch(() => {\n // Storage read failed — fall back to disconnected.\n if (generation === connectGeneration && !userHasSelected) {\n updateState({ status: 'disconnected' });\n }\n });\n } else {\n // No auto-connect: immediately transition from 'pending' to 'disconnected'.\n updateState({ status: 'disconnected' });\n }\n\n async function attemptSilentReconnect(savedAddress: string, uiWallet: UiWallet): Promise<void> {\n const generation = ++connectGeneration;\n updateState({ status: 'reconnecting' });\n\n try {\n const connectFeature = getWalletFeature(\n uiWallet,\n StandardConnect,\n ) as StandardConnectFeature[typeof StandardConnect];\n await connectFeature.connect({ silent: true });\n\n // A newer connect/signIn was started while we were awaiting — bail.\n if (generation !== connectGeneration) return;\n\n // The wallet may have unregistered (or dropped a required\n // feature/chain) while the silent reconnect was in flight.\n if (!isWalletStillAvailable(uiWallet)) {\n updateState({ status: 'disconnected' });\n clearPersistedAccount();\n return;\n }\n\n const refreshedWallet = refreshUiWallet(uiWallet);\n const allAccounts = refreshedWallet.accounts;\n\n if (allAccounts.length === 0) {\n updateState({ status: 'disconnected' });\n clearPersistedAccount();\n return;\n }\n\n // Restore the specific saved account, fall back to first from same wallet.\n const activeAccount = allAccounts.find(a => a.address === savedAddress) ?? allAccounts[0];\n\n // Persist only when we fell back to a different account\n setConnected(activeAccount, refreshedWallet, {\n persist: activeAccount.address !== savedAddress,\n });\n } catch {\n if (generation === connectGeneration) {\n updateState({ status: 'disconnected' });\n clearPersistedAccount();\n }\n }\n }\n\n // -- Public API --------------------------------------------------------\n\n return {\n connect,\n disconnect,\n getState: () => snapshot,\n selectAccount,\n signIn,\n signMessage,\n subscribe: (listener: () => void) => {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n subscribeSigner: (listener: () => void) => {\n signerListeners.add(listener);\n return () => {\n signerListeners.delete(listener);\n };\n },\n [Symbol.dispose]: () => {\n // Invalidate any in-flight connect/signIn/silent-reconnect so they\n // bail instead of resuming after disposal (which would re-subscribe\n // to wallet events and re-arm cleanup that this disposer already\n // ran). The generation bump covers attempts that already captured a\n // generation; `disposed` covers the auto-connect IIFE, which resumes\n // from its storage read and captures a fresh generation afterward.\n disposed = true;\n connectGeneration++;\n unsubRegister();\n unsubUnregister();\n walletEventsCleanup?.();\n walletEventsCleanup = null;\n cancelReconnect();\n listeners.clear();\n signerListeners.clear();\n },\n };\n}\n","import {\n type ClientWithIdentity,\n type ClientWithPayer,\n type ClientWithSubscribeToIdentity,\n type ClientWithSubscribeToPayer,\n extendClient,\n SOLANA_ERROR__WALLET__NO_SIGNER_CONNECTED,\n SOLANA_ERROR__WALLET__SIGNER_NOT_AVAILABLE,\n SolanaError,\n withCleanup,\n} from '@solana/kit';\n\nimport { createWalletStore } from './store';\nimport type { ClientWithWallet, WalletPluginConfig } from './types';\n\n// -- Internal helpers ---------------------------------------------------------\n\nfunction defineSignerGetter(\n additions: Record<string, unknown>,\n property: string,\n store: ReturnType<typeof createWalletStore>,\n): void {\n Object.defineProperty(additions, property, {\n configurable: true,\n enumerable: true,\n get() {\n const state = store.getState();\n if (!state.connected) {\n throw new SolanaError(SOLANA_ERROR__WALLET__NO_SIGNER_CONNECTED, { status: state.status });\n }\n if (!state.connected.signer) {\n throw new SolanaError(SOLANA_ERROR__WALLET__SIGNER_NOT_AVAILABLE);\n }\n return state.connected.signer;\n },\n });\n}\n\ntype SignerProperties = 'payer' | 'identity';\n\n// Maps each signer capability to the Kit-convention reactive hook installed\n// alongside it (`@solana/plugin-interfaces`). A plugin whose `client.payer` /\n// `client.identity` is dynamic installs the matching `subscribeTo*` function\n// so reactive consumers can observe changes without naming this plugin.\nconst SUBSCRIBE_PROPERTY: Record<SignerProperties, 'subscribeToIdentity' | 'subscribeToPayer'> = {\n identity: 'subscribeToIdentity',\n payer: 'subscribeToPayer',\n};\n\nfunction createPlugin<TAdditions extends ClientWithWallet>(\n config: WalletPluginConfig,\n signerProperties: SignerProperties[],\n) {\n return <T extends object & { wallet?: never }>(client: T): Disposable & Omit<T, keyof TAdditions> & TAdditions => {\n if ('wallet' in client) {\n throw new Error(\n 'Only one wallet plugin can be used per client. ' +\n 'Use walletSigner, walletPayer, walletIdentity, or walletWithoutSigner — not multiple.',\n );\n }\n\n const store = createWalletStore(config);\n\n const additions: Record<string, unknown> = { wallet: store };\n for (const prop of signerProperties) {\n defineSignerGetter(additions, prop, store);\n additions[SUBSCRIBE_PROPERTY[prop]] = store.subscribeSigner;\n }\n\n return withCleanup(extendClient(client, additions), () => store[Symbol.dispose]()) as unknown as Disposable &\n Omit<T, keyof TAdditions> &\n TAdditions;\n };\n}\n\n// -- Public API ---------------------------------------------------------------\n\n/**\n * A framework-agnostic Kit plugin that manages wallet discovery, connection\n * lifecycle, and signer creation using wallet-standard — and syncs the\n * connected wallet's signer to both `client.payer` and `client.identity`.\n *\n * This is the most common entrypoint for dApps. When a signing-capable\n * wallet is connected, `client.payer` and `client.identity` both return the\n * wallet signer. When disconnected or read-only, accessing either throws.\n *\n * Because the signer is dynamic, both `client.subscribeToPayer` and\n * `client.subscribeToIdentity` are installed so reactive consumers can observe\n * changes (the Kit reactive-capability convention).\n *\n * **SSR-safe.** Can be included in a shared client chain that runs on both\n * server and browser.\n *\n * ```ts\n * const client = createClient()\n * .use(walletSigner({ chain: 'solana:mainnet' }))\n * .use(solanaRpc({ rpcUrl: 'https://api.mainnet-beta.solana.com' }))\n * .use(planAndSendTransactions());\n * ```\n *\n * @param config - Plugin configuration.\n *\n * @see {@link walletPayer}\n * @see {@link walletIdentity}\n * @see {@link walletWithoutSigner}\n * @see {@link WalletPluginConfig}\n */\nexport function walletSigner(config: WalletPluginConfig) {\n return createPlugin<\n ClientWithIdentity &\n ClientWithPayer &\n ClientWithSubscribeToIdentity &\n ClientWithSubscribeToPayer &\n ClientWithWallet\n >(config, ['payer', 'identity']);\n}\n\n/**\n * A framework-agnostic Kit plugin that manages wallet discovery, connection\n * lifecycle, and signer creation using wallet-standard — and syncs the\n * connected wallet's signer to `client.identity`.\n *\n * Use this when `client.payer` is controlled by a separate `payer()` plugin\n * (e.g. a backend relayer pays fees, but the user's wallet is the identity).\n *\n * Because the signer is dynamic, `client.subscribeToIdentity` is installed so\n * reactive consumers can observe changes (the Kit reactive-capability convention).\n *\n * **SSR-safe.** Can be included in a shared client chain that runs on both\n * server and browser.\n *\n * ```ts\n * const client = createClient()\n * .use(payer(relayerKeypair))\n * .use(walletIdentity({ chain: 'solana:mainnet' }))\n * .use(solanaRpc({ rpcUrl: 'https://api.mainnet-beta.solana.com' }))\n * .use(planAndSendTransactions());\n * ```\n *\n * @param config - Plugin configuration.\n *\n * @see {@link walletSigner}\n * @see {@link walletPayer}\n * @see {@link walletWithoutSigner}\n * @see {@link WalletPluginConfig}\n */\nexport function walletIdentity(config: WalletPluginConfig) {\n return createPlugin<ClientWithIdentity & ClientWithSubscribeToIdentity & ClientWithWallet>(config, ['identity']);\n}\n\n/**\n * A framework-agnostic Kit plugin that manages wallet discovery, connection\n * lifecycle, and signer creation using wallet-standard — and syncs the\n * connected wallet's signer to `client.payer`.\n *\n * Use this when you need the wallet as the fee payer but don't need\n * `client.identity`. For most dApps, prefer {@link walletSigner} which\n * sets both.\n *\n * Because the signer is dynamic, `client.subscribeToPayer` is installed so\n * reactive consumers can observe changes (the Kit reactive-capability convention).\n *\n * **SSR-safe.** Can be included in a shared client chain that runs on both\n * server and browser.\n *\n * ```ts\n * const client = createClient()\n * .use(walletPayer({ chain: 'solana:mainnet' }))\n * .use(solanaRpc({ rpcUrl: 'https://api.mainnet-beta.solana.com' }))\n * .use(planAndSendTransactions());\n * ```\n *\n * @param config - Plugin configuration.\n *\n * @see {@link walletSigner}\n * @see {@link walletIdentity}\n * @see {@link walletWithoutSigner}\n * @see {@link WalletPluginConfig}\n */\nexport function walletPayer(config: WalletPluginConfig) {\n return createPlugin<ClientWithPayer & ClientWithSubscribeToPayer & ClientWithWallet>(config, ['payer']);\n}\n\n/**\n * A framework-agnostic Kit plugin that manages wallet discovery, connection\n * lifecycle, and signer creation using wallet-standard.\n *\n * Adds the `wallet` namespace to the client without setting `client.payer`\n * or `client.identity`. Use this alongside separate `payer()` and/or\n * `identity()` plugins, or when the wallet's signer is used explicitly in\n * instructions. For most dApps, prefer {@link walletSigner} instead.\n *\n * **SSR-safe.** Can be included in a shared client chain that runs on both\n * server and browser.\n *\n * ```ts\n * const client = createClient()\n * .use(payer(backendKeypair))\n * .use(walletWithoutSigner({ chain: 'solana:mainnet' }))\n * .use(solanaRpc({ rpcUrl: 'https://api.mainnet-beta.solana.com' }))\n * .use(planAndSendTransactions());\n *\n * // client.payer is always backendKeypair\n * // client.wallet.getState().connected?.signer for manual use\n * ```\n *\n * @param config - Plugin configuration.\n *\n * @see {@link walletSigner}\n * @see {@link walletPayer}\n * @see {@link walletIdentity}\n * @see {@link WalletPluginConfig}\n */\nexport function walletWithoutSigner(config: WalletPluginConfig) {\n return createPlugin<ClientWithWallet>(config, []);\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { WalletNamespace, WalletPluginConfig } from './types';
|
|
2
|
+
/** @internal */
|
|
3
|
+
export type WalletStore = WalletNamespace & {
|
|
4
|
+
/**
|
|
5
|
+
* Registers a listener notified only when the wallet signer may
|
|
6
|
+
* have changed.
|
|
7
|
+
* Returns an idempotent unsubscribe function.
|
|
8
|
+
*
|
|
9
|
+
* @internal
|
|
10
|
+
*/
|
|
11
|
+
subscribeSigner: (listener: () => void) => () => void;
|
|
12
|
+
[Symbol.dispose]: () => void;
|
|
13
|
+
};
|
|
14
|
+
/** @internal */
|
|
15
|
+
export declare function createWalletStore(config: WalletPluginConfig): WalletStore;
|
|
16
|
+
//# sourceMappingURL=store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/store.ts"],"names":[],"mappings":"AA4BA,OAAO,KAAK,EAER,eAAe,EACf,kBAAkB,EAKrB,MAAM,SAAS,CAAC;AAIjB,gBAAgB;AAChB,MAAM,MAAM,WAAW,GAAG,eAAe,GAAG;IACxC;;;;;;OAMG;IACH,eAAe,EAAE,CAAC,QAAQ,EAAE,MAAM,IAAI,KAAK,MAAM,IAAI,CAAC;IACtD,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CAChC,CAAC;AAaF,gBAAgB;AAChB,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,kBAAkB,GAAG,WAAW,CA6xBzE"}
|
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
import type { MessageSigner, SignatureBytes, TransactionSigner } from '@solana/kit';
|
|
2
|
+
import type { SolanaChain } from '@solana/wallet-standard-chains';
|
|
3
|
+
import type { SolanaSignInInput, SolanaSignInOutput } from '@solana/wallet-standard-features';
|
|
4
|
+
import type { IdentifierString } from '@wallet-standard/base';
|
|
5
|
+
import type { UiWallet, UiWalletAccount } from '@wallet-standard/ui';
|
|
6
|
+
/**
|
|
7
|
+
* The signer type for a connected wallet account.
|
|
8
|
+
*
|
|
9
|
+
* Always satisfies `TransactionSigner`. Additionally implements `MessageSigner`
|
|
10
|
+
* when the wallet supports `solana:signMessage`.
|
|
11
|
+
*/
|
|
12
|
+
export type WalletSigner = TransactionSigner | (MessageSigner & TransactionSigner);
|
|
13
|
+
/**
|
|
14
|
+
* The connection status of the wallet plugin.
|
|
15
|
+
*
|
|
16
|
+
* - `pending` — not yet initialized. Initial state on both server and browser.
|
|
17
|
+
* On the server — and in React Native, which is treated the same way — this
|
|
18
|
+
* state is permanent. In the browser it resolves to `disconnected` or
|
|
19
|
+
* `reconnecting` once the storage check completes.
|
|
20
|
+
* - `disconnected` — initialized, no wallet connected.
|
|
21
|
+
* - `connecting` — a user-initiated connection request is in progress. When
|
|
22
|
+
* connecting to a different wallet while one is already connected, the
|
|
23
|
+
* existing connection is preserved until the new one succeeds — so
|
|
24
|
+
* {@link WalletState.connected} can be non-null during this state, and a
|
|
25
|
+
* failed attempt reverts to the previous connection.
|
|
26
|
+
* - `connected` — a wallet is connected.
|
|
27
|
+
* - `disconnecting` — a user-initiated disconnection request is in progress.
|
|
28
|
+
* - `reconnecting` — auto-connect in progress (connecting to persisted wallet).
|
|
29
|
+
*/
|
|
30
|
+
export type WalletStatus = 'connected' | 'connecting' | 'disconnected' | 'disconnecting' | 'pending' | 'reconnecting';
|
|
31
|
+
/**
|
|
32
|
+
* A snapshot of the wallet plugin state at a point in time.
|
|
33
|
+
*
|
|
34
|
+
* Returned by {@link WalletNamespace.getState}. The same object reference
|
|
35
|
+
* is returned on successive calls as long as nothing has changed — a new
|
|
36
|
+
* object is only created when a field actually changes. This ensures
|
|
37
|
+
* `useSyncExternalStore` only triggers re-renders on meaningful state changes.
|
|
38
|
+
*
|
|
39
|
+
* @see {@link WalletNamespace.getState}
|
|
40
|
+
*/
|
|
41
|
+
export type WalletState = {
|
|
42
|
+
/**
|
|
43
|
+
* The active connection, or `null` when disconnected.
|
|
44
|
+
*
|
|
45
|
+
* `signer` is `null` for read-only / watch-only wallets that do not
|
|
46
|
+
* support any signing feature.
|
|
47
|
+
*
|
|
48
|
+
* Independent of {@link status}: when {@link connect} or {@link signIn} is
|
|
49
|
+
* called for a different wallet while one is already connected, `connected`
|
|
50
|
+
* keeps describing the existing connection while `status` is `'connecting'`,
|
|
51
|
+
* and a failed attempt leaves it in place.
|
|
52
|
+
*/
|
|
53
|
+
readonly connected: {
|
|
54
|
+
readonly account: UiWalletAccount;
|
|
55
|
+
/** The signer for the active account, or `null` for read-only wallets. */
|
|
56
|
+
readonly signer: WalletSigner | null;
|
|
57
|
+
readonly wallet: UiWallet;
|
|
58
|
+
} | null;
|
|
59
|
+
/** The current connection status. */
|
|
60
|
+
readonly status: WalletStatus;
|
|
61
|
+
/** All discovered wallets matching the configured chain and filter. */
|
|
62
|
+
readonly wallets: readonly UiWallet[];
|
|
63
|
+
};
|
|
64
|
+
/**
|
|
65
|
+
* Options accepted by each async wallet action.
|
|
66
|
+
*
|
|
67
|
+
* Currently only carries an `abortSignal`, but is kept as an object for
|
|
68
|
+
* consistency with the rest of the Kit ecosystem and to allow future
|
|
69
|
+
* additions without breaking the call-site shape.
|
|
70
|
+
*
|
|
71
|
+
* @see {@link WalletNamespace.connect}
|
|
72
|
+
* @see {@link WalletNamespace.disconnect}
|
|
73
|
+
* @see {@link WalletNamespace.signMessage}
|
|
74
|
+
* @see {@link WalletNamespace.signIn}
|
|
75
|
+
*/
|
|
76
|
+
export type WalletActionOptions = {
|
|
77
|
+
/**
|
|
78
|
+
* An optional `AbortSignal` used to cancel the operation.
|
|
79
|
+
*
|
|
80
|
+
* Cancellation is pre-call only: the plugin calls
|
|
81
|
+
* `abortSignal.throwIfAborted()` at the start of each action and bails
|
|
82
|
+
* out before invoking the wallet. Once the underlying wallet-standard
|
|
83
|
+
* call has been dispatched, its result is returned even if the signal
|
|
84
|
+
* is aborted mid-flight — the wallet's side effect (an approved
|
|
85
|
+
* signature, a live connection, a broadcast transaction) is the source
|
|
86
|
+
* of truth, and throwing here would discard real user work without
|
|
87
|
+
* undoing what the wallet already did.
|
|
88
|
+
*/
|
|
89
|
+
abortSignal?: AbortSignal;
|
|
90
|
+
};
|
|
91
|
+
/**
|
|
92
|
+
* A pluggable storage adapter for persisting the selected wallet account.
|
|
93
|
+
*
|
|
94
|
+
* Follows the Web Storage API shape (`getItem`/`setItem`/`removeItem`).
|
|
95
|
+
* `localStorage` and `sessionStorage` satisfy this interface directly.
|
|
96
|
+
* Async backends (IndexedDB, encrypted storage) may return `Promise`s.
|
|
97
|
+
*
|
|
98
|
+
* Writes are fire-and-forget: the plugin does not await `setItem`/`removeItem`
|
|
99
|
+
* and swallows any rejection (the live wallet connection is the source of
|
|
100
|
+
* truth, so a failed persist is non-fatal). A resolved action therefore does
|
|
101
|
+
* not guarantee the write has landed, and rapid successive writes are not
|
|
102
|
+
* ordered. This is intentional — persistence only records which account to
|
|
103
|
+
* silently reconnect to on the next load.
|
|
104
|
+
*
|
|
105
|
+
* @example
|
|
106
|
+
* ```ts
|
|
107
|
+
* // Use sessionStorage
|
|
108
|
+
* walletSigner({ chain: 'solana:mainnet', storage: sessionStorage });
|
|
109
|
+
*
|
|
110
|
+
* // Custom async adapter
|
|
111
|
+
* walletSigner({
|
|
112
|
+
* chain: 'solana:mainnet',
|
|
113
|
+
* storage: {
|
|
114
|
+
* getItem: (key) => myStore.get(key),
|
|
115
|
+
* setItem: (key, value) => myStore.set(key, value),
|
|
116
|
+
* removeItem: (key) => myStore.delete(key),
|
|
117
|
+
* },
|
|
118
|
+
* });
|
|
119
|
+
* ```
|
|
120
|
+
*/
|
|
121
|
+
export type WalletStorage = {
|
|
122
|
+
getItem(key: string): Promise<string | null> | string | null;
|
|
123
|
+
removeItem(key: string): Promise<void> | void;
|
|
124
|
+
setItem(key: string, value: string): Promise<void> | void;
|
|
125
|
+
};
|
|
126
|
+
/**
|
|
127
|
+
* Configuration for the wallet plugins.
|
|
128
|
+
*
|
|
129
|
+
* @see {@link walletSigner}
|
|
130
|
+
* @see {@link walletIdentity}
|
|
131
|
+
* @see {@link walletPayer}
|
|
132
|
+
* @see {@link walletWithoutSigner}
|
|
133
|
+
*/
|
|
134
|
+
export type WalletPluginConfig = {
|
|
135
|
+
/**
|
|
136
|
+
* Whether to attempt silent reconnection on startup using the persisted
|
|
137
|
+
* wallet account from `storage`.
|
|
138
|
+
*
|
|
139
|
+
* Has no effect if `storage` is `null`.
|
|
140
|
+
*
|
|
141
|
+
* @default true
|
|
142
|
+
*/
|
|
143
|
+
autoConnect?: boolean;
|
|
144
|
+
/**
|
|
145
|
+
* The chain this client targets (e.g. `'solana:mainnet'`).
|
|
146
|
+
*
|
|
147
|
+
* Accepts any {@link SolanaChain} (with literal autocomplete) and, as an
|
|
148
|
+
* escape hatch, any wallet-standard {@link IdentifierString} shape
|
|
149
|
+
* (`${string}:${string}`) for custom chains or non-Solana L2s. The plugin's
|
|
150
|
+
* runtime behavior is chain-agnostic — it passes the identifier to
|
|
151
|
+
* wallet-standard discovery (`uiWallet.chains.includes(chain)`) and to
|
|
152
|
+
* `createSignerFromWalletAccount`. Wallets that don't advertise the chain
|
|
153
|
+
* are filtered out, and accounts that can't produce a signer for the chain
|
|
154
|
+
* resolve to `signer: null` (matching the read-only-wallet contract).
|
|
155
|
+
*
|
|
156
|
+
* One client = one chain. To switch networks, create a separate client
|
|
157
|
+
* with a different chain and RPC endpoint.
|
|
158
|
+
*/
|
|
159
|
+
chain: SolanaChain | (IdentifierString & {});
|
|
160
|
+
/**
|
|
161
|
+
* Optional filter function for wallet discovery. Called for each wallet
|
|
162
|
+
* that supports the configured chain and `standard:connect`. Return `true`
|
|
163
|
+
* to include the wallet, `false` to exclude it.
|
|
164
|
+
*
|
|
165
|
+
* @example
|
|
166
|
+
* ```ts
|
|
167
|
+
* // Require signAndSendTransaction
|
|
168
|
+
* filter: (w) => w.features.includes('solana:signAndSendTransaction')
|
|
169
|
+
*
|
|
170
|
+
* // Whitelist specific wallets
|
|
171
|
+
* filter: (w) => ['SomeWallet', 'SomeOtherWallet'].includes(w.name)
|
|
172
|
+
* ```
|
|
173
|
+
*/
|
|
174
|
+
filter?: (wallet: UiWallet) => boolean;
|
|
175
|
+
/**
|
|
176
|
+
* Storage adapter for persisting the selected wallet account across page
|
|
177
|
+
* loads. Pass `null` to disable persistence entirely.
|
|
178
|
+
*
|
|
179
|
+
* When omitted in a browser environment, `localStorage` is used by default.
|
|
180
|
+
* On the server, storage is always skipped regardless of this option.
|
|
181
|
+
*
|
|
182
|
+
* @default localStorage (in browser)
|
|
183
|
+
* @see {@link WalletStorage}
|
|
184
|
+
*/
|
|
185
|
+
storage?: WalletStorage | null;
|
|
186
|
+
/**
|
|
187
|
+
* Storage key used for persistence.
|
|
188
|
+
*
|
|
189
|
+
* @default 'kit-wallet'
|
|
190
|
+
*/
|
|
191
|
+
storageKey?: string;
|
|
192
|
+
};
|
|
193
|
+
/**
|
|
194
|
+
* The `wallet` namespace exposed on the client as `client.wallet`.
|
|
195
|
+
*
|
|
196
|
+
* All wallet state is accessed via {@link getState}. Use {@link subscribe}
|
|
197
|
+
* to be notified of changes and integrate with framework primitives such as
|
|
198
|
+
* React's `useSyncExternalStore`.
|
|
199
|
+
*
|
|
200
|
+
* @see {@link ClientWithWallet}
|
|
201
|
+
*/
|
|
202
|
+
export type WalletNamespace = {
|
|
203
|
+
/**
|
|
204
|
+
* Connect to a wallet. Calls `standard:connect`, then selects the first
|
|
205
|
+
* newly authorized account (or the first account if reconnecting). Creates
|
|
206
|
+
* and caches a signer for the active account.
|
|
207
|
+
*
|
|
208
|
+
* Resolving means the wallet is connected; any failure rejects. If a wallet
|
|
209
|
+
* is already connected, it stays connected until the new one is established:
|
|
210
|
+
* a failed attempt — a rejected prompt, no authorized accounts, or the
|
|
211
|
+
* wallet becoming unavailable — leaves the previous connection in place
|
|
212
|
+
* rather than disconnecting it.
|
|
213
|
+
*
|
|
214
|
+
* @returns All accounts from the wallet after connection.
|
|
215
|
+
* @throws The wallet's rejection error if the user declines the prompt.
|
|
216
|
+
* @throws `SolanaError(SOLANA_ERROR__WALLET__NOT_CONNECTED)` if the wallet
|
|
217
|
+
* authorizes no accounts, or unregisters (or drops a required
|
|
218
|
+
* feature/chain) while its connect prompt is open. Any previously
|
|
219
|
+
* connected wallet is left in place.
|
|
220
|
+
* @throws `DOMException` with `name: 'AbortError'` if a newer `connect` or
|
|
221
|
+
* `signIn` is started before this call resolves. The newer request wins
|
|
222
|
+
* and owns the resulting connection; this superseded call rejects so it
|
|
223
|
+
* can be ignored like any other aborted operation (e.g. an accidental
|
|
224
|
+
* double-click still connects — only the orphaned first promise rejects).
|
|
225
|
+
* @throws `options.abortSignal.reason` if the signal is already aborted
|
|
226
|
+
* when the action is called. Aborts after the wallet call has been
|
|
227
|
+
* dispatched do not take effect.
|
|
228
|
+
*/
|
|
229
|
+
connect: (wallet: UiWallet, options?: WalletActionOptions) => Promise<readonly UiWalletAccount[]>;
|
|
230
|
+
/**
|
|
231
|
+
* Disconnect the active wallet. Calls `standard:disconnect` if supported.
|
|
232
|
+
*
|
|
233
|
+
* @throws `options.abortSignal.reason` if the signal is already aborted
|
|
234
|
+
* when the action is called. Aborts after the wallet call has been
|
|
235
|
+
* dispatched do not take effect.
|
|
236
|
+
*/
|
|
237
|
+
disconnect: (options?: WalletActionOptions) => Promise<void>;
|
|
238
|
+
/**
|
|
239
|
+
* Get the current wallet state. Referentially stable — a new object is
|
|
240
|
+
* only created when a field actually changes, so React's
|
|
241
|
+
* `useSyncExternalStore` skips re-renders when nothing meaningful changed.
|
|
242
|
+
*
|
|
243
|
+
* @see {@link WalletState}
|
|
244
|
+
*/
|
|
245
|
+
getState: () => WalletState;
|
|
246
|
+
/**
|
|
247
|
+
* Switch to a different account within the connected wallet. Creates and
|
|
248
|
+
* caches a new signer for the selected account.
|
|
249
|
+
*
|
|
250
|
+
* @throws `SolanaError(SOLANA_ERROR__WALLET__NOT_CONNECTED)` if no wallet is
|
|
251
|
+
* connected, or the connected wallet is currently disconnecting.
|
|
252
|
+
* @throws `SolanaError(SOLANA_ERROR__WALLET__ACCOUNT_NOT_AVAILABLE)` if the
|
|
253
|
+
* specified account is not among the connected wallet's accounts.
|
|
254
|
+
*/
|
|
255
|
+
selectAccount: (account: UiWalletAccount) => void;
|
|
256
|
+
/**
|
|
257
|
+
* Sign In With Solana (SIWS-as-connect).
|
|
258
|
+
*
|
|
259
|
+
* Connects the wallet, calls `solana:signIn`, sets the returned account as
|
|
260
|
+
* active, and creates a signer. Resolving means the client is in the same
|
|
261
|
+
* state as if {@link connect} had been called; any failure to connect
|
|
262
|
+
* rejects rather than resolving while disconnected.
|
|
263
|
+
*
|
|
264
|
+
* All fields on `SolanaSignInInput` are optional — pass `{}` if no sign-in
|
|
265
|
+
* customization is needed.
|
|
266
|
+
*
|
|
267
|
+
* To sign in with the already-connected wallet, pass
|
|
268
|
+
* `getState().connected.wallet`.
|
|
269
|
+
*
|
|
270
|
+
* Like {@link connect}, a failed sign-in to a different wallet rejects and
|
|
271
|
+
* leaves any existing connection in place rather than disconnecting it.
|
|
272
|
+
*
|
|
273
|
+
* @returns The wallet's sign-in output, once the connection is established.
|
|
274
|
+
* @throws `WalletStandardError(WALLET_STANDARD_ERROR__FEATURES__WALLET_ACCOUNT_FEATURE_UNIMPLEMENTED)`
|
|
275
|
+
* if the wallet does not support `solana:signIn`.
|
|
276
|
+
* @throws `SolanaError(SOLANA_ERROR__WALLET__NOT_CONNECTED)` if the wallet
|
|
277
|
+
* unregisters (or drops a required feature/chain) while its sign-in prompt
|
|
278
|
+
* is open, or signs in with an account it does not expose. Any previously
|
|
279
|
+
* connected wallet is left in place.
|
|
280
|
+
* @throws `DOMException` with `name: 'AbortError'` if a newer `connect` or
|
|
281
|
+
* `signIn` is started before this call resolves. The newer request wins
|
|
282
|
+
* and owns the resulting connection; this superseded call rejects so it
|
|
283
|
+
* can be ignored like any other aborted operation.
|
|
284
|
+
* @throws `options.abortSignal.reason` if the signal is already aborted
|
|
285
|
+
* when the action is called. Aborts after the wallet call has been
|
|
286
|
+
* dispatched do not take effect.
|
|
287
|
+
*/
|
|
288
|
+
signIn: (wallet: UiWallet, input: SolanaSignInInput, options?: WalletActionOptions) => Promise<SolanaSignInOutput>;
|
|
289
|
+
/**
|
|
290
|
+
* Sign an arbitrary message with the connected account.
|
|
291
|
+
*
|
|
292
|
+
* Calls the wallet's `solana:signMessage` feature directly (does not go
|
|
293
|
+
* through the cached signer), so message signing works even for wallets
|
|
294
|
+
* that don't support transaction signing.
|
|
295
|
+
*
|
|
296
|
+
* @throws `SolanaError(SOLANA_ERROR__WALLET__NOT_CONNECTED)` if no wallet is connected.
|
|
297
|
+
* @throws `WalletStandardError(WALLET_STANDARD_ERROR__FEATURES__WALLET_ACCOUNT_FEATURE_UNIMPLEMENTED)`
|
|
298
|
+
* if the wallet does not support `solana:signMessage`.
|
|
299
|
+
* @throws `options.abortSignal.reason` if the signal is already aborted
|
|
300
|
+
* when the action is called. Aborts after the wallet call has been
|
|
301
|
+
* dispatched do not take effect.
|
|
302
|
+
*/
|
|
303
|
+
signMessage: (message: Uint8Array, options?: WalletActionOptions) => Promise<SignatureBytes>;
|
|
304
|
+
/**
|
|
305
|
+
* Subscribe to any wallet state change. Compatible with React's
|
|
306
|
+
* `useSyncExternalStore` and similar framework primitives.
|
|
307
|
+
*
|
|
308
|
+
* @returns An unsubscribe function.
|
|
309
|
+
*
|
|
310
|
+
* @example
|
|
311
|
+
* ```ts
|
|
312
|
+
* const state = useSyncExternalStore(client.wallet.subscribe, client.wallet.getState);
|
|
313
|
+
* ```
|
|
314
|
+
*/
|
|
315
|
+
subscribe: (listener: () => void) => () => void;
|
|
316
|
+
};
|
|
317
|
+
/**
|
|
318
|
+
* Properties added to the client by the wallet plugins.
|
|
319
|
+
*
|
|
320
|
+
* All wallet state and actions are namespaced under `client.wallet`.
|
|
321
|
+
* Depending on which plugin variant is used, `client.payer` and/or
|
|
322
|
+
* `client.identity` may also be set to the connected wallet's signer.
|
|
323
|
+
* This is not part of Kit plugin-interfaces, as it depends on wallet-standard types
|
|
324
|
+
*
|
|
325
|
+
* @see {@link walletSigner}
|
|
326
|
+
* @see {@link walletPayer}
|
|
327
|
+
* @see {@link walletIdentity}
|
|
328
|
+
* @see {@link walletWithoutSigner}
|
|
329
|
+
* @see {@link WalletNamespace}
|
|
330
|
+
*
|
|
331
|
+
*/
|
|
332
|
+
export type ClientWithWallet = {
|
|
333
|
+
/** The wallet namespace — state, actions, and framework integration. */
|
|
334
|
+
readonly wallet: WalletNamespace;
|
|
335
|
+
};
|
|
336
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACpF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAC;AAClE,OAAO,KAAK,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AAC9F,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,KAAK,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAErE;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GAAG,iBAAiB,GAAG,CAAC,aAAa,GAAG,iBAAiB,CAAC,CAAC;AAInF;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,MAAM,YAAY,GAAG,WAAW,GAAG,YAAY,GAAG,cAAc,GAAG,eAAe,GAAG,SAAS,GAAG,cAAc,CAAC;AAEtH;;;;;;;;;GASG;AACH,MAAM,MAAM,WAAW,GAAG;IACtB;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,SAAS,EAAE;QAChB,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAC;QAClC,0EAA0E;QAC1E,QAAQ,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI,CAAC;QACrC,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC;KAC7B,GAAG,IAAI,CAAC;IACT,qCAAqC;IACrC,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;IAC9B,uEAAuE;IACvE,QAAQ,CAAC,OAAO,EAAE,SAAS,QAAQ,EAAE,CAAC;CACzC,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAC9B;;;;;;;;;;;OAWG;IACH,WAAW,CAAC,EAAE,WAAW,CAAC;CAC7B,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,MAAM,MAAM,aAAa,GAAG;IACxB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,GAAG,MAAM,GAAG,IAAI,CAAC;IAC7D,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAC9C,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;CAC7D,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,MAAM,kBAAkB,GAAG;IAC7B;;;;;;;OAOG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IAEtB;;;;;;;;;;;;;;OAcG;IACH,KAAK,EAAE,WAAW,GAAG,CAAC,gBAAgB,GAAG,EAAE,CAAC,CAAC;IAE7C;;;;;;;;;;;;;OAaG;IACH,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,QAAQ,KAAK,OAAO,CAAC;IAEvC;;;;;;;;;OASG;IACH,OAAO,CAAC,EAAE,aAAa,GAAG,IAAI,CAAC;IAE/B;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF;;;;;;;;GAQG;AACH,MAAM,MAAM,eAAe,GAAG;IAG1B;;;;;;;;;;;;;;;;;;;;;;;;;OAyBG;IACH,OAAO,EAAE,CAAC,MAAM,EAAE,QAAQ,EAAE,OAAO,CAAC,EAAE,mBAAmB,KAAK,OAAO,CAAC,SAAS,eAAe,EAAE,CAAC,CAAC;IAElG;;;;;;OAMG;IACH,UAAU,EAAE,CAAC,OAAO,CAAC,EAAE,mBAAmB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAG7D;;;;;;OAMG;IACH,QAAQ,EAAE,MAAM,WAAW,CAAC;IAE5B;;;;;;;;OAQG;IACH,aAAa,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,IAAI,CAAC;IAElD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA+BG;IACH,MAAM,EAAE,CAAC,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,iBAAiB,EAAE,OAAO,CAAC,EAAE,mBAAmB,KAAK,OAAO,CAAC,kBAAkB,CAAC,CAAC;IAEnH;;;;;;;;;;;;;OAaG;IACH,WAAW,EAAE,CAAC,OAAO,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,mBAAmB,KAAK,OAAO,CAAC,cAAc,CAAC,CAAC;IAE7F;;;;;;;;;;OAUG;IACH,SAAS,EAAE,CAAC,QAAQ,EAAE,MAAM,IAAI,KAAK,MAAM,IAAI,CAAC;CACnD,CAAC;AAEF;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC3B,wEAAwE;IACxE,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAC;CACpC,CAAC"}
|