@kheopskit/core 1.0.1 → 5.0.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.
Files changed (53) hide show
  1. package/MIGRATING_TO_V4.md +259 -0
  2. package/README.md +67 -0
  3. package/dist/chunk-4ENHC7G4.js +210 -0
  4. package/dist/chunk-4ENHC7G4.js.map +1 -0
  5. package/dist/chunk-6XAZANB5.mjs +450 -0
  6. package/dist/chunk-6XAZANB5.mjs.map +1 -0
  7. package/dist/chunk-7QSGAJ4A.mjs +210 -0
  8. package/dist/chunk-7QSGAJ4A.mjs.map +1 -0
  9. package/dist/chunk-B4L6GAYD.js +179 -0
  10. package/dist/chunk-B4L6GAYD.js.map +1 -0
  11. package/dist/chunk-BWUUHUDK.mjs +24 -0
  12. package/dist/chunk-BWUUHUDK.mjs.map +1 -0
  13. package/dist/chunk-D3EQMFZ2.js +24 -0
  14. package/dist/chunk-D3EQMFZ2.js.map +1 -0
  15. package/dist/chunk-XQWJM3KC.js +450 -0
  16. package/dist/chunk-XQWJM3KC.js.map +1 -0
  17. package/dist/chunk-YDLCHYHH.mjs +179 -0
  18. package/dist/chunk-YDLCHYHH.mjs.map +1 -0
  19. package/dist/ethereum.d.mts +61 -0
  20. package/dist/ethereum.d.ts +61 -0
  21. package/dist/ethereum.js +351 -0
  22. package/dist/ethereum.js.map +1 -0
  23. package/dist/ethereum.mjs +351 -0
  24. package/dist/ethereum.mjs.map +1 -0
  25. package/dist/getCachedObservable-C4E8dfMp.d.mts +20 -0
  26. package/dist/getCachedObservable-C4E8dfMp.d.ts +20 -0
  27. package/dist/index.d.mts +55 -267
  28. package/dist/index.d.ts +55 -267
  29. package/dist/index.js +327 -1355
  30. package/dist/index.js.map +1 -1
  31. package/dist/index.mjs +263 -1325
  32. package/dist/index.mjs.map +1 -1
  33. package/dist/internal.d.mts +86 -0
  34. package/dist/internal.d.ts +86 -0
  35. package/dist/internal.js +32 -0
  36. package/dist/internal.js.map +1 -0
  37. package/dist/internal.mjs +32 -0
  38. package/dist/internal.mjs.map +1 -0
  39. package/dist/polkadot.d.mts +70 -0
  40. package/dist/polkadot.d.ts +70 -0
  41. package/dist/polkadot.js +303 -0
  42. package/dist/polkadot.js.map +1 -0
  43. package/dist/polkadot.mjs +303 -0
  44. package/dist/polkadot.mjs.map +1 -0
  45. package/dist/solana.d.mts +98 -0
  46. package/dist/solana.d.ts +98 -0
  47. package/dist/solana.js +461 -0
  48. package/dist/solana.js.map +1 -0
  49. package/dist/solana.mjs +461 -0
  50. package/dist/solana.mjs.map +1 -0
  51. package/dist/types-C7V7DGlg.d.mts +349 -0
  52. package/dist/types-C7V7DGlg.d.ts +349 -0
  53. package/package.json +104 -18
