@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,450 @@
1
+ import {
2
+ WALLET_CONNECT_WALLET_ID,
3
+ __publicField,
4
+ cookieStorage,
5
+ isValidWalletId,
6
+ isWalletPlatform,
7
+ parseWalletId,
8
+ safeLocalStorage
9
+ } from "./chunk-7QSGAJ4A.mjs";
10
+
11
+ // src/utils/isEthereumAddress.ts
12
+ import { keccak_256 } from "@noble/hashes/sha3";
13
+ var HEX_ADDRESS = /^0x[0-9a-fA-F]{40}$/;
14
+ var encoder = new TextEncoder();
15
+ var isChecksumValid = (address) => {
16
+ const hex = address.slice(2);
17
+ const hash = keccak_256(encoder.encode(hex.toLowerCase()));
18
+ for (let i = 0; i < 40; i++) {
19
+ const char = hex.charAt(i);
20
+ const isLetter = char >= "a" && char <= "f" || char >= "A" && char <= "F";
21
+ if (!isLetter) continue;
22
+ const byte = hash[i >> 1] ?? 0;
23
+ const nibble = i % 2 === 0 ? byte >> 4 : byte & 15;
24
+ const isUpper = char <= "F";
25
+ if (isUpper !== nibble >= 8) return false;
26
+ }
27
+ return true;
28
+ };
29
+ var isEthereumAddress = (address) => {
30
+ if (!HEX_ADDRESS.test(address)) return false;
31
+ if (address === address.toLowerCase()) return true;
32
+ return isChecksumValid(address);
33
+ };
34
+
35
+ // src/utils/isSs58Address.ts
36
+ import { blake2b } from "@noble/hashes/blake2b";
37
+ import { base58 } from "@scure/base";
38
+ var SS58PRE = new Uint8Array([83, 83, 53, 56, 80, 82, 69]);
39
+ var isSs58Address = (address) => {
40
+ if (!address) return false;
41
+ let decoded;
42
+ try {
43
+ decoded = base58.decode(address);
44
+ } catch {
45
+ return false;
46
+ }
47
+ const firstByte = decoded[0];
48
+ if (firstByte === void 0 || firstByte & 128) return false;
49
+ const prefixLength = firstByte & 64 ? 2 : 1;
50
+ if (decoded.length !== prefixLength + 32 + 2) return false;
51
+ const body = decoded.subarray(0, decoded.length - 2);
52
+ const preimage = new Uint8Array(SS58PRE.length + body.length);
53
+ preimage.set(SS58PRE);
54
+ preimage.set(body, SS58PRE.length);
55
+ const hash = blake2b(preimage, { dkLen: 64 });
56
+ return decoded[decoded.length - 2] === hash[0] && decoded[decoded.length - 1] === hash[1];
57
+ };
58
+
59
+ // src/utils/isSolanaAddress.ts
60
+ import { base58 as base582 } from "@scure/base";
61
+ var isSolanaAddress = (address) => {
62
+ if (!address) return false;
63
+ try {
64
+ return base582.decode(address).length === 32;
65
+ } catch {
66
+ return false;
67
+ }
68
+ };
69
+
70
+ // src/utils/isValidAddress.ts
71
+ var isValidAddress = (address) => {
72
+ if (address.startsWith("0x")) return isEthereumAddress(address);
73
+ return isSs58Address(address) || isSolanaAddress(address);
74
+ };
75
+
76
+ // src/utils/WalletAccountId.ts
77
+ var getWalletAccountId = (walletId, address) => {
78
+ if (!walletId) throw new Error("Missing walletId");
79
+ if (!isValidAddress(address)) throw new Error("Invalid address");
80
+ return `${walletId}::${address}`;
81
+ };
82
+ var parseWalletAccountId = (accountId) => {
83
+ if (!accountId) throw new Error("Invalid walletAccountId");
84
+ const [walletId, address] = accountId.split("::");
85
+ if (!walletId) throw new Error("Missing walletId");
86
+ if (!address || !isValidAddress(address)) throw new Error("Invalid address");
87
+ return { walletId, address };
88
+ };
89
+
90
+ // src/api/errors.ts
91
+ var KheopskitError = class extends Error {
92
+ constructor(code, message, options) {
93
+ super(`[kheopskit] ${message}`, { cause: options?.cause });
94
+ __publicField(this, "code");
95
+ /** The wallet id this error relates to, when applicable. */
96
+ __publicField(this, "walletId");
97
+ this.name = "KheopskitError";
98
+ this.code = code;
99
+ this.walletId = options?.walletId;
100
+ }
101
+ };
102
+
103
+ // src/api/config.ts
104
+ var DEFAULT_STORAGE_KEY = "kheopskit";
105
+ var DEFAULTS = {
106
+ autoReconnect: true,
107
+ debug: false,
108
+ storageKey: DEFAULT_STORAGE_KEY,
109
+ hydrationGracePeriod: 500
110
+ };
111
+ var resolveConfig = (config) => {
112
+ const platforms = config?.platforms ?? [];
113
+ const invalidPlatforms = platforms.filter(
114
+ (p) => typeof p !== "object" || p === null || typeof p.getWallets$ !== "function"
115
+ );
116
+ if (invalidPlatforms.length > 0) {
117
+ throw new Error(
118
+ `[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.`
119
+ );
120
+ }
121
+ if (platforms.length === 0) {
122
+ console.warn(
123
+ '[kheopskit] No platforms configured; wallets and accounts will be empty. Pass e.g. platforms: [polkadot()] from "@kheopskit/core/polkadot".'
124
+ );
125
+ }
126
+ return Object.assign({}, DEFAULTS, config, { platforms });
127
+ };
128
+
129
+ // src/api/store.ts
130
+ import { uniq } from "lodash-es";
131
+
132
+ // src/utils/createStore.ts
133
+ import { BehaviorSubject } from "rxjs";
134
+ var createStore = (key, defaultValue, storage = safeLocalStorage) => {
135
+ const subject = new BehaviorSubject(
136
+ getStoredData(key, defaultValue, storage)
137
+ );
138
+ let unsubscribeStorage;
139
+ if (typeof window !== "undefined" && storage.subscribe) {
140
+ unsubscribeStorage = storage.subscribe(key, (newValue) => {
141
+ subject.next(parseData(newValue, defaultValue));
142
+ });
143
+ }
144
+ const update = (val) => {
145
+ setStoredData(key, val, storage);
146
+ subject.next(val);
147
+ };
148
+ return {
149
+ observable: subject.asObservable(),
150
+ set: (val) => update(val),
151
+ mutate: (transform) => update(transform(subject.getValue())),
152
+ get: () => structuredClone(subject.getValue()),
153
+ /**
154
+ * Cleanup subscriptions. Call this when the store is no longer needed.
155
+ */
156
+ destroy: () => {
157
+ unsubscribeStorage?.();
158
+ subject.complete();
159
+ }
160
+ };
161
+ };
162
+ var parseData = (str, defaultValue) => {
163
+ try {
164
+ if (str) return JSON.parse(str);
165
+ } catch {
166
+ }
167
+ return defaultValue;
168
+ };
169
+ var getStoredData = (key, defaultValue, storage) => {
170
+ const str = storage.getItem(key);
171
+ return parseData(str, defaultValue);
172
+ };
173
+ var setStoredData = (key, val, storage) => {
174
+ const str = JSON.stringify(val);
175
+ storage.setItem(key, str);
176
+ };
177
+
178
+ // src/api/store.ts
179
+ var DEFAULT_SETTINGS = {};
180
+ var isValidCachedWallet = (value) => {
181
+ if (!value || typeof value !== "object") return false;
182
+ const w = value;
183
+ if (!isValidWalletId(w.id)) return false;
184
+ if (typeof w.name !== "string" || typeof w.isConnected !== "boolean")
185
+ return false;
186
+ if (w.type === "walletconnect")
187
+ return w.id === WALLET_CONNECT_WALLET_ID && w.platform === void 0;
188
+ return w.type === "injected" && isWalletPlatform(w.platform);
189
+ };
190
+ var isValidCachedAccount = (value) => {
191
+ if (!value || typeof value !== "object") return false;
192
+ const a = value;
193
+ return typeof a.id === "string" && !!a.id && isWalletPlatform(a.platform) && typeof a.address === "string" && !!a.address && isValidWalletId(a.walletId) && typeof a.walletName === "string";
194
+ };
195
+ var toCompactPolkadotAccountType = (type) => {
196
+ switch (type) {
197
+ case "sr25519":
198
+ return 0;
199
+ case "ed25519":
200
+ return 1;
201
+ case "ecdsa":
202
+ return 2;
203
+ case "ethereum":
204
+ return 3;
205
+ default:
206
+ return null;
207
+ }
208
+ };
209
+ var fromCompactPolkadotAccountType = (type) => {
210
+ switch (type) {
211
+ case 0:
212
+ return "sr25519";
213
+ case 1:
214
+ return "ed25519";
215
+ case 2:
216
+ return "ecdsa";
217
+ case 3:
218
+ return "ethereum";
219
+ default:
220
+ return void 0;
221
+ }
222
+ };
223
+ var toCompactPlatform = (platform) => {
224
+ switch (platform) {
225
+ case "polkadot":
226
+ return 0;
227
+ case "ethereum":
228
+ return 1;
229
+ case "solana":
230
+ return 2;
231
+ }
232
+ };
233
+ var fromCompactPlatform = (code) => {
234
+ switch (code) {
235
+ case 0:
236
+ return "polkadot";
237
+ case 1:
238
+ return "ethereum";
239
+ case 2:
240
+ return "solana";
241
+ default:
242
+ return void 0;
243
+ }
244
+ };
245
+ var createKheopskitStore = (options = {}) => {
246
+ const { ssrCookies, storageKey = DEFAULT_STORAGE_KEY } = options;
247
+ const storage = ssrCookies !== void 0 ? createCompactCookieStorage(ssrCookies) : safeLocalStorage;
248
+ const store2 = createStore(storageKey, DEFAULT_SETTINGS, storage);
249
+ const addEnabledWalletId = (walletId) => {
250
+ parseWalletId(walletId);
251
+ store2.mutate((prev) => ({
252
+ ...prev,
253
+ autoReconnect: uniq((prev.autoReconnect ?? []).concat(walletId))
254
+ }));
255
+ };
256
+ const removeEnabledWalletId = (walletId) => {
257
+ store2.mutate((prev) => ({
258
+ ...prev,
259
+ autoReconnect: uniq(
260
+ (prev.autoReconnect ?? []).filter((id) => id !== walletId)
261
+ )
262
+ }));
263
+ };
264
+ const getCachedState = () => {
265
+ const data = store2.get();
266
+ const cachedWallets = Array.isArray(data?.cachedWallets) ? data.cachedWallets : [];
267
+ const cachedAccounts = Array.isArray(data?.cachedAccounts) ? data.cachedAccounts : [];
268
+ return {
269
+ wallets: cachedWallets.filter(isValidCachedWallet),
270
+ accounts: cachedAccounts.filter(isValidCachedAccount)
271
+ };
272
+ };
273
+ const setCachedState = (wallets, accounts) => {
274
+ store2.mutate((prev) => ({
275
+ ...prev,
276
+ cachedWallets: wallets,
277
+ cachedAccounts: accounts
278
+ }));
279
+ };
280
+ return {
281
+ observable: store2.observable,
282
+ addEnabledWalletId,
283
+ removeEnabledWalletId,
284
+ getCachedState,
285
+ setCachedState
286
+ };
287
+ };
288
+ var DEFAULT_STORE_SYMBOL = /* @__PURE__ */ Symbol.for("kheopskit.defaultStore");
289
+ var getDefaultStore = () => {
290
+ const g = globalThis;
291
+ if (!g[DEFAULT_STORE_SYMBOL]) {
292
+ g[DEFAULT_STORE_SYMBOL] = createKheopskitStore();
293
+ }
294
+ return g[DEFAULT_STORE_SYMBOL];
295
+ };
296
+ var store = {
297
+ get observable() {
298
+ return getDefaultStore().observable;
299
+ },
300
+ addEnabledWalletId: (walletId) => getDefaultStore().addEnabledWalletId(walletId),
301
+ removeEnabledWalletId: (walletId) => getDefaultStore().removeEnabledWalletId(walletId),
302
+ getCachedState: () => getDefaultStore().getCachedState(),
303
+ setCachedState: (wallets, accounts) => getDefaultStore().setCachedState(wallets, accounts)
304
+ };
305
+ var isCompactStore = (value) => {
306
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
307
+ if ("cachedWallets" in value || "cachedAccounts" in value) return false;
308
+ return "v" in value || "w" in value || "a" in value || "r" in value;
309
+ };
310
+ var toCompactStore = (data) => {
311
+ const wallets = data.cachedWallets?.map(
312
+ (wallet) => [
313
+ wallet.id,
314
+ wallet.name,
315
+ wallet.isConnected ? 1 : 0,
316
+ wallet.type === "walletconnect" ? 1 : 0
317
+ ]
318
+ );
319
+ const accounts = data.cachedAccounts?.map(
320
+ (account) => [
321
+ account.walletId,
322
+ account.address,
323
+ account.name ?? null,
324
+ account.chainId ?? null,
325
+ toCompactPolkadotAccountType(account.polkadotAccountType),
326
+ toCompactPlatform(account.platform)
327
+ ]
328
+ );
329
+ return {
330
+ v: 1,
331
+ r: data.autoReconnect,
332
+ w: wallets?.length ? wallets : void 0,
333
+ a: accounts?.length ? accounts : void 0
334
+ };
335
+ };
336
+ var fromCompactStore = (data) => {
337
+ const walletNameMap = /* @__PURE__ */ new Map();
338
+ const wallets = [];
339
+ for (const item of Array.isArray(data.w) ? data.w : []) {
340
+ if (!Array.isArray(item)) continue;
341
+ const [id, name, isConnected, type] = item;
342
+ if (!isValidWalletId(id)) continue;
343
+ const isWalletConnect = id === WALLET_CONNECT_WALLET_ID;
344
+ const walletType = type === 1 ? "walletconnect" : "injected";
345
+ if (isWalletConnect !== (walletType === "walletconnect")) continue;
346
+ walletNameMap.set(id, name);
347
+ wallets.push({
348
+ id,
349
+ platform: isWalletConnect ? void 0 : parseWalletId(id).platform,
350
+ type: walletType,
351
+ name,
352
+ isConnected: isConnected === 1
353
+ });
354
+ }
355
+ const accounts = [];
356
+ for (const item of Array.isArray(data.a) ? data.a : []) {
357
+ if (!Array.isArray(item)) continue;
358
+ const [
359
+ walletId,
360
+ address,
361
+ name,
362
+ chainId,
363
+ polkadotAccountType,
364
+ platformCode
365
+ ] = item;
366
+ if (!isValidWalletId(walletId) || typeof address !== "string" || !address)
367
+ continue;
368
+ const platform = platformCode != null ? fromCompactPlatform(platformCode) : walletId === WALLET_CONNECT_WALLET_ID ? void 0 : parseWalletId(walletId).platform;
369
+ if (!isWalletPlatform(platform)) continue;
370
+ accounts.push({
371
+ id: getWalletAccountId(walletId, address),
372
+ platform,
373
+ address,
374
+ name: name ?? void 0,
375
+ chainId: chainId ?? void 0,
376
+ polkadotAccountType: platform === "polkadot" ? fromCompactPolkadotAccountType(polkadotAccountType) : void 0,
377
+ walletId,
378
+ walletName: walletNameMap.get(walletId) ?? walletId
379
+ });
380
+ }
381
+ return {
382
+ autoReconnect: data.r,
383
+ cachedWallets: wallets,
384
+ cachedAccounts: accounts
385
+ };
386
+ };
387
+ var decodeStore = (raw, fallback) => {
388
+ try {
389
+ const parsed = JSON.parse(raw);
390
+ if (isCompactStore(parsed)) return fromCompactStore(parsed);
391
+ return parsed;
392
+ } catch {
393
+ return fallback;
394
+ }
395
+ };
396
+ var encodeStore = (data) => JSON.stringify(toCompactStore(data));
397
+ var createCompactCookieStorage = (initialCookies) => {
398
+ const base = cookieStorage(initialCookies);
399
+ return {
400
+ getItem: (key) => {
401
+ const raw = base.getItem(key);
402
+ if (!raw) return null;
403
+ const expanded = decodeStore(raw, DEFAULT_SETTINGS);
404
+ if (typeof document !== "undefined") {
405
+ try {
406
+ const parsed = JSON.parse(raw);
407
+ if (!isCompactStore(parsed)) {
408
+ base.setItem(key, encodeStore(expanded));
409
+ }
410
+ } catch {
411
+ }
412
+ }
413
+ return JSON.stringify(expanded);
414
+ },
415
+ setItem: (key, value) => {
416
+ const expanded = decodeStore(value, DEFAULT_SETTINGS);
417
+ base.setItem(key, encodeStore(expanded));
418
+ },
419
+ removeItem: base.removeItem,
420
+ subscribe: (key, callback) => {
421
+ const unsubscribe = base.subscribe?.(key, (value) => {
422
+ if (!value) {
423
+ callback(null);
424
+ return;
425
+ }
426
+ const expanded = decodeStore(value, DEFAULT_SETTINGS);
427
+ callback(JSON.stringify(expanded));
428
+ });
429
+ return () => {
430
+ unsubscribe?.();
431
+ };
432
+ }
433
+ };
434
+ };
435
+
436
+ export {
437
+ isEthereumAddress,
438
+ isSs58Address,
439
+ isSolanaAddress,
440
+ isValidAddress,
441
+ getWalletAccountId,
442
+ parseWalletAccountId,
443
+ KheopskitError,
444
+ DEFAULT_STORAGE_KEY,
445
+ resolveConfig,
446
+ createKheopskitStore,
447
+ getDefaultStore,
448
+ store
449
+ };
450
+ //# sourceMappingURL=chunk-6XAZANB5.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/isEthereumAddress.ts","../src/utils/isSs58Address.ts","../src/utils/isSolanaAddress.ts","../src/utils/isValidAddress.ts","../src/utils/WalletAccountId.ts","../src/api/errors.ts","../src/api/config.ts","../src/api/store.ts","../src/utils/createStore.ts"],"sourcesContent":["import { keccak_256 } from \"@noble/hashes/sha3\";\n\nconst HEX_ADDRESS = /^0x[0-9a-fA-F]{40}$/;\nconst encoder = new TextEncoder();\n\n/**\n * Verifies the EIP-55 mixed-case checksum of an Ethereum address.\n * Dependency-free (keccak via @noble/hashes) so core stays decoupled from viem.\n */\nconst isChecksumValid = (address: string): boolean => {\n\tconst hex = address.slice(2);\n\tconst hash = keccak_256(encoder.encode(hex.toLowerCase()));\n\tfor (let i = 0; i < 40; i++) {\n\t\tconst char = hex.charAt(i);\n\t\tconst isLetter =\n\t\t\t(char >= \"a\" && char <= \"f\") || (char >= \"A\" && char <= \"F\");\n\t\tif (!isLetter) continue;\n\t\tconst byte = hash[i >> 1] ?? 0;\n\t\tconst nibble = i % 2 === 0 ? byte >> 4 : byte & 0x0f;\n\t\tconst isUpper = char <= \"F\";\n\t\tif (isUpper !== nibble >= 8) return false;\n\t}\n\treturn true;\n};\n\n/**\n * Returns true if `address` is a valid Ethereum address.\n *\n * Mirrors viem's `isAddress` (strict): the shape must be `0x` + 40 hex, and a\n * mixed-case address must pass the EIP-55 checksum. All-lowercase addresses are\n * accepted as non-checksummed.\n */\nexport const isEthereumAddress = (address: string): boolean => {\n\tif (!HEX_ADDRESS.test(address)) return false;\n\tif (address === address.toLowerCase()) return true;\n\treturn isChecksumValid(address);\n};\n","import { blake2b } from \"@noble/hashes/blake2b\";\nimport { base58 } from \"@scure/base\";\n\n// The \"SS58PRE\" prefix used in the SS58 checksum preimage.\nconst SS58PRE = new Uint8Array([0x53, 0x53, 0x35, 0x38, 0x50, 0x52, 0x45]);\n\n/**\n * Returns true if `address` is a valid SS58-encoded 32-byte account id (the\n * only form Substrate wallets inject), verifying the blake2b-512 checksum.\n *\n * Dependency-free (base58 via @scure/base, blake2b via @noble/hashes) so core\n * stays decoupled from polkadot-api.\n */\nexport const isSs58Address = (address: string): boolean => {\n\tif (!address) return false;\n\n\tlet decoded: Uint8Array;\n\ttry {\n\t\tdecoded = base58.decode(address);\n\t} catch {\n\t\treturn false;\n\t}\n\n\t// First byte encodes the network prefix; the high bit is reserved/invalid.\n\tconst firstByte = decoded[0];\n\tif (firstByte === undefined || firstByte & 0b1000_0000) return false;\n\t// The 0b0100_0000 bit marks a 2-byte prefix.\n\tconst prefixLength = firstByte & 0b0100_0000 ? 2 : 1;\n\n\t// AccountId32 layout: prefix + 32-byte pubkey + 2-byte checksum.\n\tif (decoded.length !== prefixLength + 32 + 2) return false;\n\n\tconst body = decoded.subarray(0, decoded.length - 2);\n\tconst preimage = new Uint8Array(SS58PRE.length + body.length);\n\tpreimage.set(SS58PRE);\n\tpreimage.set(body, SS58PRE.length);\n\tconst hash = blake2b(preimage, { dkLen: 64 });\n\n\treturn (\n\t\tdecoded[decoded.length - 2] === hash[0] &&\n\t\tdecoded[decoded.length - 1] === hash[1]\n\t);\n};\n","import { base58 } from \"@scure/base\";\n\n/**\n * Returns true if the value is a valid Solana address: a base58-encoded\n * 32-byte ed25519 public key.\n *\n * Solana addresses carry no checksum, so this is a pure base58 + length check.\n * Dependency-free (base58 via @scure/base) so core stays decoupled from\n * @solana/kit.\n */\nexport const isSolanaAddress = (address: string): boolean => {\n\tif (!address) return false;\n\ttry {\n\t\treturn base58.decode(address).length === 32;\n\t} catch {\n\t\treturn false;\n\t}\n};\n","import { isEthereumAddress } from \"./isEthereumAddress\";\nimport { isSolanaAddress } from \"./isSolanaAddress\";\nimport { isSs58Address } from \"./isSs58Address\";\n\nexport const isValidAddress = (address: string): boolean => {\n\tif (address.startsWith(\"0x\")) return isEthereumAddress(address);\n\t// SS58 (checksummed) and Solana (base58, no checksum) are disjoint: a\n\t// 32-byte Solana pubkey fails SS58 decoding, and an SS58 address decodes to\n\t// more than 32 bytes so it fails the Solana length check.\n\treturn isSs58Address(address) || isSolanaAddress(address);\n};\n","import { isValidAddress } from \"./isValidAddress\";\n\nexport type WalletAccountId = string;\n\nexport const getWalletAccountId = (\n\twalletId: string,\n\taddress: string,\n): WalletAccountId => {\n\tif (!walletId) throw new Error(\"Missing walletId\");\n\tif (!isValidAddress(address)) throw new Error(\"Invalid address\");\n\treturn `${walletId}::${address}`;\n};\n\nexport const parseWalletAccountId = (accountId: string) => {\n\tif (!accountId) throw new Error(\"Invalid walletAccountId\");\n\tconst [walletId, address] = accountId.split(\"::\");\n\tif (!walletId) throw new Error(\"Missing walletId\");\n\tif (!address || !isValidAddress(address)) throw new Error(\"Invalid address\");\n\treturn { walletId, address };\n};\n","import type { WalletId } from \"../utils/WalletId\";\n\n/**\n * Stable error codes thrown by kheopskit. Catch a {@link KheopskitError} and\n * branch on `error.code` instead of matching message strings.\n */\nexport type KheopskitErrorCode =\n\t/** `connect()` called on a wallet that is already connected. */\n\t| \"WALLET_ALREADY_CONNECTED\"\n\t/** `disconnect()` called on a wallet that is not connected. */\n\t| \"WALLET_NOT_CONNECTED\"\n\t/** The wallet does not advertise a capability kheopskit needs to proceed. */\n\t| \"FEATURE_NOT_SUPPORTED\"\n\t/** No active WalletConnect session for the requested operation. */\n\t| \"NO_SESSION\"\n\t/** No provider available for the requested namespace. */\n\t| \"NO_PROVIDER\"\n\t/** The requested chain cannot be used for this operation (e.g. a Solana cluster with no CAIP-2 id over WalletConnect). */\n\t| \"UNSUPPORTED_CHAIN\";\n\n/**\n * Error thrown by kheopskit wallet/account operations. Carries a stable\n * {@link KheopskitErrorCode} and, when relevant, the offending wallet id.\n *\n * @example\n * ```ts\n * try {\n * await wallet.connect();\n * } catch (error) {\n * if (error instanceof KheopskitError && error.code === \"WALLET_ALREADY_CONNECTED\") {\n * // ignore\n * } else throw error;\n * }\n * ```\n */\nexport class KheopskitError extends Error {\n\treadonly code: KheopskitErrorCode;\n\t/** The wallet id this error relates to, when applicable. */\n\treadonly walletId?: WalletId;\n\n\tconstructor(\n\t\tcode: KheopskitErrorCode,\n\t\tmessage: string,\n\t\toptions?: { walletId?: WalletId; cause?: unknown },\n\t) {\n\t\tsuper(`[kheopskit] ${message}`, { cause: options?.cause });\n\t\tthis.name = \"KheopskitError\";\n\t\tthis.code = code;\n\t\tthis.walletId = options?.walletId;\n\t}\n}\n","import type { KheopskitConfig, KheopskitPlatform } from \"./types\";\n\n/**\n * Default storage key for persisting wallet connection state.\n * Can be overridden via config.storageKey to avoid conflicts\n * when running multiple dapps on the same domain.\n */\nexport const DEFAULT_STORAGE_KEY = \"kheopskit\";\n\nconst DEFAULTS = {\n\tautoReconnect: true,\n\tdebug: false,\n\tstorageKey: DEFAULT_STORAGE_KEY,\n\thydrationGracePeriod: 500,\n} satisfies Omit<KheopskitConfig, \"platforms\" | \"walletConnect\">;\n\nexport const resolveConfig = <\n\tconst P extends readonly KheopskitPlatform[] = readonly KheopskitPlatform[],\n>(\n\tconfig?: Partial<KheopskitConfig<P>>,\n): KheopskitConfig<P> => {\n\tconst platforms = (config?.platforms ?? []) as P;\n\n\t// Guard the v4 breaking change: platforms are plugin instances, not strings.\n\t// Catches JS callers (no tsc) and gives agents a fix that names the doc.\n\tconst invalidPlatforms = (platforms as readonly unknown[]).filter(\n\t\t(p) =>\n\t\t\ttypeof p !== \"object\" ||\n\t\t\tp === null ||\n\t\t\ttypeof (p as { getWallets$?: unknown }).getWallets$ !== \"function\",\n\t);\n\tif (invalidPlatforms.length > 0) {\n\t\tthrow new Error(\n\t\t\t\"[kheopskit] config.platforms must contain plugin instances created by the \" +\n\t\t\t\t\"per-platform factories (e.g. platforms: [polkadot(), ethereum(), solana()] \" +\n\t\t\t\t'imported from \"@kheopskit/core/<platform>\"). ' +\n\t\t\t\t`Invalid entries: ${JSON.stringify(invalidPlatforms)}. ` +\n\t\t\t\t'String platform names like \"polkadot\" were removed in v4 — see MIGRATING_TO_V4.md.',\n\t\t);\n\t}\n\n\tif (platforms.length === 0) {\n\t\tconsole.warn(\n\t\t\t\"[kheopskit] No platforms configured; wallets and accounts will be empty. \" +\n\t\t\t\t'Pass e.g. platforms: [polkadot()] from \"@kheopskit/core/polkadot\".',\n\t\t);\n\t}\n\n\treturn Object.assign({}, DEFAULTS, config, { platforms });\n};\n","import { uniq } from \"lodash-es\";\nimport { createStore } from \"../utils/createStore\";\nimport { isWalletPlatform } from \"../utils/isWalletPlatform\";\nimport { cookieStorage, safeLocalStorage } from \"../utils/storage\";\nimport { getWalletAccountId } from \"../utils/WalletAccountId\";\nimport {\n\tisValidWalletId,\n\tparseWalletId,\n\tWALLET_CONNECT_WALLET_ID,\n\ttype WalletId,\n} from \"../utils/WalletId\";\nimport { DEFAULT_STORAGE_KEY } from \"./config\";\nimport type { CachedAccount, CachedWallet, WalletPlatform } from \"./types\";\n\ntype KheopskitStoreData = {\n\tautoReconnect?: WalletId[];\n\t/** Cached wallet state for SSR hydration to prevent UI flash */\n\tcachedWallets?: CachedWallet[];\n\t/** Cached account state for SSR hydration to prevent UI flash */\n\tcachedAccounts?: CachedAccount[];\n};\n\n// wallet type: 0=injected, 1=walletconnect\ntype CompactWalletEntry = [WalletId, string, 0 | 1, 0 | 1];\ntype CompactPolkadotAccountType = 0 | 1 | 2 | 3;\n// platform: 0=polkadot, 1=ethereum, 2=solana\ntype CompactPlatformCode = 0 | 1 | 2;\ntype CompactAccountEntry = [\n\tWalletId,\n\tstring,\n\tstring | null,\n\tnumber | null,\n\tCompactPolkadotAccountType | null,\n\t// Platform code. Newly written entries always include it (the WalletConnect\n\t// connector's walletId is platform-less, so platform can't be derived from\n\t// it); absent in entries written by older versions, where it's derived from\n\t// the walletId instead.\n\t(CompactPlatformCode | null)?,\n];\n\ntype CompactStoreV1 = {\n\tv: 1;\n\t// autoReconnect\n\tr?: WalletId[];\n\t// wallets: [id, name, isConnected(0|1), type(0=injected,1=walletconnect)?]\n\tw?: CompactWalletEntry[];\n\t// accounts: [walletId, address, name?, chainId?, polkadotType?, platform?]\n\ta?: CompactAccountEntry[];\n};\n\nconst DEFAULT_SETTINGS: KheopskitStoreData = {};\n\n/**\n * Validates a cached wallet read from persisted storage. Cached state may have\n * been written by an older (or corrupted) version with a different shape, so we\n * drop anything that wouldn't survive hydration/sort rather than letting it\n * throw at render time. Only the fields downstream code relies on are checked.\n */\nconst isValidCachedWallet = (value: unknown): value is CachedWallet => {\n\tif (!value || typeof value !== \"object\") return false;\n\tconst w = value as Record<string, unknown>;\n\tif (!isValidWalletId(w.id)) return false;\n\tif (typeof w.name !== \"string\" || typeof w.isConnected !== \"boolean\")\n\t\treturn false;\n\t// The WalletConnect connector is platform-less with a fixed id; drop stale\n\t// per-platform WC entries written by older versions.\n\tif (w.type === \"walletconnect\")\n\t\treturn w.id === WALLET_CONNECT_WALLET_ID && w.platform === undefined;\n\treturn w.type === \"injected\" && isWalletPlatform(w.platform);\n};\n\n/** Validates a cached account read from persisted storage. See {@link isValidCachedWallet}. */\nconst isValidCachedAccount = (value: unknown): value is CachedAccount => {\n\tif (!value || typeof value !== \"object\") return false;\n\tconst a = value as Record<string, unknown>;\n\treturn (\n\t\ttypeof a.id === \"string\" &&\n\t\t!!a.id &&\n\t\tisWalletPlatform(a.platform) &&\n\t\ttypeof a.address === \"string\" &&\n\t\t!!a.address &&\n\t\tisValidWalletId(a.walletId) &&\n\t\ttypeof a.walletName === \"string\"\n\t);\n};\n\nconst toCompactPolkadotAccountType = (\n\ttype: CachedAccount[\"polkadotAccountType\"],\n): CompactPolkadotAccountType | null => {\n\tswitch (type) {\n\t\tcase \"sr25519\":\n\t\t\treturn 0;\n\t\tcase \"ed25519\":\n\t\t\treturn 1;\n\t\tcase \"ecdsa\":\n\t\t\treturn 2;\n\t\tcase \"ethereum\":\n\t\t\treturn 3;\n\t\tdefault:\n\t\t\treturn null;\n\t}\n};\n\nconst fromCompactPolkadotAccountType = (\n\ttype: CompactPolkadotAccountType | null | undefined,\n): CachedAccount[\"polkadotAccountType\"] => {\n\tswitch (type) {\n\t\tcase 0:\n\t\t\treturn \"sr25519\";\n\t\tcase 1:\n\t\t\treturn \"ed25519\";\n\t\tcase 2:\n\t\t\treturn \"ecdsa\";\n\t\tcase 3:\n\t\t\treturn \"ethereum\";\n\t\tdefault:\n\t\t\treturn undefined;\n\t}\n};\n\nconst toCompactPlatform = (platform: WalletPlatform): CompactPlatformCode => {\n\tswitch (platform) {\n\t\tcase \"polkadot\":\n\t\t\treturn 0;\n\t\tcase \"ethereum\":\n\t\t\treturn 1;\n\t\tcase \"solana\":\n\t\t\treturn 2;\n\t}\n};\n\nconst fromCompactPlatform = (\n\tcode: CompactPlatformCode | null | undefined,\n): WalletPlatform | undefined => {\n\tswitch (code) {\n\t\tcase 0:\n\t\t\treturn \"polkadot\";\n\t\tcase 1:\n\t\t\treturn \"ethereum\";\n\t\tcase 2:\n\t\t\treturn \"solana\";\n\t\tdefault:\n\t\t\treturn undefined;\n\t}\n};\n\ntype CreateKheopskitStoreOptions = {\n\t/**\n\t * Cookie string for SSR hydration.\n\t * When provided, uses cookieStorage instead of localStorage.\n\t */\n\tssrCookies?: string;\n\t/**\n\t * Custom storage key to namespace the stored data.\n\t * @default \"kheopskit\"\n\t */\n\tstorageKey?: string;\n};\n\n/**\n * Creates a kheopskit store with the appropriate storage backend.\n * Uses cookieStorage when ssrCookies is provided (for SSR hydration),\n * otherwise falls back to safeLocalStorage.\n *\n * @param options - Configuration options for the store\n */\nexport const createKheopskitStore = (\n\toptions: CreateKheopskitStoreOptions = {},\n) => {\n\tconst { ssrCookies, storageKey = DEFAULT_STORAGE_KEY } = options;\n\tconst storage =\n\t\tssrCookies !== undefined\n\t\t\t? createCompactCookieStorage(ssrCookies)\n\t\t\t: safeLocalStorage;\n\tconst store = createStore(storageKey, DEFAULT_SETTINGS, storage);\n\n\tconst addEnabledWalletId = (walletId: WalletId) => {\n\t\tparseWalletId(walletId); // validate walletId\n\t\tstore.mutate((prev) => ({\n\t\t\t...prev,\n\t\t\tautoReconnect: uniq((prev.autoReconnect ?? []).concat(walletId)),\n\t\t}));\n\t};\n\n\tconst removeEnabledWalletId = (walletId: WalletId) => {\n\t\tstore.mutate((prev) => ({\n\t\t\t...prev,\n\t\t\tautoReconnect: uniq(\n\t\t\t\t(prev.autoReconnect ?? []).filter((id) => id !== walletId),\n\t\t\t),\n\t\t}));\n\t};\n\n\tconst getCachedState = () => {\n\t\t// `store.get()` returns whatever JSON was persisted — it may be from an\n\t\t// older version, a different shape, or outright corrupt. Read defensively:\n\t\t// coerce non-objects/arrays to empty and drop any entry that fails\n\t\t// validation, so stale cache degrades to \"start fresh\" instead of throwing\n\t\t// during hydration (which renders eagerly, so a throw blanks the dapp).\n\t\tconst data = store.get() as Partial<KheopskitStoreData> | null | undefined;\n\t\tconst cachedWallets = Array.isArray(data?.cachedWallets)\n\t\t\t? data.cachedWallets\n\t\t\t: [];\n\t\tconst cachedAccounts = Array.isArray(data?.cachedAccounts)\n\t\t\t? data.cachedAccounts\n\t\t\t: [];\n\t\treturn {\n\t\t\twallets: cachedWallets.filter(isValidCachedWallet),\n\t\t\taccounts: cachedAccounts.filter(isValidCachedAccount),\n\t\t};\n\t};\n\n\tconst setCachedState = (\n\t\twallets: CachedWallet[],\n\t\taccounts: CachedAccount[],\n\t) => {\n\t\tstore.mutate((prev) => ({\n\t\t\t...prev,\n\t\t\tcachedWallets: wallets,\n\t\t\tcachedAccounts: accounts,\n\t\t}));\n\t};\n\n\treturn {\n\t\tobservable: store.observable,\n\t\taddEnabledWalletId,\n\t\tremoveEnabledWalletId,\n\t\tgetCachedState,\n\t\tsetCachedState,\n\t};\n};\n\nexport type KheopskitStore = ReturnType<typeof createKheopskitStore>;\n\n/**\n * Cached default store instance, anchored on globalThis so it stays a single\n * instance even if this module is duplicated across bundle chunks (e.g. CJS\n * subpath entries). Lazily initialized on first access to be SSR-safe.\n */\nconst DEFAULT_STORE_SYMBOL = Symbol.for(\"kheopskit.defaultStore\");\n\n/**\n * Gets the default store, creating it on first access.\n * Uses localStorage on client, noop on server.\n * Lazily initialized to avoid SSR issues with module-level code.\n */\nexport const getDefaultStore = (): KheopskitStore => {\n\tconst g = globalThis as unknown as Record<symbol, KheopskitStore | undefined>;\n\tif (!g[DEFAULT_STORE_SYMBOL]) {\n\t\tg[DEFAULT_STORE_SYMBOL] = createKheopskitStore();\n\t}\n\treturn g[DEFAULT_STORE_SYMBOL];\n};\n\n/**\n * @deprecated Use createKheopskitStore() or getDefaultStore() instead.\n * This export is kept for backward compatibility but may cause SSR issues\n * if imported at module level in server environments.\n */\nexport const store = {\n\tget observable() {\n\t\treturn getDefaultStore().observable;\n\t},\n\taddEnabledWalletId: (walletId: WalletId) =>\n\t\tgetDefaultStore().addEnabledWalletId(walletId),\n\tremoveEnabledWalletId: (walletId: WalletId) =>\n\t\tgetDefaultStore().removeEnabledWalletId(walletId),\n\tgetCachedState: () => getDefaultStore().getCachedState(),\n\tsetCachedState: (wallets: CachedWallet[], accounts: CachedAccount[]) =>\n\t\tgetDefaultStore().setCachedState(wallets, accounts),\n};\n\nconst isCompactStore = (value: unknown): value is CompactStoreV1 => {\n\tif (!value || typeof value !== \"object\" || Array.isArray(value)) return false;\n\tif (\"cachedWallets\" in value || \"cachedAccounts\" in value) return false;\n\treturn \"v\" in value || \"w\" in value || \"a\" in value || \"r\" in value;\n};\n\nconst toCompactStore = (data: KheopskitStoreData): CompactStoreV1 => {\n\tconst wallets = data.cachedWallets?.map(\n\t\t(wallet): CompactWalletEntry => [\n\t\t\twallet.id,\n\t\t\twallet.name,\n\t\t\twallet.isConnected ? 1 : 0,\n\t\t\twallet.type === \"walletconnect\" ? 1 : 0,\n\t\t],\n\t);\n\n\tconst accounts = data.cachedAccounts?.map(\n\t\t(account): CompactAccountEntry => [\n\t\t\taccount.walletId,\n\t\t\taccount.address,\n\t\t\taccount.name ?? null,\n\t\t\taccount.chainId ?? null,\n\t\t\ttoCompactPolkadotAccountType(account.polkadotAccountType),\n\t\t\ttoCompactPlatform(account.platform),\n\t\t],\n\t);\n\n\treturn {\n\t\tv: 1,\n\t\tr: data.autoReconnect,\n\t\tw: wallets?.length ? wallets : undefined,\n\t\ta: accounts?.length ? accounts : undefined,\n\t};\n};\n\nconst fromCompactStore = (data: CompactStoreV1): KheopskitStoreData => {\n\tconst walletNameMap = new Map<WalletId, string>();\n\n\t// Decode defensively: a compact payload may be malformed (older/corrupt\n\t// cookie, hand-edited). Skip entries with an unparseable wallet id rather\n\t// than throwing, which would crash store initialisation.\n\tconst wallets: CachedWallet[] = [];\n\tfor (const item of Array.isArray(data.w) ? data.w : []) {\n\t\tif (!Array.isArray(item)) continue;\n\t\tconst [id, name, isConnected, type] = item;\n\t\tif (!isValidWalletId(id)) continue;\n\t\tconst isWalletConnect = id === WALLET_CONNECT_WALLET_ID;\n\t\tconst walletType = type === 1 ? \"walletconnect\" : \"injected\";\n\t\t// Keep id/type consistent: the platform-less connector uses the fixed WC\n\t\t// id; everything else is a platform-prefixed injected wallet. Drop stale\n\t\t// mismatches (e.g. per-platform WC ids from older versions).\n\t\tif (isWalletConnect !== (walletType === \"walletconnect\")) continue;\n\t\twalletNameMap.set(id, name);\n\t\twallets.push({\n\t\t\tid,\n\t\t\tplatform: isWalletConnect ? undefined : parseWalletId(id).platform,\n\t\t\ttype: walletType,\n\t\t\tname,\n\t\t\tisConnected: isConnected === 1,\n\t\t});\n\t}\n\n\tconst accounts: CachedAccount[] = [];\n\tfor (const item of Array.isArray(data.a) ? data.a : []) {\n\t\tif (!Array.isArray(item)) continue;\n\t\tconst [\n\t\t\twalletId,\n\t\t\taddress,\n\t\t\tname,\n\t\t\tchainId,\n\t\t\tpolkadotAccountType,\n\t\t\tplatformCode,\n\t\t] = item;\n\t\tif (!isValidWalletId(walletId) || typeof address !== \"string\" || !address)\n\t\t\tcontinue;\n\t\t// Prefer the explicit platform code; fall back to deriving it from the\n\t\t// walletId for entries written before the code existed (never WC ones).\n\t\tconst platform =\n\t\t\tplatformCode != null\n\t\t\t\t? fromCompactPlatform(platformCode)\n\t\t\t\t: walletId === WALLET_CONNECT_WALLET_ID\n\t\t\t\t\t? undefined\n\t\t\t\t\t: parseWalletId(walletId).platform;\n\t\tif (!isWalletPlatform(platform)) continue;\n\t\taccounts.push({\n\t\t\tid: getWalletAccountId(walletId, address),\n\t\t\tplatform,\n\t\t\taddress,\n\t\t\tname: name ?? undefined,\n\t\t\tchainId: chainId ?? undefined,\n\t\t\tpolkadotAccountType:\n\t\t\t\tplatform === \"polkadot\"\n\t\t\t\t\t? fromCompactPolkadotAccountType(polkadotAccountType)\n\t\t\t\t\t: undefined,\n\t\t\twalletId,\n\t\t\twalletName: walletNameMap.get(walletId) ?? walletId,\n\t\t});\n\t}\n\n\treturn {\n\t\tautoReconnect: data.r,\n\t\tcachedWallets: wallets,\n\t\tcachedAccounts: accounts,\n\t};\n};\n\nconst decodeStore = (raw: string, fallback: KheopskitStoreData) => {\n\ttry {\n\t\tconst parsed = JSON.parse(raw) as unknown;\n\t\tif (isCompactStore(parsed)) return fromCompactStore(parsed);\n\t\treturn parsed as KheopskitStoreData;\n\t} catch {\n\t\treturn fallback;\n\t}\n};\n\nconst encodeStore = (data: KheopskitStoreData): string =>\n\tJSON.stringify(toCompactStore(data));\n\nconst createCompactCookieStorage = (initialCookies?: string) => {\n\tconst base = cookieStorage(initialCookies);\n\n\treturn {\n\t\tgetItem: (key: string) => {\n\t\t\tconst raw = base.getItem(key);\n\t\t\tif (!raw) return null;\n\t\t\tconst expanded = decodeStore(raw, DEFAULT_SETTINGS);\n\t\t\tif (typeof document !== \"undefined\") {\n\t\t\t\ttry {\n\t\t\t\t\tconst parsed = JSON.parse(raw) as unknown;\n\t\t\t\t\tif (!isCompactStore(parsed)) {\n\t\t\t\t\t\tbase.setItem(key, encodeStore(expanded));\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t// Ignore malformed cookie during migration\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn JSON.stringify(expanded);\n\t\t},\n\t\tsetItem: (key: string, value: string) => {\n\t\t\tconst expanded = decodeStore(value, DEFAULT_SETTINGS);\n\t\t\tbase.setItem(key, encodeStore(expanded));\n\t\t},\n\t\tremoveItem: base.removeItem,\n\t\tsubscribe: (key: string, callback: (value: string | null) => void) => {\n\t\t\tconst unsubscribe = base.subscribe?.(key, (value) => {\n\t\t\t\tif (!value) {\n\t\t\t\t\tcallback(null);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst expanded = decodeStore(value, DEFAULT_SETTINGS);\n\t\t\t\tcallback(JSON.stringify(expanded));\n\t\t\t});\n\t\t\treturn () => {\n\t\t\t\tunsubscribe?.();\n\t\t\t};\n\t\t},\n\t};\n};\n","import { BehaviorSubject } from \"rxjs\";\nimport { type SyncableStorage, safeLocalStorage } from \"./storage\";\n\nexport const createStore = <T>(\n\tkey: string,\n\tdefaultValue: T,\n\tstorage: SyncableStorage = safeLocalStorage,\n) => {\n\tconst subject = new BehaviorSubject<T>(\n\t\tgetStoredData(key, defaultValue, storage),\n\t);\n\n\t// Cross-tab sync via storage.subscribe (uses storage event for localStorage, BroadcastChannel for cookies)\n\t// Only subscribe if window is available (client-side) and storage supports it\n\tlet unsubscribeStorage: (() => void) | undefined;\n\tif (typeof window !== \"undefined\" && storage.subscribe) {\n\t\tunsubscribeStorage = storage.subscribe(key, (newValue) => {\n\t\t\tsubject.next(parseData(newValue, defaultValue));\n\t\t});\n\t}\n\n\tconst update = (val: T) => {\n\t\tsetStoredData(key, val, storage);\n\t\tsubject.next(val);\n\t};\n\n\treturn {\n\t\tobservable: subject.asObservable(),\n\t\tset: (val: T) => update(val),\n\t\tmutate: (transform: (prev: T) => T) =>\n\t\t\tupdate(transform(subject.getValue())),\n\t\tget: () => structuredClone(subject.getValue()),\n\t\t/**\n\t\t * Cleanup subscriptions. Call this when the store is no longer needed.\n\t\t */\n\t\tdestroy: () => {\n\t\t\tunsubscribeStorage?.();\n\t\t\tsubject.complete();\n\t\t},\n\t};\n};\n\nconst parseData = <T>(str: string | null, defaultValue: T): T => {\n\ttry {\n\t\tif (str) return JSON.parse(str);\n\t} catch {\n\t\t// invalid data\n\t}\n\treturn defaultValue;\n};\n\nconst getStoredData = <T>(\n\tkey: string,\n\tdefaultValue: T,\n\tstorage: SyncableStorage,\n): T => {\n\tconst str = storage.getItem(key);\n\treturn parseData(str, defaultValue);\n};\n\nconst setStoredData = <T>(key: string, val: T, storage: SyncableStorage) => {\n\tconst str = JSON.stringify(val);\n\tstorage.setItem(key, str);\n};\n"],"mappings":"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","names":["base58","store"]}