@kheopskit/core 5.3.0 → 6.0.0-rc.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/CHANGELOG.md +426 -0
- package/MIGRATING_TO_V6.md +837 -0
- package/README.md +628 -12
- package/dist/ethereum.d.mts +12 -6
- package/dist/ethereum.d.ts +12 -6
- package/dist/ethereum.js +13 -10
- package/dist/ethereum.js.map +1 -1
- package/dist/ethereum.mjs +7 -4
- package/dist/ethereum.mjs.map +1 -1
- package/dist/{iconCache-pP2ES4pJ.mjs → iconCache-C-U3IEbP.mjs} +21 -40
- package/dist/iconCache-C-U3IEbP.mjs.map +1 -0
- package/dist/{iconCache-DUtkJlaX.js → iconCache-FR4iy6go.js} +21 -40
- package/dist/iconCache-FR4iy6go.js.map +1 -0
- package/dist/index.d.mts +17 -63
- package/dist/index.d.ts +17 -63
- package/dist/index.js +387 -373
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +384 -369
- package/dist/index.mjs.map +1 -1
- package/dist/injectedWallets-48L-AL1I.mjs +173 -0
- package/dist/injectedWallets-48L-AL1I.mjs.map +1 -0
- package/dist/injectedWallets-Dy8YGKL-.js +178 -0
- package/dist/injectedWallets-Dy8YGKL-.js.map +1 -0
- package/dist/internal.d.mts +3 -27
- package/dist/internal.d.ts +3 -27
- package/dist/internal.js +2 -2
- package/dist/internal.mjs +2 -2
- package/dist/pageLoad-BZ7o8p5R.mjs +24 -0
- package/dist/pageLoad-BZ7o8p5R.mjs.map +1 -0
- package/dist/pageLoad-D1wLeuFG.js +28 -0
- package/dist/pageLoad-D1wLeuFG.js.map +1 -0
- package/dist/platformAccounts-BjIeRcFN.js +321 -0
- package/dist/platformAccounts-BjIeRcFN.js.map +1 -0
- package/dist/platformAccounts-yyRUrnel.mjs +256 -0
- package/dist/platformAccounts-yyRUrnel.mjs.map +1 -0
- package/dist/polkadot.d.mts +127 -11
- package/dist/polkadot.d.ts +127 -11
- package/dist/polkadot.js +170 -106
- package/dist/polkadot.js.map +1 -1
- package/dist/polkadot.mjs +163 -99
- package/dist/polkadot.mjs.map +1 -1
- package/dist/solana.d.mts +11 -5
- package/dist/solana.d.ts +11 -5
- package/dist/solana.js +18 -15
- package/dist/solana.js.map +1 -1
- package/dist/solana.mjs +7 -4
- package/dist/solana.mjs.map +1 -1
- package/dist/storage-D3AwNgqc.d.mts +27 -0
- package/dist/storage-D3AwNgqc.d.ts +27 -0
- package/dist/testing.d.mts +71 -0
- package/dist/testing.d.ts +71 -0
- package/dist/testing.js +136 -0
- package/dist/testing.js.map +1 -0
- package/dist/testing.mjs +131 -0
- package/dist/testing.mjs.map +1 -0
- package/dist/{types-DKU-SNio.js → types-BWHvNFlN.js} +149 -2
- package/dist/types-BWHvNFlN.js.map +1 -0
- package/dist/types-Dkyt9bf4.d.mts +683 -0
- package/dist/types-Dkyt9bf4.d.ts +683 -0
- package/dist/types-Yor672md.mjs +215 -0
- package/dist/types-Yor672md.mjs.map +1 -0
- package/dist/walletconnect.d.mts +41 -0
- package/dist/walletconnect.d.ts +41 -0
- package/dist/walletconnect.js +219 -0
- package/dist/walletconnect.js.map +1 -0
- package/dist/walletconnect.mjs +216 -0
- package/dist/walletconnect.mjs.map +1 -0
- package/package.json +30 -6
- package/dist/errors-BLFP1dQI.mjs +0 -181
- package/dist/errors-BLFP1dQI.mjs.map +0 -1
- package/dist/errors-DYWVPHrR.js +0 -222
- package/dist/errors-DYWVPHrR.js.map +0 -1
- package/dist/iconCache-DUtkJlaX.js.map +0 -1
- package/dist/iconCache-pP2ES4pJ.mjs.map +0 -1
- package/dist/injectedWallets-BqR-_aAy.js +0 -148
- package/dist/injectedWallets-BqR-_aAy.js.map +0 -1
- package/dist/injectedWallets-Uz4hPuoW.mjs +0 -119
- package/dist/injectedWallets-Uz4hPuoW.mjs.map +0 -1
- package/dist/types-AxUDtkR-.d.mts +0 -338
- package/dist/types-AxUDtkR-.d.ts +0 -338
- package/dist/types-DKU-SNio.js.map +0 -1
- package/dist/types-jnifAAA0.mjs +0 -104
- package/dist/types-jnifAAA0.mjs.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,172 +1,10 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_types = require("./types-
|
|
3
|
-
const
|
|
4
|
-
const require_iconCache = require("./iconCache-
|
|
2
|
+
const require_types = require("./types-BWHvNFlN.js");
|
|
3
|
+
const require_platformAccounts = require("./platformAccounts-BjIeRcFN.js");
|
|
4
|
+
const require_iconCache = require("./iconCache-FR4iy6go.js");
|
|
5
|
+
const require_pageLoad = require("./pageLoad-D1wLeuFG.js");
|
|
5
6
|
let rxjs = require("rxjs");
|
|
6
7
|
let lodash_es = require("lodash-es");
|
|
7
|
-
//#region src/api/appKit.ts
|
|
8
|
-
/**
|
|
9
|
-
* Dynamically import @reown/appkit/core to avoid loading browser-only code
|
|
10
|
-
* during SSR or in edge runtimes like Cloudflare Workers.
|
|
11
|
-
* The AppKit library uses Lit for web components which requires document.
|
|
12
|
-
*/
|
|
13
|
-
const loadAppKit = async () => {
|
|
14
|
-
try {
|
|
15
|
-
const { createAppKit } = await import("@reown/appkit/core");
|
|
16
|
-
return createAppKit;
|
|
17
|
-
} catch (cause) {
|
|
18
|
-
console.error("[kheopskit] WalletConnect is configured but @reown/appkit could not be loaded. Install it with `pnpm add @reown/appkit` (or remove config.walletConnect). WalletConnect wallets are disabled; injected wallets still work.", cause);
|
|
19
|
-
return null;
|
|
20
|
-
}
|
|
21
|
-
};
|
|
22
|
-
const WALLET_CONNECT_ICON = "data:image/svg+xml;base64,PHN2ZyBmaWxsPSJub25lIiBoZWlnaHQ9IjQwMCIgdmlld0JveD0iMCAwIDQwMCA0MDAiIHdpZHRoPSI0MDAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxjbGlwUGF0aCBpZD0iYSI+PHBhdGggZD0ibTAgMGg0MDB2NDAwaC00MDB6Ii8+PC9jbGlwUGF0aD48ZyBjbGlwLXBhdGg9InVybCgjYSkiPjxjaXJjbGUgY3g9IjIwMCIgY3k9IjIwMCIgZmlsbD0iIzMzOTZmZiIgcj0iMTk5LjUiIHN0cm9rZT0iIzY2YjFmZiIvPjxwYXRoIGQ9Im0xMjIuNTE5IDE0OC45NjVjNDIuNzkxLTQxLjcyOSAxMTIuMTcxLTQxLjcyOSAxNTQuOTYyIDBsNS4xNSA1LjAyMmMyLjE0IDIuMDg2IDIuMTQgNS40NjkgMCA3LjU1NWwtMTcuNjE3IDE3LjE4Yy0xLjA3IDEuMDQzLTIuODA0IDEuMDQzLTMuODc0IDBsLTcuMDg3LTYuOTExYy0yOS44NTMtMjkuMTExLTc4LjI1My0yOS4xMTEtMTA4LjEwNiAwbC03LjU5IDcuNDAxYy0xLjA3IDEuMDQzLTIuODA0IDEuMDQzLTMuODc0IDBsLTE3LjYxNy0xNy4xOGMtMi4xNC0yLjA4Ni0yLjE0LTUuNDY5IDAtNy41NTV6bTE5MS4zOTcgMzUuNTI5IDE1LjY3OSAxNS4yOWMyLjE0IDIuMDg2IDIuMTQgNS40NjkgMCA3LjU1NWwtNzAuNyA2OC45NDRjLTIuMTM5IDIuMDg3LTUuNjA4IDIuMDg3LTcuNzQ4IDBsLTUwLjE3OC00OC45MzFjLS41MzUtLjUyMi0xLjQwMi0uNTIyLTEuOTM3IDBsLTUwLjE3OCA0OC45MzFjLTIuMTM5IDIuMDg3LTUuNjA4IDIuMDg3LTcuNzQ4IDBsLTcwLjcwMTUtNjguOTQ1Yy0yLjEzOTYtMi4wODYtMi4xMzk2LTUuNDY5IDAtNy41NTVsMTUuNjc5NS0xNS4yOWMyLjEzOTYtMi4wODYgNS42MDg1LTIuMDg2IDcuNzQ4MSAwbDUwLjE3ODkgNDguOTMyYy41MzUuNTIyIDEuNDAyLjUyMiAxLjkzNyAwbDUwLjE3Ny00OC45MzJjMi4xMzktMi4wODcgNS42MDgtMi4wODcgNy43NDggMGw1MC4xNzkgNDguOTMyYy41MzUuNTIyIDEuNDAyLjUyMiAxLjkzNyAwbDUwLjE3OS00OC45MzFjMi4xMzktMi4wODcgNS42MDgtMi4wODcgNy43NDggMHoiIGZpbGw9IiNmZmYiLz48L2c+PC9zdmc+";
|
|
23
|
-
const APPKIT_SYMBOL = Symbol.for("kheopskit.cachedAppKit");
|
|
24
|
-
const APPKIT_PROJECT_ID_SYMBOL = Symbol.for("kheopskit.cachedAppKitProjectId");
|
|
25
|
-
const getCachedAppKit = () => globalThis[APPKIT_SYMBOL];
|
|
26
|
-
const getCachedAppKitProjectId = () => globalThis[APPKIT_PROJECT_ID_SYMBOL];
|
|
27
|
-
const setCachedAppKit = (value, projectId) => {
|
|
28
|
-
globalThis[APPKIT_SYMBOL] = value;
|
|
29
|
-
globalThis[APPKIT_PROJECT_ID_SYMBOL] = projectId;
|
|
30
|
-
};
|
|
31
|
-
/**
|
|
32
|
-
* Clears the cached AppKit observable.
|
|
33
|
-
* Use when configuration changes or for testing purposes.
|
|
34
|
-
* Note: This does NOT destroy the appKit instance created by Reown.
|
|
35
|
-
*/
|
|
36
|
-
const resetAppKitCache = () => {
|
|
37
|
-
setCachedAppKit(void 0);
|
|
38
|
-
};
|
|
39
|
-
/**
|
|
40
|
-
* Drops a WalletConnect namespace's cached account observables. Called when a
|
|
41
|
-
* namespace transitions to disconnected — including external disconnects (from
|
|
42
|
-
* the wallet app), which flip status via `subscribeProviders` without ever
|
|
43
|
-
* calling `disconnect()` — so a later reconnect rebuilds the account observables
|
|
44
|
-
* against the fresh session instead of a stale closure.
|
|
45
|
-
*
|
|
46
|
-
* Keys are `accounts:<walletId>:...`; the trailing colon scopes the prefix to
|
|
47
|
-
* this wallet and avoids matching a sibling whose id is a string prefix.
|
|
48
|
-
*/
|
|
49
|
-
const dropAccountsCache = (platform) => {
|
|
50
|
-
require_types.clearCachedObservablesByPrefix(`accounts:${require_types.WALLET_CONNECT_WALLET_ID}:${platform}:`);
|
|
51
|
-
};
|
|
52
|
-
/**
|
|
53
|
-
* The single, platform-less WalletConnect connector for the given config — or
|
|
54
|
-
* `null` when WalletConnect isn't configured, on the server, or when
|
|
55
|
-
* `@reown/appkit` isn't installed.
|
|
56
|
-
*
|
|
57
|
-
* @remarks
|
|
58
|
-
* The AppKit instance is a process-wide singleton (Reown AppKit itself cannot
|
|
59
|
-
* be instantiated twice). The **first** call with a `walletConnect` config wins;
|
|
60
|
-
* later calls with a *different* `walletConnect` config reuse that first
|
|
61
|
-
* instance. Call {@link resetAppKitCache} before re-initialising if the
|
|
62
|
-
* WalletConnect config must change.
|
|
63
|
-
*
|
|
64
|
-
* One WalletConnect session is shared across every namespace, and a namespace
|
|
65
|
-
* can only be established during the initial pairing — it can't be added to a
|
|
66
|
-
* live session afterwards. So this exposes ONE connector (not one per platform):
|
|
67
|
-
* `connect()` opens the modal; the wallet approves whichever namespaces it
|
|
68
|
-
* supports in that single pairing, and `platforms` reflects what the live
|
|
69
|
-
* session carries. Per-namespace accounts are derived by each platform plugin.
|
|
70
|
-
*/
|
|
71
|
-
const getWalletConnectWallet$ = (config) => {
|
|
72
|
-
if (!config.walletConnect) return (0, rxjs.of)(null);
|
|
73
|
-
if (typeof window === "undefined") return (0, rxjs.of)(null);
|
|
74
|
-
const walletConnect = config.walletConnect;
|
|
75
|
-
const cachedProjectId = getCachedAppKitProjectId();
|
|
76
|
-
if (cachedProjectId !== void 0 && cachedProjectId !== walletConnect.projectId) console.warn("[kheopskit] WalletConnect is already initialised with projectId %s; AppKit is a process-wide singleton, so the first configuration wins and projectId %s is ignored. Call resetAppKitCache() before re-initialising if the WalletConnect config must change.", cachedProjectId, walletConnect.projectId);
|
|
77
|
-
let cachedAppKit = getCachedAppKit();
|
|
78
|
-
if (!cachedAppKit) {
|
|
79
|
-
cachedAppKit = (0, rxjs.from)(loadAppKit()).pipe((0, rxjs.switchMap)((createAppKit) => {
|
|
80
|
-
if (!createAppKit) return (0, rxjs.of)(null);
|
|
81
|
-
return new rxjs.Observable((subscriber) => {
|
|
82
|
-
const appKit = createAppKit({
|
|
83
|
-
projectId: walletConnect.projectId,
|
|
84
|
-
metadata: walletConnect.metadata,
|
|
85
|
-
networks: walletConnect.networks,
|
|
86
|
-
themeMode: walletConnect.themeMode,
|
|
87
|
-
themeVariables: walletConnect.themeVariables,
|
|
88
|
-
universalProviderConfigOverride: { methods: {
|
|
89
|
-
polkadot: ["polkadot_signTransaction", "polkadot_signMessage"],
|
|
90
|
-
solana: [
|
|
91
|
-
"solana_signTransaction",
|
|
92
|
-
"solana_signMessage",
|
|
93
|
-
"solana_signAndSendTransaction"
|
|
94
|
-
]
|
|
95
|
-
} },
|
|
96
|
-
allWallets: "HIDE",
|
|
97
|
-
debug: config.debug,
|
|
98
|
-
allowUnsupportedChain: true
|
|
99
|
-
});
|
|
100
|
-
const appKitInstance = appKit;
|
|
101
|
-
const status$ = new rxjs.BehaviorSubject({
|
|
102
|
-
isPolkadotConnected: false,
|
|
103
|
-
isEthereumConnected: false,
|
|
104
|
-
isSolanaConnected: false
|
|
105
|
-
});
|
|
106
|
-
const unsubProviders = appKit.subscribeProviders((providers) => {
|
|
107
|
-
status$.next({
|
|
108
|
-
isPolkadotConnected: !!providers.polkadot,
|
|
109
|
-
isEthereumConnected: !!providers.eip155,
|
|
110
|
-
isSolanaConnected: !!providers.solana
|
|
111
|
-
});
|
|
112
|
-
});
|
|
113
|
-
const namespaceOf = {
|
|
114
|
-
polkadot: "polkadot",
|
|
115
|
-
ethereum: "eip155",
|
|
116
|
-
solana: "solana"
|
|
117
|
-
};
|
|
118
|
-
const allPlatforms = [
|
|
119
|
-
"polkadot",
|
|
120
|
-
"ethereum",
|
|
121
|
-
"solana"
|
|
122
|
-
];
|
|
123
|
-
const enabledPlatforms = allPlatforms.filter((p) => appKit.chainNamespaces.includes(namespaceOf[p]));
|
|
124
|
-
let prevConnected = {
|
|
125
|
-
polkadot: false,
|
|
126
|
-
ethereum: false,
|
|
127
|
-
solana: false
|
|
128
|
-
};
|
|
129
|
-
const sub = status$.pipe((0, rxjs.map)((s) => ({
|
|
130
|
-
polkadot: s.isPolkadotConnected,
|
|
131
|
-
ethereum: s.isEthereumConnected,
|
|
132
|
-
solana: s.isSolanaConnected
|
|
133
|
-
})), (0, rxjs.distinctUntilChanged)((a, b) => a.polkadot === b.polkadot && a.ethereum === b.ethereum && a.solana === b.solana), (0, rxjs.tap)((connected) => {
|
|
134
|
-
for (const platform of allPlatforms) if (prevConnected[platform] && !connected[platform]) dropAccountsCache(platform);
|
|
135
|
-
prevConnected = connected;
|
|
136
|
-
}), (0, rxjs.map)((connected) => {
|
|
137
|
-
const platforms = enabledPlatforms.filter((p) => connected[p]);
|
|
138
|
-
const isConnected = platforms.length > 0;
|
|
139
|
-
const walletInfo = appKit.getWalletInfo();
|
|
140
|
-
return {
|
|
141
|
-
id: require_types.WALLET_CONNECT_WALLET_ID,
|
|
142
|
-
type: "walletconnect",
|
|
143
|
-
platforms,
|
|
144
|
-
appKit: appKitInstance,
|
|
145
|
-
name: walletInfo?.name ?? "WalletConnect",
|
|
146
|
-
icon: walletInfo?.icon ?? WALLET_CONNECT_ICON,
|
|
147
|
-
connect: async () => {
|
|
148
|
-
if (!isConnected) await appKit.open();
|
|
149
|
-
},
|
|
150
|
-
disconnect: async () => {
|
|
151
|
-
if (isConnected) await appKit.disconnect();
|
|
152
|
-
},
|
|
153
|
-
isConnected
|
|
154
|
-
};
|
|
155
|
-
})).subscribe(subscriber);
|
|
156
|
-
return () => {
|
|
157
|
-
sub.unsubscribe();
|
|
158
|
-
unsubProviders();
|
|
159
|
-
};
|
|
160
|
-
});
|
|
161
|
-
}), (0, rxjs.shareReplay)({
|
|
162
|
-
refCount: false,
|
|
163
|
-
bufferSize: 1
|
|
164
|
-
}));
|
|
165
|
-
setCachedAppKit(cachedAppKit, walletConnect.projectId);
|
|
166
|
-
}
|
|
167
|
-
return cachedAppKit;
|
|
168
|
-
};
|
|
169
|
-
//#endregion
|
|
170
8
|
//#region src/api/config.ts
|
|
171
9
|
/**
|
|
172
10
|
* Default storage key for persisting wallet connection state.
|
|
@@ -178,12 +16,14 @@ const DEFAULTS = {
|
|
|
178
16
|
autoReconnect: true,
|
|
179
17
|
debug: false,
|
|
180
18
|
storageKey: DEFAULT_STORAGE_KEY,
|
|
181
|
-
hydrationGracePeriod: 500
|
|
19
|
+
hydrationGracePeriod: 500,
|
|
20
|
+
hydrationTimeout: 3e3
|
|
182
21
|
};
|
|
183
22
|
const resolveConfig = (config) => {
|
|
184
23
|
const platforms = config?.platforms ?? [];
|
|
185
24
|
const invalidPlatforms = platforms.filter((p) => typeof p !== "object" || p === null || typeof p.getWallets$ !== "function");
|
|
186
25
|
if (invalidPlatforms.length > 0) throw new Error(`[kheopskit] config.platforms must contain plugin instances created by the per-platform factories (e.g. platforms: [polkadot(), ethereum(), solana()] imported from "@kheopskit/core/<platform>"). Invalid entries: ${JSON.stringify(invalidPlatforms)}. String platform names like "polkadot" were removed in v4 — see MIGRATING_TO_V4.md.`);
|
|
26
|
+
if (config?.walletConnect !== void 0 && typeof config.walletConnect?.getWallet$ !== "function") throw new require_types.KheopskitError("INVALID_CONFIG", "config.walletConnect must be created by walletConnect() imported from \"@kheopskit/core/walletconnect\", e.g. walletConnect: walletConnect({ projectId, metadata, networks }). Raw WalletConnect options were removed in v6, see MIGRATING_TO_V6.md.");
|
|
187
27
|
if (platforms.length === 0) console.warn("[kheopskit] No platforms configured; wallets and accounts will be empty. Pass e.g. platforms: [polkadot()] from \"@kheopskit/core/polkadot\".");
|
|
188
28
|
return Object.assign({}, DEFAULTS, config, { platforms });
|
|
189
29
|
};
|
|
@@ -194,17 +34,14 @@ const PLATFORM_ORDER$1 = {
|
|
|
194
34
|
ethereum: 1,
|
|
195
35
|
solana: 2
|
|
196
36
|
};
|
|
37
|
+
const byPlatform = (a1, a2) => PLATFORM_ORDER$1[a1.platform] - PLATFORM_ORDER$1[a2.platform];
|
|
197
38
|
const byWalletName = (a1, a2) => {
|
|
39
|
+
if (a1.walletName === a2.walletName) return 0;
|
|
198
40
|
if (a1.walletName.toLowerCase() === "talisman") return -1;
|
|
199
41
|
if (a2.walletName.toLowerCase() === "talisman") return 1;
|
|
200
42
|
return a1.walletName.localeCompare(a2.walletName);
|
|
201
43
|
};
|
|
202
|
-
const sortAccounts = (a1, a2) =>
|
|
203
|
-
if (a1.platform !== a2.platform) return PLATFORM_ORDER$1[a1.platform] - PLATFORM_ORDER$1[a2.platform];
|
|
204
|
-
if (a1.walletName !== a2.walletName) return byWalletName(a1, a2);
|
|
205
|
-
if (a1.platform === "polkadot" && a2.platform === "polkadot") return a1.name !== a2.name ? (a1.name ?? "").localeCompare(a2.name ?? "") : a1.address.localeCompare(a2.address);
|
|
206
|
-
return a1.id.localeCompare(a2.id);
|
|
207
|
-
};
|
|
44
|
+
const sortAccounts = (a1, a2) => byPlatform(a1, a2) || byWalletName(a1, a2);
|
|
208
45
|
//#endregion
|
|
209
46
|
//#region src/utils/sortWallets.ts
|
|
210
47
|
const PLATFORM_ORDER = {
|
|
@@ -233,58 +70,80 @@ const acceptsCachedAccount = (cached, platforms) => {
|
|
|
233
70
|
return platforms.find((p) => p.platform === cached.platform)?.acceptsCachedAccount?.(cached) ?? true;
|
|
234
71
|
};
|
|
235
72
|
//#endregion
|
|
73
|
+
//#region src/api/reconnectPolicy.ts
|
|
74
|
+
const createReconnectPolicy = ({ maxAttempts = 3 } = {}) => {
|
|
75
|
+
const states = /* @__PURE__ */ new Map();
|
|
76
|
+
const shouldAttempt = (walletId) => {
|
|
77
|
+
const state = states.get(walletId);
|
|
78
|
+
if (!state) return true;
|
|
79
|
+
return state.kind === "failed" && state.attempts < maxAttempts;
|
|
80
|
+
};
|
|
81
|
+
const attempt = async (walletId, connect) => {
|
|
82
|
+
if (!shouldAttempt(walletId)) return false;
|
|
83
|
+
const before = states.get(walletId);
|
|
84
|
+
states.set(walletId, { kind: "pending" });
|
|
85
|
+
const recordOutcome = (outcome) => {
|
|
86
|
+
if (states.get(walletId)?.kind !== "settled") states.set(walletId, outcome);
|
|
87
|
+
};
|
|
88
|
+
try {
|
|
89
|
+
await connect();
|
|
90
|
+
recordOutcome({ kind: "reconnected" });
|
|
91
|
+
return true;
|
|
92
|
+
} catch (err) {
|
|
93
|
+
recordOutcome({
|
|
94
|
+
kind: "failed",
|
|
95
|
+
attempts: before?.kind === "failed" ? before.attempts + 1 : 1
|
|
96
|
+
});
|
|
97
|
+
throw err;
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
const settle = (walletId) => {
|
|
101
|
+
states.set(walletId, { kind: "settled" });
|
|
102
|
+
};
|
|
103
|
+
return {
|
|
104
|
+
shouldAttempt,
|
|
105
|
+
attempt,
|
|
106
|
+
settle
|
|
107
|
+
};
|
|
108
|
+
};
|
|
109
|
+
const reconnectAccepts = (autoReconnect, wallet) => typeof autoReconnect === "function" ? autoReconnect(wallet) : autoReconnect;
|
|
110
|
+
//#endregion
|
|
236
111
|
//#region src/api/hydration.ts
|
|
237
112
|
/**
|
|
238
113
|
* Builds the cached-state snapshot every consumer must agree on: read the
|
|
239
|
-
* store's cached state, hydrate wallets/accounts into placeholders,
|
|
240
|
-
*
|
|
241
|
-
*
|
|
114
|
+
* store's cached state, hydrate wallets/accounts into placeholders, apply the
|
|
115
|
+
* `autoReconnect` rule (a cached connected wallet it rejects becomes
|
|
116
|
+
* `disconnected` and loses its cached accounts), filter accounts through each
|
|
117
|
+
* platform plugin's `acceptsCachedAccount` hook, and sort with the same
|
|
118
|
+
* comparators the live pipeline uses.
|
|
242
119
|
*
|
|
243
120
|
* This is the single implementation behind the observable's initial seed and
|
|
244
121
|
* both React snapshot sources (`serverValue`/`initialValue`). They must produce
|
|
245
122
|
* the same shape in the same order or the UI flickers/reorders on reload — by
|
|
246
123
|
* sharing this helper, that agreement holds by construction.
|
|
247
124
|
*/
|
|
248
|
-
const getHydratedSnapshot = (store,
|
|
125
|
+
const getHydratedSnapshot = (store, config, options = {}) => {
|
|
249
126
|
const cached = store.getCachedState();
|
|
127
|
+
const wallets = cached.wallets.map(require_iconCache.hydrateWallet).map((wallet) => wallet.status === "connecting" && require_types.isInjectedWallet(wallet) && !reconnectAccepts(config.autoReconnect, wallet) ? {
|
|
128
|
+
...wallet,
|
|
129
|
+
status: "disconnected"
|
|
130
|
+
} : wallet).map((wallet) => {
|
|
131
|
+
if (!options.icons || wallet.icon) return wallet;
|
|
132
|
+
const icon = require_iconCache.getCachedIcon(wallet.id);
|
|
133
|
+
return icon ? {
|
|
134
|
+
...wallet,
|
|
135
|
+
icon
|
|
136
|
+
} : wallet;
|
|
137
|
+
}).sort(sortWallets);
|
|
138
|
+
const disconnected = new Set(wallets.filter((w) => w.status === "disconnected").map((w) => w.id));
|
|
250
139
|
return {
|
|
251
|
-
wallets
|
|
252
|
-
|
|
253
|
-
if (!options.icons || wallet.icon) return wallet;
|
|
254
|
-
const icon = require_iconCache.getCachedIcon(wallet.id);
|
|
255
|
-
return icon ? {
|
|
256
|
-
...wallet,
|
|
257
|
-
icon
|
|
258
|
-
} : wallet;
|
|
259
|
-
}).sort(sortWallets),
|
|
260
|
-
accounts: cached.accounts.filter((account) => acceptsCachedAccount(account, platforms)).map(require_iconCache.hydrateAccount).sort(sortAccounts)
|
|
140
|
+
wallets,
|
|
141
|
+
accounts: cached.accounts.filter((account) => !disconnected.has(account.walletId)).filter((account) => acceptsCachedAccount(account, config.platforms)).map(require_iconCache.hydrateAccount).sort(sortAccounts)
|
|
261
142
|
};
|
|
262
143
|
};
|
|
263
144
|
//#endregion
|
|
264
145
|
//#region src/utils/createHydrationBuffer.ts
|
|
265
|
-
|
|
266
|
-
* Core hydration buffer logic shared between wallets and accounts.
|
|
267
|
-
*
|
|
268
|
-
* During the grace period:
|
|
269
|
-
* - Emits cached items immediately (before any live emissions)
|
|
270
|
-
* - As live items arrive, merges them using the provided merge function
|
|
271
|
-
*
|
|
272
|
-
* After the grace period (and convergence, if provided):
|
|
273
|
-
* - Only live items are returned
|
|
274
|
-
* - isHydrating becomes false
|
|
275
|
-
*
|
|
276
|
-
* If an `isConverged` function is provided, hydration ends only when BOTH
|
|
277
|
-
* the grace period has elapsed AND the live items have converged with cached
|
|
278
|
-
* items (e.g., all previously-connected wallets have reconnected).
|
|
279
|
-
* A max timeout (6x grace period) prevents infinite hydration.
|
|
280
|
-
*
|
|
281
|
-
* @param cachedItems - Initial cached items from storage
|
|
282
|
-
* @param liveItems$ - Observable of live items as they become available
|
|
283
|
-
* @param gracePeriodMs - Time in ms to wait before syncing to live state
|
|
284
|
-
* @param mergeFn - Function to merge live and cached items during hydration
|
|
285
|
-
* @param isConverged - Optional function to check if live items have caught up to cached items
|
|
286
|
-
*/
|
|
287
|
-
const createBufferCore = (cachedItems, liveItems$, gracePeriodMs, mergeFn, isConverged) => {
|
|
146
|
+
const createBufferCore = (cachedItems, liveItems$, gracePeriodMs, timeoutMs, mergeFn, settled$) => {
|
|
288
147
|
if (gracePeriodMs <= 0 || cachedItems.length === 0) return liveItems$.pipe((0, rxjs.map)((items) => ({
|
|
289
148
|
items,
|
|
290
149
|
isHydrating: false
|
|
@@ -292,30 +151,19 @@ const createBufferCore = (cachedItems, liveItems$, gracePeriodMs, mergeFn, isCon
|
|
|
292
151
|
return new rxjs.Observable((subscriber) => {
|
|
293
152
|
const subscriptions = new rxjs.Subscription();
|
|
294
153
|
const isHydrating$ = new rxjs.BehaviorSubject(true);
|
|
295
|
-
const
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
isHydrating$.next(false);
|
|
309
|
-
}));
|
|
310
|
-
subscriptions.add((0, rxjs.combineLatest)([liveWithInitial$, isHydrating$]).pipe((0, rxjs.map)(([liveItems, isHydrating]) => {
|
|
311
|
-
if (!isHydrating) return {
|
|
312
|
-
items: liveItems,
|
|
313
|
-
isHydrating: false
|
|
314
|
-
};
|
|
315
|
-
return {
|
|
316
|
-
items: mergeFn(liveItems, cachedItems),
|
|
317
|
-
isHydrating: true
|
|
318
|
-
};
|
|
154
|
+
const endHydration = () => {
|
|
155
|
+
if (isHydrating$.value) isHydrating$.next(false);
|
|
156
|
+
};
|
|
157
|
+
const graceElapsed$ = (0, rxjs.timer)(gracePeriodMs).pipe((0, rxjs.map)(() => true), (0, rxjs.startWith)(false));
|
|
158
|
+
const ended$ = settled$ ? (0, rxjs.combineLatest)([graceElapsed$, settled$]).pipe((0, rxjs.filter)(([graceElapsed, settled]) => graceElapsed && settled), (0, rxjs.take)(1)) : (0, rxjs.timer)(gracePeriodMs);
|
|
159
|
+
subscriptions.add(ended$.subscribe(endHydration));
|
|
160
|
+
subscriptions.add((0, rxjs.timer)(timeoutMs).subscribe(endHydration));
|
|
161
|
+
subscriptions.add((0, rxjs.combineLatest)([liveItems$.pipe((0, rxjs.startWith)([])), isHydrating$]).pipe((0, rxjs.map)(([liveItems, isHydrating]) => isHydrating ? {
|
|
162
|
+
items: mergeFn(liveItems, cachedItems),
|
|
163
|
+
isHydrating
|
|
164
|
+
} : {
|
|
165
|
+
items: liveItems,
|
|
166
|
+
isHydrating
|
|
319
167
|
})).subscribe(subscriber));
|
|
320
168
|
return () => {
|
|
321
169
|
subscriptions.unsubscribe();
|
|
@@ -332,14 +180,15 @@ const createBufferCore = (cachedItems, liveItems$, gracePeriodMs, mergeFn, isCon
|
|
|
332
180
|
*
|
|
333
181
|
* @param cachedItems - Initial cached items from storage
|
|
334
182
|
* @param liveItems$ - Observable of live items as they become available
|
|
335
|
-
* @param gracePeriodMs -
|
|
183
|
+
* @param gracePeriodMs - Minimum time in ms the cached items are shown
|
|
184
|
+
* @param timeoutMs - Upper bound in ms on hydration
|
|
336
185
|
* @param getKey - Function to extract a unique key from an item
|
|
337
|
-
* @param
|
|
186
|
+
* @param settled$ - Whether the live state has caught up with the cache
|
|
338
187
|
* @param mergeItem - Optional function to merge a live item with its cached counterpart
|
|
339
188
|
* @param transformCachedOnly - Optional function to transform cached items not yet in the live stream
|
|
340
189
|
*/
|
|
341
|
-
const createHydrationBuffer = (cachedItems, liveItems$, gracePeriodMs,
|
|
342
|
-
return createBufferCore(cachedItems, liveItems$, gracePeriodMs, (liveItems, cached) => {
|
|
190
|
+
const createHydrationBuffer = (cachedItems, liveItems$, gracePeriodMs, timeoutMs, getKey, settled$, mergeItem, transformCachedOnly) => {
|
|
191
|
+
return createBufferCore(cachedItems, liveItems$, gracePeriodMs, timeoutMs, (liveItems, cached) => {
|
|
343
192
|
const cachedByKey = new Map(cached.map((item) => [getKey(item), item]));
|
|
344
193
|
const liveKeys = new Set(liveItems.map(getKey));
|
|
345
194
|
const mergedItems = liveItems.map((liveItem) => {
|
|
@@ -353,7 +202,7 @@ const createHydrationBuffer = (cachedItems, liveItems$, gracePeriodMs, getKey, i
|
|
|
353
202
|
mergedItems.push(transformed);
|
|
354
203
|
}
|
|
355
204
|
return mergedItems;
|
|
356
|
-
},
|
|
205
|
+
}, settled$);
|
|
357
206
|
};
|
|
358
207
|
/**
|
|
359
208
|
* Creates a hydration buffer specifically for accounts, which need special handling.
|
|
@@ -364,17 +213,18 @@ const createHydrationBuffer = (cachedItems, liveItems$, gracePeriodMs, getKey, i
|
|
|
364
213
|
*
|
|
365
214
|
* @param cachedAccounts - Initial cached accounts from storage
|
|
366
215
|
* @param liveAccounts$ - Observable of live accounts as they become available
|
|
367
|
-
* @param gracePeriodMs -
|
|
216
|
+
* @param gracePeriodMs - Minimum time in ms the cached accounts are shown
|
|
217
|
+
* @param timeoutMs - Upper bound in ms on hydration
|
|
368
218
|
* @param getWalletId - Function to extract the wallet ID from an account
|
|
369
|
-
* @param
|
|
219
|
+
* @param settled$ - Whether the live state has caught up with the cache
|
|
370
220
|
*/
|
|
371
|
-
const createAccountHydrationBuffer = (cachedAccounts, liveAccounts$, gracePeriodMs, getWalletId,
|
|
372
|
-
return createBufferCore(cachedAccounts, liveAccounts$, gracePeriodMs, (liveAccounts, cached) => {
|
|
221
|
+
const createAccountHydrationBuffer = (cachedAccounts, liveAccounts$, gracePeriodMs, timeoutMs, getWalletId, settled$) => {
|
|
222
|
+
return createBufferCore(cachedAccounts, liveAccounts$, gracePeriodMs, timeoutMs, (liveAccounts, cached) => {
|
|
373
223
|
const walletsWithLiveAccounts = new Set(liveAccounts.map(getWalletId));
|
|
374
224
|
const mergedAccounts = [...liveAccounts];
|
|
375
225
|
for (const cachedAccount of cached) if (!walletsWithLiveAccounts.has(getWalletId(cachedAccount))) mergedAccounts.push(cachedAccount);
|
|
376
226
|
return mergedAccounts;
|
|
377
|
-
},
|
|
227
|
+
}, settled$);
|
|
378
228
|
};
|
|
379
229
|
//#endregion
|
|
380
230
|
//#region src/utils/logObservable.ts
|
|
@@ -389,8 +239,14 @@ const logObservable = (label, opts) => (0, rxjs.tap)((value) => {
|
|
|
389
239
|
//#region src/api/accounts.ts
|
|
390
240
|
const getAccounts$ = (config, wallets) => {
|
|
391
241
|
return new rxjs.Observable((subscriber) => {
|
|
392
|
-
const sources = config.platforms.map((plugin) => plugin.getAccounts$(wallets.pipe((0, rxjs.map)((ws) => ws.filter((w) => require_types.isWalletConnectWallet(w) || w.platform === plugin.platform)))));
|
|
393
|
-
const sub = (sources.length ? (0, rxjs.combineLatest)(sources).pipe((0, rxjs.map)((
|
|
242
|
+
const sources = config.platforms.map((plugin) => require_platformAccounts.framesOf(plugin.getAccounts$(wallets.pipe((0, rxjs.map)((ws) => ws.filter((w) => require_types.isWalletConnectWallet(w) || w.platform === plugin.platform))))));
|
|
243
|
+
const sub = (sources.length ? (0, rxjs.combineLatest)(sources).pipe((0, rxjs.map)((frames) => ({
|
|
244
|
+
accounts: frames.flatMap((frame) => frame.accounts).sort(sortAccounts),
|
|
245
|
+
answered: frames.flatMap((frame) => frame.answered)
|
|
246
|
+
}))) : (0, rxjs.of)({
|
|
247
|
+
accounts: [],
|
|
248
|
+
answered: []
|
|
249
|
+
})).subscribe(subscriber);
|
|
394
250
|
return () => {
|
|
395
251
|
sub.unsubscribe();
|
|
396
252
|
};
|
|
@@ -401,28 +257,41 @@ const getAccounts$ = (config, wallets) => {
|
|
|
401
257
|
};
|
|
402
258
|
//#endregion
|
|
403
259
|
//#region src/utils/createStore.ts
|
|
404
|
-
const createStore = (key, defaultValue, storage = require_iconCache.safeLocalStorage) => {
|
|
405
|
-
const
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
260
|
+
const createStore = (key, defaultValue, storage = require_iconCache.safeLocalStorage, normalize = (value) => value) => {
|
|
261
|
+
const read = (str) => normalize(parseData(str, defaultValue));
|
|
262
|
+
const syncsAcrossTabs = typeof window !== "undefined" && storage.subscribe !== void 0;
|
|
263
|
+
const subject = new rxjs.BehaviorSubject(read(storage.getItem(key)));
|
|
264
|
+
let observers = 0;
|
|
265
|
+
let stopSync;
|
|
266
|
+
const latestValue = () => {
|
|
267
|
+
if (syncsAcrossTabs && observers === 0) subject.next(read(storage.getItem(key)));
|
|
268
|
+
return subject.getValue();
|
|
269
|
+
};
|
|
270
|
+
const observable = new rxjs.Observable((subscriber) => {
|
|
271
|
+
if (observers === 0 && syncsAcrossTabs) {
|
|
272
|
+
latestValue();
|
|
273
|
+
stopSync = storage.subscribe?.(key, (newValue) => {
|
|
274
|
+
subject.next(read(newValue));
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
observers++;
|
|
278
|
+
const subscription = subject.subscribe(subscriber);
|
|
279
|
+
return () => {
|
|
280
|
+
subscription.unsubscribe();
|
|
281
|
+
observers--;
|
|
282
|
+
if (observers === 0) stopSync?.();
|
|
283
|
+
};
|
|
409
284
|
});
|
|
410
285
|
const update = (val) => {
|
|
411
|
-
|
|
286
|
+
persistBestEffort(key, val, storage);
|
|
412
287
|
subject.next(val);
|
|
413
288
|
};
|
|
414
289
|
return {
|
|
415
|
-
observable
|
|
290
|
+
observable,
|
|
416
291
|
set: (val) => update(val),
|
|
417
|
-
mutate: (transform) => update(transform(
|
|
418
|
-
get: () => structuredClone(
|
|
419
|
-
|
|
420
|
-
* Cleanup subscriptions. Call this when the store is no longer needed.
|
|
421
|
-
*/
|
|
422
|
-
destroy: () => {
|
|
423
|
-
unsubscribeStorage?.();
|
|
424
|
-
subject.complete();
|
|
425
|
-
}
|
|
292
|
+
mutate: (transform) => update(transform(latestValue())),
|
|
293
|
+
get: () => structuredClone(latestValue()),
|
|
294
|
+
destroy: () => subject.complete()
|
|
426
295
|
};
|
|
427
296
|
};
|
|
428
297
|
const parseData = (str, defaultValue) => {
|
|
@@ -431,17 +300,23 @@ const parseData = (str, defaultValue) => {
|
|
|
431
300
|
} catch {}
|
|
432
301
|
return defaultValue;
|
|
433
302
|
};
|
|
434
|
-
const
|
|
435
|
-
const str = storage.getItem(key);
|
|
436
|
-
return parseData(str, defaultValue);
|
|
437
|
-
};
|
|
438
|
-
const setStoredData = (key, val, storage) => {
|
|
303
|
+
const persistBestEffort = (key, val, storage) => {
|
|
439
304
|
const str = JSON.stringify(val);
|
|
440
|
-
|
|
305
|
+
try {
|
|
306
|
+
storage.setItem(key, str);
|
|
307
|
+
} catch {}
|
|
441
308
|
};
|
|
442
309
|
//#endregion
|
|
443
310
|
//#region src/api/compactStore.ts
|
|
444
311
|
const EMPTY_STORE_DATA = {};
|
|
312
|
+
const migratePreV6StoreData = (data) => {
|
|
313
|
+
if (data?.autoReconnect === void 0) return data;
|
|
314
|
+
const { autoReconnect, ...rest } = data;
|
|
315
|
+
return rest.remembered === void 0 && Array.isArray(autoReconnect) ? {
|
|
316
|
+
...rest,
|
|
317
|
+
remembered: autoReconnect
|
|
318
|
+
} : rest;
|
|
319
|
+
};
|
|
445
320
|
const toCompactPolkadotAccountType = (type) => {
|
|
446
321
|
switch (type) {
|
|
447
322
|
case "sr25519": return 0;
|
|
@@ -497,7 +372,7 @@ const toCompactStore = (data) => {
|
|
|
497
372
|
]);
|
|
498
373
|
return {
|
|
499
374
|
v: 1,
|
|
500
|
-
r: data.
|
|
375
|
+
r: data.remembered,
|
|
501
376
|
w: wallets?.length ? wallets : void 0,
|
|
502
377
|
a: accounts?.length ? accounts : void 0
|
|
503
378
|
};
|
|
@@ -529,7 +404,7 @@ const fromCompactStore = (data) => {
|
|
|
529
404
|
const platform = platformCode != null ? fromCompactPlatform(platformCode) : walletId === "walletconnect" ? void 0 : require_types.parseWalletId(walletId).platform;
|
|
530
405
|
if (!require_types.isWalletPlatform(platform)) continue;
|
|
531
406
|
accounts.push({
|
|
532
|
-
id:
|
|
407
|
+
id: require_platformAccounts.getWalletAccountId(walletId, address),
|
|
533
408
|
platform,
|
|
534
409
|
address,
|
|
535
410
|
name: name ?? void 0,
|
|
@@ -540,7 +415,7 @@ const fromCompactStore = (data) => {
|
|
|
540
415
|
});
|
|
541
416
|
}
|
|
542
417
|
return {
|
|
543
|
-
|
|
418
|
+
remembered: data.r,
|
|
544
419
|
cachedWallets: wallets,
|
|
545
420
|
cachedAccounts: accounts
|
|
546
421
|
};
|
|
@@ -549,7 +424,7 @@ const decodeStore = (raw, fallback) => {
|
|
|
549
424
|
try {
|
|
550
425
|
const parsed = JSON.parse(raw);
|
|
551
426
|
if (isCompactStore(parsed)) return fromCompactStore(parsed);
|
|
552
|
-
return parsed;
|
|
427
|
+
return migratePreV6StoreData(parsed);
|
|
553
428
|
} catch {
|
|
554
429
|
return fallback;
|
|
555
430
|
}
|
|
@@ -617,26 +492,26 @@ const isValidCachedAccount = (value) => {
|
|
|
617
492
|
};
|
|
618
493
|
/**
|
|
619
494
|
* Creates a kheopskit store with the appropriate storage backend.
|
|
620
|
-
* Uses
|
|
621
|
-
*
|
|
495
|
+
* Uses the given `storage` when provided, else cookieStorage when ssrCookies is
|
|
496
|
+
* provided (for SSR hydration), otherwise safeLocalStorage.
|
|
622
497
|
*
|
|
623
498
|
* @param options - Configuration options for the store
|
|
624
499
|
*/
|
|
625
500
|
const createKheopskitStore = (options = {}) => {
|
|
626
501
|
const { ssrCookies, storageKey = DEFAULT_STORAGE_KEY } = options;
|
|
627
|
-
const storage = ssrCookies !== void 0 ? createCompactCookieStorage(ssrCookies) : require_iconCache.safeLocalStorage;
|
|
628
|
-
const store = createStore(storageKey, DEFAULT_SETTINGS, storage);
|
|
629
|
-
const
|
|
502
|
+
const storage = options.storage ?? (ssrCookies !== void 0 ? createCompactCookieStorage(ssrCookies) : require_iconCache.safeLocalStorage);
|
|
503
|
+
const store = createStore(storageKey, DEFAULT_SETTINGS, storage, migratePreV6StoreData);
|
|
504
|
+
const rememberWallet = (walletId) => {
|
|
630
505
|
require_types.parseWalletId(walletId);
|
|
631
506
|
store.mutate((prev) => ({
|
|
632
507
|
...prev,
|
|
633
|
-
|
|
508
|
+
remembered: (0, lodash_es.uniq)((prev.remembered ?? []).concat(walletId))
|
|
634
509
|
}));
|
|
635
510
|
};
|
|
636
|
-
const
|
|
511
|
+
const forgetWallet = (walletId) => {
|
|
637
512
|
store.mutate((prev) => ({
|
|
638
513
|
...prev,
|
|
639
|
-
|
|
514
|
+
remembered: (0, lodash_es.uniq)((prev.remembered ?? []).filter((id) => id !== walletId))
|
|
640
515
|
}));
|
|
641
516
|
};
|
|
642
517
|
const getCachedState = () => {
|
|
@@ -657,10 +532,11 @@ const createKheopskitStore = (options = {}) => {
|
|
|
657
532
|
};
|
|
658
533
|
return {
|
|
659
534
|
observable: store.observable,
|
|
660
|
-
|
|
661
|
-
|
|
535
|
+
rememberWallet,
|
|
536
|
+
forgetWallet,
|
|
662
537
|
getCachedState,
|
|
663
|
-
setCachedState
|
|
538
|
+
setCachedState,
|
|
539
|
+
destroy: store.destroy
|
|
664
540
|
};
|
|
665
541
|
};
|
|
666
542
|
/**
|
|
@@ -680,43 +556,9 @@ const getDefaultStore = () => {
|
|
|
680
556
|
return g[DEFAULT_STORE_SYMBOL];
|
|
681
557
|
};
|
|
682
558
|
//#endregion
|
|
683
|
-
//#region src/api/reconnectPolicy.ts
|
|
684
|
-
const createReconnectPolicy = ({ maxAttempts = 3 } = {}) => {
|
|
685
|
-
const reconnecting = /* @__PURE__ */ new Set();
|
|
686
|
-
const reconnected = /* @__PURE__ */ new Set();
|
|
687
|
-
const failedAttempts = /* @__PURE__ */ new Map();
|
|
688
|
-
const shouldAttempt = (walletId) => !reconnecting.has(walletId) && !reconnected.has(walletId) && (failedAttempts.get(walletId) ?? 0) < maxAttempts;
|
|
689
|
-
/**
|
|
690
|
-
* Runs `connect` under the policy. Resolves `false` without connecting when
|
|
691
|
-
* an attempt is already in flight, the wallet already reconnected, or it
|
|
692
|
-
* exhausted its attempts; resolves `true` on success. A rejection from
|
|
693
|
-
* `connect` is recorded as a failed attempt and rethrown for the caller to
|
|
694
|
-
* report.
|
|
695
|
-
*/
|
|
696
|
-
const attempt = async (walletId, connect) => {
|
|
697
|
-
if (!shouldAttempt(walletId)) return false;
|
|
698
|
-
reconnecting.add(walletId);
|
|
699
|
-
try {
|
|
700
|
-
await connect();
|
|
701
|
-
reconnected.add(walletId);
|
|
702
|
-
failedAttempts.delete(walletId);
|
|
703
|
-
return true;
|
|
704
|
-
} catch (err) {
|
|
705
|
-
failedAttempts.set(walletId, (failedAttempts.get(walletId) ?? 0) + 1);
|
|
706
|
-
throw err;
|
|
707
|
-
} finally {
|
|
708
|
-
reconnecting.delete(walletId);
|
|
709
|
-
}
|
|
710
|
-
};
|
|
711
|
-
return {
|
|
712
|
-
shouldAttempt,
|
|
713
|
-
attempt
|
|
714
|
-
};
|
|
715
|
-
};
|
|
716
|
-
//#endregion
|
|
717
559
|
//#region src/api/wallets.ts
|
|
718
560
|
const getWallets$ = (config, store) => {
|
|
719
|
-
const
|
|
561
|
+
const rememberedWalletIds$ = store.observable.pipe((0, rxjs.map)((s) => s.remembered ?? []), (0, rxjs.take)(1), (0, rxjs.shareReplay)({
|
|
720
562
|
bufferSize: 1,
|
|
721
563
|
refCount: true
|
|
722
564
|
}));
|
|
@@ -726,20 +568,39 @@ const getWallets$ = (config, store) => {
|
|
|
726
568
|
store
|
|
727
569
|
};
|
|
728
570
|
const observables = config.platforms.map((plugin) => plugin.getWallets$(ctx));
|
|
729
|
-
const platformWallets$ = observables.length ? (0, rxjs.combineLatest)(observables).pipe((0, rxjs.map)((wallets) => wallets.flat())) : (0, rxjs.of)([]);
|
|
730
|
-
const wallets$ = (0, rxjs.combineLatest)([platformWallets$, getWalletConnectWallet$(config)]).pipe((0, rxjs.map)(([platformWallets, walletConnect]) => {
|
|
731
|
-
return (walletConnect ? [...platformWallets, walletConnect] : platformWallets).sort(sortWallets);
|
|
732
|
-
}));
|
|
733
571
|
const reconnectPolicy = createReconnectPolicy();
|
|
734
|
-
const
|
|
735
|
-
|
|
572
|
+
const settleOnUserCall = (wallet) => ({
|
|
573
|
+
...wallet,
|
|
574
|
+
connect: (options) => {
|
|
575
|
+
reconnectPolicy.settle(wallet.id);
|
|
576
|
+
return wallet.connect(options);
|
|
577
|
+
},
|
|
578
|
+
disconnect: () => {
|
|
579
|
+
reconnectPolicy.settle(wallet.id);
|
|
580
|
+
return wallet.disconnect();
|
|
581
|
+
}
|
|
582
|
+
});
|
|
583
|
+
const platformWallets$ = observables.length ? (0, rxjs.combineLatest)(observables).pipe((0, rxjs.map)((wallets) => wallets.flat())) : (0, rxjs.of)([]);
|
|
584
|
+
const walletConnect$ = config.walletConnect?.getWallet$(ctx);
|
|
585
|
+
const rawWallets$ = (walletConnect$ ? (0, rxjs.combineLatest)([platformWallets$, walletConnect$]).pipe((0, rxjs.map)(([platformWallets, walletConnect]) => walletConnect ? [...platformWallets, walletConnect] : platformWallets)) : platformWallets$).pipe((0, rxjs.map)((all) => all.sort(sortWallets)));
|
|
586
|
+
const wallets$ = rawWallets$.pipe((0, rxjs.map)((wallets) => wallets.map((wallet) => require_types.isInjectedWallet(wallet) ? settleOnUserCall(wallet) : wallet)));
|
|
587
|
+
const reconnect = async (wallet) => {
|
|
736
588
|
try {
|
|
737
589
|
await reconnectPolicy.attempt(wallet.id, () => wallet.connect());
|
|
738
590
|
} catch (err) {
|
|
739
|
-
|
|
591
|
+
if (subscriber.closed) return;
|
|
592
|
+
if (err instanceof require_types.KheopskitError) {
|
|
593
|
+
if (err.code === "USER_REJECTED") store.forgetWallet(wallet.id);
|
|
594
|
+
if (err.code === "USER_REJECTED" || err.code === "ABORTED") reconnectPolicy.settle(wallet.id);
|
|
595
|
+
}
|
|
596
|
+
if (config.debug) console.debug("[kheopskit] reconnect of %s failed", wallet.id, err);
|
|
740
597
|
}
|
|
741
|
-
}
|
|
598
|
+
};
|
|
742
599
|
const subWallets = wallets$.subscribe(subscriber);
|
|
600
|
+
if (subscriber.closed) return subWallets;
|
|
601
|
+
const subAutoReconnect = (0, rxjs.combineLatest)([rawWallets$, rememberedWalletIds$]).pipe((0, rxjs.filter)(([, walletIds]) => config.autoReconnect !== false && walletIds.length > 0), (0, rxjs.mergeMap)(([wallets, walletIds]) => wallets.filter(require_types.isInjectedWallet).filter((wallet) => walletIds.includes(wallet.id))), (0, rxjs.tap)((wallet) => {
|
|
602
|
+
if (wallet.status === "connected") reconnectPolicy.settle(wallet.id);
|
|
603
|
+
}), (0, rxjs.filter)((wallet) => wallet.status === "disconnected" && reconnectAccepts(config.autoReconnect, wallet))).subscribe(reconnect);
|
|
743
604
|
return () => {
|
|
744
605
|
subAutoReconnect.unsubscribe();
|
|
745
606
|
subWallets.unsubscribe();
|
|
@@ -760,33 +621,129 @@ const getKheopskit$ = (config, options = {}) => {
|
|
|
760
621
|
});
|
|
761
622
|
if (kc.debug) console.debug("[kheopskit] config", kc);
|
|
762
623
|
if (kc.debug && typeof window === "undefined" && ssrCookies === void 0) console.warn("[kheopskit] Running on server without `ssrCookies`. Wallet state will not be hydrated. Pass cookies for SSR support.");
|
|
763
|
-
const { wallets: cachedWallets, accounts: cachedAccounts } = getHydratedSnapshot(store, kc
|
|
624
|
+
const { wallets: cachedWallets, accounts: cachedAccounts } = getHydratedSnapshot(store, kc, { icons: true });
|
|
764
625
|
if (kc.debug && cachedWallets.length > 0) console.debug("[kheopskit] hydrating from cache:", {
|
|
765
626
|
wallets: cachedWallets.length,
|
|
766
627
|
accounts: cachedAccounts.length
|
|
767
628
|
});
|
|
768
629
|
return new rxjs.Observable((subscriber) => {
|
|
630
|
+
let latest;
|
|
631
|
+
let closed = false;
|
|
632
|
+
const waiters = /* @__PURE__ */ new Set();
|
|
633
|
+
const liveWaiters = /* @__PURE__ */ new Set();
|
|
634
|
+
const wrapped = /* @__PURE__ */ new WeakMap();
|
|
635
|
+
const abandon = (waiter) => {
|
|
636
|
+
waiters.delete(waiter);
|
|
637
|
+
if (waiter.isConnected) waiter.reject(require_types.abortedError(waiter.walletId, "connect"));
|
|
638
|
+
else waiter.resolve();
|
|
639
|
+
};
|
|
640
|
+
const settleIn = (waiter, state) => {
|
|
641
|
+
const wallet = state.wallets.find((w) => w.id === waiter.walletId);
|
|
642
|
+
if (!wallet || wallet.isConnected === waiter.isConnected) {
|
|
643
|
+
waiters.delete(waiter);
|
|
644
|
+
waiter.resolve();
|
|
645
|
+
}
|
|
646
|
+
};
|
|
647
|
+
const supersede = (walletId, isConnected) => {
|
|
648
|
+
for (const waiter of waiters) if (waiter.walletId === walletId && waiter.isConnected !== isConnected) abandon(waiter);
|
|
649
|
+
};
|
|
650
|
+
const whenState = (walletId, isConnected) => new Promise((resolve, reject) => {
|
|
651
|
+
const waiter = {
|
|
652
|
+
walletId,
|
|
653
|
+
isConnected,
|
|
654
|
+
resolve,
|
|
655
|
+
reject
|
|
656
|
+
};
|
|
657
|
+
waiters.add(waiter);
|
|
658
|
+
if (latest) settleIn(waiter, latest);
|
|
659
|
+
if (closed && waiters.has(waiter)) abandon(waiter);
|
|
660
|
+
});
|
|
661
|
+
const whenLive = (walletId, signal) => new Promise((resolve, reject) => {
|
|
662
|
+
const settle = (finish) => {
|
|
663
|
+
liveWaiters.delete(check);
|
|
664
|
+
signal?.removeEventListener("abort", onAbort);
|
|
665
|
+
finish();
|
|
666
|
+
};
|
|
667
|
+
const onAbort = () => settle(() => reject(require_types.abortedError(walletId, "connect")));
|
|
668
|
+
const check = (state) => {
|
|
669
|
+
if (!state) return onAbort();
|
|
670
|
+
const live = state.wallets.find((w) => w.id === walletId && !w.isPlaceholder);
|
|
671
|
+
if (live) settle(() => resolve(live));
|
|
672
|
+
else if (!state.isHydrating) settle(() => reject(require_types.walletPendingError(walletId)));
|
|
673
|
+
};
|
|
674
|
+
if (closed || signal?.aborted) return onAbort();
|
|
675
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
676
|
+
liveWaiters.add(check);
|
|
677
|
+
if (latest) check(latest);
|
|
678
|
+
});
|
|
679
|
+
const deferToLive = (placeholder) => ({
|
|
680
|
+
...placeholder,
|
|
681
|
+
connect: async (options) => {
|
|
682
|
+
const live = await whenLive(placeholder.id, options?.signal);
|
|
683
|
+
if (closed) throw require_types.abortedError(placeholder.id, "connect");
|
|
684
|
+
await live.connect(options);
|
|
685
|
+
}
|
|
686
|
+
});
|
|
687
|
+
const settleWithState = (wallet) => {
|
|
688
|
+
const existing = wrapped.get(wallet);
|
|
689
|
+
if (existing) return existing;
|
|
690
|
+
if (wallet.isPlaceholder) {
|
|
691
|
+
const deferred = deferToLive(wallet);
|
|
692
|
+
wrapped.set(wallet, deferred);
|
|
693
|
+
return deferred;
|
|
694
|
+
}
|
|
695
|
+
if (!require_types.isInjectedWallet(wallet)) return wallet;
|
|
696
|
+
const settled = {
|
|
697
|
+
...wallet,
|
|
698
|
+
connect: async (options) => {
|
|
699
|
+
if (closed) throw require_types.abortedError(wallet.id, "connect");
|
|
700
|
+
await wallet.connect(options);
|
|
701
|
+
supersede(wallet.id, true);
|
|
702
|
+
await whenState(wallet.id, true);
|
|
703
|
+
},
|
|
704
|
+
disconnect: async () => {
|
|
705
|
+
if (closed) throw require_types.abortedError(wallet.id, "disconnect");
|
|
706
|
+
await wallet.disconnect();
|
|
707
|
+
supersede(wallet.id, false);
|
|
708
|
+
await whenState(wallet.id, false);
|
|
709
|
+
}
|
|
710
|
+
};
|
|
711
|
+
wrapped.set(wallet, settled);
|
|
712
|
+
return settled;
|
|
713
|
+
};
|
|
769
714
|
const liveWallets$ = getWallets$(kc, store);
|
|
770
|
-
const
|
|
771
|
-
const
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
}
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
715
|
+
const accountFrames$ = getAccounts$(kc, liveWallets$);
|
|
716
|
+
const liveAccounts$ = accountFrames$.pipe((0, rxjs.map)(({ accounts }) => accounts.map(asLive)));
|
|
717
|
+
const isDiscovering$ = require_pageLoad.pageLoaded$.pipe((0, rxjs.switchMap)((loaded) => loaded ? liveWallets$.pipe((0, rxjs.take)(1), (0, rxjs.map)(() => false)) : (0, rxjs.of)(true)), (0, rxjs.shareReplay)({
|
|
718
|
+
bufferSize: 1,
|
|
719
|
+
refCount: true
|
|
720
|
+
}));
|
|
721
|
+
const reconnecting = cachedWallets.filter((w) => w.status === "connecting");
|
|
722
|
+
const hydrationSettled$ = (0, rxjs.combineLatest)([
|
|
723
|
+
liveWallets$,
|
|
724
|
+
accountFrames$.pipe((0, rxjs.startWith)(noAnswerYet)),
|
|
725
|
+
isDiscovering$,
|
|
726
|
+
store.observable.pipe((0, rxjs.map)((s) => s.remembered ?? []), (0, rxjs.take)(1))
|
|
727
|
+
]).pipe((0, rxjs.map)(([wallets, { accounts, answered }, isDiscovering, remembered]) => !isDiscovering && reconnecting.every((cached) => reconnectSettled(cached, {
|
|
728
|
+
wallets,
|
|
729
|
+
accounts,
|
|
730
|
+
answered,
|
|
731
|
+
cachedAccounts,
|
|
732
|
+
remembered
|
|
733
|
+
}))), (0, rxjs.debounceTime)(0, rxjs.asapScheduler), (0, rxjs.distinctUntilChanged)());
|
|
734
|
+
const bufferedWallets$ = createHydrationBuffer(cachedWallets, liveWallets$.pipe((0, rxjs.map)((wallets) => wallets.map(asLive))), kc.hydrationGracePeriod, kc.hydrationTimeout, (w) => w.id, hydrationSettled$, (live, cached) => {
|
|
735
|
+
const status = sessionRestoring(live, cached) ? "connecting" : live.status;
|
|
736
|
+
return {
|
|
737
|
+
...live,
|
|
738
|
+
status,
|
|
739
|
+
isConnected: status === "connected",
|
|
740
|
+
icon: cached.icon || require_iconCache.getCachedIcon(cached.id) || live.icon
|
|
741
|
+
};
|
|
742
|
+
}, (cached) => ({
|
|
781
743
|
...cached,
|
|
782
744
|
icon: cached.icon || require_iconCache.getCachedIcon(cached.id) || ""
|
|
783
745
|
}));
|
|
784
|
-
const bufferedAccounts$ = createAccountHydrationBuffer(cachedAccounts, liveAccounts$, kc.hydrationGracePeriod, (a) => a.walletId,
|
|
785
|
-
const cachedWalletIds = new Set(cached.map((a) => a.walletId));
|
|
786
|
-
if (cachedWalletIds.size === 0) return true;
|
|
787
|
-
const liveWalletIds = new Set(liveAccounts.map((a) => a.walletId));
|
|
788
|
-
return [...cachedWalletIds].every((id) => liveWalletIds.has(id));
|
|
789
|
-
});
|
|
746
|
+
const bufferedAccounts$ = createAccountHydrationBuffer(cachedAccounts, liveAccounts$, kc.hydrationGracePeriod, kc.hydrationTimeout, (a) => a.walletId, hydrationSettled$);
|
|
790
747
|
const sharedWallets$ = bufferedWallets$.pipe((0, rxjs.shareReplay)({
|
|
791
748
|
bufferSize: 1,
|
|
792
749
|
refCount: true
|
|
@@ -797,38 +754,44 @@ const getKheopskit$ = (config, options = {}) => {
|
|
|
797
754
|
}));
|
|
798
755
|
const subscription = (0, rxjs.combineLatest)({
|
|
799
756
|
wallets: sharedWallets$,
|
|
800
|
-
accounts: sharedAccounts
|
|
801
|
-
|
|
757
|
+
accounts: sharedAccounts$,
|
|
758
|
+
isDiscovering: isDiscovering$
|
|
759
|
+
}).pipe((0, rxjs.map)(({ wallets, accounts, isDiscovering }) => {
|
|
802
760
|
if (kc.debug) console.debug("[kheopskit] hydration state", {
|
|
803
761
|
walletsHydrating: wallets.isHydrating,
|
|
804
762
|
accountsHydrating: accounts.isHydrating,
|
|
763
|
+
isDiscovering,
|
|
805
764
|
walletsConnected: wallets.items.filter((w) => w.isConnected).length,
|
|
806
765
|
walletsTotal: wallets.items.length
|
|
807
766
|
});
|
|
808
767
|
return {
|
|
809
768
|
config: kc,
|
|
810
|
-
wallets: [...wallets.items].sort(sortWallets),
|
|
769
|
+
wallets: [...wallets.items].sort(sortWallets).map(settleWithState),
|
|
811
770
|
accounts: [...accounts.items].sort(sortAccounts),
|
|
812
|
-
isHydrating: wallets.isHydrating || accounts.isHydrating
|
|
771
|
+
isHydrating: wallets.isHydrating || accounts.isHydrating,
|
|
772
|
+
isDiscovering
|
|
813
773
|
};
|
|
774
|
+
}), (0, rxjs.debounceTime)(0, rxjs.asapScheduler), (0, rxjs.distinctUntilChanged)(statesEqual), (0, rxjs.tap)((state) => {
|
|
775
|
+
latest = state;
|
|
776
|
+
for (const check of liveWaiters) check(state);
|
|
777
|
+
for (const waiter of waiters) settleIn(waiter, state);
|
|
814
778
|
})).subscribe(subscriber);
|
|
815
779
|
const persistSub = (0, rxjs.combineLatest)({
|
|
816
780
|
wallets: sharedWallets$,
|
|
817
781
|
accounts: sharedAccounts$
|
|
818
782
|
}).pipe((0, rxjs.filter)(({ wallets, accounts }) => !wallets.isHydrating && !accounts.isHydrating), (0, rxjs.debounceTime)(1e3), (0, rxjs.distinctUntilChanged)((prev, curr) => {
|
|
819
|
-
const prevWalletKeys = prev.wallets.items.map(
|
|
820
|
-
const currWalletKeys = curr.wallets.items.map(
|
|
783
|
+
const prevWalletKeys = prev.wallets.items.map(persistedWalletKey);
|
|
784
|
+
const currWalletKeys = curr.wallets.items.map(persistedWalletKey);
|
|
821
785
|
const prevAccountKeys = prev.accounts.items.map(accountChangeKey);
|
|
822
786
|
const currAccountKeys = curr.accounts.items.map(accountChangeKey);
|
|
823
787
|
return arraysEqual(prevWalletKeys, currWalletKeys) && arraysEqual(prevAccountKeys, currAccountKeys);
|
|
824
788
|
})).subscribe(({ wallets, accounts }) => {
|
|
825
|
-
const
|
|
826
|
-
const relevantAccounts = accounts.items.filter((a) => connectedWalletIds.has(a.walletId));
|
|
789
|
+
const snapshot = persistedSnapshot(wallets.items, accounts.items, store.getCachedState());
|
|
827
790
|
if (kc.debug) console.debug("[kheopskit] persisting state snapshot:", {
|
|
828
|
-
wallets: wallets.
|
|
829
|
-
accounts:
|
|
791
|
+
wallets: snapshot.wallets.length,
|
|
792
|
+
accounts: snapshot.accounts.length
|
|
830
793
|
});
|
|
831
|
-
store.setCachedState(wallets
|
|
794
|
+
store.setCachedState(snapshot.wallets, snapshot.accounts);
|
|
832
795
|
const icons = {};
|
|
833
796
|
for (const wallet of wallets.items) if (wallet.icon) icons[wallet.id] = wallet.icon;
|
|
834
797
|
require_iconCache.setCachedIcons(icons);
|
|
@@ -836,11 +799,11 @@ const getKheopskit$ = (config, options = {}) => {
|
|
|
836
799
|
return () => {
|
|
837
800
|
subscription.unsubscribe();
|
|
838
801
|
persistSub.unsubscribe();
|
|
802
|
+
closed = true;
|
|
803
|
+
for (const waiter of waiters) abandon(waiter);
|
|
804
|
+
for (const check of liveWaiters) check(void 0);
|
|
839
805
|
};
|
|
840
|
-
}).pipe((
|
|
841
|
-
leading: true,
|
|
842
|
-
trailing: true
|
|
843
|
-
}), logObservable("kheopskit$", {
|
|
806
|
+
}).pipe(logObservable("kheopskit$", {
|
|
844
807
|
enabled: kc.debug,
|
|
845
808
|
printValue: true
|
|
846
809
|
}), (0, rxjs.shareReplay)({
|
|
@@ -848,14 +811,60 @@ const getKheopskit$ = (config, options = {}) => {
|
|
|
848
811
|
refCount: true
|
|
849
812
|
}));
|
|
850
813
|
};
|
|
814
|
+
const sessionRestoring = (live, cached) => require_types.isWalletConnectWallet(live) && live.status === "disconnected" && cached.status === "connecting";
|
|
815
|
+
const flagged = /* @__PURE__ */ new WeakMap();
|
|
816
|
+
const asLive = (item) => {
|
|
817
|
+
const existing = flagged.get(item);
|
|
818
|
+
if (existing) return existing;
|
|
819
|
+
const live = {
|
|
820
|
+
...item,
|
|
821
|
+
isPlaceholder: false
|
|
822
|
+
};
|
|
823
|
+
flagged.set(item, live);
|
|
824
|
+
return live;
|
|
825
|
+
};
|
|
826
|
+
const noAnswerYet = {
|
|
827
|
+
accounts: [],
|
|
828
|
+
answered: []
|
|
829
|
+
};
|
|
830
|
+
/**
|
|
831
|
+
* A remembered wallet that reads disconnected without an error is still
|
|
832
|
+
* waiting for its reconnect: getWallets$ emits the wallet list before its
|
|
833
|
+
* connect() flips the status to connecting.
|
|
834
|
+
*/
|
|
835
|
+
const reconnectSettled = (cached, live) => {
|
|
836
|
+
const wallet = live.wallets.find((w) => w.id === cached.id);
|
|
837
|
+
if (!wallet) return true;
|
|
838
|
+
const status = sessionRestoring(wallet, cached) ? "connecting" : wallet.status;
|
|
839
|
+
if (status === "connecting") return false;
|
|
840
|
+
if (status === "disconnected") return wallet.error !== void 0 || !live.remembered.includes(cached.id);
|
|
841
|
+
return live.answered.includes(cached.id) || !live.cachedAccounts.some((a) => a.walletId === cached.id) || live.accounts.some((a) => a.walletId === cached.id);
|
|
842
|
+
};
|
|
843
|
+
/**
|
|
844
|
+
* A wallet still connecting keeps its previous cache entry and accounts:
|
|
845
|
+
* written live it would read isConnected: false with no accounts, and the
|
|
846
|
+
* next load would hydrate it as disconnected without placeholders while its
|
|
847
|
+
* reconnect runs.
|
|
848
|
+
*/
|
|
849
|
+
const persistedSnapshot = (wallets, accounts, previous) => {
|
|
850
|
+
const connecting = new Set(wallets.filter((w) => w.status === "connecting").map((w) => w.id));
|
|
851
|
+
const connected = new Set(wallets.filter((w) => w.isConnected).map((w) => w.id));
|
|
852
|
+
const previousWallets = new Map(previous.wallets.map((w) => [w.id, w]));
|
|
853
|
+
return {
|
|
854
|
+
wallets: wallets.map((w) => connecting.has(w.id) && previousWallets.get(w.id) || require_iconCache.serializeWallet(w)),
|
|
855
|
+
accounts: [...accounts.filter((a) => connected.has(a.walletId)).map(require_iconCache.serializeAccount), ...previous.accounts.filter((a) => connecting.has(a.walletId))]
|
|
856
|
+
};
|
|
857
|
+
};
|
|
851
858
|
const arraysEqual = (a, b) => a.length === b.length && a.every((v, i) => v === b[i]);
|
|
852
859
|
/**
|
|
853
|
-
* A signature of an account covering its id plus the
|
|
854
|
-
* that can change without the id changing (Ethereum
|
|
855
|
-
* Solana clusters). Used to detect both UI-relevant
|
|
856
|
-
* re-persisting, so the two comparators can't drift
|
|
860
|
+
* A signature of an account covering its id, its name, plus the
|
|
861
|
+
* platform-specific fields that can change without the id changing (Ethereum
|
|
862
|
+
* chainId, Polkadot key type, Solana clusters). Used to detect both UI-relevant
|
|
863
|
+
* changes and changes worth re-persisting, so the two comparators can't drift
|
|
864
|
+
* apart.
|
|
857
865
|
*/
|
|
858
|
-
const accountChangeKey = (account) => {
|
|
866
|
+
const accountChangeKey = (account) => `${account.name ?? ""}|${accountPlatformKey(account)}`;
|
|
867
|
+
const accountPlatformKey = (account) => {
|
|
859
868
|
switch (account.platform) {
|
|
860
869
|
case "ethereum": return `${account.id}|${account.chainId ?? ""}`;
|
|
861
870
|
case "polkadot": return `${account.id}|${account.type ?? ""}`;
|
|
@@ -868,40 +877,45 @@ const walletPlatforms = (wallet) => require_types.isWalletConnectWallet(wallet)
|
|
|
868
877
|
/** Fields persisted to the cache for a wallet (icon is stored separately). */
|
|
869
878
|
const walletPersistKey = (wallet) => `${wallet.id}|${wallet.isConnected ? 1 : 0}|${wallet.name}`;
|
|
870
879
|
/**
|
|
880
|
+
* What decides a cache write: the persisted fields plus `status`, because a
|
|
881
|
+
* wallet whose kept entry was written while connecting must be rewritten live
|
|
882
|
+
* once its reconnect fails, and `isConnected` reads false on both sides.
|
|
883
|
+
*/
|
|
884
|
+
const persistedWalletKey = (wallet) => `${walletPersistKey(wallet)}|${wallet.status}`;
|
|
885
|
+
/**
|
|
871
886
|
* UI-significant signature of a wallet: the persisted fields plus icon and the
|
|
872
887
|
* WalletConnect connector's `platforms` array, which drive what the UI shows but
|
|
873
888
|
* aren't part of the id (icon and platforms can arrive asynchronously after
|
|
874
889
|
* connect without isConnected flipping).
|
|
875
890
|
*/
|
|
876
|
-
const walletUiKey = (wallet) => `${walletPersistKey(wallet)}|${wallet.icon}|${walletPlatforms(wallet)}`;
|
|
891
|
+
const walletUiKey = (wallet) => `${walletPersistKey(wallet)}|${wallet.status}|${wallet.error?.code ?? ""}|${wallet.icon}|${walletPlatforms(wallet)}`;
|
|
877
892
|
/**
|
|
878
893
|
* Equality check for KheopskitState to prevent unnecessary emissions. Compares
|
|
879
894
|
* the UI-significant signature of every wallet and account, in order.
|
|
880
895
|
*/
|
|
881
|
-
const statesEqual = (a, b) => a.isHydrating === b.isHydrating && a.wallets.length === b.wallets.length && a.accounts.length === b.accounts.length && a.wallets.every((w, i) => {
|
|
896
|
+
const statesEqual = (a, b) => a.isHydrating === b.isHydrating && a.isDiscovering === b.isDiscovering && a.wallets.length === b.wallets.length && a.accounts.length === b.accounts.length && a.wallets.every((w, i) => {
|
|
882
897
|
const other = b.wallets[i];
|
|
883
|
-
return !!other && walletUiKey(w) === walletUiKey(other);
|
|
898
|
+
return !!other && w.isPlaceholder === other.isPlaceholder && walletUiKey(w) === walletUiKey(other);
|
|
884
899
|
}) && a.accounts.every((acc, i) => {
|
|
885
900
|
const other = b.accounts[i];
|
|
886
|
-
return !!other && accountChangeKey(acc) === accountChangeKey(other);
|
|
901
|
+
return !!other && acc.isPlaceholder === other.isPlaceholder && accountChangeKey(acc) === accountChangeKey(other);
|
|
887
902
|
});
|
|
888
903
|
//#endregion
|
|
889
904
|
exports.DEFAULT_STORAGE_KEY = DEFAULT_STORAGE_KEY;
|
|
890
|
-
exports.KheopskitError =
|
|
905
|
+
exports.KheopskitError = require_types.KheopskitError;
|
|
891
906
|
exports.clearAllCachedObservables = require_types.clearAllCachedObservables;
|
|
892
907
|
exports.createKheopskitStore = createKheopskitStore;
|
|
893
908
|
exports.getDefaultStore = getDefaultStore;
|
|
894
909
|
exports.getHydratedSnapshot = getHydratedSnapshot;
|
|
895
910
|
exports.getKheopskit$ = getKheopskit$;
|
|
896
|
-
exports.getWalletAccountId =
|
|
911
|
+
exports.getWalletAccountId = require_platformAccounts.getWalletAccountId;
|
|
897
912
|
exports.getWalletId = require_types.getWalletId;
|
|
898
913
|
exports.isInjectedWallet = require_types.isInjectedWallet;
|
|
899
|
-
exports.isValidAddress =
|
|
914
|
+
exports.isValidAddress = require_platformAccounts.isValidAddress;
|
|
900
915
|
exports.isValidWalletId = require_types.isValidWalletId;
|
|
901
916
|
exports.isWalletConnectWallet = require_types.isWalletConnectWallet;
|
|
902
|
-
exports.parseWalletAccountId =
|
|
917
|
+
exports.parseWalletAccountId = require_platformAccounts.parseWalletAccountId;
|
|
903
918
|
exports.parseWalletId = require_types.parseWalletId;
|
|
904
|
-
exports.resetAppKitCache = resetAppKitCache;
|
|
905
919
|
exports.resolveConfig = resolveConfig;
|
|
906
920
|
|
|
907
921
|
//# sourceMappingURL=index.js.map
|