@@ -0,0 +1,259 @@
1
+ # Migrating to Kheopskit v4
2
+
3
+ v4 turns each chain into a **plugin** behind its own entry point, and makes every
4
+ platform SDK (plus WalletConnect) an **optional peer dependency**. A dapp now
5
+ installs and bundles only what it actually uses.
6
+
7
+ `@kheopskit/core`, `@kheopskit/react` and the repo release tag are unified at
8
+ **`4.0.0`** and move in lockstep from here on.
9
+
10
+ ---
11
+
12
+ ## At a glance
13
+
14
+ | Area | v3 | v4 |
15
+ |------|----|----|
16
+ | Enable platforms | `platforms: ["polkadot", "ethereum"]` | `platforms: [polkadot(), ethereum()]` (plugin factories) |
17
+ | Platform options | top-level `polkadotAccountTypes`, `solanaChain` | `polkadot({ accountTypes })`, `solana({ chain })` |
18
+ | SDK install | bundled in core | optional peer deps — install per platform |
19
+ | WalletConnect | `@reown/appkit` bundled | optional peer dep — install only if you use it |
20
+ | Platform types | from `@kheopskit/core` | from `@kheopskit/core/<platform>` |
21
+ | Precise React types | `useWallets()` | `createKheopskit({ platforms })` (recommended) or `useWallets<typeof platforms>()` |
22
+ | `wallet.disconnect()` | `() => void` | `() => Promise<void>` (awaitable, rejects on failure) |
23
+ | Injected source id | `providerId` / `walletStandardId` / `extensionId` | unified `wallet.sourceId` |
24
+ | Thrown errors | `Error` with ad-hoc message | `KheopskitError` with stable `.code` |
25
+ | `account.isWalletDefault` | present (Ethereum/Solana) | **removed** |
26
+
27
+ ---
28
+
29
+ ## 1. Install only what you use
30
+
31
+ `rxjs` is always required. Everything else is optional — add the row(s) for the
32
+ platforms (and WalletConnect) you actually use:
33
+
34
+ ```bash
35
+ # always
36
+ pnpm add @kheopskit/core @kheopskit/react rxjs
37
+
38
+ # per platform — pick what you use
39
+ pnpm add polkadot-api # Polkadot
40
+ pnpm add viem mipd # Ethereum
41
+ pnpm add @solana/kit @wallet-standard/app @wallet-standard/base # Solana
42
+
43
+ # only if you pass config.walletConnect
44
+ pnpm add @reown/appkit
45
+ ```
46
+
47
+ A Polkadot-only dapp that doesn't use WalletConnect installs none of the
48
+ Ethereum/Solana SDKs nor `@reown/appkit`, and none of their code is pulled into
49
+ its bundle.
50
+
51
+ ## 2. Platforms are plugins
52
+
53
+ Import a factory from each platform's entry point and pass instances to
54
+ `config.platforms`. `platforms` is **required in the type** — if you omit it at
55
+ runtime, kheopskit warns and yields no wallets/accounts, so always pass it.
56
+
57
+ ```diff
58
+ import { getKheopskit$ } from "@kheopskit/core";
59
+ + import { polkadot } from "@kheopskit/core/polkadot";
60
+ + import { ethereum } from "@kheopskit/core/ethereum";
61
+ + import { solana } from "@kheopskit/core/solana";
62
+
63
+ getKheopskit$({
64
+ - platforms: ["polkadot", "ethereum", "solana"],
65
+ - polkadotAccountTypes: ["sr25519", "ed25519"],
66
+ - solanaChain: "solana:devnet",
67
+ + platforms: [
68
+ + polkadot({ accountTypes: ["sr25519", "ed25519"] }),
69
+ + ethereum(),
70
+ + solana({ chain: "solana:devnet" }),
71
+ + ],
72
+ });
73
+ ```
74
+
75
+ - `polkadotAccountTypes` → `polkadot({ accountTypes })`.
76
+ - `solanaChain` → `solana({ chain })` (defaults to `"solana:mainnet"`).
77
+ - `autoReconnect`, `walletConnect`, `debug`, `storageKey`, `hydrationGracePeriod`
78
+ stay at the top level, unchanged.
79
+
80
+ ## 3. Platform types move to the platform entry points
81
+
82
+ ```diff
83
+ - import type { PolkadotAccount, EthereumAccount } from "@kheopskit/core";
84
+ + import type { PolkadotAccount } from "@kheopskit/core/polkadot";
85
+ + import type { EthereumAccount } from "@kheopskit/core/ethereum";
86
+ + import type { SolanaAccount } from "@kheopskit/core/solana";
87
+ ```
88
+
89
+ `KheopskitConfig`, `KheopskitState`, `BaseWallet`, `BaseWalletAccount` and the
90
+ WalletConnect/AppKit types remain on the root `@kheopskit/core`.
91
+
92
+ ## 4. React: recover precise account types
93
+
94
+ React context can't be generic, so the bare `useWallets()` returns the SDK-free
95
+ base shapes. v4 adds two ways to get platform-precise types (`account.signer` on
96
+ Solana, `account.client` on Ethereum, …).
97
+
98
+ ### Recommended: `createKheopskit` (bind the tuple once)
99
+
100
+ `createKheopskit({ platforms })` returns a `KheopskitProvider` and hooks already
101
+ typed to your platforms — no generic to repeat, one source of truth:
102
+
103
+ ```ts
104
+ // kheopskit.ts
105
+ import { createKheopskit } from "@kheopskit/react";
106
+ import { polkadot } from "@kheopskit/core/polkadot";
107
+ import { ethereum } from "@kheopskit/core/ethereum";
108
+ import { solana } from "@kheopskit/core/solana";
109
+
110
+ export const { KheopskitProvider, useWallets, useAccounts } = createKheopskit({
111
+ platforms: [polkadot(), ethereum(), solana()],
112
+ autoReconnect: true,
113
+ });
114
+ ```
115
+
116
+ ```tsx
117
+ // anywhere — accounts/wallets are already platform-precise
118
+ import { useWallets, useAccounts } from "./kheopskit";
119
+
120
+ const { wallets } = useWallets();
121
+ const accounts = useAccounts(); // account.signer / account.client are typed
122
+ ```
123
+
124
+ ### Manual: type argument on `useWallets`
125
+
126
+ If you keep the plain `<KheopskitProvider config={...}>`, pass the tuple as a
127
+ type argument where you read SDK fields:
128
+
129
+ ```diff
130
+ - const { accounts } = useWallets();
131
+ + const platforms = [polkadot(), ethereum(), solana()] as const;
132
+ + // ...pass `platforms` to your KheopskitProvider config...
133
+ + const { accounts } = useWallets<typeof platforms>();
134
+ ```
135
+
136
+ Define `platforms` once (e.g. exported `as const` from your config module) and
137
+ reuse it for both the provider config and the `useWallets` type argument.
138
+
139
+ > During hydration (`state.isHydrating === true`) accounts/wallets are cached
140
+ > placeholders that carry only the base fields — SDK fields (`signer`, `client`,
141
+ > provider/extension handles) are **absent at runtime** even though the types
142
+ > show them. Guard SDK-field access behind `!isHydrating`.
143
+
144
+ ## 5. WalletConnect is optional
145
+
146
+ `@reown/appkit` is no longer bundled. Install it only if you set
147
+ `config.walletConnect`:
148
+
149
+ ```bash
150
+ pnpm add @reown/appkit
151
+ ```
152
+
153
+ If `config.walletConnect` is set but `@reown/appkit` isn't installed,
154
+ WalletConnect is disabled with a console error and injected wallets keep working.
155
+
156
+ Two WalletConnect types are now loosened so core doesn't depend on
157
+ `@reown/appkit`'s types:
158
+
159
+ - `config.walletConnect.networks` is typed `unknown[]`. Keep passing
160
+ `AppKitNetwork[]` from `@reown/appkit/networks` — it's forwarded as-is.
161
+ - A wallet's `appKit` escape hatch is typed as a minimal local `AppKitInstance`.
162
+ Cast it to `@reown/appkit`'s `AppKit` if you need the full API:
163
+ `wallet.appKit as unknown as import("@reown/appkit").AppKit`.
164
+
165
+ ## 6. `isWalletDefault` removed
166
+
167
+ The unused, never-persisted `isWalletDefault` field was removed from
168
+ `EthereumAccount` and `SolanaAccount`. If you relied on "first account",
169
+ compute it from array position yourself (`accounts[0]`).
170
+
171
+ ## 7. `disconnect()` is now async
172
+
173
+ Every wallet's `disconnect` returns `Promise<void>` (matching `connect`). It
174
+ resolves once the underlying provider/extension disconnect completes and
175
+ **rejects if it fails**, so you can await and handle it:
176
+
177
+ ```diff
178
+ - wallet.disconnect();
179
+ + await wallet.disconnect();
180
+ ```
181
+
182
+ Fire-and-forget (`onClick={() => wallet.disconnect()}`) still works; you just
183
+ gain the ability to await and catch failures.
184
+
185
+ ## 8. Unified `sourceId` on injected wallets
186
+
187
+ The per-platform identifier fields are unified to **`sourceId`** so you can read
188
+ the underlying wallet source the same way everywhere:
189
+
190
+ ```diff
191
+ - ethereumWallet.providerId // EIP-6963 rdns
192
+ - solanaWallet.walletStandardId // Wallet Standard name
193
+ - polkadotWallet.extensionId // extension identifier
194
+ + wallet.sourceId
195
+ ```
196
+
197
+ The value is unchanged per platform (rdns / Wallet Standard name / extension id).
198
+
199
+ ## 9. Typed errors
200
+
201
+ Wallet operations now throw a `KheopskitError` with a stable `code` instead of
202
+ an ad-hoc `Error`, so you can branch on failures:
203
+
204
+ ```ts
205
+ import { KheopskitError } from "@kheopskit/core";
206
+
207
+ try {
208
+ await wallet.connect();
209
+ } catch (error) {
210
+ if (error instanceof KheopskitError && error.code === "WALLET_ALREADY_CONNECTED") {
211
+ // ignore
212
+ } else throw error;
213
+ }
214
+ ```
215
+
216
+ Codes: `WALLET_ALREADY_CONNECTED`, `WALLET_NOT_CONNECTED`,
217
+ `FEATURE_NOT_SUPPORTED`, `NO_SESSION`, `NO_PROVIDER`, `UNSUPPORTED_CHAIN`.
218
+
219
+ ## 10. Signing per platform
220
+
221
+ Each platform exposes its native signing primitive — there is no single
222
+ cross-platform signer, by design (the SDKs differ). Guard access behind
223
+ `!isHydrating`:
224
+
225
+ | Platform | Signing surface |
226
+ |----------|-----------------|
227
+ | Ethereum | `account.client` — a viem `WalletClient` (switch chains via the client) |
228
+ | Solana | `account.signer` (bound to the configured cluster) and `account.getSigner(chain)` for another cluster |
229
+ | Polkadot | `account.polkadotSigner` — a polkadot-api signer |
230
+
231
+ ## 11. New public exports
232
+
233
+ v4 widens the public surface so you can name what you use:
234
+
235
+ - From `@kheopskit/core`: `WalletId`, `getWalletId`, `parseWalletId`,
236
+ `WalletAccountId`, `getWalletAccountId`, `KheopskitError`,
237
+ `KheopskitErrorCode`, `isValidAddress`.
238
+ - From the platform entries: `isEthereumAddress` (`/ethereum`), `isSs58Address`
239
+ (`/polkadot`), `isSolanaAddress` (`/solana`).
240
+
241
+ Internal plumbing (hydration/cache/icon helpers) moved to
242
+ `@kheopskit/core/internal` and is **not** semver-stable — don't import it in
243
+ app code.
244
+
245
+ ---
246
+
247
+ ## Upgrade checklist
248
+
249
+ - [ ] Bump `@kheopskit/core` and `@kheopskit/react` to `^4.0.0`.
250
+ - [ ] Install the peer deps for the platforms you use (+ `@reown/appkit` if using WalletConnect).
251
+ - [ ] Replace `platforms: [...strings]` with plugin factories from the subpath entries.
252
+ - [ ] Move `polkadotAccountTypes` / `solanaChain` into `polkadot({ accountTypes })` / `solana({ chain })`.
253
+ - [ ] Update platform-type imports to the subpath entries.
254
+ - [ ] Adopt `createKheopskit({ platforms })` (recommended) or switch `useWallets()` → `useWallets<typeof platforms>()` where you read SDK fields.
255
+ - [ ] `await wallet.disconnect()` where you want to handle disconnect failures.
256
+ - [ ] Rename `providerId` / `walletStandardId` / `extensionId` reads to `wallet.sourceId`.
257
+ - [ ] Catch `KheopskitError` (branch on `.code`) instead of matching error message strings.
258
+ - [ ] Remove any use of `account.isWalletDefault`.
259
+ - [ ] Run `tsc` — remaining type errors point at anything missed.
package/README.md ADDED
@@ -0,0 +1,67 @@
1
+ # @kheopskit/core
2
+
3
+ Framework-agnostic core for [Kheopskit](https://github.com/kheopskit/kheopskit) —
4
+ list wallets and accounts across Polkadot, Ethereum and Solana, with injected
5
+ wallets and WalletConnect (Reown AppKit).
6
+
7
+ For React, use [`@kheopskit/react`](https://www.npmjs.com/package/@kheopskit/react).
8
+ Full docs and the interactive playground: https://github.com/kheopskit/kheopskit
9
+
10
+ > **Upgrading from v3?** v4 moves platforms to plugins and makes platform SDKs
11
+ > (and WalletConnect) optional peer dependencies. See
12
+ > [MIGRATING_TO_V4.md](./MIGRATING_TO_V4.md).
13
+
14
+ ## Install
15
+
16
+ `rxjs` is always required; every platform SDK (and WalletConnect) is an optional
17
+ peer dependency — install only what you use:
18
+
19
+ ```bash
20
+ pnpm add @kheopskit/core rxjs
21
+
22
+ pnpm add polkadot-api # Polkadot
23
+ pnpm add viem mipd # Ethereum
24
+ pnpm add @solana/kit @wallet-standard/app @wallet-standard/base # Solana
25
+ pnpm add @reown/appkit # WalletConnect (optional)
26
+ ```
27
+
28
+ A Polkadot-only dapp that doesn't use WalletConnect installs none of the
29
+ Ethereum/Solana SDKs nor `@reown/appkit`, and none of their code is bundled.
30
+
31
+ ## Usage
32
+
33
+ Platforms are enabled by passing **plugins** (imported from their entry points)
34
+ to `config.platforms`:
35
+
36
+ ```ts
37
+ import { getKheopskit$ } from "@kheopskit/core";
38
+ import { polkadot } from "@kheopskit/core/polkadot";
39
+ import { ethereum } from "@kheopskit/core/ethereum";
40
+ import { solana } from "@kheopskit/core/solana";
41
+
42
+ const kheopskit$ = getKheopskit$({
43
+ platforms: [
44
+ polkadot({ accountTypes: ["sr25519", "ed25519", "ecdsa"] }),
45
+ ethereum(),
46
+ solana({ chain: "solana:mainnet" }),
47
+ ],
48
+ autoReconnect: true,
49
+ });
50
+
51
+ kheopskit$.subscribe(({ wallets, accounts, isHydrating }) => {
52
+ // ...
53
+ });
54
+ ```
55
+
56
+ Each platform entry point also exports its SDK-precise types
57
+ (`PolkadotAccount`, `EthereumAccount`, `SolanaAccount`, …). The root export
58
+ carries the SDK-free `KheopskitConfig`, `KheopskitState`, `BaseWallet` and
59
+ `BaseWalletAccount`.
60
+
61
+ > While `state.isHydrating` is `true`, wallets/accounts are cached placeholders
62
+ > carrying only the base fields — SDK fields (`signer`, `client`,
63
+ > provider/extension handles) are absent until hydration completes.
64
+
65
+ ## License
66
+
67
+ ISC
@@ -0,0 +1,210 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }var __defProp = Object.defineProperty;
2
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
+
5
+ // src/utils/storage.ts
6
+ var noopStorage = {
7
+ getItem: () => null,
8
+ setItem: () => {
9
+ },
10
+ removeItem: () => {
11
+ }
12
+ };
13
+ var _safeLocalStorage = null;
14
+ var createSafeLocalStorage = () => {
15
+ if (typeof window === "undefined") return noopStorage;
16
+ try {
17
+ const testKey = "__kheopskit_test__";
18
+ window.localStorage.setItem(testKey, testKey);
19
+ window.localStorage.removeItem(testKey);
20
+ return {
21
+ getItem: (key) => window.localStorage.getItem(key),
22
+ setItem: (key, value) => window.localStorage.setItem(key, value),
23
+ removeItem: (key) => window.localStorage.removeItem(key),
24
+ subscribe: (key, callback) => {
25
+ const handler = (event) => {
26
+ if (event.key === key) {
27
+ callback(event.newValue);
28
+ }
29
+ };
30
+ window.addEventListener("storage", handler);
31
+ return () => window.removeEventListener("storage", handler);
32
+ }
33
+ };
34
+ } catch (e) {
35
+ return noopStorage;
36
+ }
37
+ };
38
+ var getSafeLocalStorage = () => {
39
+ if (_safeLocalStorage === null) {
40
+ _safeLocalStorage = createSafeLocalStorage();
41
+ }
42
+ return _safeLocalStorage;
43
+ };
44
+ var safeLocalStorage = {
45
+ getItem: (key) => getSafeLocalStorage().getItem(key),
46
+ setItem: (key, value) => getSafeLocalStorage().setItem(key, value),
47
+ removeItem: (key) => getSafeLocalStorage().removeItem(key),
48
+ subscribe: (key, callback) => {
49
+ const storage = getSafeLocalStorage();
50
+ return _nullishCoalesce(_optionalChain([storage, 'access', _ => _.subscribe, 'optionalCall', _2 => _2(key, callback)]), () => ( (() => {
51
+ })));
52
+ }
53
+ };
54
+ var parseCookie = (cookieString, key) => {
55
+ if (!cookieString) return null;
56
+ for (const cookie of cookieString.split(";")) {
57
+ const [k, ...v] = cookie.split("=");
58
+ const cookieKey = _optionalChain([k, 'optionalAccess', _3 => _3.trim, 'call', _4 => _4()]);
59
+ if (cookieKey === key) {
60
+ try {
61
+ return decodeURIComponent(v.join("=").trim());
62
+ } catch (e2) {
63
+ return null;
64
+ }
65
+ }
66
+ }
67
+ return null;
68
+ };
69
+ var COOKIE_MAX_SIZE = 3 * 1024;
70
+ var BROADCAST_CHANNEL_NAME = "kheopskit-storage-sync";
71
+ var sharedBroadcastChannel = null;
72
+ var getBroadcastChannel = () => {
73
+ if (sharedBroadcastChannel) return sharedBroadcastChannel;
74
+ if (typeof BroadcastChannel === "undefined") return null;
75
+ try {
76
+ sharedBroadcastChannel = new BroadcastChannel(BROADCAST_CHANNEL_NAME);
77
+ } catch (e3) {
78
+ }
79
+ return sharedBroadcastChannel;
80
+ };
81
+ var isSecureConnection = () => typeof window !== "undefined" && window.location.protocol === "https:";
82
+ var cookieStorage = (initialCookies) => {
83
+ return {
84
+ getItem: (key) => {
85
+ const cookieString = typeof document !== "undefined" ? document.cookie : initialCookies;
86
+ return parseCookie(cookieString, key);
87
+ },
88
+ setItem: (key, value) => {
89
+ if (typeof document === "undefined") return;
90
+ const encodedValue = encodeURIComponent(value);
91
+ if (encodedValue.length > COOKIE_MAX_SIZE) {
92
+ console.warn(
93
+ `[kheopskit] Cookie value for "${key}" exceeds recommended size limit (${encodedValue.length} > ${COOKIE_MAX_SIZE} bytes). This may cause issues with cookie storage. Consider reducing the number of connected wallets.`
94
+ );
95
+ }
96
+ const expires = /* @__PURE__ */ new Date();
97
+ expires.setFullYear(expires.getFullYear() + 1);
98
+ let cookieStr = `${key}=${encodedValue};expires=${expires.toUTCString()};path=/;SameSite=Lax`;
99
+ if (isSecureConnection()) {
100
+ cookieStr += ";Secure";
101
+ }
102
+ document.cookie = cookieStr;
103
+ _optionalChain([getBroadcastChannel, 'call', _5 => _5(), 'optionalAccess', _6 => _6.postMessage, 'call', _7 => _7({ type: "set", key, value })]);
104
+ },
105
+ removeItem: (key) => {
106
+ if (typeof document === "undefined") return;
107
+ let cookieStr = `${key}=;expires=Thu, 01 Jan 1970 00:00:01 GMT;path=/;SameSite=Lax`;
108
+ if (isSecureConnection()) {
109
+ cookieStr += ";Secure";
110
+ }
111
+ document.cookie = cookieStr;
112
+ _optionalChain([getBroadcastChannel, 'call', _8 => _8(), 'optionalAccess', _9 => _9.postMessage, 'call', _10 => _10({ type: "remove", key })]);
113
+ },
114
+ subscribe: (key, callback) => {
115
+ const channel = getBroadcastChannel();
116
+ if (!channel) return () => {
117
+ };
118
+ const handler = (event) => {
119
+ const data = event.data;
120
+ if (data.key === key) {
121
+ if (data.type === "set") {
122
+ callback(_nullishCoalesce(data.value, () => ( null)));
123
+ } else if (data.type === "remove") {
124
+ callback(null);
125
+ }
126
+ }
127
+ };
128
+ channel.addEventListener("message", handler);
129
+ return () => {
130
+ channel.removeEventListener("message", handler);
131
+ };
132
+ }
133
+ };
134
+ };
135
+
136
+ // src/utils/isWalletPlatform.ts
137
+ var isWalletPlatform = (platform) => typeof platform === "string" && ["polkadot", "ethereum", "solana"].includes(platform);
138
+
139
+ // src/utils/WalletId.ts
140
+ var WALLET_CONNECT_WALLET_ID = "walletconnect";
141
+ var getWalletId = (platform, identifier) => {
142
+ if (!isWalletPlatform(platform)) throw new Error("Invalid platform");
143
+ if (!identifier) throw new Error("Invalid name");
144
+ return `${platform}:${identifier}`;
145
+ };
146
+ var parseWalletId = (walletId) => {
147
+ if (!walletId) throw new Error("Invalid walletId");
148
+ const [platform, identifier] = walletId.split(":");
149
+ if (!isWalletPlatform(platform)) throw new Error("Invalid platform");
150
+ if (!identifier) throw new Error("Invalid address");
151
+ return { platform, identifier };
152
+ };
153
+ var isValidWalletId = (walletId) => {
154
+ if (typeof walletId !== "string" || !walletId) return false;
155
+ if (walletId === WALLET_CONNECT_WALLET_ID) return true;
156
+ const [platform, identifier] = walletId.split(":");
157
+ return isWalletPlatform(platform) && !!identifier;
158
+ };
159
+
160
+ // src/utils/getCachedObservable.ts
161
+ var CACHE_SYMBOL = /* @__PURE__ */ Symbol.for("kheopskit.observableCache");
162
+ var getCache = () => {
163
+ const g = globalThis;
164
+ let cache = g[CACHE_SYMBOL];
165
+ if (!cache) {
166
+ cache = /* @__PURE__ */ new Map();
167
+ g[CACHE_SYMBOL] = cache;
168
+ }
169
+ return cache;
170
+ };
171
+ var getCachedObservable$ = (key, create) => {
172
+ const cache = getCache();
173
+ if (!cache.has(key)) cache.set(key, create());
174
+ return cache.get(key);
175
+ };
176
+ var clearCachedObservable = (key) => {
177
+ getCache().delete(key);
178
+ };
179
+ var clearCachedObservablesByPrefix = (prefix) => {
180
+ const cache = getCache();
181
+ for (const key of cache.keys()) {
182
+ if (key.startsWith(prefix)) cache.delete(key);
183
+ }
184
+ };
185
+ var clearAllCachedObservables = () => {
186
+ getCache().clear();
187
+ };
188
+
189
+ // src/api/types.ts
190
+ var isWalletConnectWallet = (wallet) => wallet.type === "walletconnect";
191
+ var isInjectedWallet = (wallet) => wallet.type === "injected";
192
+
193
+
194
+
195
+
196
+
197
+
198
+
199
+
200
+
201
+
202
+
203
+
204
+
205
+
206
+
207
+
208
+
209
+ exports.__publicField = __publicField; exports.getSafeLocalStorage = getSafeLocalStorage; exports.safeLocalStorage = safeLocalStorage; exports.cookieStorage = cookieStorage; exports.isWalletPlatform = isWalletPlatform; exports.WALLET_CONNECT_WALLET_ID = WALLET_CONNECT_WALLET_ID; exports.getWalletId = getWalletId; exports.parseWalletId = parseWalletId; exports.isValidWalletId = isValidWalletId; exports.getCachedObservable$ = getCachedObservable$; exports.clearCachedObservable = clearCachedObservable; exports.clearCachedObservablesByPrefix = clearCachedObservablesByPrefix; exports.clearAllCachedObservables = clearAllCachedObservables; exports.isWalletConnectWallet = isWalletConnectWallet; exports.isInjectedWallet = isInjectedWallet;
210
+ //# sourceMappingURL=chunk-4ENHC7G4.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/home/runner/work/kheopskit/kheopskit/packages/core/dist/chunk-4ENHC7G4.js","../src/utils/storage.ts","../src/utils/isWalletPlatform.ts","../src/utils/WalletId.ts","../src/utils/getCachedObservable.ts","../src/api/types.ts"],"names":[],"mappings":"AAAA,qrBAAI,UAAU,EAAE,MAAM,CAAC,cAAc;AACrC,IAAI,gBAAgB,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,IAAI,GAAG,IAAI,EAAE,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,CAAC,EAAE,EAAE,GAAG,CAAC,GAAG,EAAE,EAAE,KAAK;AAC/J,IAAI,cAAc,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,eAAe,CAAC,GAAG,EAAE,OAAO,IAAI,IAAI,SAAS,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,KAAK,CAAC;AAC9G;AACA;ACoBO,IAAM,YAAA,EAA+B;AAAA,EAC3C,OAAA,EAAS,CAAA,EAAA,GAAM,IAAA;AAAA,EACf,OAAA,EAAS,CAAA,EAAA,GAAM;AAAA,EAAC,CAAA;AAAA,EAChB,UAAA,EAAY,CAAA,EAAA,GAAM;AAAA,EAAC;AACpB,CAAA;AAMA,IAAI,kBAAA,EAA4C,IAAA;AAMhD,IAAM,uBAAA,EAAyB,CAAA,EAAA,GAAuB;AACrD,EAAA,GAAA,CAAI,OAAO,OAAA,IAAW,WAAA,EAAa,OAAO,WAAA;AAE1C,EAAA,IAAI;AAEH,IAAA,MAAM,QAAA,EAAU,oBAAA;AAChB,IAAA,MAAA,CAAO,YAAA,CAAa,OAAA,CAAQ,OAAA,EAAS,OAAO,CAAA;AAC5C,IAAA,MAAA,CAAO,YAAA,CAAa,UAAA,CAAW,OAAO,CAAA;AAEtC,IAAA,OAAO;AAAA,MACN,OAAA,EAAS,CAAC,GAAA,EAAA,GAAgB,MAAA,CAAO,YAAA,CAAa,OAAA,CAAQ,GAAG,CAAA;AAAA,MACzD,OAAA,EAAS,CAAC,GAAA,EAAa,KAAA,EAAA,GACtB,MAAA,CAAO,YAAA,CAAa,OAAA,CAAQ,GAAA,EAAK,KAAK,CAAA;AAAA,MACvC,UAAA,EAAY,CAAC,GAAA,EAAA,GAAgB,MAAA,CAAO,YAAA,CAAa,UAAA,CAAW,GAAG,CAAA;AAAA,MAC/D,SAAA,EAAW,CAAC,GAAA,EAAa,QAAA,EAAA,GAA6C;AACrE,QAAA,MAAM,QAAA,EAAU,CAAC,KAAA,EAAA,GAAwB;AACxC,UAAA,GAAA,CAAI,KAAA,CAAM,IAAA,IAAQ,GAAA,EAAK;AACtB,YAAA,QAAA,CAAS,KAAA,CAAM,QAAQ,CAAA;AAAA,UACxB;AAAA,QACD,CAAA;AACA,QAAA,MAAA,CAAO,gBAAA,CAAiB,SAAA,EAAW,OAAO,CAAA;AAC1C,QAAA,OAAO,CAAA,EAAA,GAAM,MAAA,CAAO,mBAAA,CAAoB,SAAA,EAAW,OAAO,CAAA;AAAA,MAC3D;AAAA,IACD,CAAA;AAAA,EACD,EAAA,UAAQ;AACP,IAAA,OAAO,WAAA;AAAA,EACR;AACD,CAAA;AASO,IAAM,oBAAA,EAAsB,CAAA,EAAA,GAAuB;AACzD,EAAA,GAAA,CAAI,kBAAA,IAAsB,IAAA,EAAM;AAC/B,IAAA,kBAAA,EAAoB,sBAAA,CAAuB,CAAA;AAAA,EAC5C;AACA,EAAA,OAAO,iBAAA;AACR,CAAA;AAMO,IAAM,iBAAA,EAAoC;AAAA,EAChD,OAAA,EAAS,CAAC,GAAA,EAAA,GAAgB,mBAAA,CAAoB,CAAA,CAAE,OAAA,CAAQ,GAAG,CAAA;AAAA,EAC3D,OAAA,EAAS,CAAC,GAAA,EAAa,KAAA,EAAA,GACtB,mBAAA,CAAoB,CAAA,CAAE,OAAA,CAAQ,GAAA,EAAK,KAAK,CAAA;AAAA,EACzC,UAAA,EAAY,CAAC,GAAA,EAAA,GAAgB,mBAAA,CAAoB,CAAA,CAAE,UAAA,CAAW,GAAG,CAAA;AAAA,EACjE,SAAA,EAAW,CAAC,GAAA,EAAa,QAAA,EAAA,GAA6C;AACrE,IAAA,MAAM,QAAA,EAAU,mBAAA,CAAoB,CAAA;AACpC,IAAA,wCAAO,OAAA,mBAAQ,SAAA,0BAAA,CAAY,GAAA,EAAK,QAAQ,GAAA,UAAA,CAAM,CAAA,EAAA,GAAM;AAAA,IAAC,CAAA,GAAA;AAAA,EACtD;AACD,CAAA;AAQO,IAAM,YAAA,EAAc,CAC1B,YAAA,EACA,GAAA,EAAA,GACmB;AACnB,EAAA,GAAA,CAAI,CAAC,YAAA,EAAc,OAAO,IAAA;AAE1B,EAAA,IAAA,CAAA,MAAW,OAAA,GAAU,YAAA,CAAa,KAAA,CAAM,GAAG,CAAA,EAAG;AAC7C,IAAA,MAAM,CAAC,CAAA,EAAG,GAAG,CAAC,EAAA,EAAI,MAAA,CAAO,KAAA,CAAM,GAAG,CAAA;AAClC,IAAA,MAAM,UAAA,kBAAY,CAAA,6BAAG,IAAA,mBAAK,GAAA;AAC1B,IAAA,GAAA,CAAI,UAAA,IAAc,GAAA,EAAK;AACtB,MAAA,IAAI;AACH,QAAA,OAAO,kBAAA,CAAmB,CAAA,CAAE,IAAA,CAAK,GAAG,CAAA,CAAE,IAAA,CAAK,CAAC,CAAA;AAAA,MAC7C,EAAA,WAAQ;AACP,QAAA,OAAO,IAAA;AAAA,MACR;AAAA,IACD;AAAA,EACD;AAEA,EAAA,OAAO,IAAA;AACR,CAAA;AAgBO,IAAM,gBAAA,EAAkB,EAAA,EAAI,IAAA;AAKnC,IAAM,uBAAA,EAAyB,wBAAA;AAM/B,IAAI,uBAAA,EAAkD,IAAA;AAEtD,IAAM,oBAAA,EAAsB,CAAA,EAAA,GAA+B;AAC1D,EAAA,GAAA,CAAI,sBAAA,EAAwB,OAAO,sBAAA;AACnC,EAAA,GAAA,CAAI,OAAO,iBAAA,IAAqB,WAAA,EAAa,OAAO,IAAA;AAEpD,EAAA,IAAI;AACH,IAAA,uBAAA,EAAyB,IAAI,gBAAA,CAAiB,sBAAsB,CAAA;AAAA,EACrE,EAAA,WAAQ;AAAA,EAER;AACA,EAAA,OAAO,sBAAA;AACR,CAAA;AAMA,IAAM,mBAAA,EAAqB,CAAA,EAAA,GAC1B,OAAO,OAAA,IAAW,YAAA,GAAe,MAAA,CAAO,QAAA,CAAS,SAAA,IAAa,QAAA;AAcxD,IAAM,cAAA,EAAgB,CAAC,cAAA,EAAA,GAA6C;AAC1E,EAAA,OAAO;AAAA,IACN,OAAA,EAAS,CAAC,GAAA,EAAA,GAAgB;AAEzB,MAAA,MAAM,aAAA,EACL,OAAO,SAAA,IAAa,YAAA,EAAc,QAAA,CAAS,OAAA,EAAS,cAAA;AACrD,MAAA,OAAO,WAAA,CAAY,YAAA,EAAc,GAAG,CAAA;AAAA,IACrC,CAAA;AAAA,IACA,OAAA,EAAS,CAAC,GAAA,EAAa,KAAA,EAAA,GAAkB;AACxC,MAAA,GAAA,CAAI,OAAO,SAAA,IAAa,WAAA,EAAa,MAAA;AAGrC,MAAA,MAAM,aAAA,EAAe,kBAAA,CAAmB,KAAK,CAAA;AAC7C,MAAA,GAAA,CAAI,YAAA,CAAa,OAAA,EAAS,eAAA,EAAiB;AAC1C,QAAA,OAAA,CAAQ,IAAA;AAAA,UACP,CAAA,8BAAA,EAAiC,GAAG,CAAA,kCAAA,EAAqC,YAAA,CAAa,MAAM,CAAA,GAAA,EAAM,eAAe,CAAA,sGAAA;AAAA,QAElH,CAAA;AAAA,MACD;AAGA,MAAA,MAAM,QAAA,kBAAU,IAAI,IAAA,CAAK,CAAA;AACzB,MAAA,OAAA,CAAQ,WAAA,CAAY,OAAA,CAAQ,WAAA,CAAY,EAAA,EAAI,CAAC,CAAA;AAG7C,MAAA,IAAI,UAAA,EAAY,CAAA,EAAA;AACZ,MAAA;AACU,QAAA;AACd,MAAA;AAGS,MAAA;AAGT,sBAAA;AACD,IAAA;AACa,IAAA;AACD,MAAA;AAGK,MAAA;AACZ,MAAA;AACU,QAAA;AACd,MAAA;AAGS,MAAA;AAGT,sBAAA;AACD,IAAA;AACyB,IAAA;AACR,MAAA;AACF,MAAA;AAAc,MAAA;AAEX,MAAA;AACH,QAAA;AAKJ,QAAA;AACC,UAAA;AACC,YAAA;AACC,UAAA;AACD,YAAA;AACV,UAAA;AACD,QAAA;AACD,MAAA;AAEQ,MAAA;AACK,MAAA;AACJ,QAAA;AACT,MAAA;AACD,IAAA;AACD,EAAA;AACD;AD7HuB;AACA;AErIS;AFuIT;AACA;AGhIV;AAGZ;AAGK,EAAA;AACY,EAAA;AACC,EAAA;AACnB;AAE8B;AACR,EAAA;AACJ,EAAA;AACZ,EAAA;AACY,EAAA;AACE,EAAA;AACpB;AAMgC;AACpB,EAAA;AACM,EAAA;AACA,EAAA;AACV,EAAA;AACR;AHwHuB;AACA;AI3JF;AAEoC;AAC9C,EAAA;AAII,EAAA;AACF,EAAA;AACH,IAAA;AACU,IAAA;AACnB,EAAA;AACO,EAAA;AACR;AAEa;AAIE,EAAA;AACO,EAAA;AAED,EAAA;AACrB;AAMa;AACS,EAAA;AACtB;AASa;AACE,EAAA;AACI,EAAA;AACE,IAAA;AACpB,EAAA;AACD;AAMa;AACK,EAAA;AAClB;AJkIuB;AACA;AKzEV;AAkBmB;AL0DT;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"/home/runner/work/kheopskit/kheopskit/packages/core/dist/chunk-4ENHC7G4.js","sourcesContent":[null,"export type Storage = {\n\tgetItem: (key: string) => string | null;\n\tsetItem: (key: string, value: string) => void;\n\tremoveItem: (key: string) => void;\n};\n\n/**\n * Extended storage interface with cross-tab sync support.\n */\nexport type SyncableStorage = Storage & {\n\t/**\n\t * Subscribe to storage changes from other tabs.\n\t * Returns an unsubscribe function.\n\t */\n\tsubscribe?: (\n\t\tkey: string,\n\t\tcallback: (value: string | null) => void,\n\t) => () => void;\n};\n\n/**\n * A no-op storage implementation that does nothing.\n * Useful for testing or SSR environments where no storage is needed.\n */\nexport const noopStorage: SyncableStorage = {\n\tgetItem: () => null,\n\tsetItem: () => {},\n\tremoveItem: () => {},\n};\n\n/**\n * Cached localStorage wrapper instance.\n * Lazily initialized on first access to avoid SSR issues.\n */\nlet _safeLocalStorage: SyncableStorage | null = null;\n\n/**\n * Creates the localStorage wrapper, testing for availability.\n * Returns noopStorage if localStorage is unavailable (SSR, private browsing, etc.)\n */\nconst createSafeLocalStorage = (): SyncableStorage => {\n\tif (typeof window === \"undefined\") return noopStorage;\n\n\ttry {\n\t\t// Test that localStorage is accessible (may throw in private browsing)\n\t\tconst testKey = \"__kheopskit_test__\";\n\t\twindow.localStorage.setItem(testKey, testKey);\n\t\twindow.localStorage.removeItem(testKey);\n\n\t\treturn {\n\t\t\tgetItem: (key: string) => window.localStorage.getItem(key),\n\t\t\tsetItem: (key: string, value: string) =>\n\t\t\t\twindow.localStorage.setItem(key, value),\n\t\t\tremoveItem: (key: string) => window.localStorage.removeItem(key),\n\t\t\tsubscribe: (key: string, callback: (value: string | null) => void) => {\n\t\t\t\tconst handler = (event: StorageEvent) => {\n\t\t\t\t\tif (event.key === key) {\n\t\t\t\t\t\tcallback(event.newValue);\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t\twindow.addEventListener(\"storage\", handler);\n\t\t\t\treturn () => window.removeEventListener(\"storage\", handler);\n\t\t\t},\n\t\t};\n\t} catch {\n\t\treturn noopStorage;\n\t}\n};\n\n/**\n * A safe localStorage wrapper that falls back to noopStorage\n * when localStorage is not available (e.g., during SSR).\n * Includes cross-tab sync via the native 'storage' event.\n *\n * Lazily initialized on first access to be SSR-safe.\n */\nexport const getSafeLocalStorage = (): SyncableStorage => {\n\tif (_safeLocalStorage === null) {\n\t\t_safeLocalStorage = createSafeLocalStorage();\n\t}\n\treturn _safeLocalStorage;\n};\n\n/**\n * @deprecated Use getSafeLocalStorage() instead. This is kept for backward compatibility.\n * Returns a proxy that lazily initializes on first method call.\n */\nexport const safeLocalStorage: SyncableStorage = {\n\tgetItem: (key: string) => getSafeLocalStorage().getItem(key),\n\tsetItem: (key: string, value: string) =>\n\t\tgetSafeLocalStorage().setItem(key, value),\n\tremoveItem: (key: string) => getSafeLocalStorage().removeItem(key),\n\tsubscribe: (key: string, callback: (value: string | null) => void) => {\n\t\tconst storage = getSafeLocalStorage();\n\t\treturn storage.subscribe?.(key, callback) ?? (() => {});\n\t},\n};\n\n/**\n * Parse a cookie string to extract the value for a specific key.\n * @param cookieString - The full cookie header string (e.g., document.cookie or req.headers.cookie)\n * @param key - The cookie key to find\n * @returns The cookie value or null if not found\n */\nexport const parseCookie = (\n\tcookieString: string | undefined,\n\tkey: string,\n): string | null => {\n\tif (!cookieString) return null;\n\n\tfor (const cookie of cookieString.split(\";\")) {\n\t\tconst [k, ...v] = cookie.split(\"=\");\n\t\tconst cookieKey = k?.trim();\n\t\tif (cookieKey === key) {\n\t\t\ttry {\n\t\t\t\treturn decodeURIComponent(v.join(\"=\").trim());\n\t\t\t} catch {\n\t\t\t\treturn null; // Malformed cookie value\n\t\t\t}\n\t\t}\n\t}\n\n\treturn null;\n};\n\n/**\n * Maximum recommended size for cookie storage (3KB to stay well under 4KB limit).\n *\n * @remarks\n * Cookie storage is subject to browser limits (typically 4KB per cookie).\n * When users connect many wallets, the auto-reconnect list may exceed this limit.\n * If this happens:\n * - A console warning will be logged\n * - Some browsers may silently reject the cookie\n * - Consider using fewer simultaneous wallet connections\n *\n * The stored data includes wallet IDs in format `platform:identifier`\n * (e.g., `polkadot:talisman`, `ethereum:io.metamask`).\n */\nexport const COOKIE_MAX_SIZE = 3 * 1024;\n\n/**\n * BroadcastChannel name for cross-tab cookie sync.\n */\nconst BROADCAST_CHANNEL_NAME = \"kheopskit-storage-sync\";\n\n/**\n * Singleton BroadcastChannel for cross-tab cookie sync.\n * Created once and shared across all cookieStorage instances.\n */\nlet sharedBroadcastChannel: BroadcastChannel | null = null;\n\nconst getBroadcastChannel = (): BroadcastChannel | null => {\n\tif (sharedBroadcastChannel) return sharedBroadcastChannel;\n\tif (typeof BroadcastChannel === \"undefined\") return null;\n\n\ttry {\n\t\tsharedBroadcastChannel = new BroadcastChannel(BROADCAST_CHANNEL_NAME);\n\t} catch {\n\t\t// BroadcastChannel not supported or failed\n\t}\n\treturn sharedBroadcastChannel;\n};\n\n/**\n * Check if the current connection is secure (HTTPS).\n * Must be called at runtime (inside methods) to work correctly after SSR hydration.\n */\nconst isSecureConnection = (): boolean =>\n\ttypeof window !== \"undefined\" && window.location.protocol === \"https:\";\n\n/**\n * A cookie-based storage implementation for SSR environments.\n * Reads cookies from an optional initial cookie string (for SSR hydration),\n * writes to document.cookie on the client.\n *\n * Features:\n * - Secure flag automatically added for HTTPS connections\n * - Cross-tab synchronization via BroadcastChannel API\n * - Size limit warning when data exceeds recommended limits\n *\n * @param initialCookies - Optional cookie string for server-side hydration\n */\nexport const cookieStorage = (initialCookies?: string): SyncableStorage => {\n\treturn {\n\t\tgetItem: (key: string) => {\n\t\t\t// On server, use initialCookies. On client, read from document.cookie\n\t\t\tconst cookieString =\n\t\t\t\ttypeof document !== \"undefined\" ? document.cookie : initialCookies;\n\t\t\treturn parseCookie(cookieString, key);\n\t\t},\n\t\tsetItem: (key: string, value: string) => {\n\t\t\tif (typeof document === \"undefined\") return;\n\n\t\t\t// Warn if value exceeds recommended size\n\t\t\tconst encodedValue = encodeURIComponent(value);\n\t\t\tif (encodedValue.length > COOKIE_MAX_SIZE) {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t`[kheopskit] Cookie value for \"${key}\" exceeds recommended size limit (${encodedValue.length} > ${COOKIE_MAX_SIZE} bytes). ` +\n\t\t\t\t\t\t\"This may cause issues with cookie storage. Consider reducing the number of connected wallets.\",\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Set cookie with 1 year expiry\n\t\t\tconst expires = new Date();\n\t\t\texpires.setFullYear(expires.getFullYear() + 1);\n\n\t\t\t// Build cookie string with security attributes\n\t\t\tlet cookieStr = `${key}=${encodedValue};expires=${expires.toUTCString()};path=/;SameSite=Lax`;\n\t\t\tif (isSecureConnection()) {\n\t\t\t\tcookieStr += \";Secure\";\n\t\t\t}\n\n\t\t\t// biome-ignore lint: necessary for cookie storage - direct cookie assignment is the standard API\n\t\t\tdocument.cookie = cookieStr;\n\n\t\t\t// Broadcast change to other tabs\n\t\t\tgetBroadcastChannel()?.postMessage({ type: \"set\", key, value });\n\t\t},\n\t\tremoveItem: (key: string) => {\n\t\t\tif (typeof document === \"undefined\") return;\n\n\t\t\t// Build delete cookie string\n\t\t\tlet cookieStr = `${key}=;expires=Thu, 01 Jan 1970 00:00:01 GMT;path=/;SameSite=Lax`;\n\t\t\tif (isSecureConnection()) {\n\t\t\t\tcookieStr += \";Secure\";\n\t\t\t}\n\n\t\t\t// biome-ignore lint: necessary for cookie storage - direct cookie assignment is the standard API\n\t\t\tdocument.cookie = cookieStr;\n\n\t\t\t// Broadcast change to other tabs\n\t\t\tgetBroadcastChannel()?.postMessage({ type: \"remove\", key });\n\t\t},\n\t\tsubscribe: (key: string, callback: (value: string | null) => void) => {\n\t\t\tconst channel = getBroadcastChannel();\n\t\t\tif (!channel) return () => {};\n\n\t\t\tconst handler = (event: MessageEvent) => {\n\t\t\t\tconst data = event.data as {\n\t\t\t\t\ttype: string;\n\t\t\t\t\tkey: string;\n\t\t\t\t\tvalue?: string;\n\t\t\t\t};\n\t\t\t\tif (data.key === key) {\n\t\t\t\t\tif (data.type === \"set\") {\n\t\t\t\t\t\tcallback(data.value ?? null);\n\t\t\t\t\t} else if (data.type === \"remove\") {\n\t\t\t\t\t\tcallback(null);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tchannel.addEventListener(\"message\", handler);\n\t\t\treturn () => {\n\t\t\t\tchannel.removeEventListener(\"message\", handler);\n\t\t\t};\n\t\t},\n\t};\n};\n\n/**\n * Cleanup the shared BroadcastChannel used for cross-tab cookie sync.\n * Call this when you're done using cookie storage (e.g., in tests or when unmounting).\n *\n * @remarks\n * In normal browser usage, you typically don't need to call this -\n * the channel will be cleaned up when the page is closed.\n * This is primarily useful for testing environments where multiple\n * test runs may accumulate channels.\n */\nexport const cleanupBroadcastChannel = (): void => {\n\tif (sharedBroadcastChannel) {\n\t\tsharedBroadcastChannel.close();\n\t\tsharedBroadcastChannel = null;\n\t}\n};\n","import type { WalletPlatform } from \"../api/types\";\n\nexport const isWalletPlatform = (\n\tplatform: unknown,\n): platform is WalletPlatform =>\n\ttypeof platform === \"string\" &&\n\t[\"polkadot\", \"ethereum\", \"solana\"].includes(platform as WalletPlatform);\n","import type { WalletPlatform } from \"../api/types\";\nimport { isWalletPlatform } from \"./isWalletPlatform\";\n\nexport type WalletId = string;\n\n/**\n * Stable id of the single WalletConnect connector. It is platform-less: one WC\n * session spans whichever namespaces the wallet approves, so it isn't tied to a\n * platform (unlike injected wallet ids, which are `<platform>:<identifier>`).\n */\nexport const WALLET_CONNECT_WALLET_ID: WalletId = \"walletconnect\";\n\nexport const getWalletId = (\n\tplatform: WalletPlatform,\n\tidentifier: string,\n): WalletId => {\n\tif (!isWalletPlatform(platform)) throw new Error(\"Invalid platform\");\n\tif (!identifier) throw new Error(\"Invalid name\");\n\treturn `${platform}:${identifier}`;\n};\n\nexport const parseWalletId = (walletId: string) => {\n\tif (!walletId) throw new Error(\"Invalid walletId\");\n\tconst [platform, identifier] = walletId.split(\":\");\n\tif (!isWalletPlatform(platform)) throw new Error(\"Invalid platform\");\n\tif (!identifier) throw new Error(\"Invalid address\");\n\treturn { platform, identifier };\n};\n\n/**\n * Non-throwing variant of {@link parseWalletId}. Use when validating untrusted\n * input (e.g. cached state persisted by an older version) before parsing.\n */\nexport const isValidWalletId = (walletId: unknown): walletId is WalletId => {\n\tif (typeof walletId !== \"string\" || !walletId) return false;\n\tif (walletId === WALLET_CONNECT_WALLET_ID) return true;\n\tconst [platform, identifier] = walletId.split(\":\");\n\treturn isWalletPlatform(platform) && !!identifier;\n};\n","import type { Observable } from \"rxjs\";\n\n// Anchored on globalThis so the cache stays a single instance even if the\n// module is duplicated across bundle chunks (e.g. CJS subpath entries).\nconst CACHE_SYMBOL = Symbol.for(\"kheopskit.observableCache\");\n\nconst getCache = (): Map<string, Observable<unknown>> => {\n\tconst g = globalThis as unknown as Record<\n\t\tsymbol,\n\t\tMap<string, Observable<unknown>> | undefined\n\t>;\n\tlet cache = g[CACHE_SYMBOL];\n\tif (!cache) {\n\t\tcache = new Map();\n\t\tg[CACHE_SYMBOL] = cache;\n\t}\n\treturn cache;\n};\n\nexport const getCachedObservable$ = <T, Obs = Observable<T>>(\n\tkey: string,\n\tcreate: () => Obs,\n): Obs => {\n\tconst cache = getCache();\n\tif (!cache.has(key)) cache.set(key, create() as Observable<unknown>);\n\n\treturn cache.get(key) as Obs;\n};\n\n/**\n * Clears an observable from the cache.\n * Use when a wallet disconnects or configuration changes.\n */\nexport const clearCachedObservable = (key: string): void => {\n\tgetCache().delete(key);\n};\n\n/**\n * Clears all cached observables whose key starts with `prefix`.\n *\n * Used to drop a wallet's account observables when it disconnects, so a later\n * reconnect rebuilds them against the current wallet handle instead of a stale\n * closure — and to keep the cache from growing unbounded across connect cycles.\n */\nexport const clearCachedObservablesByPrefix = (prefix: string): void => {\n\tconst cache = getCache();\n\tfor (const key of cache.keys()) {\n\t\tif (key.startsWith(prefix)) cache.delete(key);\n\t}\n};\n\n/**\n * Clears all cached observables.\n * Use when resetting the entire kheopskit state.\n */\nexport const clearAllCachedObservables = (): void => {\n\tgetCache().clear();\n};\n","import type { Observable } from \"rxjs\";\nimport type { WalletAccountId } from \"../utils\";\nimport type { WalletId } from \"../utils/WalletId\";\nimport type { KheopskitStore } from \"./store\";\n\nexport type WalletPlatform = \"polkadot\" | \"ethereum\" | \"solana\";\n\n/**\n * Minimal structural view of a WalletConnect `UniversalProvider` — the subset\n * kheopskit reads. Declared locally so core never depends on\n * `@walletconnect/universal-provider`. The concrete instance comes from\n * `@reown/appkit` at runtime.\n */\nexport type WalletConnectProvider = {\n\tsession?: {\n\t\ttopic: string;\n\t\tnamespaces: Record<string, { accounts?: string[] }>;\n\t};\n\tclient: {\n\t\trequest<T = unknown>(args: {\n\t\t\ttopic: string;\n\t\t\tchainId: string;\n\t\t\trequest: { method: string; params: unknown };\n\t\t}): Promise<T>;\n\t};\n\trequest(args: { method: string; params?: unknown }): Promise<unknown>;\n\ton(event: string, listener: (...args: unknown[]) => void): void;\n\toff(event: string, listener: (...args: unknown[]) => void): void;\n};\n\n/**\n * Minimal structural view of the Reown AppKit instance — the subset kheopskit's\n * account factories use. Exposed as the `appKit` escape hatch on AppKit wallets;\n * cast it to `@reown/appkit`'s `AppKit` type for the full API. Declared locally\n * so core never depends on `@reown/appkit`'s types (it's an optional peer).\n */\nexport type AppKitInstance = {\n\tgetProvider<T = WalletConnectProvider>(namespace: string): T | undefined;\n\tgetAccount(\n\t\tnamespace: string,\n\t): { allAccounts: { address: string }[] } | undefined;\n\tgetCaipNetworks(namespace: string): { caipNetworkId?: string }[];\n};\n\nexport type WalletType = \"injected\" | \"walletconnect\";\n\nexport type PolkadotAccountType = \"sr25519\" | \"ed25519\" | \"ecdsa\" | \"ethereum\";\n\n/**\n * SDK-free fields common to every wallet, regardless of platform. Platform\n * packages (`@kheopskit/core/<platform>`) extend this with SDK-typed fields\n * (the injected provider/extension/standard-wallet handle).\n */\nexport type BaseWallet = {\n\tid: WalletId;\n\tplatform: WalletPlatform;\n\ttype: WalletType;\n\tname: string;\n\ticon: string;\n\tisConnected: boolean;\n\tconnect: () => Promise<void>;\n\t/**\n\t * Disconnect the wallet. Resolves once the underlying provider/extension\n\t * disconnect completes; rejects if it fails so callers can surface or retry.\n\t */\n\tdisconnect: () => Promise<void>;\n};\n\n/**\n * SDK-free fields common to every account, regardless of platform. Platform\n * packages extend this with their SDK-typed signer/client.\n */\nexport type BaseWalletAccount = {\n\tid: WalletAccountId;\n\tplatform: WalletPlatform;\n\t/** Base58 (Solana), SS58 (Polkadot) or 0x-hex (Ethereum) address. */\n\taddress: string;\n\t/** Friendly account name, when the wallet exposes one (e.g. Polkadot). */\n\tname?: string;\n\twalletName: string;\n\twalletId: WalletId;\n};\n\n/**\n * The single WalletConnect connector, shared across every platform.\n *\n * Unlike injected wallets, it is **not tied to a platform**: one WalletConnect\n * session spans whichever namespaces the remote wallet approves in a single\n * pairing (a namespace can't be added to a live session afterwards). So there is\n * exactly one of these in `wallets`, discriminated by `type: \"walletconnect\"`.\n * Its accounts appear in `accounts`, each carrying its own `platform`.\n */\nexport type WalletConnectWallet = {\n\tid: WalletId;\n\ttype: \"walletconnect\";\n\t/**\n\t * Platforms (namespaces) the live session currently carries. Empty until\n\t * connected; populated with whatever the wallet approved (e.g. just\n\t * `[\"ethereum\"]`, or `[\"polkadot\", \"ethereum\"]`).\n\t */\n\tplatforms: WalletPlatform[];\n\t/**\n\t * Raw Reown AppKit instance, exposed as an escape hatch for advanced use\n\t * (custom modal control, reading providers directly). Most consumers should\n\t * use `connect`/`disconnect` and the derived accounts instead.\n\t */\n\tappKit: AppKitInstance;\n\tname: string;\n\ticon: string;\n\tisConnected: boolean;\n\tconnect: () => Promise<void>;\n\tdisconnect: () => Promise<void>;\n};\n\n/** Narrows a wallet to the platform-less {@link WalletConnectWallet}. */\nexport const isWalletConnectWallet = (\n\twallet: BaseWallet | WalletConnectWallet,\n): wallet is WalletConnectWallet => wallet.type === \"walletconnect\";\n\n/**\n * Narrows a wallet to an injected (browser-extension / Wallet Standard) wallet —\n * the complement of {@link isWalletConnectWallet}. `state.wallets` is\n * `(InjectedWallet | WalletConnectWallet)[]`, so use this to recover the\n * injected-only fields when iterating: `platform`, `sourceId`, and the SDK\n * handle (`provider` on Ethereum, `extension` on Polkadot, `wallet` on Solana).\n *\n * @example\n * ```ts\n * for (const wallet of wallets.filter(isInjectedWallet)) {\n * console.log(wallet.platform, wallet.sourceId); // typed, no WC widening\n * }\n * ```\n */\nexport const isInjectedWallet = <W extends BaseWallet | WalletConnectWallet>(\n\twallet: W,\n): wallet is Exclude<W, WalletConnectWallet> => wallet.type === \"injected\";\n\n/**\n * Dapp metadata shown in the WalletConnect modal. Mirrors WalletConnect's\n * `Metadata`, declared locally so core doesn't depend on\n * `@walletconnect/universal-provider`.\n */\nexport type WalletConnectMetadata = {\n\tname: string;\n\tdescription: string;\n\turl: string;\n\ticons: string[];\n};\n\nexport type WalletConnectConfig = {\n\tprojectId: string;\n\tmetadata: WalletConnectMetadata;\n\t/** Defaults to wss://relay.walletconnect.com */\n\trelayUrl?: string;\n\t/**\n\t * Networks AppKit should enable. Pass `AppKitNetwork[]` from\n\t * `@reown/appkit/networks` (see\n\t * https://docs.reown.com/advanced/multichain/polkadot/dapp-integration-guide#walletconnect-code%2Fcomponent-setup).\n\t * Loosely typed (`unknown`) so core doesn't depend on `@reown/appkit`'s\n\t * types — the value is forwarded to AppKit as-is.\n\t */\n\tnetworks: [unknown, ...unknown[]];\n\tthemeMode?: \"light\" | \"dark\";\n\tthemeVariables?: Record<string, string | number>;\n};\n\n/**\n * Context passed to a platform plugin's `getWallets$`. Carries the shared store\n * and the resolved core config (for WalletConnect / debug).\n */\nexport type PlatformContext = {\n\tstore: KheopskitStore;\n\tconfig: KheopskitConfig;\n};\n\n/**\n * A platform plugin. Created by the per-platform factories exported from\n * `@kheopskit/core/polkadot`, `/ethereum`, `/solana`. Core iterates plugins\n * generically and never imports a platform SDK itself.\n *\n * @typeParam TPlatform - the platform discriminant\n * @typeParam TWallet - the platform's wallet type (extends {@link BaseWallet})\n * @typeParam TAccount - the platform's account type (extends {@link BaseWalletAccount})\n */\nexport type KheopskitPlatform<\n\tTPlatform extends WalletPlatform = WalletPlatform,\n\tTWallet extends BaseWallet = BaseWallet,\n\tTAccount extends BaseWalletAccount = BaseWalletAccount,\n> = {\n\treadonly platform: TPlatform;\n\t// Declared as methods (not arrow properties) so parameters are checked\n\t// bivariantly — this lets a specific `KheopskitPlatform<\"polkadot\", …>` be\n\t// assigned to the base `KheopskitPlatform` despite the contravariant\n\t// `wallets$` parameter.\n\tgetWallets$(ctx: PlatformContext): Observable<TWallet[]>;\n\t// Receives this platform's (injected) wallets plus the shared, platform-less\n\t// WalletConnect connector — the plugin derives its injected accounts and, if\n\t// the WC session carries its namespace, its WalletConnect accounts.\n\tgetAccounts$(\n\t\twallets$: Observable<(TWallet | WalletConnectWallet)[]>,\n\t): Observable<TAccount[]>;\n\t/**\n\t * Optional hydration filter. Cached accounts for which this returns false are\n\t * dropped during SSR hydration (Polkadot uses it to honour `accountTypes`).\n\t */\n\tacceptsCachedAccount?(cached: CachedAccount): boolean;\n};\n\ntype ElementOf<T> = T extends readonly (infer E)[] ? E : never;\n\n/** The account type produced by a plugin (inferred from its `getAccounts$`). */\nexport type AccountOf<T> = T extends {\n\tgetAccounts$: (wallets$: never) => Observable<infer R>;\n}\n\t? ElementOf<R>\n\t: never;\n\n/** The wallet type produced by a plugin (inferred from its `getWallets$`). */\nexport type WalletOf<T> = T extends {\n\tgetWallets$: (ctx: never) => Observable<infer R>;\n}\n\t? ElementOf<R>\n\t: never;\n\nexport type KheopskitConfig<\n\tP extends readonly KheopskitPlatform[] = readonly KheopskitPlatform[],\n> = {\n\tautoReconnect: boolean;\n\t/**\n\t * Platform plugins to enable, e.g. `[polkadot(), solana({ chain })]`.\n\t * Import factories from `@kheopskit/core/<platform>`.\n\t */\n\tplatforms: P;\n\twalletConnect?: WalletConnectConfig;\n\tdebug: boolean;\n\t/**\n\t * Custom storage key for persisting wallet connection state.\n\t * Useful when running multiple kheopskit instances on the same domain\n\t * to prevent state conflicts between different dapps.\n\t *\n\t * @default \"kheopskit\"\n\t */\n\tstorageKey: string;\n\t/**\n\t * Grace period in milliseconds to wait for wallets to inject before\n\t * syncing to actual state. During this period, cached wallet/account\n\t * state from storage is preserved to prevent UI flashing.\n\t *\n\t * Set to 0 to disable hydration buffering.\n\t *\n\t * @default 500\n\t */\n\thydrationGracePeriod: number;\n};\n\n/**\n * The current kheopskit state.\n *\n * @remarks\n * While {@link KheopskitState.isHydrating} is `true`, `wallets` and `accounts`\n * may contain cached placeholders restored from storage. The **SDK handles** the\n * platform types advertise (e.g. `account.signer` / `getSigner` on Solana,\n * `account.client` on Ethereum, `wallet.provider` / `extension`) are **absent at\n * runtime even though the types claim them**, and placeholder wallets throw if\n * `connect`/`disconnect` is called. Guard all access to those behind\n * `!isHydrating`.\n *\n * The plain, serializable platform data that is persisted in the cache IS\n * restored on the placeholders (Ethereum `chainId`, Polkadot key `type`), so it\n * renders immediately on reload without flashing. Solana `chains` is not cached,\n * so it remains absent until the live account loads.\n */\nexport type KheopskitState<\n\tP extends readonly KheopskitPlatform[] = readonly KheopskitPlatform[],\n> = {\n\twallets: (WalletOf<P[number]> | WalletConnectWallet)[];\n\taccounts: AccountOf<P[number]>[];\n\tconfig: KheopskitConfig<P>;\n\t/**\n\t * Whether the state is still being hydrated from cache.\n\t *\n\t * During hydration, cached wallets/accounts may be displayed before the\n\t * actual wallet extensions have injected. See the type-level remarks: while\n\t * this is `true`, SDK-typed fields (signer/client/provider/extension) are not\n\t * present at runtime — guard all access behind `!isHydrating`.\n\t */\n\tisHydrating: boolean;\n};\n\n/**\n * Serializable wallet data for SSR hydration cache.\n * Contains only the data needed to render wallet UI without flash.\n * Note: icon is NOT stored to save cookie space - it's looked up at hydration time.\n */\nexport type CachedWallet = {\n\tid: WalletId;\n\t/** Absent for the platform-less WalletConnect connector. */\n\tplatform?: WalletPlatform;\n\ttype: WalletType;\n\tname: string;\n\tisConnected: boolean;\n};\n\n/**\n * Serializable account data for SSR hydration cache.\n * Contains only the data needed to render account UI without flash.\n */\nexport type CachedAccount = {\n\tid: string;\n\tplatform: WalletPlatform;\n\taddress: string;\n\tname?: string;\n\t/** Cached chain ID for Ethereum accounts. */\n\tchainId?: number;\n\t/** Cached key type for Polkadot accounts. */\n\tpolkadotAccountType?: PolkadotAccountType;\n\twalletId: WalletId;\n\twalletName: string;\n};\n"]}