@kheopskit/core 5.1.0 → 5.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/errors-Caq2csKx.mjs +181 -0
- package/dist/errors-Caq2csKx.mjs.map +1 -0
- package/dist/errors-DPw6pc2H.js +222 -0
- package/dist/errors-DPw6pc2H.js.map +1 -0
- package/dist/ethereum.d.mts +39 -38
- package/dist/ethereum.d.ts +39 -38
- package/dist/ethereum.js +214 -281
- package/dist/ethereum.js.map +1 -1
- package/dist/ethereum.mjs +211 -282
- package/dist/ethereum.mjs.map +1 -1
- package/dist/{getCachedObservable-C4E8dfMp.d.ts → getCachedObservable-LUq9IfEx.d.mts} +5 -2
- package/dist/{getCachedObservable-C4E8dfMp.d.mts → getCachedObservable-LUq9IfEx.d.ts} +5 -2
- package/dist/iconCache-BSa42Sui.mjs +343 -0
- package/dist/iconCache-BSa42Sui.mjs.map +1 -0
- package/dist/iconCache-DS4zNiP1.js +396 -0
- package/dist/iconCache-DS4zNiP1.js.map +1 -0
- package/dist/index.d.mts +65 -59
- package/dist/index.d.ts +65 -59
- package/dist/index.js +901 -1015
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +885 -1016
- package/dist/index.mjs.map +1 -1
- package/dist/injectedWallets-B1MCxAE9.js +148 -0
- package/dist/injectedWallets-B1MCxAE9.js.map +1 -0
- package/dist/injectedWallets-B3Vysfvr.mjs +119 -0
- package/dist/injectedWallets-B3Vysfvr.mjs.map +1 -0
- package/dist/internal.d.mts +18 -16
- package/dist/internal.d.ts +18 -16
- package/dist/internal.js +9 -20
- package/dist/internal.mjs +3 -20
- package/dist/polkadot.d.mts +37 -36
- package/dist/polkadot.d.ts +37 -36
- package/dist/polkadot.js +178 -220
- package/dist/polkadot.js.map +1 -1
- package/dist/polkadot.mjs +175 -221
- package/dist/polkadot.mjs.map +1 -1
- package/dist/polkadotExtensions-BoVKhNU_.mjs +23 -0
- package/dist/polkadotExtensions-BoVKhNU_.mjs.map +1 -0
- package/dist/polkadotExtensions-y4J9UzYl.js +28 -0
- package/dist/polkadotExtensions-y4J9UzYl.js.map +1 -0
- package/dist/solana.d.mts +50 -48
- package/dist/solana.d.ts +50 -48
- package/dist/solana.js +320 -378
- package/dist/solana.js.map +1 -1
- package/dist/solana.mjs +314 -379
- package/dist/solana.mjs.map +1 -1
- package/dist/types-Cb-vuEru.d.mts +330 -0
- package/dist/types-Cb-vuEru.d.ts +330 -0
- package/dist/types-DKU-SNio.js +169 -0
- package/dist/{chunk-P7BPHHVV.js.map → types-DKU-SNio.js.map} +1 -1
- package/dist/types-jnifAAA0.mjs +104 -0
- package/dist/types-jnifAAA0.mjs.map +1 -0
- package/package.json +12 -12
- package/dist/chunk-3Y5N4N3Q.mjs +0 -267
- package/dist/chunk-3Y5N4N3Q.mjs.map +0 -1
- package/dist/chunk-BWUUHUDK.mjs +0 -24
- package/dist/chunk-BWUUHUDK.mjs.map +0 -1
- package/dist/chunk-D3EQMFZ2.js +0 -24
- package/dist/chunk-D3EQMFZ2.js.map +0 -1
- package/dist/chunk-DF7EKK4G.mjs +0 -106
- package/dist/chunk-DF7EKK4G.mjs.map +0 -1
- package/dist/chunk-EH73P476.js +0 -106
- package/dist/chunk-EH73P476.js.map +0 -1
- package/dist/chunk-JVSCMJY5.mjs +0 -147
- package/dist/chunk-JVSCMJY5.mjs.map +0 -1
- package/dist/chunk-P7BPHHVV.js +0 -76
- package/dist/chunk-SO64R7Z6.js +0 -267
- package/dist/chunk-SO64R7Z6.js.map +0 -1
- package/dist/chunk-WOPCW2OX.mjs +0 -76
- package/dist/chunk-WOPCW2OX.mjs.map +0 -1
- package/dist/chunk-XOGQMEV2.js +0 -147
- package/dist/chunk-XOGQMEV2.js.map +0 -1
- package/dist/internal.js.map +0 -1
- package/dist/internal.mjs.map +0 -1
- package/dist/types-0iDBydsK.d.mts +0 -327
- package/dist/types-0iDBydsK.d.ts +0 -327
package/dist/index.mjs
CHANGED
|
@@ -1,1020 +1,889 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
hydrateWallet,
|
|
6
|
-
safeLocalStorage,
|
|
7
|
-
serializeAccount,
|
|
8
|
-
serializeWallet,
|
|
9
|
-
setCachedIcons
|
|
10
|
-
} from "./chunk-3Y5N4N3Q.mjs";
|
|
11
|
-
import "./chunk-BWUUHUDK.mjs";
|
|
12
|
-
import {
|
|
13
|
-
KheopskitError,
|
|
14
|
-
getWalletAccountId,
|
|
15
|
-
isValidAddress,
|
|
16
|
-
parseWalletAccountId
|
|
17
|
-
} from "./chunk-DF7EKK4G.mjs";
|
|
18
|
-
import {
|
|
19
|
-
WALLET_CONNECT_WALLET_ID,
|
|
20
|
-
clearAllCachedObservables,
|
|
21
|
-
clearCachedObservablesByPrefix,
|
|
22
|
-
getWalletId,
|
|
23
|
-
isInjectedWallet,
|
|
24
|
-
isValidWalletId,
|
|
25
|
-
isWalletConnectWallet,
|
|
26
|
-
isWalletPlatform,
|
|
27
|
-
parseWalletId
|
|
28
|
-
} from "./chunk-WOPCW2OX.mjs";
|
|
29
|
-
|
|
30
|
-
// src/api/appKit.ts
|
|
31
|
-
import {
|
|
32
|
-
BehaviorSubject,
|
|
33
|
-
distinctUntilChanged,
|
|
34
|
-
from,
|
|
35
|
-
map,
|
|
36
|
-
Observable,
|
|
37
|
-
of,
|
|
38
|
-
shareReplay,
|
|
39
|
-
switchMap,
|
|
40
|
-
tap
|
|
41
|
-
} from "rxjs";
|
|
42
|
-
var loadAppKit = async () => {
|
|
43
|
-
try {
|
|
44
|
-
const { createAppKit } = await import("@reown/appkit/core");
|
|
45
|
-
return createAppKit;
|
|
46
|
-
} catch (cause) {
|
|
47
|
-
console.error(
|
|
48
|
-
"[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.",
|
|
49
|
-
cause
|
|
50
|
-
);
|
|
51
|
-
return null;
|
|
52
|
-
}
|
|
53
|
-
};
|
|
54
|
-
var WALLET_CONNECT_ICON = "data:image/svg+xml;base64,PHN2ZyBmaWxsPSJub25lIiBoZWlnaHQ9IjQwMCIgdmlld0JveD0iMCAwIDQwMCA0MDAiIHdpZHRoPSI0MDAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxjbGlwUGF0aCBpZD0iYSI+PHBhdGggZD0ibTAgMGg0MDB2NDAwaC00MDB6Ii8+PC9jbGlwUGF0aD48ZyBjbGlwLXBhdGg9InVybCgjYSkiPjxjaXJjbGUgY3g9IjIwMCIgY3k9IjIwMCIgZmlsbD0iIzMzOTZmZiIgcj0iMTk5LjUiIHN0cm9rZT0iIzY2YjFmZiIvPjxwYXRoIGQ9Im0xMjIuNTE5IDE0OC45NjVjNDIuNzkxLTQxLjcyOSAxMTIuMTcxLTQxLjcyOSAxNTQuOTYyIDBsNS4xNSA1LjAyMmMyLjE0IDIuMDg2IDIuMTQgNS40NjkgMCA3LjU1NWwtMTcuNjE3IDE3LjE4Yy0xLjA3IDEuMDQzLTIuODA0IDEuMDQzLTMuODc0IDBsLTcuMDg3LTYuOTExYy0yOS44NTMtMjkuMTExLTc4LjI1My0yOS4xMTEtMTA4LjEwNiAwbC03LjU5IDcuNDAxYy0xLjA3IDEuMDQzLTIuODA0IDEuMDQzLTMuODc0IDBsLTE3LjYxNy0xNy4xOGMtMi4xNC0yLjA4Ni0yLjE0LTUuNDY5IDAtNy41NTV6bTE5MS4zOTcgMzUuNTI5IDE1LjY3OSAxNS4yOWMyLjE0IDIuMDg2IDIuMTQgNS40NjkgMCA3LjU1NWwtNzAuNyA2OC45NDRjLTIuMTM5IDIuMDg3LTUuNjA4IDIuMDg3LTcuNzQ4IDBsLTUwLjE3OC00OC45MzFjLS41MzUtLjUyMi0xLjQwMi0uNTIyLTEuOTM3IDBsLTUwLjE3OCA0OC45MzFjLTIuMTM5IDIuMDg3LTUuNjA4IDIuMDg3LTcuNzQ4IDBsLTcwLjcwMTUtNjguOTQ1Yy0yLjEzOTYtMi4wODYtMi4xMzk2LTUuNDY5IDAtNy41NTVsMTUuNjc5NS0xNS4yOWMyLjEzOTYtMi4wODYgNS42MDg1LTIuMDg2IDcuNzQ4MSAwbDUwLjE3ODkgNDguOTMyYy41MzUuNTIyIDEuNDAyLjUyMiAxLjkzNyAwbDUwLjE3Ny00OC45MzJjMi4xMzktMi4wODcgNS42MDgtMi4wODcgNy43NDggMGw1MC4xNzkgNDguOTMyYy41MzUuNTIyIDEuNDAyLjUyMiAxLjkzNyAwbDUwLjE3OS00OC45MzFjMi4xMzktMi4wODcgNS42MDgtMi4wODcgNy43NDggMHoiIGZpbGw9IiNmZmYiLz48L2c+PC9zdmc+";
|
|
55
|
-
var APPKIT_SYMBOL = /* @__PURE__ */ Symbol.for("kheopskit.cachedAppKit");
|
|
56
|
-
var APPKIT_PROJECT_ID_SYMBOL = /* @__PURE__ */ Symbol.for("kheopskit.cachedAppKitProjectId");
|
|
57
|
-
var getCachedAppKit = () => globalThis[APPKIT_SYMBOL];
|
|
58
|
-
var getCachedAppKitProjectId = () => globalThis[APPKIT_PROJECT_ID_SYMBOL];
|
|
59
|
-
var setCachedAppKit = (value, projectId) => {
|
|
60
|
-
globalThis[APPKIT_SYMBOL] = value;
|
|
61
|
-
globalThis[APPKIT_PROJECT_ID_SYMBOL] = projectId;
|
|
62
|
-
};
|
|
63
|
-
var resetAppKitCache = () => {
|
|
64
|
-
setCachedAppKit(void 0);
|
|
65
|
-
};
|
|
66
|
-
var dropAccountsCache = (platform) => {
|
|
67
|
-
clearCachedObservablesByPrefix(
|
|
68
|
-
`accounts:${WALLET_CONNECT_WALLET_ID}:${platform}:`
|
|
69
|
-
);
|
|
70
|
-
};
|
|
71
|
-
var getWalletConnectWallet$ = (config) => {
|
|
72
|
-
if (!config.walletConnect) return of(null);
|
|
73
|
-
if (typeof window === "undefined") return of(null);
|
|
74
|
-
const walletConnect = config.walletConnect;
|
|
75
|
-
const cachedProjectId = getCachedAppKitProjectId();
|
|
76
|
-
if (cachedProjectId !== void 0 && cachedProjectId !== walletConnect.projectId) {
|
|
77
|
-
console.warn(
|
|
78
|
-
"[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.",
|
|
79
|
-
cachedProjectId,
|
|
80
|
-
walletConnect.projectId
|
|
81
|
-
);
|
|
82
|
-
}
|
|
83
|
-
let cachedAppKit = getCachedAppKit();
|
|
84
|
-
if (!cachedAppKit) {
|
|
85
|
-
cachedAppKit = from(loadAppKit()).pipe(
|
|
86
|
-
switchMap((createAppKit) => {
|
|
87
|
-
if (!createAppKit) return of(null);
|
|
88
|
-
return new Observable((subscriber) => {
|
|
89
|
-
const appKit = createAppKit({
|
|
90
|
-
projectId: walletConnect.projectId,
|
|
91
|
-
metadata: walletConnect.metadata,
|
|
92
|
-
// Loosely typed in WalletConnectConfig to keep @reown/appkit's
|
|
93
|
-
// types out of core; forwarded to AppKit verbatim.
|
|
94
|
-
networks: walletConnect.networks,
|
|
95
|
-
themeMode: walletConnect.themeMode,
|
|
96
|
-
themeVariables: walletConnect.themeVariables,
|
|
97
|
-
universalProviderConfigOverride: {
|
|
98
|
-
methods: {
|
|
99
|
-
polkadot: ["polkadot_signTransaction", "polkadot_signMessage"],
|
|
100
|
-
solana: [
|
|
101
|
-
"solana_signTransaction",
|
|
102
|
-
"solana_signMessage",
|
|
103
|
-
"solana_signAndSendTransaction"
|
|
104
|
-
]
|
|
105
|
-
}
|
|
106
|
-
},
|
|
107
|
-
allWallets: "HIDE",
|
|
108
|
-
debug: config.debug,
|
|
109
|
-
allowUnsupportedChain: true
|
|
110
|
-
});
|
|
111
|
-
const appKitInstance = appKit;
|
|
112
|
-
const status$ = new BehaviorSubject({
|
|
113
|
-
isPolkadotConnected: false,
|
|
114
|
-
isEthereumConnected: false,
|
|
115
|
-
isSolanaConnected: false
|
|
116
|
-
});
|
|
117
|
-
const unsubProviders = appKit.subscribeProviders((providers) => {
|
|
118
|
-
status$.next({
|
|
119
|
-
isPolkadotConnected: !!providers.polkadot,
|
|
120
|
-
isEthereumConnected: !!providers.eip155,
|
|
121
|
-
isSolanaConnected: !!providers.solana
|
|
122
|
-
});
|
|
123
|
-
});
|
|
124
|
-
const namespaceOf = {
|
|
125
|
-
polkadot: "polkadot",
|
|
126
|
-
ethereum: "eip155",
|
|
127
|
-
solana: "solana"
|
|
128
|
-
};
|
|
129
|
-
const allPlatforms = [
|
|
130
|
-
"polkadot",
|
|
131
|
-
"ethereum",
|
|
132
|
-
"solana"
|
|
133
|
-
];
|
|
134
|
-
const enabledPlatforms = allPlatforms.filter(
|
|
135
|
-
(p) => appKit.chainNamespaces.includes(namespaceOf[p])
|
|
136
|
-
);
|
|
137
|
-
let prevConnected = {
|
|
138
|
-
polkadot: false,
|
|
139
|
-
ethereum: false,
|
|
140
|
-
solana: false
|
|
141
|
-
};
|
|
142
|
-
const sub = status$.pipe(
|
|
143
|
-
map((s) => ({
|
|
144
|
-
polkadot: s.isPolkadotConnected,
|
|
145
|
-
ethereum: s.isEthereumConnected,
|
|
146
|
-
solana: s.isSolanaConnected
|
|
147
|
-
})),
|
|
148
|
-
distinctUntilChanged(
|
|
149
|
-
(a, b) => a.polkadot === b.polkadot && a.ethereum === b.ethereum && a.solana === b.solana
|
|
150
|
-
),
|
|
151
|
-
tap((connected) => {
|
|
152
|
-
for (const platform of allPlatforms)
|
|
153
|
-
if (prevConnected[platform] && !connected[platform])
|
|
154
|
-
dropAccountsCache(platform);
|
|
155
|
-
prevConnected = connected;
|
|
156
|
-
}),
|
|
157
|
-
map((connected) => {
|
|
158
|
-
const platforms = enabledPlatforms.filter((p) => connected[p]);
|
|
159
|
-
const isConnected = platforms.length > 0;
|
|
160
|
-
const walletInfo = appKit.getWalletInfo();
|
|
161
|
-
return {
|
|
162
|
-
id: WALLET_CONNECT_WALLET_ID,
|
|
163
|
-
type: "walletconnect",
|
|
164
|
-
platforms,
|
|
165
|
-
appKit: appKitInstance,
|
|
166
|
-
name: walletInfo?.name ?? "WalletConnect",
|
|
167
|
-
icon: walletInfo?.icon ?? WALLET_CONNECT_ICON,
|
|
168
|
-
// One shared session: connecting opens the modal; the wallet
|
|
169
|
-
// approves namespaces in that single pairing. Disconnect is
|
|
170
|
-
// session-wide; re-pair to change the approved set.
|
|
171
|
-
connect: async () => {
|
|
172
|
-
if (!isConnected) await appKit.open();
|
|
173
|
-
},
|
|
174
|
-
disconnect: async () => {
|
|
175
|
-
if (isConnected) await appKit.disconnect();
|
|
176
|
-
},
|
|
177
|
-
isConnected
|
|
178
|
-
};
|
|
179
|
-
})
|
|
180
|
-
).subscribe(subscriber);
|
|
181
|
-
return () => {
|
|
182
|
-
sub.unsubscribe();
|
|
183
|
-
unsubProviders();
|
|
184
|
-
};
|
|
185
|
-
});
|
|
186
|
-
}),
|
|
187
|
-
// refCount:false keeps the AppKit instance alive for the process lifetime
|
|
188
|
-
// once created: createAppKit runs at most once (Reown AppKit cannot be
|
|
189
|
-
// instantiated twice). With refCount:true the producer would re-run
|
|
190
|
-
// createAppKit whenever every subscriber drained and a new one
|
|
191
|
-
// re-subscribed — e.g. a React unmount/remount or StrictMode's
|
|
192
|
-
// mount→unmount→remount — throwing on the duplicate Lit web components and
|
|
193
|
-
// WalletConnect modal singleton. The replayed connector value also keeps
|
|
194
|
-
// the wallet list stable across such teardown/rebuild cycles.
|
|
195
|
-
shareReplay({ refCount: false, bufferSize: 1 })
|
|
196
|
-
);
|
|
197
|
-
setCachedAppKit(cachedAppKit, walletConnect.projectId);
|
|
198
|
-
}
|
|
199
|
-
return cachedAppKit;
|
|
200
|
-
};
|
|
201
|
-
|
|
202
|
-
// src/api/config.ts
|
|
203
|
-
var DEFAULT_STORAGE_KEY = "kheopskit";
|
|
204
|
-
var DEFAULTS = {
|
|
205
|
-
autoReconnect: true,
|
|
206
|
-
debug: false,
|
|
207
|
-
storageKey: DEFAULT_STORAGE_KEY,
|
|
208
|
-
hydrationGracePeriod: 500
|
|
209
|
-
};
|
|
210
|
-
var resolveConfig = (config) => {
|
|
211
|
-
const platforms = config?.platforms ?? [];
|
|
212
|
-
const invalidPlatforms = platforms.filter(
|
|
213
|
-
(p) => typeof p !== "object" || p === null || typeof p.getWallets$ !== "function"
|
|
214
|
-
);
|
|
215
|
-
if (invalidPlatforms.length > 0) {
|
|
216
|
-
throw new Error(
|
|
217
|
-
`[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 \u2014 see MIGRATING_TO_V4.md.`
|
|
218
|
-
);
|
|
219
|
-
}
|
|
220
|
-
if (platforms.length === 0) {
|
|
221
|
-
console.warn(
|
|
222
|
-
'[kheopskit] No platforms configured; wallets and accounts will be empty. Pass e.g. platforms: [polkadot()] from "@kheopskit/core/polkadot".'
|
|
223
|
-
);
|
|
224
|
-
}
|
|
225
|
-
return Object.assign({}, DEFAULTS, config, { platforms });
|
|
226
|
-
};
|
|
227
|
-
|
|
228
|
-
// src/utils/sortAccounts.ts
|
|
229
|
-
var PLATFORM_ORDER = {
|
|
230
|
-
polkadot: 0,
|
|
231
|
-
ethereum: 1,
|
|
232
|
-
solana: 2
|
|
233
|
-
};
|
|
234
|
-
var byWalletName = (a1, a2) => {
|
|
235
|
-
if (a1.walletName.toLowerCase() === "talisman") return -1;
|
|
236
|
-
if (a2.walletName.toLowerCase() === "talisman") return 1;
|
|
237
|
-
return a1.walletName.localeCompare(a2.walletName);
|
|
238
|
-
};
|
|
239
|
-
var sortAccounts = (a1, a2) => {
|
|
240
|
-
if (a1.platform !== a2.platform)
|
|
241
|
-
return PLATFORM_ORDER[a1.platform] - PLATFORM_ORDER[a2.platform];
|
|
242
|
-
if (a1.walletName !== a2.walletName) return byWalletName(a1, a2);
|
|
243
|
-
if (a1.platform === "polkadot" && a2.platform === "polkadot")
|
|
244
|
-
return a1.name !== a2.name ? (a1.name ?? "").localeCompare(a2.name ?? "") : a1.address.localeCompare(a2.address);
|
|
245
|
-
return a1.id.localeCompare(a2.id);
|
|
246
|
-
};
|
|
247
|
-
|
|
248
|
-
// src/utils/sortWallets.ts
|
|
249
|
-
var PLATFORM_ORDER2 = {
|
|
250
|
-
polkadot: 0,
|
|
251
|
-
ethereum: 1,
|
|
252
|
-
solana: 2
|
|
253
|
-
};
|
|
254
|
-
var orderOf = (wallet) => isWalletConnectWallet(wallet) ? 3 : PLATFORM_ORDER2[wallet.platform];
|
|
255
|
-
var sortWallets = (w1, w2) => {
|
|
256
|
-
const o1 = orderOf(w1);
|
|
257
|
-
const o2 = orderOf(w2);
|
|
258
|
-
if (o1 !== o2) return o1 - o2;
|
|
259
|
-
if (w1.name.toLowerCase() === "talisman") return -1;
|
|
260
|
-
if (w2.name.toLowerCase() === "talisman") return 1;
|
|
261
|
-
return w1.name.localeCompare(w2.name);
|
|
262
|
-
};
|
|
263
|
-
|
|
264
|
-
// src/api/platform.ts
|
|
265
|
-
var acceptsCachedAccount = (cached, platforms) => {
|
|
266
|
-
const plugin = platforms.find((p) => p.platform === cached.platform);
|
|
267
|
-
return plugin?.acceptsCachedAccount?.(cached) ?? true;
|
|
268
|
-
};
|
|
269
|
-
|
|
270
|
-
// src/api/hydration.ts
|
|
271
|
-
var getHydratedSnapshot = (store, platforms, options = {}) => {
|
|
272
|
-
const cached = store.getCachedState();
|
|
273
|
-
const wallets = cached.wallets.map((cachedWallet) => {
|
|
274
|
-
const wallet = hydrateWallet(cachedWallet);
|
|
275
|
-
if (!options.icons || wallet.icon) return wallet;
|
|
276
|
-
const icon = getCachedIcon(wallet.id);
|
|
277
|
-
return icon ? { ...wallet, icon } : wallet;
|
|
278
|
-
}).sort(sortWallets);
|
|
279
|
-
const accounts = cached.accounts.filter((account) => acceptsCachedAccount(account, platforms)).map(hydrateAccount).sort(sortAccounts);
|
|
280
|
-
return { wallets, accounts };
|
|
281
|
-
};
|
|
282
|
-
|
|
283
|
-
// src/api/kheopskit.ts
|
|
284
|
-
import {
|
|
285
|
-
combineLatest as combineLatest4,
|
|
286
|
-
debounceTime,
|
|
287
|
-
distinctUntilChanged as distinctUntilChanged2,
|
|
288
|
-
filter as filter3,
|
|
289
|
-
map as map5,
|
|
290
|
-
Observable as Observable5,
|
|
291
|
-
shareReplay as shareReplay5,
|
|
292
|
-
throttleTime
|
|
293
|
-
} from "rxjs";
|
|
294
|
-
|
|
295
|
-
// src/utils/createHydrationBuffer.ts
|
|
296
|
-
import {
|
|
297
|
-
BehaviorSubject as BehaviorSubject2,
|
|
298
|
-
combineLatest,
|
|
299
|
-
filter,
|
|
300
|
-
map as map2,
|
|
301
|
-
Observable as Observable2,
|
|
302
|
-
Subscription,
|
|
303
|
-
shareReplay as shareReplay2,
|
|
304
|
-
startWith,
|
|
305
|
-
take,
|
|
306
|
-
timer
|
|
307
|
-
} from "rxjs";
|
|
308
|
-
var createBufferCore = (cachedItems, liveItems$, gracePeriodMs, mergeFn, isConverged) => {
|
|
309
|
-
if (gracePeriodMs <= 0 || cachedItems.length === 0) {
|
|
310
|
-
return liveItems$.pipe(map2((items) => ({ items, isHydrating: false })));
|
|
311
|
-
}
|
|
312
|
-
return new Observable2((subscriber) => {
|
|
313
|
-
const subscriptions = new Subscription();
|
|
314
|
-
const isHydrating$ = new BehaviorSubject2(true);
|
|
315
|
-
const liveWithInitial$ = liveItems$.pipe(startWith([]));
|
|
316
|
-
if (isConverged) {
|
|
317
|
-
const timerFired$ = timer(gracePeriodMs).pipe(
|
|
318
|
-
map2(() => true),
|
|
319
|
-
startWith(false),
|
|
320
|
-
shareReplay2({ bufferSize: 1, refCount: true })
|
|
321
|
-
);
|
|
322
|
-
subscriptions.add(
|
|
323
|
-
combineLatest([liveWithInitial$, timerFired$]).pipe(
|
|
324
|
-
filter(([, timerFired]) => timerFired),
|
|
325
|
-
filter(([liveItems]) => isConverged(liveItems, cachedItems)),
|
|
326
|
-
take(1)
|
|
327
|
-
).subscribe(() => {
|
|
328
|
-
isHydrating$.next(false);
|
|
329
|
-
})
|
|
330
|
-
);
|
|
331
|
-
subscriptions.add(
|
|
332
|
-
timer(gracePeriodMs * 6).subscribe(() => {
|
|
333
|
-
if (isHydrating$.value) {
|
|
334
|
-
isHydrating$.next(false);
|
|
335
|
-
}
|
|
336
|
-
})
|
|
337
|
-
);
|
|
338
|
-
} else {
|
|
339
|
-
subscriptions.add(
|
|
340
|
-
timer(gracePeriodMs).subscribe(() => {
|
|
341
|
-
isHydrating$.next(false);
|
|
342
|
-
})
|
|
343
|
-
);
|
|
344
|
-
}
|
|
345
|
-
subscriptions.add(
|
|
346
|
-
combineLatest([liveWithInitial$, isHydrating$]).pipe(
|
|
347
|
-
map2(([liveItems, isHydrating]) => {
|
|
348
|
-
if (!isHydrating) {
|
|
349
|
-
return { items: liveItems, isHydrating: false };
|
|
350
|
-
}
|
|
351
|
-
return {
|
|
352
|
-
items: mergeFn(liveItems, cachedItems),
|
|
353
|
-
isHydrating: true
|
|
354
|
-
};
|
|
355
|
-
})
|
|
356
|
-
).subscribe(subscriber)
|
|
357
|
-
);
|
|
358
|
-
return () => {
|
|
359
|
-
subscriptions.unsubscribe();
|
|
360
|
-
isHydrating$.complete();
|
|
361
|
-
};
|
|
362
|
-
});
|
|
363
|
-
};
|
|
364
|
-
var createHydrationBuffer = (cachedItems, liveItems$, gracePeriodMs, getKey, isConverged, mergeItem, transformCachedOnly) => {
|
|
365
|
-
return createBufferCore(
|
|
366
|
-
cachedItems,
|
|
367
|
-
liveItems$,
|
|
368
|
-
gracePeriodMs,
|
|
369
|
-
(liveItems, cached) => {
|
|
370
|
-
const cachedByKey = new Map(cached.map((item) => [getKey(item), item]));
|
|
371
|
-
const liveKeys = new Set(liveItems.map(getKey));
|
|
372
|
-
const mergedItems = liveItems.map((liveItem) => {
|
|
373
|
-
const key = getKey(liveItem);
|
|
374
|
-
const cachedItem = cachedByKey.get(key);
|
|
375
|
-
if (!cachedItem) return liveItem;
|
|
376
|
-
return mergeItem ? mergeItem(liveItem, cachedItem) : cachedItem;
|
|
377
|
-
});
|
|
378
|
-
for (const cachedItem of cached) {
|
|
379
|
-
if (!liveKeys.has(getKey(cachedItem))) {
|
|
380
|
-
const transformed = transformCachedOnly ? transformCachedOnly(cachedItem) : cachedItem;
|
|
381
|
-
mergedItems.push(transformed);
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
return mergedItems;
|
|
385
|
-
},
|
|
386
|
-
isConverged
|
|
387
|
-
);
|
|
388
|
-
};
|
|
389
|
-
var createAccountHydrationBuffer = (cachedAccounts, liveAccounts$, gracePeriodMs, getWalletId2, isConverged) => {
|
|
390
|
-
return createBufferCore(
|
|
391
|
-
cachedAccounts,
|
|
392
|
-
liveAccounts$,
|
|
393
|
-
gracePeriodMs,
|
|
394
|
-
(liveAccounts, cached) => {
|
|
395
|
-
const walletsWithLiveAccounts = new Set(liveAccounts.map(getWalletId2));
|
|
396
|
-
const mergedAccounts = [...liveAccounts];
|
|
397
|
-
for (const cachedAccount of cached) {
|
|
398
|
-
if (!walletsWithLiveAccounts.has(getWalletId2(cachedAccount))) {
|
|
399
|
-
mergedAccounts.push(cachedAccount);
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
return mergedAccounts;
|
|
403
|
-
},
|
|
404
|
-
isConverged
|
|
405
|
-
);
|
|
406
|
-
};
|
|
407
|
-
|
|
408
|
-
// src/utils/logObservable.ts
|
|
409
|
-
import { tap as tap2 } from "rxjs";
|
|
410
|
-
var logObservable = (label, opts) => tap2((value) => {
|
|
411
|
-
const { printValue = false, enabled = true } = opts || {};
|
|
412
|
-
if (!label || !enabled) return;
|
|
413
|
-
const text = `[kheopskit] observable ${label} emit`;
|
|
414
|
-
if (printValue) console.debug(text, value);
|
|
415
|
-
else console.debug(text);
|
|
416
|
-
});
|
|
417
|
-
|
|
418
|
-
// src/api/accounts.ts
|
|
419
|
-
import { combineLatest as combineLatest2, map as map3, Observable as Observable3, of as of2, shareReplay as shareReplay3 } from "rxjs";
|
|
420
|
-
var getAccounts$ = (config, wallets) => {
|
|
421
|
-
return new Observable3((subscriber) => {
|
|
422
|
-
const sources = config.platforms.map(
|
|
423
|
-
(plugin) => plugin.getAccounts$(
|
|
424
|
-
wallets.pipe(
|
|
425
|
-
map3(
|
|
426
|
-
(ws) => ws.filter(
|
|
427
|
-
(w) => isWalletConnectWallet(w) || w.platform === plugin.platform
|
|
428
|
-
)
|
|
429
|
-
)
|
|
430
|
-
)
|
|
431
|
-
)
|
|
432
|
-
);
|
|
433
|
-
const accounts$ = sources.length ? combineLatest2(sources).pipe(
|
|
434
|
-
map3((accounts) => accounts.flat().sort(sortAccounts))
|
|
435
|
-
) : of2([]);
|
|
436
|
-
const sub = accounts$.subscribe(subscriber);
|
|
437
|
-
return () => {
|
|
438
|
-
sub.unsubscribe();
|
|
439
|
-
};
|
|
440
|
-
}).pipe(shareReplay3({ refCount: true, bufferSize: 1 }));
|
|
441
|
-
};
|
|
442
|
-
|
|
443
|
-
// src/api/store.ts
|
|
1
|
+
import { a as isValidWalletId, c as clearAllCachedObservables, i as getWalletId, n as isWalletConnectWallet, o as parseWalletId, r as WALLET_CONNECT_WALLET_ID, s as isWalletPlatform, t as isInjectedWallet, u as clearCachedObservablesByPrefix } from "./types-jnifAAA0.mjs";
|
|
2
|
+
import { i as isValidAddress, n as getWalletAccountId, r as parseWalletAccountId, t as KheopskitError } from "./errors-Caq2csKx.mjs";
|
|
3
|
+
import { a as safeLocalStorage, c as serializeAccount, l as serializeWallet, n as setCachedIcons, o as hydrateAccount, r as cookieStorage, s as hydrateWallet, t as getCachedIcon } from "./iconCache-BSa42Sui.mjs";
|
|
4
|
+
import { BehaviorSubject, Observable, Subscription, combineLatest, debounceTime, distinctUntilChanged, filter, from, map, mergeMap, of, shareReplay, startWith, switchMap, take, tap, throttleTime, timer } from "rxjs";
|
|
444
5
|
import { uniq } from "lodash-es";
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
import
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
};
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
};
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
}
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
};
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
6
|
+
//#region src/api/appKit.ts
|
|
7
|
+
/**
|
|
8
|
+
* Dynamically import @reown/appkit/core to avoid loading browser-only code
|
|
9
|
+
* during SSR or in edge runtimes like Cloudflare Workers.
|
|
10
|
+
* The AppKit library uses Lit for web components which requires document.
|
|
11
|
+
*/
|
|
12
|
+
const loadAppKit = async () => {
|
|
13
|
+
try {
|
|
14
|
+
const { createAppKit } = await import("@reown/appkit/core");
|
|
15
|
+
return createAppKit;
|
|
16
|
+
} catch (cause) {
|
|
17
|
+
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);
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
const WALLET_CONNECT_ICON = "data:image/svg+xml;base64,PHN2ZyBmaWxsPSJub25lIiBoZWlnaHQ9IjQwMCIgdmlld0JveD0iMCAwIDQwMCA0MDAiIHdpZHRoPSI0MDAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxjbGlwUGF0aCBpZD0iYSI+PHBhdGggZD0ibTAgMGg0MDB2NDAwaC00MDB6Ii8+PC9jbGlwUGF0aD48ZyBjbGlwLXBhdGg9InVybCgjYSkiPjxjaXJjbGUgY3g9IjIwMCIgY3k9IjIwMCIgZmlsbD0iIzMzOTZmZiIgcj0iMTk5LjUiIHN0cm9rZT0iIzY2YjFmZiIvPjxwYXRoIGQ9Im0xMjIuNTE5IDE0OC45NjVjNDIuNzkxLTQxLjcyOSAxMTIuMTcxLTQxLjcyOSAxNTQuOTYyIDBsNS4xNSA1LjAyMmMyLjE0IDIuMDg2IDIuMTQgNS40NjkgMCA3LjU1NWwtMTcuNjE3IDE3LjE4Yy0xLjA3IDEuMDQzLTIuODA0IDEuMDQzLTMuODc0IDBsLTcuMDg3LTYuOTExYy0yOS44NTMtMjkuMTExLTc4LjI1My0yOS4xMTEtMTA4LjEwNiAwbC03LjU5IDcuNDAxYy0xLjA3IDEuMDQzLTIuODA0IDEuMDQzLTMuODc0IDBsLTE3LjYxNy0xNy4xOGMtMi4xNC0yLjA4Ni0yLjE0LTUuNDY5IDAtNy41NTV6bTE5MS4zOTcgMzUuNTI5IDE1LjY3OSAxNS4yOWMyLjE0IDIuMDg2IDIuMTQgNS40NjkgMCA3LjU1NWwtNzAuNyA2OC45NDRjLTIuMTM5IDIuMDg3LTUuNjA4IDIuMDg3LTcuNzQ4IDBsLTUwLjE3OC00OC45MzFjLS41MzUtLjUyMi0xLjQwMi0uNTIyLTEuOTM3IDBsLTUwLjE3OCA0OC45MzFjLTIuMTM5IDIuMDg3LTUuNjA4IDIuMDg3LTcuNzQ4IDBsLTcwLjcwMTUtNjguOTQ1Yy0yLjEzOTYtMi4wODYtMi4xMzk2LTUuNDY5IDAtNy41NTVsMTUuNjc5NS0xNS4yOWMyLjEzOTYtMi4wODYgNS42MDg1LTIuMDg2IDcuNzQ4MSAwbDUwLjE3ODkgNDguOTMyYy41MzUuNTIyIDEuNDAyLjUyMiAxLjkzNyAwbDUwLjE3Ny00OC45MzJjMi4xMzktMi4wODcgNS42MDgtMi4wODcgNy43NDggMGw1MC4xNzkgNDguOTMyYy41MzUuNTIyIDEuNDAyLjUyMiAxLjkzNyAwbDUwLjE3OS00OC45MzFjMi4xMzktMi4wODcgNS42MDgtMi4wODcgNy43NDggMHoiIGZpbGw9IiNmZmYiLz48L2c+PC9zdmc+";
|
|
22
|
+
const APPKIT_SYMBOL = Symbol.for("kheopskit.cachedAppKit");
|
|
23
|
+
const APPKIT_PROJECT_ID_SYMBOL = Symbol.for("kheopskit.cachedAppKitProjectId");
|
|
24
|
+
const getCachedAppKit = () => globalThis[APPKIT_SYMBOL];
|
|
25
|
+
const getCachedAppKitProjectId = () => globalThis[APPKIT_PROJECT_ID_SYMBOL];
|
|
26
|
+
const setCachedAppKit = (value, projectId) => {
|
|
27
|
+
globalThis[APPKIT_SYMBOL] = value;
|
|
28
|
+
globalThis[APPKIT_PROJECT_ID_SYMBOL] = projectId;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Clears the cached AppKit observable.
|
|
32
|
+
* Use when configuration changes or for testing purposes.
|
|
33
|
+
* Note: This does NOT destroy the appKit instance created by Reown.
|
|
34
|
+
*/
|
|
35
|
+
const resetAppKitCache = () => {
|
|
36
|
+
setCachedAppKit(void 0);
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Drops a WalletConnect namespace's cached account observables. Called when a
|
|
40
|
+
* namespace transitions to disconnected — including external disconnects (from
|
|
41
|
+
* the wallet app), which flip status via `subscribeProviders` without ever
|
|
42
|
+
* calling `disconnect()` — so a later reconnect rebuilds the account observables
|
|
43
|
+
* against the fresh session instead of a stale closure.
|
|
44
|
+
*
|
|
45
|
+
* Keys are `accounts:<walletId>:...`; the trailing colon scopes the prefix to
|
|
46
|
+
* this wallet and avoids matching a sibling whose id is a string prefix.
|
|
47
|
+
*/
|
|
48
|
+
const dropAccountsCache = (platform) => {
|
|
49
|
+
clearCachedObservablesByPrefix(`accounts:${WALLET_CONNECT_WALLET_ID}:${platform}:`);
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* The single, platform-less WalletConnect connector for the given config — or
|
|
53
|
+
* `null` when WalletConnect isn't configured, on the server, or when
|
|
54
|
+
* `@reown/appkit` isn't installed.
|
|
55
|
+
*
|
|
56
|
+
* @remarks
|
|
57
|
+
* The AppKit instance is a process-wide singleton (Reown AppKit itself cannot
|
|
58
|
+
* be instantiated twice). The **first** call with a `walletConnect` config wins;
|
|
59
|
+
* later calls with a *different* `walletConnect` config reuse that first
|
|
60
|
+
* instance. Call {@link resetAppKitCache} before re-initialising if the
|
|
61
|
+
* WalletConnect config must change.
|
|
62
|
+
*
|
|
63
|
+
* One WalletConnect session is shared across every namespace, and a namespace
|
|
64
|
+
* can only be established during the initial pairing — it can't be added to a
|
|
65
|
+
* live session afterwards. So this exposes ONE connector (not one per platform):
|
|
66
|
+
* `connect()` opens the modal; the wallet approves whichever namespaces it
|
|
67
|
+
* supports in that single pairing, and `platforms` reflects what the live
|
|
68
|
+
* session carries. Per-namespace accounts are derived by each platform plugin.
|
|
69
|
+
*/
|
|
70
|
+
const getWalletConnectWallet$ = (config) => {
|
|
71
|
+
if (!config.walletConnect) return of(null);
|
|
72
|
+
if (typeof window === "undefined") return of(null);
|
|
73
|
+
const walletConnect = config.walletConnect;
|
|
74
|
+
const cachedProjectId = getCachedAppKitProjectId();
|
|
75
|
+
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);
|
|
76
|
+
let cachedAppKit = getCachedAppKit();
|
|
77
|
+
if (!cachedAppKit) {
|
|
78
|
+
cachedAppKit = from(loadAppKit()).pipe(switchMap((createAppKit) => {
|
|
79
|
+
if (!createAppKit) return of(null);
|
|
80
|
+
return new Observable((subscriber) => {
|
|
81
|
+
const appKit = createAppKit({
|
|
82
|
+
projectId: walletConnect.projectId,
|
|
83
|
+
metadata: walletConnect.metadata,
|
|
84
|
+
networks: walletConnect.networks,
|
|
85
|
+
themeMode: walletConnect.themeMode,
|
|
86
|
+
themeVariables: walletConnect.themeVariables,
|
|
87
|
+
universalProviderConfigOverride: { methods: {
|
|
88
|
+
polkadot: ["polkadot_signTransaction", "polkadot_signMessage"],
|
|
89
|
+
solana: [
|
|
90
|
+
"solana_signTransaction",
|
|
91
|
+
"solana_signMessage",
|
|
92
|
+
"solana_signAndSendTransaction"
|
|
93
|
+
]
|
|
94
|
+
} },
|
|
95
|
+
allWallets: "HIDE",
|
|
96
|
+
debug: config.debug,
|
|
97
|
+
allowUnsupportedChain: true
|
|
98
|
+
});
|
|
99
|
+
const appKitInstance = appKit;
|
|
100
|
+
const status$ = new BehaviorSubject({
|
|
101
|
+
isPolkadotConnected: false,
|
|
102
|
+
isEthereumConnected: false,
|
|
103
|
+
isSolanaConnected: false
|
|
104
|
+
});
|
|
105
|
+
const unsubProviders = appKit.subscribeProviders((providers) => {
|
|
106
|
+
status$.next({
|
|
107
|
+
isPolkadotConnected: !!providers.polkadot,
|
|
108
|
+
isEthereumConnected: !!providers.eip155,
|
|
109
|
+
isSolanaConnected: !!providers.solana
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
const namespaceOf = {
|
|
113
|
+
polkadot: "polkadot",
|
|
114
|
+
ethereum: "eip155",
|
|
115
|
+
solana: "solana"
|
|
116
|
+
};
|
|
117
|
+
const allPlatforms = [
|
|
118
|
+
"polkadot",
|
|
119
|
+
"ethereum",
|
|
120
|
+
"solana"
|
|
121
|
+
];
|
|
122
|
+
const enabledPlatforms = allPlatforms.filter((p) => appKit.chainNamespaces.includes(namespaceOf[p]));
|
|
123
|
+
let prevConnected = {
|
|
124
|
+
polkadot: false,
|
|
125
|
+
ethereum: false,
|
|
126
|
+
solana: false
|
|
127
|
+
};
|
|
128
|
+
const sub = status$.pipe(map((s) => ({
|
|
129
|
+
polkadot: s.isPolkadotConnected,
|
|
130
|
+
ethereum: s.isEthereumConnected,
|
|
131
|
+
solana: s.isSolanaConnected
|
|
132
|
+
})), distinctUntilChanged((a, b) => a.polkadot === b.polkadot && a.ethereum === b.ethereum && a.solana === b.solana), tap((connected) => {
|
|
133
|
+
for (const platform of allPlatforms) if (prevConnected[platform] && !connected[platform]) dropAccountsCache(platform);
|
|
134
|
+
prevConnected = connected;
|
|
135
|
+
}), map((connected) => {
|
|
136
|
+
const platforms = enabledPlatforms.filter((p) => connected[p]);
|
|
137
|
+
const isConnected = platforms.length > 0;
|
|
138
|
+
const walletInfo = appKit.getWalletInfo();
|
|
139
|
+
return {
|
|
140
|
+
id: WALLET_CONNECT_WALLET_ID,
|
|
141
|
+
type: "walletconnect",
|
|
142
|
+
platforms,
|
|
143
|
+
appKit: appKitInstance,
|
|
144
|
+
name: walletInfo?.name ?? "WalletConnect",
|
|
145
|
+
icon: walletInfo?.icon ?? WALLET_CONNECT_ICON,
|
|
146
|
+
connect: async () => {
|
|
147
|
+
if (!isConnected) await appKit.open();
|
|
148
|
+
},
|
|
149
|
+
disconnect: async () => {
|
|
150
|
+
if (isConnected) await appKit.disconnect();
|
|
151
|
+
},
|
|
152
|
+
isConnected
|
|
153
|
+
};
|
|
154
|
+
})).subscribe(subscriber);
|
|
155
|
+
return () => {
|
|
156
|
+
sub.unsubscribe();
|
|
157
|
+
unsubProviders();
|
|
158
|
+
};
|
|
159
|
+
});
|
|
160
|
+
}), shareReplay({
|
|
161
|
+
refCount: false,
|
|
162
|
+
bufferSize: 1
|
|
163
|
+
}));
|
|
164
|
+
setCachedAppKit(cachedAppKit, walletConnect.projectId);
|
|
165
|
+
}
|
|
166
|
+
return cachedAppKit;
|
|
167
|
+
};
|
|
168
|
+
//#endregion
|
|
169
|
+
//#region src/api/config.ts
|
|
170
|
+
/**
|
|
171
|
+
* Default storage key for persisting wallet connection state.
|
|
172
|
+
* Can be overridden via config.storageKey to avoid conflicts
|
|
173
|
+
* when running multiple dapps on the same domain.
|
|
174
|
+
*/
|
|
175
|
+
const DEFAULT_STORAGE_KEY = "kheopskit";
|
|
176
|
+
const DEFAULTS = {
|
|
177
|
+
autoReconnect: true,
|
|
178
|
+
debug: false,
|
|
179
|
+
storageKey: DEFAULT_STORAGE_KEY,
|
|
180
|
+
hydrationGracePeriod: 500
|
|
181
|
+
};
|
|
182
|
+
const resolveConfig = (config) => {
|
|
183
|
+
const platforms = config?.platforms ?? [];
|
|
184
|
+
const invalidPlatforms = platforms.filter((p) => typeof p !== "object" || p === null || typeof p.getWallets$ !== "function");
|
|
185
|
+
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.`);
|
|
186
|
+
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\".");
|
|
187
|
+
return Object.assign({}, DEFAULTS, config, { platforms });
|
|
188
|
+
};
|
|
189
|
+
//#endregion
|
|
190
|
+
//#region src/utils/sortAccounts.ts
|
|
191
|
+
const PLATFORM_ORDER$1 = {
|
|
192
|
+
polkadot: 0,
|
|
193
|
+
ethereum: 1,
|
|
194
|
+
solana: 2
|
|
195
|
+
};
|
|
196
|
+
const byWalletName = (a1, a2) => {
|
|
197
|
+
if (a1.walletName.toLowerCase() === "talisman") return -1;
|
|
198
|
+
if (a2.walletName.toLowerCase() === "talisman") return 1;
|
|
199
|
+
return a1.walletName.localeCompare(a2.walletName);
|
|
200
|
+
};
|
|
201
|
+
const sortAccounts = (a1, a2) => {
|
|
202
|
+
if (a1.platform !== a2.platform) return PLATFORM_ORDER$1[a1.platform] - PLATFORM_ORDER$1[a2.platform];
|
|
203
|
+
if (a1.walletName !== a2.walletName) return byWalletName(a1, a2);
|
|
204
|
+
if (a1.platform === "polkadot" && a2.platform === "polkadot") return a1.name !== a2.name ? (a1.name ?? "").localeCompare(a2.name ?? "") : a1.address.localeCompare(a2.address);
|
|
205
|
+
return a1.id.localeCompare(a2.id);
|
|
206
|
+
};
|
|
207
|
+
//#endregion
|
|
208
|
+
//#region src/utils/sortWallets.ts
|
|
209
|
+
const PLATFORM_ORDER = {
|
|
210
|
+
polkadot: 0,
|
|
211
|
+
ethereum: 1,
|
|
212
|
+
solana: 2
|
|
213
|
+
};
|
|
214
|
+
const orderOf = (wallet) => isWalletConnectWallet(wallet) ? 3 : PLATFORM_ORDER[wallet.platform];
|
|
215
|
+
const sortWallets = (w1, w2) => {
|
|
216
|
+
const o1 = orderOf(w1);
|
|
217
|
+
const o2 = orderOf(w2);
|
|
218
|
+
if (o1 !== o2) return o1 - o2;
|
|
219
|
+
if (w1.name.toLowerCase() === "talisman") return -1;
|
|
220
|
+
if (w2.name.toLowerCase() === "talisman") return 1;
|
|
221
|
+
return w1.name.localeCompare(w2.name);
|
|
222
|
+
};
|
|
223
|
+
//#endregion
|
|
224
|
+
//#region src/api/platform.ts
|
|
225
|
+
/**
|
|
226
|
+
* Whether a cached account should survive SSR hydration, per its platform
|
|
227
|
+
* plugin's `acceptsCachedAccount` hook. Plugins without the hook (and platforms
|
|
228
|
+
* with no enabled plugin) accept all — matching the pre-plugin behavior where
|
|
229
|
+
* only Polkadot accounts were filtered (by key type).
|
|
230
|
+
*/
|
|
231
|
+
const acceptsCachedAccount = (cached, platforms) => {
|
|
232
|
+
return platforms.find((p) => p.platform === cached.platform)?.acceptsCachedAccount?.(cached) ?? true;
|
|
233
|
+
};
|
|
234
|
+
//#endregion
|
|
235
|
+
//#region src/api/hydration.ts
|
|
236
|
+
/**
|
|
237
|
+
* Builds the cached-state snapshot every consumer must agree on: read the
|
|
238
|
+
* store's cached state, hydrate wallets/accounts into placeholders, filter
|
|
239
|
+
* accounts through each platform plugin's `acceptsCachedAccount` hook, and sort
|
|
240
|
+
* with the same comparators the live pipeline uses.
|
|
241
|
+
*
|
|
242
|
+
* This is the single implementation behind the observable's initial seed and
|
|
243
|
+
* both React snapshot sources (`serverValue`/`initialValue`). They must produce
|
|
244
|
+
* the same shape in the same order or the UI flickers/reorders on reload — by
|
|
245
|
+
* sharing this helper, that agreement holds by construction.
|
|
246
|
+
*/
|
|
247
|
+
const getHydratedSnapshot = (store, platforms, options = {}) => {
|
|
248
|
+
const cached = store.getCachedState();
|
|
249
|
+
return {
|
|
250
|
+
wallets: cached.wallets.map((cachedWallet) => {
|
|
251
|
+
const wallet = hydrateWallet(cachedWallet);
|
|
252
|
+
if (!options.icons || wallet.icon) return wallet;
|
|
253
|
+
const icon = getCachedIcon(wallet.id);
|
|
254
|
+
return icon ? {
|
|
255
|
+
...wallet,
|
|
256
|
+
icon
|
|
257
|
+
} : wallet;
|
|
258
|
+
}).sort(sortWallets),
|
|
259
|
+
accounts: cached.accounts.filter((account) => acceptsCachedAccount(account, platforms)).map(hydrateAccount).sort(sortAccounts)
|
|
260
|
+
};
|
|
261
|
+
};
|
|
262
|
+
//#endregion
|
|
263
|
+
//#region src/utils/createHydrationBuffer.ts
|
|
264
|
+
/**
|
|
265
|
+
* Core hydration buffer logic shared between wallets and accounts.
|
|
266
|
+
*
|
|
267
|
+
* During the grace period:
|
|
268
|
+
* - Emits cached items immediately (before any live emissions)
|
|
269
|
+
* - As live items arrive, merges them using the provided merge function
|
|
270
|
+
*
|
|
271
|
+
* After the grace period (and convergence, if provided):
|
|
272
|
+
* - Only live items are returned
|
|
273
|
+
* - isHydrating becomes false
|
|
274
|
+
*
|
|
275
|
+
* If an `isConverged` function is provided, hydration ends only when BOTH
|
|
276
|
+
* the grace period has elapsed AND the live items have converged with cached
|
|
277
|
+
* items (e.g., all previously-connected wallets have reconnected).
|
|
278
|
+
* A max timeout (6x grace period) prevents infinite hydration.
|
|
279
|
+
*
|
|
280
|
+
* @param cachedItems - Initial cached items from storage
|
|
281
|
+
* @param liveItems$ - Observable of live items as they become available
|
|
282
|
+
* @param gracePeriodMs - Time in ms to wait before syncing to live state
|
|
283
|
+
* @param mergeFn - Function to merge live and cached items during hydration
|
|
284
|
+
* @param isConverged - Optional function to check if live items have caught up to cached items
|
|
285
|
+
*/
|
|
286
|
+
const createBufferCore = (cachedItems, liveItems$, gracePeriodMs, mergeFn, isConverged) => {
|
|
287
|
+
if (gracePeriodMs <= 0 || cachedItems.length === 0) return liveItems$.pipe(map((items) => ({
|
|
288
|
+
items,
|
|
289
|
+
isHydrating: false
|
|
290
|
+
})));
|
|
291
|
+
return new Observable((subscriber) => {
|
|
292
|
+
const subscriptions = new Subscription();
|
|
293
|
+
const isHydrating$ = new BehaviorSubject(true);
|
|
294
|
+
const liveWithInitial$ = liveItems$.pipe(startWith([]));
|
|
295
|
+
if (isConverged) {
|
|
296
|
+
const timerFired$ = timer(gracePeriodMs).pipe(map(() => true), startWith(false), shareReplay({
|
|
297
|
+
bufferSize: 1,
|
|
298
|
+
refCount: true
|
|
299
|
+
}));
|
|
300
|
+
subscriptions.add(combineLatest([liveWithInitial$, timerFired$]).pipe(filter(([, timerFired]) => timerFired), filter(([liveItems]) => isConverged(liveItems, cachedItems)), take(1)).subscribe(() => {
|
|
301
|
+
isHydrating$.next(false);
|
|
302
|
+
}));
|
|
303
|
+
subscriptions.add(timer(gracePeriodMs * 6).subscribe(() => {
|
|
304
|
+
if (isHydrating$.value) isHydrating$.next(false);
|
|
305
|
+
}));
|
|
306
|
+
} else subscriptions.add(timer(gracePeriodMs).subscribe(() => {
|
|
307
|
+
isHydrating$.next(false);
|
|
308
|
+
}));
|
|
309
|
+
subscriptions.add(combineLatest([liveWithInitial$, isHydrating$]).pipe(map(([liveItems, isHydrating]) => {
|
|
310
|
+
if (!isHydrating) return {
|
|
311
|
+
items: liveItems,
|
|
312
|
+
isHydrating: false
|
|
313
|
+
};
|
|
314
|
+
return {
|
|
315
|
+
items: mergeFn(liveItems, cachedItems),
|
|
316
|
+
isHydrating: true
|
|
317
|
+
};
|
|
318
|
+
})).subscribe(subscriber));
|
|
319
|
+
return () => {
|
|
320
|
+
subscriptions.unsubscribe();
|
|
321
|
+
isHydrating$.complete();
|
|
322
|
+
};
|
|
323
|
+
});
|
|
324
|
+
};
|
|
325
|
+
/**
|
|
326
|
+
* Creates a hydration buffer that merges cached items with live items.
|
|
327
|
+
*
|
|
328
|
+
* During hydration, cached items take precedence over live items with the same key.
|
|
329
|
+
* This preserves the cached connected state (e.g., isConnected: true) while
|
|
330
|
+
* auto-reconnect is still in progress. New live items not in the cache are added.
|
|
331
|
+
*
|
|
332
|
+
* @param cachedItems - Initial cached items from storage
|
|
333
|
+
* @param liveItems$ - Observable of live items as they become available
|
|
334
|
+
* @param gracePeriodMs - Time in ms to wait before syncing to live state
|
|
335
|
+
* @param getKey - Function to extract a unique key from an item
|
|
336
|
+
* @param isConverged - Optional function to check if live items have caught up to cached items
|
|
337
|
+
* @param mergeItem - Optional function to merge a live item with its cached counterpart
|
|
338
|
+
* @param transformCachedOnly - Optional function to transform cached items not yet in the live stream
|
|
339
|
+
*/
|
|
340
|
+
const createHydrationBuffer = (cachedItems, liveItems$, gracePeriodMs, getKey, isConverged, mergeItem, transformCachedOnly) => {
|
|
341
|
+
return createBufferCore(cachedItems, liveItems$, gracePeriodMs, (liveItems, cached) => {
|
|
342
|
+
const cachedByKey = new Map(cached.map((item) => [getKey(item), item]));
|
|
343
|
+
const liveKeys = new Set(liveItems.map(getKey));
|
|
344
|
+
const mergedItems = liveItems.map((liveItem) => {
|
|
345
|
+
const key = getKey(liveItem);
|
|
346
|
+
const cachedItem = cachedByKey.get(key);
|
|
347
|
+
if (!cachedItem) return liveItem;
|
|
348
|
+
return mergeItem ? mergeItem(liveItem, cachedItem) : cachedItem;
|
|
349
|
+
});
|
|
350
|
+
for (const cachedItem of cached) if (!liveKeys.has(getKey(cachedItem))) {
|
|
351
|
+
const transformed = transformCachedOnly ? transformCachedOnly(cachedItem) : cachedItem;
|
|
352
|
+
mergedItems.push(transformed);
|
|
353
|
+
}
|
|
354
|
+
return mergedItems;
|
|
355
|
+
}, isConverged);
|
|
356
|
+
};
|
|
357
|
+
/**
|
|
358
|
+
* Creates a hydration buffer specifically for accounts, which need special handling.
|
|
359
|
+
*
|
|
360
|
+
* Accounts are grouped by wallet - when a wallet provides its account list,
|
|
361
|
+
* we replace all cached accounts for that wallet with the live ones.
|
|
362
|
+
* Cached accounts for wallets that haven't provided accounts yet are preserved.
|
|
363
|
+
*
|
|
364
|
+
* @param cachedAccounts - Initial cached accounts from storage
|
|
365
|
+
* @param liveAccounts$ - Observable of live accounts as they become available
|
|
366
|
+
* @param gracePeriodMs - Time in ms to wait before syncing to live state
|
|
367
|
+
* @param getWalletId - Function to extract the wallet ID from an account
|
|
368
|
+
* @param isConverged - Optional function to check if live accounts have caught up to cached accounts
|
|
369
|
+
*/
|
|
370
|
+
const createAccountHydrationBuffer = (cachedAccounts, liveAccounts$, gracePeriodMs, getWalletId, isConverged) => {
|
|
371
|
+
return createBufferCore(cachedAccounts, liveAccounts$, gracePeriodMs, (liveAccounts, cached) => {
|
|
372
|
+
const walletsWithLiveAccounts = new Set(liveAccounts.map(getWalletId));
|
|
373
|
+
const mergedAccounts = [...liveAccounts];
|
|
374
|
+
for (const cachedAccount of cached) if (!walletsWithLiveAccounts.has(getWalletId(cachedAccount))) mergedAccounts.push(cachedAccount);
|
|
375
|
+
return mergedAccounts;
|
|
376
|
+
}, isConverged);
|
|
377
|
+
};
|
|
378
|
+
//#endregion
|
|
379
|
+
//#region src/utils/logObservable.ts
|
|
380
|
+
const logObservable = (label, opts) => tap((value) => {
|
|
381
|
+
const { printValue = false, enabled = true } = opts || {};
|
|
382
|
+
if (!label || !enabled) return;
|
|
383
|
+
const text = `[kheopskit] observable ${label} emit`;
|
|
384
|
+
if (printValue) console.debug(text, value);
|
|
385
|
+
else console.debug(text);
|
|
386
|
+
});
|
|
387
|
+
//#endregion
|
|
388
|
+
//#region src/api/accounts.ts
|
|
389
|
+
const getAccounts$ = (config, wallets) => {
|
|
390
|
+
return new Observable((subscriber) => {
|
|
391
|
+
const sources = config.platforms.map((plugin) => plugin.getAccounts$(wallets.pipe(map((ws) => ws.filter((w) => isWalletConnectWallet(w) || w.platform === plugin.platform)))));
|
|
392
|
+
const sub = (sources.length ? combineLatest(sources).pipe(map((accounts) => accounts.flat().sort(sortAccounts))) : of([])).subscribe(subscriber);
|
|
393
|
+
return () => {
|
|
394
|
+
sub.unsubscribe();
|
|
395
|
+
};
|
|
396
|
+
}).pipe(shareReplay({
|
|
397
|
+
refCount: true,
|
|
398
|
+
bufferSize: 1
|
|
399
|
+
}));
|
|
400
|
+
};
|
|
401
|
+
//#endregion
|
|
402
|
+
//#region src/utils/createStore.ts
|
|
403
|
+
const createStore = (key, defaultValue, storage = safeLocalStorage) => {
|
|
404
|
+
const subject = new BehaviorSubject(getStoredData(key, defaultValue, storage));
|
|
405
|
+
let unsubscribeStorage;
|
|
406
|
+
if (typeof window !== "undefined" && storage.subscribe) unsubscribeStorage = storage.subscribe(key, (newValue) => {
|
|
407
|
+
subject.next(parseData(newValue, defaultValue));
|
|
408
|
+
});
|
|
409
|
+
const update = (val) => {
|
|
410
|
+
setStoredData(key, val, storage);
|
|
411
|
+
subject.next(val);
|
|
412
|
+
};
|
|
413
|
+
return {
|
|
414
|
+
observable: subject.asObservable(),
|
|
415
|
+
set: (val) => update(val),
|
|
416
|
+
mutate: (transform) => update(transform(subject.getValue())),
|
|
417
|
+
get: () => structuredClone(subject.getValue()),
|
|
418
|
+
/**
|
|
419
|
+
* Cleanup subscriptions. Call this when the store is no longer needed.
|
|
420
|
+
*/
|
|
421
|
+
destroy: () => {
|
|
422
|
+
unsubscribeStorage?.();
|
|
423
|
+
subject.complete();
|
|
424
|
+
}
|
|
425
|
+
};
|
|
426
|
+
};
|
|
427
|
+
const parseData = (str, defaultValue) => {
|
|
428
|
+
try {
|
|
429
|
+
if (str) return JSON.parse(str);
|
|
430
|
+
} catch {}
|
|
431
|
+
return defaultValue;
|
|
432
|
+
};
|
|
433
|
+
const getStoredData = (key, defaultValue, storage) => {
|
|
434
|
+
const str = storage.getItem(key);
|
|
435
|
+
return parseData(str, defaultValue);
|
|
436
|
+
};
|
|
437
|
+
const setStoredData = (key, val, storage) => {
|
|
438
|
+
const str = JSON.stringify(val);
|
|
439
|
+
storage.setItem(key, str);
|
|
440
|
+
};
|
|
441
|
+
//#endregion
|
|
442
|
+
//#region src/api/compactStore.ts
|
|
443
|
+
const EMPTY_STORE_DATA = {};
|
|
444
|
+
const toCompactPolkadotAccountType = (type) => {
|
|
445
|
+
switch (type) {
|
|
446
|
+
case "sr25519": return 0;
|
|
447
|
+
case "ed25519": return 1;
|
|
448
|
+
case "ecdsa": return 2;
|
|
449
|
+
case "ethereum": return 3;
|
|
450
|
+
default: return null;
|
|
451
|
+
}
|
|
452
|
+
};
|
|
453
|
+
const fromCompactPolkadotAccountType = (type) => {
|
|
454
|
+
switch (type) {
|
|
455
|
+
case 0: return "sr25519";
|
|
456
|
+
case 1: return "ed25519";
|
|
457
|
+
case 2: return "ecdsa";
|
|
458
|
+
case 3: return "ethereum";
|
|
459
|
+
default: return;
|
|
460
|
+
}
|
|
461
|
+
};
|
|
462
|
+
const toCompactPlatform = (platform) => {
|
|
463
|
+
switch (platform) {
|
|
464
|
+
case "polkadot": return 0;
|
|
465
|
+
case "ethereum": return 1;
|
|
466
|
+
case "solana": return 2;
|
|
467
|
+
}
|
|
468
|
+
};
|
|
469
|
+
const fromCompactPlatform = (code) => {
|
|
470
|
+
switch (code) {
|
|
471
|
+
case 0: return "polkadot";
|
|
472
|
+
case 1: return "ethereum";
|
|
473
|
+
case 2: return "solana";
|
|
474
|
+
default: return;
|
|
475
|
+
}
|
|
476
|
+
};
|
|
477
|
+
const isCompactStore = (value) => {
|
|
478
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
479
|
+
if ("cachedWallets" in value || "cachedAccounts" in value) return false;
|
|
480
|
+
return "v" in value || "w" in value || "a" in value || "r" in value;
|
|
481
|
+
};
|
|
482
|
+
const toCompactStore = (data) => {
|
|
483
|
+
const wallets = data.cachedWallets?.map((wallet) => [
|
|
484
|
+
wallet.id,
|
|
485
|
+
wallet.name,
|
|
486
|
+
wallet.isConnected ? 1 : 0,
|
|
487
|
+
wallet.type === "walletconnect" ? 1 : 0
|
|
488
|
+
]);
|
|
489
|
+
const accounts = data.cachedAccounts?.map((account) => [
|
|
490
|
+
account.walletId,
|
|
491
|
+
account.address,
|
|
492
|
+
account.name ?? null,
|
|
493
|
+
account.chainId ?? null,
|
|
494
|
+
toCompactPolkadotAccountType(account.polkadotAccountType),
|
|
495
|
+
toCompactPlatform(account.platform)
|
|
496
|
+
]);
|
|
497
|
+
return {
|
|
498
|
+
v: 1,
|
|
499
|
+
r: data.autoReconnect,
|
|
500
|
+
w: wallets?.length ? wallets : void 0,
|
|
501
|
+
a: accounts?.length ? accounts : void 0
|
|
502
|
+
};
|
|
503
|
+
};
|
|
504
|
+
const fromCompactStore = (data) => {
|
|
505
|
+
const walletNameMap = /* @__PURE__ */ new Map();
|
|
506
|
+
const wallets = [];
|
|
507
|
+
for (const item of Array.isArray(data.w) ? data.w : []) {
|
|
508
|
+
if (!Array.isArray(item)) continue;
|
|
509
|
+
const [id, name, isConnected, type] = item;
|
|
510
|
+
if (!isValidWalletId(id)) continue;
|
|
511
|
+
const isWalletConnect = id === WALLET_CONNECT_WALLET_ID;
|
|
512
|
+
const walletType = type === 1 ? "walletconnect" : "injected";
|
|
513
|
+
if (isWalletConnect !== (walletType === "walletconnect")) continue;
|
|
514
|
+
walletNameMap.set(id, name);
|
|
515
|
+
wallets.push({
|
|
516
|
+
id,
|
|
517
|
+
platform: isWalletConnect ? void 0 : parseWalletId(id).platform,
|
|
518
|
+
type: walletType,
|
|
519
|
+
name,
|
|
520
|
+
isConnected: isConnected === 1
|
|
521
|
+
});
|
|
522
|
+
}
|
|
523
|
+
const accounts = [];
|
|
524
|
+
for (const item of Array.isArray(data.a) ? data.a : []) {
|
|
525
|
+
if (!Array.isArray(item)) continue;
|
|
526
|
+
const [walletId, address, name, chainId, polkadotAccountType, platformCode] = item;
|
|
527
|
+
if (!isValidWalletId(walletId) || typeof address !== "string" || !address) continue;
|
|
528
|
+
const platform = platformCode != null ? fromCompactPlatform(platformCode) : walletId === "walletconnect" ? void 0 : parseWalletId(walletId).platform;
|
|
529
|
+
if (!isWalletPlatform(platform)) continue;
|
|
530
|
+
accounts.push({
|
|
531
|
+
id: getWalletAccountId(walletId, address),
|
|
532
|
+
platform,
|
|
533
|
+
address,
|
|
534
|
+
name: name ?? void 0,
|
|
535
|
+
chainId: chainId ?? void 0,
|
|
536
|
+
polkadotAccountType: platform === "polkadot" ? fromCompactPolkadotAccountType(polkadotAccountType) : void 0,
|
|
537
|
+
walletId,
|
|
538
|
+
walletName: walletNameMap.get(walletId) ?? walletId
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
return {
|
|
542
|
+
autoReconnect: data.r,
|
|
543
|
+
cachedWallets: wallets,
|
|
544
|
+
cachedAccounts: accounts
|
|
545
|
+
};
|
|
546
|
+
};
|
|
547
|
+
const decodeStore = (raw, fallback) => {
|
|
548
|
+
try {
|
|
549
|
+
const parsed = JSON.parse(raw);
|
|
550
|
+
if (isCompactStore(parsed)) return fromCompactStore(parsed);
|
|
551
|
+
return parsed;
|
|
552
|
+
} catch {
|
|
553
|
+
return fallback;
|
|
554
|
+
}
|
|
555
|
+
};
|
|
556
|
+
const encodeStore = (data) => JSON.stringify(toCompactStore(data));
|
|
557
|
+
/**
|
|
558
|
+
* Cookie storage that transparently encodes/decodes {@link CompactStoreV1},
|
|
559
|
+
* migrating legacy verbose-JSON cookies to the compact format on first read.
|
|
560
|
+
*/
|
|
561
|
+
const createCompactCookieStorage = (initialCookies) => {
|
|
562
|
+
const base = cookieStorage(initialCookies);
|
|
563
|
+
return {
|
|
564
|
+
getItem: (key) => {
|
|
565
|
+
const raw = base.getItem(key);
|
|
566
|
+
if (!raw) return null;
|
|
567
|
+
const expanded = decodeStore(raw, EMPTY_STORE_DATA);
|
|
568
|
+
if (typeof document !== "undefined") try {
|
|
569
|
+
const parsed = JSON.parse(raw);
|
|
570
|
+
if (!isCompactStore(parsed)) base.setItem(key, encodeStore(expanded));
|
|
571
|
+
} catch {}
|
|
572
|
+
return JSON.stringify(expanded);
|
|
573
|
+
},
|
|
574
|
+
setItem: (key, value) => {
|
|
575
|
+
const expanded = decodeStore(value, EMPTY_STORE_DATA);
|
|
576
|
+
base.setItem(key, encodeStore(expanded));
|
|
577
|
+
},
|
|
578
|
+
removeItem: base.removeItem,
|
|
579
|
+
subscribe: (key, callback) => {
|
|
580
|
+
const unsubscribe = base.subscribe?.(key, (value) => {
|
|
581
|
+
if (!value) {
|
|
582
|
+
callback(null);
|
|
583
|
+
return;
|
|
584
|
+
}
|
|
585
|
+
const expanded = decodeStore(value, EMPTY_STORE_DATA);
|
|
586
|
+
callback(JSON.stringify(expanded));
|
|
587
|
+
});
|
|
588
|
+
return () => {
|
|
589
|
+
unsubscribe?.();
|
|
590
|
+
};
|
|
591
|
+
}
|
|
592
|
+
};
|
|
593
|
+
};
|
|
594
|
+
//#endregion
|
|
595
|
+
//#region src/api/store.ts
|
|
596
|
+
const DEFAULT_SETTINGS = {};
|
|
597
|
+
/**
|
|
598
|
+
* Validates a cached wallet read from persisted storage. Cached state may have
|
|
599
|
+
* been written by an older (or corrupted) version with a different shape, so we
|
|
600
|
+
* drop anything that wouldn't survive hydration/sort rather than letting it
|
|
601
|
+
* throw at render time. Only the fields downstream code relies on are checked.
|
|
602
|
+
*/
|
|
603
|
+
const isValidCachedWallet = (value) => {
|
|
604
|
+
if (!value || typeof value !== "object") return false;
|
|
605
|
+
const w = value;
|
|
606
|
+
if (!isValidWalletId(w.id)) return false;
|
|
607
|
+
if (typeof w.name !== "string" || typeof w.isConnected !== "boolean") return false;
|
|
608
|
+
if (w.type === "walletconnect") return w.id === "walletconnect" && w.platform === void 0;
|
|
609
|
+
return w.type === "injected" && isWalletPlatform(w.platform);
|
|
610
|
+
};
|
|
611
|
+
/** Validates a cached account read from persisted storage. See {@link isValidCachedWallet}. */
|
|
612
|
+
const isValidCachedAccount = (value) => {
|
|
613
|
+
if (!value || typeof value !== "object") return false;
|
|
614
|
+
const a = value;
|
|
615
|
+
return typeof a.id === "string" && !!a.id && isWalletPlatform(a.platform) && typeof a.address === "string" && !!a.address && isValidWalletId(a.walletId) && typeof a.walletName === "string";
|
|
616
|
+
};
|
|
617
|
+
/**
|
|
618
|
+
* Creates a kheopskit store with the appropriate storage backend.
|
|
619
|
+
* Uses cookieStorage when ssrCookies is provided (for SSR hydration),
|
|
620
|
+
* otherwise falls back to safeLocalStorage.
|
|
621
|
+
*
|
|
622
|
+
* @param options - Configuration options for the store
|
|
623
|
+
*/
|
|
624
|
+
const createKheopskitStore = (options = {}) => {
|
|
625
|
+
const { ssrCookies, storageKey = DEFAULT_STORAGE_KEY } = options;
|
|
626
|
+
const storage = ssrCookies !== void 0 ? createCompactCookieStorage(ssrCookies) : safeLocalStorage;
|
|
627
|
+
const store = createStore(storageKey, DEFAULT_SETTINGS, storage);
|
|
628
|
+
const addEnabledWalletId = (walletId) => {
|
|
629
|
+
parseWalletId(walletId);
|
|
630
|
+
store.mutate((prev) => ({
|
|
631
|
+
...prev,
|
|
632
|
+
autoReconnect: uniq((prev.autoReconnect ?? []).concat(walletId))
|
|
633
|
+
}));
|
|
634
|
+
};
|
|
635
|
+
const removeEnabledWalletId = (walletId) => {
|
|
636
|
+
store.mutate((prev) => ({
|
|
637
|
+
...prev,
|
|
638
|
+
autoReconnect: uniq((prev.autoReconnect ?? []).filter((id) => id !== walletId))
|
|
639
|
+
}));
|
|
640
|
+
};
|
|
641
|
+
const getCachedState = () => {
|
|
642
|
+
const data = store.get();
|
|
643
|
+
const cachedWallets = Array.isArray(data?.cachedWallets) ? data.cachedWallets : [];
|
|
644
|
+
const cachedAccounts = Array.isArray(data?.cachedAccounts) ? data.cachedAccounts : [];
|
|
645
|
+
return {
|
|
646
|
+
wallets: cachedWallets.filter(isValidCachedWallet),
|
|
647
|
+
accounts: cachedAccounts.filter(isValidCachedAccount)
|
|
648
|
+
};
|
|
649
|
+
};
|
|
650
|
+
const setCachedState = (wallets, accounts) => {
|
|
651
|
+
store.mutate((prev) => ({
|
|
652
|
+
...prev,
|
|
653
|
+
cachedWallets: wallets,
|
|
654
|
+
cachedAccounts: accounts
|
|
655
|
+
}));
|
|
656
|
+
};
|
|
657
|
+
return {
|
|
658
|
+
observable: store.observable,
|
|
659
|
+
addEnabledWalletId,
|
|
660
|
+
removeEnabledWalletId,
|
|
661
|
+
getCachedState,
|
|
662
|
+
setCachedState
|
|
663
|
+
};
|
|
664
|
+
};
|
|
665
|
+
/**
|
|
666
|
+
* Cached default store instance, anchored on globalThis so it stays a single
|
|
667
|
+
* instance even if this module is duplicated across bundle chunks (e.g. CJS
|
|
668
|
+
* subpath entries). Lazily initialized on first access to be SSR-safe.
|
|
669
|
+
*/
|
|
670
|
+
const DEFAULT_STORE_SYMBOL = Symbol.for("kheopskit.defaultStore");
|
|
671
|
+
/**
|
|
672
|
+
* Gets the default store, creating it on first access.
|
|
673
|
+
* Uses localStorage on client, noop on server.
|
|
674
|
+
* Lazily initialized to avoid SSR issues with module-level code.
|
|
675
|
+
*/
|
|
676
|
+
const getDefaultStore = () => {
|
|
677
|
+
const g = globalThis;
|
|
678
|
+
if (!g[DEFAULT_STORE_SYMBOL]) g[DEFAULT_STORE_SYMBOL] = createKheopskitStore();
|
|
679
|
+
return g[DEFAULT_STORE_SYMBOL];
|
|
680
|
+
};
|
|
681
|
+
//#endregion
|
|
682
|
+
//#region src/api/reconnectPolicy.ts
|
|
683
|
+
const createReconnectPolicy = ({ maxAttempts = 3 } = {}) => {
|
|
684
|
+
const reconnecting = /* @__PURE__ */ new Set();
|
|
685
|
+
const reconnected = /* @__PURE__ */ new Set();
|
|
686
|
+
const failedAttempts = /* @__PURE__ */ new Map();
|
|
687
|
+
const shouldAttempt = (walletId) => !reconnecting.has(walletId) && !reconnected.has(walletId) && (failedAttempts.get(walletId) ?? 0) < maxAttempts;
|
|
688
|
+
/**
|
|
689
|
+
* Runs `connect` under the policy. Resolves `false` without connecting when
|
|
690
|
+
* an attempt is already in flight, the wallet already reconnected, or it
|
|
691
|
+
* exhausted its attempts; resolves `true` on success. A rejection from
|
|
692
|
+
* `connect` is recorded as a failed attempt and rethrown for the caller to
|
|
693
|
+
* report.
|
|
694
|
+
*/
|
|
695
|
+
const attempt = async (walletId, connect) => {
|
|
696
|
+
if (!shouldAttempt(walletId)) return false;
|
|
697
|
+
reconnecting.add(walletId);
|
|
698
|
+
try {
|
|
699
|
+
await connect();
|
|
700
|
+
reconnected.add(walletId);
|
|
701
|
+
failedAttempts.delete(walletId);
|
|
702
|
+
return true;
|
|
703
|
+
} catch (err) {
|
|
704
|
+
failedAttempts.set(walletId, (failedAttempts.get(walletId) ?? 0) + 1);
|
|
705
|
+
throw err;
|
|
706
|
+
} finally {
|
|
707
|
+
reconnecting.delete(walletId);
|
|
708
|
+
}
|
|
709
|
+
};
|
|
710
|
+
return {
|
|
711
|
+
shouldAttempt,
|
|
712
|
+
attempt
|
|
713
|
+
};
|
|
714
|
+
};
|
|
715
|
+
//#endregion
|
|
716
|
+
//#region src/api/wallets.ts
|
|
717
|
+
const getWallets$ = (config, store) => {
|
|
718
|
+
const autoReconnectWalletIds$ = store.observable.pipe(map((s) => s.autoReconnect ?? []), take(1), shareReplay({
|
|
719
|
+
bufferSize: 1,
|
|
720
|
+
refCount: true
|
|
721
|
+
}));
|
|
722
|
+
return new Observable((subscriber) => {
|
|
723
|
+
const ctx = {
|
|
724
|
+
config,
|
|
725
|
+
store
|
|
726
|
+
};
|
|
727
|
+
const observables = config.platforms.map((plugin) => plugin.getWallets$(ctx));
|
|
728
|
+
const wallets$ = combineLatest([observables.length ? combineLatest(observables).pipe(map((wallets) => wallets.flat())) : of([]), getWalletConnectWallet$(config)]).pipe(map(([platformWallets, walletConnect]) => {
|
|
729
|
+
return (walletConnect ? [...platformWallets, walletConnect] : platformWallets).sort(sortWallets);
|
|
730
|
+
}));
|
|
731
|
+
const reconnectPolicy = createReconnectPolicy();
|
|
732
|
+
const subAutoReconnect = combineLatest([wallets$, autoReconnectWalletIds$]).pipe(filter(([, walletIds]) => config.autoReconnect && !!walletIds?.length), mergeMap(([wallets, walletIds]) => wallets.filter((wallet) => walletIds?.includes(wallet.id)))).subscribe(async (wallet) => {
|
|
733
|
+
if (wallet.isConnected) return;
|
|
734
|
+
try {
|
|
735
|
+
await reconnectPolicy.attempt(wallet.id, () => wallet.connect());
|
|
736
|
+
} catch (err) {
|
|
737
|
+
console.error("Failed to reconnect wallet %s", wallet.id, { err });
|
|
738
|
+
}
|
|
739
|
+
});
|
|
740
|
+
const subWallets = wallets$.subscribe(subscriber);
|
|
741
|
+
return () => {
|
|
742
|
+
subAutoReconnect.unsubscribe();
|
|
743
|
+
subWallets.unsubscribe();
|
|
744
|
+
};
|
|
745
|
+
}).pipe(shareReplay({
|
|
746
|
+
refCount: true,
|
|
747
|
+
bufferSize: 1
|
|
748
|
+
}));
|
|
749
|
+
};
|
|
750
|
+
//#endregion
|
|
751
|
+
//#region src/api/kheopskit.ts
|
|
752
|
+
const getKheopskit$ = (config, options = {}) => {
|
|
753
|
+
const { ssrCookies, store: existingStore } = options;
|
|
754
|
+
const kc = resolveConfig(config);
|
|
755
|
+
const store = existingStore ?? createKheopskitStore({
|
|
756
|
+
ssrCookies,
|
|
757
|
+
storageKey: kc.storageKey
|
|
758
|
+
});
|
|
759
|
+
if (kc.debug) console.debug("[kheopskit] config", kc);
|
|
760
|
+
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.");
|
|
761
|
+
const { wallets: cachedWallets, accounts: cachedAccounts } = getHydratedSnapshot(store, kc.platforms, { icons: true });
|
|
762
|
+
if (kc.debug && cachedWallets.length > 0) console.debug("[kheopskit] hydrating from cache:", {
|
|
763
|
+
wallets: cachedWallets.length,
|
|
764
|
+
accounts: cachedAccounts.length
|
|
765
|
+
});
|
|
766
|
+
return new Observable((subscriber) => {
|
|
767
|
+
const liveWallets$ = getWallets$(kc, store);
|
|
768
|
+
const liveAccounts$ = getAccounts$(kc, liveWallets$);
|
|
769
|
+
const bufferedWallets$ = createHydrationBuffer(cachedWallets, liveWallets$, kc.hydrationGracePeriod, (w) => w.id, (liveWallets, cached) => {
|
|
770
|
+
const cachedConnectedIds = new Set(cached.filter((w) => w.isConnected).map((w) => w.id));
|
|
771
|
+
if (cachedConnectedIds.size === 0) return true;
|
|
772
|
+
return [...cachedConnectedIds].every((id) => liveWallets.some((w) => w.id === id && w.isConnected));
|
|
773
|
+
}, (live, cached) => ({
|
|
774
|
+
...cached,
|
|
775
|
+
icon: cached.icon || getCachedIcon(cached.id) || live.icon,
|
|
776
|
+
connect: live.connect,
|
|
777
|
+
disconnect: live.disconnect
|
|
778
|
+
}), (cached) => ({
|
|
779
|
+
...cached,
|
|
780
|
+
icon: cached.icon || getCachedIcon(cached.id) || ""
|
|
781
|
+
}));
|
|
782
|
+
const bufferedAccounts$ = createAccountHydrationBuffer(cachedAccounts, liveAccounts$, kc.hydrationGracePeriod, (a) => a.walletId, (liveAccounts, cached) => {
|
|
783
|
+
const cachedWalletIds = new Set(cached.map((a) => a.walletId));
|
|
784
|
+
if (cachedWalletIds.size === 0) return true;
|
|
785
|
+
const liveWalletIds = new Set(liveAccounts.map((a) => a.walletId));
|
|
786
|
+
return [...cachedWalletIds].every((id) => liveWalletIds.has(id));
|
|
787
|
+
});
|
|
788
|
+
const sharedWallets$ = bufferedWallets$.pipe(shareReplay({
|
|
789
|
+
bufferSize: 1,
|
|
790
|
+
refCount: true
|
|
791
|
+
}));
|
|
792
|
+
const sharedAccounts$ = bufferedAccounts$.pipe(shareReplay({
|
|
793
|
+
bufferSize: 1,
|
|
794
|
+
refCount: true
|
|
795
|
+
}));
|
|
796
|
+
const subscription = combineLatest({
|
|
797
|
+
wallets: sharedWallets$,
|
|
798
|
+
accounts: sharedAccounts$
|
|
799
|
+
}).pipe(map(({ wallets, accounts }) => {
|
|
800
|
+
if (kc.debug) console.debug("[kheopskit] hydration state", {
|
|
801
|
+
walletsHydrating: wallets.isHydrating,
|
|
802
|
+
accountsHydrating: accounts.isHydrating,
|
|
803
|
+
walletsConnected: wallets.items.filter((w) => w.isConnected).length,
|
|
804
|
+
walletsTotal: wallets.items.length
|
|
805
|
+
});
|
|
806
|
+
return {
|
|
807
|
+
config: kc,
|
|
808
|
+
wallets: [...wallets.items].sort(sortWallets),
|
|
809
|
+
accounts: [...accounts.items].sort(sortAccounts),
|
|
810
|
+
isHydrating: wallets.isHydrating || accounts.isHydrating
|
|
811
|
+
};
|
|
812
|
+
})).subscribe(subscriber);
|
|
813
|
+
const persistSub = combineLatest({
|
|
814
|
+
wallets: sharedWallets$,
|
|
815
|
+
accounts: sharedAccounts$
|
|
816
|
+
}).pipe(filter(({ wallets, accounts }) => !wallets.isHydrating && !accounts.isHydrating), debounceTime(1e3), distinctUntilChanged((prev, curr) => {
|
|
817
|
+
const prevWalletKeys = prev.wallets.items.map(walletPersistKey);
|
|
818
|
+
const currWalletKeys = curr.wallets.items.map(walletPersistKey);
|
|
819
|
+
const prevAccountKeys = prev.accounts.items.map(accountChangeKey);
|
|
820
|
+
const currAccountKeys = curr.accounts.items.map(accountChangeKey);
|
|
821
|
+
return arraysEqual(prevWalletKeys, currWalletKeys) && arraysEqual(prevAccountKeys, currAccountKeys);
|
|
822
|
+
})).subscribe(({ wallets, accounts }) => {
|
|
823
|
+
const connectedWalletIds = new Set(wallets.items.filter((w) => w.isConnected).map((w) => w.id));
|
|
824
|
+
const relevantAccounts = accounts.items.filter((a) => connectedWalletIds.has(a.walletId));
|
|
825
|
+
if (kc.debug) console.debug("[kheopskit] persisting state snapshot:", {
|
|
826
|
+
wallets: wallets.items.length,
|
|
827
|
+
accounts: relevantAccounts.length
|
|
828
|
+
});
|
|
829
|
+
store.setCachedState(wallets.items.map(serializeWallet), relevantAccounts.map(serializeAccount));
|
|
830
|
+
const icons = {};
|
|
831
|
+
for (const wallet of wallets.items) if (wallet.icon) icons[wallet.id] = wallet.icon;
|
|
832
|
+
setCachedIcons(icons);
|
|
833
|
+
});
|
|
834
|
+
return () => {
|
|
835
|
+
subscription.unsubscribe();
|
|
836
|
+
persistSub.unsubscribe();
|
|
837
|
+
};
|
|
838
|
+
}).pipe(distinctUntilChanged(statesEqual), throttleTime(16, void 0, {
|
|
839
|
+
leading: true,
|
|
840
|
+
trailing: true
|
|
841
|
+
}), logObservable("kheopskit$", {
|
|
842
|
+
enabled: kc.debug,
|
|
843
|
+
printValue: true
|
|
844
|
+
}), shareReplay({
|
|
845
|
+
bufferSize: 1,
|
|
846
|
+
refCount: true
|
|
847
|
+
}));
|
|
848
|
+
};
|
|
849
|
+
const arraysEqual = (a, b) => a.length === b.length && a.every((v, i) => v === b[i]);
|
|
850
|
+
/**
|
|
851
|
+
* A signature of an account covering its id plus the platform-specific fields
|
|
852
|
+
* that can change without the id changing (Ethereum chainId, Polkadot key type,
|
|
853
|
+
* Solana clusters). Used to detect both UI-relevant changes and changes worth
|
|
854
|
+
* re-persisting, so the two comparators can't drift apart.
|
|
855
|
+
*/
|
|
856
|
+
const accountChangeKey = (account) => {
|
|
857
|
+
switch (account.platform) {
|
|
858
|
+
case "ethereum": return `${account.id}|${account.chainId ?? ""}`;
|
|
859
|
+
case "polkadot": return `${account.id}|${account.type ?? ""}`;
|
|
860
|
+
case "solana": return `${account.id}|${(account.chains ?? []).join(",")}`;
|
|
861
|
+
default: return account.id;
|
|
862
|
+
}
|
|
863
|
+
};
|
|
864
|
+
/** Platforms a wallet's live session carries (only the WalletConnect connector). */
|
|
865
|
+
const walletPlatforms = (wallet) => isWalletConnectWallet(wallet) ? wallet.platforms.join(",") : "";
|
|
866
|
+
/** Fields persisted to the cache for a wallet (icon is stored separately). */
|
|
867
|
+
const walletPersistKey = (wallet) => `${wallet.id}|${wallet.isConnected ? 1 : 0}|${wallet.name}`;
|
|
868
|
+
/**
|
|
869
|
+
* UI-significant signature of a wallet: the persisted fields plus icon and the
|
|
870
|
+
* WalletConnect connector's `platforms` array, which drive what the UI shows but
|
|
871
|
+
* aren't part of the id (icon and platforms can arrive asynchronously after
|
|
872
|
+
* connect without isConnected flipping).
|
|
873
|
+
*/
|
|
874
|
+
const walletUiKey = (wallet) => `${walletPersistKey(wallet)}|${wallet.icon}|${walletPlatforms(wallet)}`;
|
|
875
|
+
/**
|
|
876
|
+
* Equality check for KheopskitState to prevent unnecessary emissions. Compares
|
|
877
|
+
* the UI-significant signature of every wallet and account, in order.
|
|
878
|
+
*/
|
|
879
|
+
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) => {
|
|
880
|
+
const other = b.wallets[i];
|
|
881
|
+
return !!other && walletUiKey(w) === walletUiKey(other);
|
|
997
882
|
}) && a.accounts.every((acc, i) => {
|
|
998
|
-
|
|
999
|
-
|
|
883
|
+
const other = b.accounts[i];
|
|
884
|
+
return !!other && accountChangeKey(acc) === accountChangeKey(other);
|
|
1000
885
|
});
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
clearAllCachedObservables,
|
|
1005
|
-
createKheopskitStore,
|
|
1006
|
-
getDefaultStore,
|
|
1007
|
-
getHydratedSnapshot,
|
|
1008
|
-
getKheopskit$,
|
|
1009
|
-
getWalletAccountId,
|
|
1010
|
-
getWalletId,
|
|
1011
|
-
isInjectedWallet,
|
|
1012
|
-
isValidAddress,
|
|
1013
|
-
isValidWalletId,
|
|
1014
|
-
isWalletConnectWallet,
|
|
1015
|
-
parseWalletAccountId,
|
|
1016
|
-
parseWalletId,
|
|
1017
|
-
resetAppKitCache,
|
|
1018
|
-
resolveConfig
|
|
1019
|
-
};
|
|
886
|
+
//#endregion
|
|
887
|
+
export { DEFAULT_STORAGE_KEY, KheopskitError, clearAllCachedObservables, createKheopskitStore, getDefaultStore, getHydratedSnapshot, getKheopskit$, getWalletAccountId, getWalletId, isInjectedWallet, isValidAddress, isValidWalletId, isWalletConnectWallet, parseWalletAccountId, parseWalletId, resetAppKitCache, resolveConfig };
|
|
888
|
+
|
|
1020
889
|
//# sourceMappingURL=index.mjs.map
|