@kheopskit/core 6.0.0-rc.1 → 6.0.0-rc.3

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 (47) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/MIGRATING_TO_V6.md +52 -40
  3. package/README.md +64 -45
  4. package/dist/ethereum.d.mts +2 -1
  5. package/dist/ethereum.d.ts +2 -1
  6. package/dist/ethereum.js +1 -1
  7. package/dist/ethereum.mjs +1 -1
  8. package/dist/index.d.mts +1 -1
  9. package/dist/index.d.ts +1 -1
  10. package/dist/index.js +34 -23
  11. package/dist/index.js.map +1 -1
  12. package/dist/index.mjs +35 -24
  13. package/dist/index.mjs.map +1 -1
  14. package/dist/{injectedWallets-48L-AL1I.mjs → injectedWallets-5GL1Jder.mjs} +2 -2
  15. package/dist/{injectedWallets-48L-AL1I.mjs.map → injectedWallets-5GL1Jder.mjs.map} +1 -1
  16. package/dist/{injectedWallets-Dy8YGKL-.js → injectedWallets-BQBgS-f0.js} +2 -2
  17. package/dist/{injectedWallets-Dy8YGKL-.js.map → injectedWallets-BQBgS-f0.js.map} +1 -1
  18. package/dist/internal.d.mts +1 -1
  19. package/dist/internal.d.ts +1 -1
  20. package/dist/polkadot.d.mts +1 -1
  21. package/dist/polkadot.d.ts +1 -1
  22. package/dist/polkadot.js +6 -40
  23. package/dist/polkadot.js.map +1 -1
  24. package/dist/polkadot.mjs +6 -40
  25. package/dist/polkadot.mjs.map +1 -1
  26. package/dist/registryReads-BpHsWLd7.js +38 -0
  27. package/dist/registryReads-BpHsWLd7.js.map +1 -0
  28. package/dist/{pageLoad-BZ7o8p5R.mjs → registryReads-BvtznSo3.mjs} +12 -3
  29. package/dist/registryReads-BvtznSo3.mjs.map +1 -0
  30. package/dist/solana.d.mts +2 -1
  31. package/dist/solana.d.ts +2 -1
  32. package/dist/solana.js +2 -1
  33. package/dist/solana.js.map +1 -1
  34. package/dist/solana.mjs +2 -1
  35. package/dist/solana.mjs.map +1 -1
  36. package/dist/testing.d.mts +1 -1
  37. package/dist/testing.d.ts +1 -1
  38. package/dist/types-BWHvNFlN.js.map +1 -1
  39. package/dist/{types-lkMXVJNG.d.ts → types-C3I3w0jb.d.mts} +47 -32
  40. package/dist/{types-lkMXVJNG.d.mts → types-C3I3w0jb.d.ts} +47 -32
  41. package/dist/types-Yor672md.mjs.map +1 -1
  42. package/dist/walletconnect.d.mts +1 -1
  43. package/dist/walletconnect.d.ts +1 -1
  44. package/package.json +1 -1
  45. package/dist/pageLoad-BZ7o8p5R.mjs.map +0 -1
  46. package/dist/pageLoad-D1wLeuFG.js +0 -28
  47. package/dist/pageLoad-D1wLeuFG.js.map +0 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # @kheopskit/core
2
2
 
3
+ ## 6.0.0-rc.3
4
+
5
+ ### Patch Changes
6
+
7
+ - [#160](https://github.com/kheopskit/kheopskit/pull/160) [`4e55488`](https://github.com/kheopskit/kheopskit/commit/4e5548833b782669f00e33452f5ed4dff9ddcac0) Thanks [@0xKheops](https://github.com/0xKheops)! - Fixed: a remembered wallet forgotten before it injects is no longer reconnected when it does. A placeholder's `disconnect()` now forgets the wallet, or calls the live wallet's `disconnect()` once that one is discovered. `store.forgetWallet(walletId)` during the session cancels the automatic reconnect for good, even if the wallet is remembered again. `disconnect()` now forgets the wallet whatever its status, including while its automatic reconnect waits for the extension or after that reconnect failed, so the next load does not reconnect it. A wallet the user disconnects during hydration no longer holds `isHydrating` until `hydrationTimeout`.
8
+
9
+ ## 6.0.0-rc.2
10
+
11
+ ### Minor Changes
12
+
13
+ - [#155](https://github.com/kheopskit/kheopskit/pull/155) [`d3a05e1`](https://github.com/kheopskit/kheopskit/commit/d3a05e14f6eebebf182f3d2ddfee4057321fbab0) Thanks [@0xKheops](https://github.com/0xKheops)! - Added: `account.name` is readable on every account type. Solana accounts take it from the Wallet Standard `label`, and Ethereum accounts leave it `undefined`.
14
+
15
+ ### Patch Changes
16
+
17
+ - [#157](https://github.com/kheopskit/kheopskit/pull/157) [`2eb5c08`](https://github.com/kheopskit/kheopskit/commit/2eb5c086c201763e84065e05785012d0a845d05d) Thanks [@0xKheops](https://github.com/0xKheops)! - Fixed: Polkadot discovery now reads `window.injectedWeb3` at `load` and 100, 300, 600 and 1100 ms after it, as it already did after subscribe. `isDiscovering` is `true` until the last read, 1100 ms after `load` or after subscribe, whichever is later, so no read is pending once it turns `false`, and that emission lists every extension injected before that read. With `config.walletConnect`, it also waits for the connector to load. A subscription that starts after `load` no longer reads `false` from its first emission. On the server, the registry is read once and no timer is set.
18
+
19
+ Hydration waits for discovery only while a cached wallet has not been discovered. When every cached wallet is discovered and settled, `isHydrating` ends at the grace period without waiting for `load`.
20
+
3
21
  ## 6.0.0-rc.1
4
22
 
5
23
  ### Patch Changes
@@ -48,8 +48,8 @@ This guide grows with each v6 change. Each section covers one area.
48
48
  | Test kit | none; apps fake `injectedWeb3` themselves | `@kheopskit/core/testing` | [6](#6-test-kit) |
49
49
  | Placeholder SDK handles | typed as present, `undefined` at runtime while hydrating | absent from the placeholder type; narrow on `isPlaceholder` | [7](#7-placeholders-carry-isplaceholder) |
50
50
  | `connect()` on a placeholder | throws `PendingWalletError` | waits for the live wallet, or rejects `WALLET_PENDING` | [7](#7-placeholders-carry-isplaceholder) |
51
- | Hydration end | `isHydrating` stays `true` until every cached connected wallet reconnected, capped at six times the grace period | `isHydrating` ends once the grace period has elapsed, discovery has ended and every wallet that hydrated as `connecting` has settled, or at `hydrationTimeout` (default 3000 ms) | [8](#8-discovery-hydration-end-and-persistence) |
52
- | Discovery at page load | the Polkadot registry was read at 0, 100, 300, 600 and 1100 ms after subscribe only | read again at `load` and 100 ms later; `state.isDiscovering` says whether that read has landed | [8](#8-discovery-hydration-end-and-persistence) |
51
+ | Hydration end | `isHydrating` stays `true` until every cached connected wallet reconnected, capped at six times the grace period | `isHydrating` ends once the grace period has elapsed and every cached wallet has settled, or at `hydrationTimeout` (default 3000 ms); a cached wallet not yet discovered settles when discovery ends | [8](#8-discovery-hydration-end-and-persistence) |
52
+ | Discovery at page load | the Polkadot registry was read at 0, 100, 300, 600 and 1100 ms after subscribe only | read at subscribe and at `load`, then 100, 300, 600 and 1100 ms after each; `state.isDiscovering` is `true` until the last read, 1100 ms after `load` or after subscribe, whichever is later, and while a WalletConnect connector loads | [8](#8-discovery-hydration-end-and-persistence) |
53
53
  | Cache during a slow reconnect | a reconnect slower than the persistence debounce wrote `isConnected: false` and no accounts | a wallet still `connecting` keeps its previous cached entry and accounts | [8](#8-discovery-hydration-end-and-persistence) |
54
54
  | React instance and hooks | `createKheopskit` returns a provider and `useWallets`/`useAccounts`; reaching one wallet means reading the whole state; unmounting the provider leaves the pipeline running | `createKheopskit` also returns `kheopskit$` and `store`; `useWallet`, `useAccount` and `useConnect` read one item; unmounting the provider stops the pipeline | [9](#9-kheopskitreact-one-shared-instance-and-wallet-hooks) |
55
55
 
@@ -59,10 +59,13 @@ This guide grows with each v6 change. Each section covers one area.
59
59
 
60
60
  Each section ends with the app code it makes redundant. In short:
61
61
 
62
- - A page-load gate that waits a fixed delay before concluding that a wallet is
63
- missing. Wait for `isDiscovering` to turn `false` (section 8).
62
+ - A gate that delays subscribing until `load`, and a fixed grace after it
63
+ before concluding that a wallet is missing. Both are redundant, because the
64
+ read window now runs until 1100 ms after `load` or after subscribe,
65
+ whichever is later. Wait for `isDiscovering` to turn `false` (section 8).
64
66
  - Connect dedup wrappers, such as a `requestOnce` or `connectOnce` map keyed by
65
- wallet id (section 1).
67
+ wallet id (section 1). An app that calls `connect()` in reaction to state
68
+ must then skip a wallet whose `error.code` is `USER_REJECTED`.
66
69
  - `auditTime(20)` or `debounceTime(20)` placed after `kheopskit$` to skip the
67
70
  stale intermediate state (section 1).
68
71
  - Refusal detection by `error.name === "Error"` or by message text. Branch on
@@ -152,7 +155,9 @@ try {
152
155
  no caller without one joined, on `disconnect()`, or when the last `kheopskit$`
153
156
  subscriber unsubscribes; those reject every remaining caller `ABORTED`.
154
157
  - **Resolution follows the state.** `connect()` resolves once `kheopskit$` has
155
- emitted a state in which the wallet has `isConnected: true`. `disconnect()`
158
+ emitted a state in which the wallet has `isConnected: true`. An injected
159
+ wallet's refused or failed `connect()` rejects after `kheopskit$` has
160
+ emitted the wallet `disconnected` with that `error`. `disconnect()`
156
161
  first waits for the platform's disconnect, when the platform has one, then
157
162
  resolves once a state shows `isConnected: false`, or once a later
158
163
  `connect()`, the wallet leaving the list or the end of the subscription
@@ -238,7 +243,12 @@ try {
238
243
  ### What to delete in your app
239
244
 
240
245
  - Dedup wrappers around `connect()`, such as a `requestOnce` or `connectOnce`
241
- map keyed by wallet id.
246
+ map keyed by wallet id. Kheopskit shares only concurrent calls. A refused
247
+ attempt has ended when its `USER_REJECTED` state is emitted, so a
248
+ `connect()` made in reaction to that state asks the extension again. An app
249
+ that connects from state skips a wallet whose `error.code` is
250
+ `USER_REJECTED`. The map used to hide this, because its entry outlived the
251
+ emission.
242
252
  - `auditTime(20)` or `debounceTime(20)` placed after `kheopskit$` to skip the
243
253
  stale intermediate state.
244
254
  - Treating `WALLET_ALREADY_CONNECTED` as success and swallowing
@@ -264,7 +274,7 @@ try {
264
274
 
265
275
  The list holds the injected wallets the user connected. Kheopskit adds a
266
276
  wallet on every successful connect, whatever `config.autoReconnect` says, and
267
- removes it when a connected wallet is disconnected. The WalletConnect
277
+ removes it on every `disconnect()`, whatever the wallet's status. The WalletConnect
268
278
  connector is never added; AppKit restores its own session. The `autoReconnect`
269
279
  config option keeps its name and meaning. It decides whether the remembered
270
280
  wallets reconnect on load.
@@ -482,8 +492,8 @@ stays silent. Any other failure sets `wallet.error`, keeps the wallet
482
492
  remembered and is retried on the next change to the wallet list, 3 attempts in
483
493
  total. A manual `connect()` or `disconnect()` on a remembered wallet ends its
484
494
  automatic reconnect for the session. A `disconnect()` while the automatic
485
- reconnect still waits for the extension aborts it without forgetting the
486
- wallet, so the next load reconnects it. Automatic reconnect also ends when the
495
+ reconnect still waits for the extension aborts it and forgets the wallet, so
496
+ the next load does not reconnect it. Automatic reconnect also ends when the
487
497
  wallet disappears mid-attempt; the wallet stays remembered for the next load.
488
498
  Nothing is written to `console.error` any more; `debug: true` logs the failure
489
499
  with `console.debug`.
@@ -676,19 +686,19 @@ has no live stream behind it, `connect()` rejects `WALLET_PENDING`.
676
686
 
677
687
  ### New state field: `isDiscovering`
678
688
 
679
- `KheopskitState.isDiscovering` is new and required. It is `true` until the page
680
- has fired `load` and kheopskit has read `window.injectedWeb3` again, so the
681
- emission that turns it `false` already lists every Polkadot extension injected
682
- by then. It is `false` from the first emission when you subscribe after
683
- `load`, and always `true` on the server.
689
+ `KheopskitState.isDiscovering` is new and required. It is `true` until the
690
+ last read of `window.injectedWeb3`, 1100 ms after `load` or after subscribe,
691
+ whichever is later, so the emission that turns it `false` already lists every
692
+ Polkadot extension injected before that read. With `config.walletConnect`, it
693
+ also waits for the connector to load. It is always `true` on the server.
684
694
 
685
- Kheopskit now reads the Polkadot registry at `load` and 100 ms after `load`, on
686
- top of the reads at subscribe and 100, 300, 600 and 1100 ms after it. An
687
- extension that injects after the 1100 ms read but before `load` is listed
688
- without a reload. Nothing reads the registry after the last of these reads. A
689
- wallet that injects after `load` is listed only when a read still runs after
690
- it, the one 100 ms after `load` or a subscribe read still pending, so its
691
- discovery is not guaranteed.
695
+ Kheopskit reads the Polkadot registry at subscribe and 100, 300, 600 and
696
+ 1100 ms after it, as in 5.x. At `load` it starts the same schedule again, and
697
+ drops the subscribe reads still pending. An extension that injects after the
698
+ 1100 ms read but before `load`, or up to 1100 ms after `load`, is listed
699
+ without a reload. A wallet that injects after the last read is not listed
700
+ until the next subscription. On the server kheopskit reads once and sets no
701
+ timer.
692
702
 
693
703
  A state literal or test mock typed as `KheopskitState` must add the field:
694
704
 
@@ -702,8 +712,10 @@ A state literal or test mock typed as `KheopskitState` must add the field:
702
712
  };
703
713
  ```
704
714
 
705
- Hydration now also waits for discovery. With a cache, `isHydrating` stays
706
- `true` until `load` or `hydrationTimeout`, whichever comes first. A
715
+ Hydration waits for discovery only while a cached wallet has not been
716
+ discovered. When every cached wallet injects early, `isHydrating` ends at the
717
+ grace period without waiting for `load`. A cached wallet that has not appeared
718
+ holds it until discovery ends or `hydrationTimeout`, whichever comes first. A
707
719
  `hydrationGracePeriod` of `0` still turns hydration off in `kheopskit$`. Under
708
720
  `KheopskitProvider`, the first render still shows the cached placeholders with
709
721
  `isHydrating: true` until the first emission.
@@ -724,14 +736,15 @@ grace period.
724
736
 
725
737
  ### New end rule
726
738
 
727
- `isHydrating` turns `false` once `hydrationGracePeriod` has elapsed, discovery
728
- has ended (`isDiscovering` is `false`), and every wallet that hydrated as
729
- `connecting` has settled. A wallet has settled when it is absent from the
730
- discovered wallets, connected once its account list has been read (an empty
731
- list counts) or connected with no cached accounts, disconnected with `error`
732
- set, or disconnected and not remembered when hydration began. At
733
- `hydrationTimeout` hydration ends whatever is pending, and a wallet whose
734
- reconnect is still pending stays `connecting`.
739
+ `isHydrating` turns `false` once `hydrationGracePeriod` has elapsed and every
740
+ cached wallet has settled. A cached wallet missing from the discovered wallets
741
+ settles when discovery ends (`isDiscovering` is `false`). A discovered one
742
+ settles at once unless it hydrated as `connecting`. Then it settles when
743
+ connected once its account list has been read (an empty list counts) or
744
+ connected with no cached accounts, disconnected with `error` set, or
745
+ disconnected and not remembered when hydration began. At `hydrationTimeout`
746
+ hydration ends whatever is pending, and a wallet whose reconnect is still
747
+ pending stays `connecting`.
735
748
 
736
749
  "Its account list has been read" relies on a signal the built-in plugins send.
737
750
  A custom `KheopskitPlatform` plugin, or a wrapper that pipes a built-in
@@ -739,9 +752,8 @@ plugin's `getAccounts$`, does not send it. Such a wallet settles on its first
739
752
  listed account, as in 5.x, and a reconnected wallet with cached accounts that
740
753
  now lists none holds hydration until `hydrationTimeout`.
741
754
 
742
- - Discovery ends only after the page fires `load`, so a page with a cache
743
- hydrates until `load` or `hydrationTimeout`, whichever comes first, even
744
- when no wallet reconnects.
755
+ - When every cached wallet is discovered and settled, hydration ends at the
756
+ grace period, without waiting for `load`.
745
757
  - A wallet that reconnects without sharing any account with the site settles
746
758
  as soon as it answers; its cached accounts leave the list when hydration
747
759
  ends.
@@ -751,15 +763,15 @@ now lists none holds hydration until `hydrationTimeout`.
751
763
  may never happen, so waiting
752
764
  for it would hold most failed reconnects until `hydrationTimeout`. A retry
753
765
  that does run shows on the live wallet's `status`, not as a placeholder.
754
- - A remembered wallet the user disconnects during or right after its reconnect
755
- holds hydration, and shows its cached accounts, until `hydrationTimeout`.
756
766
  - A prompt the extension opens for a revoked site holds hydration until the
757
767
  user answers or `hydrationTimeout` elapses. After a refusal the wallet is
758
768
  forgotten, so the next load opens no prompt.
759
769
 
760
- In practice, with the extension removed or with `autoReconnect: false`,
761
- hydration no longer runs to 3 s. It ends once the grace period has elapsed and
762
- the page has loaded, or at `hydrationTimeout` if that comes first.
770
+ In practice, hydration no longer runs to 3 s. With `autoReconnect: false` and
771
+ the extension installed, it ends at the grace period. With the extension
772
+ removed, it ends when discovery ends, 1100 ms after `load` or after subscribe,
773
+ whichever is later (or once a WalletConnect connector has loaded, if later),
774
+ or at `hydrationTimeout` if that comes first.
763
775
 
764
776
  ### Persistence guarantee
765
777
 
package/README.md CHANGED
@@ -110,6 +110,7 @@ implements them.
110
110
  ### Discovery
111
111
 
112
112
  Source: [`api/polkadot/wallets.ts`](https://github.com/kheopskit/kheopskit/blob/main/packages/core/src/api/polkadot/wallets.ts),
113
+ [`utils/registryReads.ts`](https://github.com/kheopskit/kheopskit/blob/main/packages/core/src/utils/registryReads.ts),
113
114
  [`utils/pageLoad.ts`](https://github.com/kheopskit/kheopskit/blob/main/packages/core/src/utils/pageLoad.ts),
114
115
  [`api/kheopskit.ts`](https://github.com/kheopskit/kheopskit/blob/main/packages/core/src/api/kheopskit.ts).
115
116
 
@@ -117,16 +118,15 @@ Polkadot extensions register themselves on `window.injectedWeb3` and send no
117
118
  event, so kheopskit reads that registry on a fixed schedule:
118
119
 
119
120
  - At subscribe, then 100, 300, 600 and 1100 ms after subscribe.
120
- - When `window` fires `load`, and again 100 ms after `load`. A subscription
121
- that starts after `load` skips these two reads.
121
+ - When `window` fires `load`, then 100, 300, 600 and 1100 ms after `load`. The
122
+ subscribe reads still pending at `load` are dropped. A subscription that
123
+ starts after `load` has only the subscribe reads.
122
124
 
123
- Nothing reads the registry after the last of these reads. A wallet that
124
- injects after `load` is listed only when a read still runs after it, the one
125
- 100 ms after `load` or a subscribe read still pending, so its discovery is not
126
- guaranteed. Wallets inject `injectedWeb3` while the page loads, so a wallet
127
- that injects after the last read is not listed until the next subscription,
128
- usually the next page load. Kheopskit has no
129
- `refreshWallets()` and does not read again on focus or on connect.
125
+ The read window ends 1100 ms after `load` or after subscribe, whichever is
126
+ later. Nothing reads the registry after that, so a wallet that injects later
127
+ is not listed until the next subscription, usually the next page load.
128
+ Kheopskit has no `refreshWallets()` and does not read again on focus or on
129
+ connect. On the server it reads once and sets no timer.
130
130
 
131
131
  Ethereum (EIP-6963, through `mipd`) and Solana (Wallet Standard) wallets
132
132
  announce themselves with events, so those lists update whenever a wallet
@@ -134,19 +134,19 @@ announces or unregisters.
134
134
 
135
135
  `state.isDiscovering` tells you whether the list may still grow:
136
136
 
137
- - `true` when the subscription started before `document.readyState` was
138
- `"complete"`.
139
- - It turns `false` in the first emission after `load` in which the registry was
140
- read again. That emission already lists every extension injected by `load`.
141
- Wait for it before you conclude that a wallet is missing.
142
- - `false` from the first emission when you subscribe after `load`.
137
+ - `true` until the read window ends, 1100 ms after `load` or after subscribe,
138
+ whichever is later, and while a WalletConnect connector loads.
139
+ - It turns `false` in the emission of the last read, or once the
140
+ WalletConnect connector has loaded if that comes later. That emission lists
141
+ every Polkadot extension injected before the last read. Wait for it before
142
+ you conclude that a wallet is missing.
143
143
  - Always `true` on the server.
144
144
 
145
- It says nothing about wallets that inject after `load`. In the end-to-end test
146
- `e2e/tests/discovery-at-load.spec.ts`, an extension injected 1500 ms after the
147
- first read and before `load` showed in the wallet list 16.2 ms after `load` on
148
- vite-react, and 15.8 to 18.3 ms across the three example apps (the test's
149
- limit is 300 ms).
145
+ In the end-to-end test `e2e/tests/discovery-at-load.spec.ts`, an extension
146
+ injected 1500 ms after the first read and before `load` showed in the wallet
147
+ list 23.9 ms after `load` on vite-react, and 13.1 to 23.9 ms across the three
148
+ example apps (the test's limit is 300 ms). The "Looking for wallets…" status
149
+ went away 1114 to 1124 ms after `load`.
150
150
 
151
151
  ### Hydration
152
152
 
@@ -180,7 +180,9 @@ A placeholder wallet is never connected. Its `connect()` waits for kheopskit to
180
180
  discover the live wallet with the same id and connects that one. It rejects
181
181
  with `WALLET_PENDING` if hydration ends first, and with `ABORTED` if its
182
182
  `signal` fires or the last subscriber unsubscribes while it waits. Its
183
- `disconnect()` resolves at once. A cached wallet that kheopskit has already
183
+ `disconnect()` forgets the wallet and resolves at once, so kheopskit does not
184
+ reconnect it when it injects. Once the live wallet is discovered, it calls the
185
+ live wallet's `disconnect()` instead. A cached wallet that kheopskit has already
184
186
  discovered is no longer a placeholder. It carries its live `connect()` and
185
187
  `disconnect()` and shows `status: "connecting"` while its reconnect runs, and
186
188
  its `connect()` joins that reconnect. A Polkadot wallet's `extension` stays
@@ -190,13 +192,15 @@ its `connect()` joins that reconnect. A Polkadot wallet's `extension` stays
190
192
  `isHydrating` turns `false` once all three hold:
191
193
 
192
194
  1. `hydrationGracePeriod` has elapsed (default 500 ms).
193
- 2. Discovery has ended (`isDiscovering` is `false`), so a page with a cache
194
- hydrates until `load` or `hydrationTimeout`, whichever comes first.
195
- 3. Every wallet that hydrated as `connecting` has settled. A wallet has
196
- settled when it is absent from the discovered wallets, connected once its
197
- account list has been read (an empty list counts) or connected with no
198
- cached accounts, disconnected with `error` set, or disconnected and not
199
- remembered when hydration began.
195
+ 2. Every cached wallet has been discovered, or discovery has ended
196
+ (`isDiscovering` is `false`). When every cached wallet injects early,
197
+ hydration does not wait for `load`. A cached wallet that has not appeared
198
+ holds it until discovery ends.
199
+ 3. Every discovered wallet that hydrated as `connecting` has settled. It has
200
+ settled when connected once its account list has been read (an empty list
201
+ counts) or connected with no cached accounts, disconnected with `error`
202
+ set, or disconnected and not remembered. A wallet remembered after
203
+ hydration began does not count.
200
204
 
201
205
  At `hydrationTimeout` (default 3000 ms) hydration ends whatever is pending, and
202
206
  a wallet whose reconnect is still pending stays `connecting`. The timeout wins
@@ -208,8 +212,6 @@ over a longer grace period, so an app that raises `hydrationGracePeriod` sets
208
212
  ends.
209
213
  - A failed reconnect ends that wallet's wait, even when the reconnect policy
210
214
  still permits a retry.
211
- - A remembered wallet the user disconnects during or right after its reconnect
212
- holds hydration, and shows its cached accounts, until `hydrationTimeout`.
213
215
  - A prompt the extension opens for a revoked site holds hydration until the
214
216
  user answers or `hydrationTimeout` elapses.
215
217
  - "Its account list has been read" relies on a signal that the built-in
@@ -240,8 +242,8 @@ Source: [`api/wallets.ts`](https://github.com/kheopskit/kheopskit/blob/main/pack
240
242
  [`api/injectedWallets.ts`](https://github.com/kheopskit/kheopskit/blob/main/packages/core/src/api/injectedWallets.ts).
241
243
 
242
244
  Kheopskit adds an injected wallet to the store's `remembered` list on every
243
- successful `connect()` and removes it when `disconnect()` starts on a
244
- connected wallet, whatever `autoReconnect` says, so an app can turn reconnect
245
+ successful `connect()` and removes it when `disconnect()` starts, whatever the
246
+ wallet's status and whatever `autoReconnect` says, so an app can turn reconnect
245
247
  on later. On the next load it reconnects the remembered injected wallets that
246
248
  `autoReconnect` accepts:
247
249
 
@@ -276,7 +278,13 @@ or removed, or any wallet's status changing), 3 attempts in total. If the list
276
278
  never changes again, no retry runs. A manual `connect()` or `disconnect()` on a
277
279
  remembered wallet ends its automatic reconnect for the session. A
278
280
  `disconnect()` while the automatic reconnect still waits for the extension
279
- aborts it without forgetting the wallet, so the next load reconnects it.
281
+ aborts it and forgets the wallet, so the next load does not reconnect it.
282
+ Forgetting a wallet with `store.forgetWallet(walletId)`, or calling
283
+ `disconnect()` on its placeholder before it injects, ends its automatic
284
+ reconnect for the rest of the session, even if the wallet is remembered again.
285
+ The next load reconnects it only if it is remembered by then.
286
+ `store.forgetWallet(walletId)` does not stop an attempt that already
287
+ waits for the extension. The wallet's `disconnect()` does.
280
288
  Automatic reconnect also ends when the wallet disappears mid-attempt, and the
281
289
  wallet stays remembered for the next load.
282
290
 
@@ -401,6 +409,13 @@ the order the extension lists them in. `state.wallets` follows the same
401
409
  platform order, then the WalletConnect connector, with Talisman first, then by
402
410
  name.
403
411
 
412
+ Every account has an optional `name`. On Polkadot it is the account name the
413
+ extension lists. On Solana it is the Wallet Standard `label`, when the wallet
414
+ provides one. On Ethereum it is always `undefined`, because EIP-1193 and
415
+ EIP-6963 expose no account name. A wallet can report an empty name, so
416
+ `account.name || account.address` gives a display label on every platform. A
417
+ cached placeholder keeps the `name` of the account it stands for.
418
+
404
419
  ### Persistence
405
420
 
406
421
  Source: [`api/store.ts`](https://github.com/kheopskit/kheopskit/blob/main/packages/core/src/api/store.ts),
@@ -424,9 +439,8 @@ goes to `localStorage` as JSON:
424
439
  ```
425
440
 
426
441
  - `remembered` lists the injected wallets the user connected. It is written as
427
- soon as a connect succeeds and cleared as soon as `disconnect()` starts on a
428
- connected wallet. A `disconnect()` that aborts a pending connect leaves it
429
- unchanged.
442
+ soon as a connect succeeds and cleared as soon as `disconnect()` starts,
443
+ whatever the wallet's status.
430
444
  - `cachedWallets` and `cachedAccounts` are written by the hydration rule above:
431
445
  after hydration ends, 1 second after the state stops changing.
432
446
  - Wallet icons go to a separate `localStorage` key, `kheopskit-icons`, to keep
@@ -666,17 +680,22 @@ fail.
666
680
 
667
681
  ### Design choices
668
682
 
669
- - Kheopskit does not look for wallets that inject after `load`, and has no
670
- `refreshWallets()`. Wallets inject at page load, and a rescan would add
671
- timers and states for a case no supported wallet produces.
683
+ - Kheopskit stops reading the Polkadot registry when the read window ends, and
684
+ has no `refreshWallets()`. Wallets inject at page load, and a permanent
685
+ watcher would add timers and states for a case no supported wallet produces.
672
686
  - No `walletsReady` promise. It would be a second surface for the one fact
673
687
  `isDiscovering` carries.
674
- - No longer fixed poll. Any duration is still a guess about how long a page
675
- takes to boot, and wallets inject before `load`.
676
- - Discovery does not end at the last scheduled read (1100 ms). On a warm load
677
- that is slower than `load`, for no gain.
678
- - `remembered` is read once at subscribe. A live read would make a connect in
679
- one tab connect every open tab, with no matching disconnect.
688
+ - The read window restarts at `load` instead of running a longer poll from
689
+ subscribe. Its coverage after `load` then no longer depends on when the app
690
+ subscribes.
691
+ - Discovery ends with the last read, 1100 ms after `load` (or once a
692
+ WalletConnect connector has loaded, if later), not at `load`, so a wallet
693
+ that injects shortly after `load` is listed before `isDiscovering` turns
694
+ `false`.
695
+ Hydration waits for that end only while a cached wallet is missing.
696
+ - A wallet remembered after subscribe is not reconnected. Reconnecting it
697
+ would make a connect in one tab connect every open tab, with no matching
698
+ disconnect. A wallet forgotten after subscribe is not reconnected either.
680
699
  - No `persist: false` option. The `autoReconnect` predicate covers "do not
681
700
  reconnect", and `storageKey` keeps two dapps apart.
682
701
 
@@ -1,4 +1,4 @@
1
- import { E as isWalletConnectWallet, N as WalletAccountId, T as isInjectedWallet, b as WalletConnectWallet, k as WalletId, l as KheopskitPlatform, m as PlaceholderWallet, p as Placeholder, r as BaseWallet } from "./types-lkMXVJNG.mjs";
1
+ import { E as isWalletConnectWallet, N as WalletAccountId, T as isInjectedWallet, b as WalletConnectWallet, k as WalletId, l as KheopskitPlatform, m as PlaceholderWallet, p as Placeholder, r as BaseWallet } from "./types-C3I3w0jb.mjs";
2
2
  import { Account, CustomTransport, EIP1193Provider, WalletClient } from "viem";
3
3
  //#region src/utils/isEthereumAddress.d.ts
4
4
  /**
@@ -47,6 +47,7 @@ type LiveEthereumAccount = {
47
47
  address: `0x${string}`;
48
48
  /** Current chain ID the wallet is connected to. `undefined` while loading or after provider disconnect. */
49
49
  chainId: number | undefined;
50
+ name?: string;
50
51
  walletName: string;
51
52
  walletId: WalletId;
52
53
  };
@@ -1,4 +1,4 @@
1
- import { E as isWalletConnectWallet, N as WalletAccountId, T as isInjectedWallet, b as WalletConnectWallet, k as WalletId, l as KheopskitPlatform, m as PlaceholderWallet, p as Placeholder, r as BaseWallet } from "./types-lkMXVJNG.js";
1
+ import { E as isWalletConnectWallet, N as WalletAccountId, T as isInjectedWallet, b as WalletConnectWallet, k as WalletId, l as KheopskitPlatform, m as PlaceholderWallet, p as Placeholder, r as BaseWallet } from "./types-C3I3w0jb.js";
2
2
  import { Account, CustomTransport, EIP1193Provider, WalletClient } from "viem";
3
3
  //#region src/utils/isEthereumAddress.d.ts
4
4
  /**
@@ -47,6 +47,7 @@ type LiveEthereumAccount = {
47
47
  address: `0x${string}`;
48
48
  /** Current chain ID the wallet is connected to. `undefined` while loading or after provider disconnect. */
49
49
  chainId: number | undefined;
50
+ name?: string;
50
51
  walletName: string;
51
52
  walletId: WalletId;
52
53
  };
package/dist/ethereum.js CHANGED
@@ -1,7 +1,7 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_types = require("./types-BWHvNFlN.js");
3
3
  const require_platformAccounts = require("./platformAccounts-BjIeRcFN.js");
4
- const require_injectedWallets = require("./injectedWallets-Dy8YGKL-.js");
4
+ const require_injectedWallets = require("./injectedWallets-BQBgS-f0.js");
5
5
  let rxjs = require("rxjs");
6
6
  let viem = require("viem");
7
7
  let mipd = require("mipd");
package/dist/ethereum.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { _ as getCachedObservable$, a as isEip1193UserRejection, h as clearCachedObservable, n as isWalletConnectWallet, t as isInjectedWallet, u as getWalletId } from "./types-Yor672md.mjs";
2
2
  import { d as isEthereumAddress, i as getSessionCaip10s, o as getWalletAccountId, r as getCaip10Addresses, t as createPlatformAccounts$ } from "./platformAccounts-yyRUrnel.mjs";
3
- import { t as createInjectedWallets$ } from "./injectedWallets-48L-AL1I.mjs";
3
+ import { t as createInjectedWallets$ } from "./injectedWallets-5GL1Jder.mjs";
4
4
  import { Observable, ReplaySubject, combineLatest, distinctUntilChanged, map, of, shareReplay } from "rxjs";
5
5
  import { createWalletClient, custom, getAddress } from "viem";
6
6
  import { createStore } from "mipd";
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { t as SyncableStorage } from "./storage-D3AwNgqc.mjs";
2
- import { A as getWalletId, C as WalletStatus, D as KheopskitError, E as isWalletConnectWallet, F as parseWalletAccountId, M as parseWalletId, N as WalletAccountId, O as KheopskitErrorCode, P as getWalletAccountId, S as WalletPlatform, T as isInjectedWallet, _ as WalletConnectMetadata, a as CachedAccount, b as WalletConnectWallet, c as KheopskitConfig, d as KheopskitStore, f as KheopskitStoreData, g as PolkadotAccountType, h as PlatformContext, i as BaseWalletAccount, j as isValidWalletId, k as WalletId, l as KheopskitPlatform, m as PlaceholderWallet, n as AppKitInstance, o as CachedWallet, p as Placeholder, r as BaseWallet, s as ConnectOptions, t as AccountOf, u as KheopskitState, v as WalletConnectPlugin, w as WalletType, x as WalletOf, y as WalletConnectProvider } from "./types-lkMXVJNG.mjs";
2
+ import { A as getWalletId, C as WalletStatus, D as KheopskitError, E as isWalletConnectWallet, F as parseWalletAccountId, M as parseWalletId, N as WalletAccountId, O as KheopskitErrorCode, P as getWalletAccountId, S as WalletPlatform, T as isInjectedWallet, _ as WalletConnectMetadata, a as CachedAccount, b as WalletConnectWallet, c as KheopskitConfig, d as KheopskitStore, f as KheopskitStoreData, g as PolkadotAccountType, h as PlatformContext, i as BaseWalletAccount, j as isValidWalletId, k as WalletId, l as KheopskitPlatform, m as PlaceholderWallet, n as AppKitInstance, o as CachedWallet, p as Placeholder, r as BaseWallet, s as ConnectOptions, t as AccountOf, u as KheopskitState, v as WalletConnectPlugin, w as WalletType, x as WalletOf, y as WalletConnectProvider } from "./types-C3I3w0jb.mjs";
3
3
  import { t as clearAllCachedObservables } from "./getCachedObservable-LUq9IfEx.mjs";
4
4
  import { Observable } from "rxjs";
5
5
  //#region src/utils/isValidAddress.d.ts
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { t as SyncableStorage } from "./storage-D3AwNgqc.js";
2
- import { A as getWalletId, C as WalletStatus, D as KheopskitError, E as isWalletConnectWallet, F as parseWalletAccountId, M as parseWalletId, N as WalletAccountId, O as KheopskitErrorCode, P as getWalletAccountId, S as WalletPlatform, T as isInjectedWallet, _ as WalletConnectMetadata, a as CachedAccount, b as WalletConnectWallet, c as KheopskitConfig, d as KheopskitStore, f as KheopskitStoreData, g as PolkadotAccountType, h as PlatformContext, i as BaseWalletAccount, j as isValidWalletId, k as WalletId, l as KheopskitPlatform, m as PlaceholderWallet, n as AppKitInstance, o as CachedWallet, p as Placeholder, r as BaseWallet, s as ConnectOptions, t as AccountOf, u as KheopskitState, v as WalletConnectPlugin, w as WalletType, x as WalletOf, y as WalletConnectProvider } from "./types-lkMXVJNG.js";
2
+ import { A as getWalletId, C as WalletStatus, D as KheopskitError, E as isWalletConnectWallet, F as parseWalletAccountId, M as parseWalletId, N as WalletAccountId, O as KheopskitErrorCode, P as getWalletAccountId, S as WalletPlatform, T as isInjectedWallet, _ as WalletConnectMetadata, a as CachedAccount, b as WalletConnectWallet, c as KheopskitConfig, d as KheopskitStore, f as KheopskitStoreData, g as PolkadotAccountType, h as PlatformContext, i as BaseWalletAccount, j as isValidWalletId, k as WalletId, l as KheopskitPlatform, m as PlaceholderWallet, n as AppKitInstance, o as CachedWallet, p as Placeholder, r as BaseWallet, s as ConnectOptions, t as AccountOf, u as KheopskitState, v as WalletConnectPlugin, w as WalletType, x as WalletOf, y as WalletConnectProvider } from "./types-C3I3w0jb.js";
3
3
  import { t as clearAllCachedObservables } from "./getCachedObservable-LUq9IfEx.js";
4
4
  import { Observable } from "rxjs";
5
5
  //#region src/utils/isValidAddress.d.ts
package/dist/index.js CHANGED
@@ -2,9 +2,9 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_types = require("./types-BWHvNFlN.js");
3
3
  const require_platformAccounts = require("./platformAccounts-BjIeRcFN.js");
4
4
  const require_iconCache = require("./iconCache-FR4iy6go.js");
5
- const require_pageLoad = require("./pageLoad-D1wLeuFG.js");
6
- let rxjs = require("rxjs");
5
+ const require_registryReads = require("./registryReads-BpHsWLd7.js");
7
6
  let lodash_es = require("lodash-es");
7
+ let rxjs = require("rxjs");
8
8
  //#region src/api/config.ts
9
9
  /**
10
10
  * Default storage key for persisting wallet connection state.
@@ -107,6 +107,10 @@ const createReconnectPolicy = ({ maxAttempts = 3 } = {}) => {
107
107
  };
108
108
  };
109
109
  const reconnectAccepts = (autoReconnect, wallet) => typeof autoReconnect === "function" ? autoReconnect(wallet) : autoReconnect;
110
+ const reconnectCandidates$ = (store) => {
111
+ const remembered$ = store.observable.pipe((0, rxjs.map)((s) => s.remembered ?? []));
112
+ return remembered$.pipe((0, rxjs.take)(1), (0, rxjs.switchMap)((initial) => remembered$.pipe((0, rxjs.scan)((remaining, current) => remaining.filter((id) => current.includes(id)), initial))), (0, rxjs.distinctUntilChanged)(lodash_es.isEqual));
113
+ };
110
114
  //#endregion
111
115
  //#region src/api/hydration.ts
112
116
  /**
@@ -557,15 +561,12 @@ const getDefaultStore = () => {
557
561
  };
558
562
  //#endregion
559
563
  //#region src/api/wallets.ts
560
- const getWallets$ = (config, store) => {
561
- const rememberedWalletIds$ = store.observable.pipe((0, rxjs.map)((s) => s.remembered ?? []), (0, rxjs.take)(1), (0, rxjs.shareReplay)({
562
- bufferSize: 1,
563
- refCount: true
564
- }));
564
+ const getWallets$ = (config, store, registryReads$) => {
565
565
  return new rxjs.Observable((subscriber) => {
566
566
  const ctx = {
567
567
  config,
568
- store
568
+ store,
569
+ registryReads$
569
570
  };
570
571
  const observables = config.platforms.map((plugin) => plugin.getWallets$(ctx));
571
572
  const reconnectPolicy = createReconnectPolicy();
@@ -598,7 +599,7 @@ const getWallets$ = (config, store) => {
598
599
  };
599
600
  const subWallets = wallets$.subscribe(subscriber);
600
601
  if (subscriber.closed) return subWallets;
601
- const subAutoReconnect = (0, rxjs.combineLatest)([rawWallets$, rememberedWalletIds$]).pipe((0, rxjs.filter)(([, walletIds]) => config.autoReconnect !== false && walletIds.length > 0), (0, rxjs.mergeMap)(([wallets, walletIds]) => wallets.filter(require_types.isInjectedWallet).filter((wallet) => walletIds.includes(wallet.id))), (0, rxjs.tap)((wallet) => {
602
+ const subAutoReconnect = rawWallets$.pipe((0, rxjs.withLatestFrom)(reconnectCandidates$(store)), (0, rxjs.filter)(([, walletIds]) => config.autoReconnect !== false && walletIds.length > 0), (0, rxjs.mergeMap)(([wallets, walletIds]) => wallets.filter(require_types.isInjectedWallet).filter((wallet) => walletIds.includes(wallet.id))), (0, rxjs.tap)((wallet) => {
602
603
  if (wallet.status === "connected") reconnectPolicy.settle(wallet.id);
603
604
  }), (0, rxjs.filter)((wallet) => wallet.status === "disconnected" && reconnectAccepts(config.autoReconnect, wallet))).subscribe(reconnect);
604
605
  return () => {
@@ -682,6 +683,12 @@ const getKheopskit$ = (config, options = {}) => {
682
683
  const live = await whenLive(placeholder.id, options?.signal);
683
684
  if (closed) throw require_types.abortedError(placeholder.id, "connect");
684
685
  await live.connect(options);
686
+ },
687
+ disconnect: async () => {
688
+ if (closed) return;
689
+ const live = latest?.wallets.find((w) => w.id === placeholder.id && !w.isPlaceholder);
690
+ if (live) return live.disconnect();
691
+ store.forgetWallet(placeholder.id);
685
692
  }
686
693
  });
687
694
  const settleWithState = (wallet) => {
@@ -711,26 +718,32 @@ const getKheopskit$ = (config, options = {}) => {
711
718
  wrapped.set(wallet, settled);
712
719
  return settled;
713
720
  };
714
- const liveWallets$ = getWallets$(kc, store);
721
+ const registryReads$ = require_registryReads.createRegistryReads$().pipe((0, rxjs.shareReplay)({
722
+ bufferSize: 1,
723
+ refCount: true
724
+ }));
725
+ const liveWallets$ = getWallets$(kc, store, registryReads$);
715
726
  const accountFrames$ = getAccounts$(kc, liveWallets$);
716
727
  const liveAccounts$ = accountFrames$.pipe((0, rxjs.map)(({ accounts }) => accounts.map(asLive)));
717
- const isDiscovering$ = require_pageLoad.pageLoaded$.pipe((0, rxjs.switchMap)((loaded) => loaded ? liveWallets$.pipe((0, rxjs.take)(1), (0, rxjs.map)(() => false)) : (0, rxjs.of)(true)), (0, rxjs.shareReplay)({
728
+ const isDiscovering$ = (0, rxjs.concat)((0, rxjs.of)(true), registryReads$.pipe((0, rxjs.ignoreElements)()), liveWallets$.pipe((0, rxjs.take)(1), (0, rxjs.map)(() => false))).pipe((0, rxjs.shareReplay)({
718
729
  bufferSize: 1,
719
730
  refCount: true
720
731
  }));
721
- const reconnecting = cachedWallets.filter((w) => w.status === "connecting");
722
732
  const hydrationSettled$ = (0, rxjs.combineLatest)([
723
733
  liveWallets$,
724
734
  accountFrames$.pipe((0, rxjs.startWith)(noAnswerYet)),
725
735
  isDiscovering$,
726
- store.observable.pipe((0, rxjs.map)((s) => s.remembered ?? []), (0, rxjs.take)(1))
727
- ]).pipe((0, rxjs.map)(([wallets, { accounts, answered }, isDiscovering, remembered]) => !isDiscovering && reconnecting.every((cached) => reconnectSettled(cached, {
728
- wallets,
729
- accounts,
730
- answered,
731
- cachedAccounts,
732
- remembered
733
- }))), (0, rxjs.debounceTime)(0, rxjs.asapScheduler), (0, rxjs.distinctUntilChanged)());
736
+ reconnectCandidates$(store)
737
+ ]).pipe((0, rxjs.map)(([wallets, { accounts, answered }, isDiscovering, remembered]) => cachedWallets.every((cached) => {
738
+ const wallet = wallets.find((w) => w.id === cached.id);
739
+ if (!wallet) return !isDiscovering;
740
+ return cached.status !== "connecting" || reconnectSettled(cached, wallet, {
741
+ accounts,
742
+ answered,
743
+ cachedAccounts,
744
+ remembered
745
+ });
746
+ })), (0, rxjs.debounceTime)(0, rxjs.asapScheduler), (0, rxjs.distinctUntilChanged)());
734
747
  const bufferedWallets$ = createHydrationBuffer(cachedWallets, liveWallets$.pipe((0, rxjs.map)((wallets) => wallets.map(asLive))), kc.hydrationGracePeriod, kc.hydrationTimeout, (w) => w.id, hydrationSettled$, (live, cached) => {
735
748
  const status = sessionRestoring(live, cached) ? "connecting" : live.status;
736
749
  return {
@@ -832,9 +845,7 @@ const noAnswerYet = {
832
845
  * waiting for its reconnect: getWallets$ emits the wallet list before its
833
846
  * connect() flips the status to connecting.
834
847
  */
835
- const reconnectSettled = (cached, live) => {
836
- const wallet = live.wallets.find((w) => w.id === cached.id);
837
- if (!wallet) return true;
848
+ const reconnectSettled = (cached, wallet, live) => {
838
849
  const status = sessionRestoring(wallet, cached) ? "connecting" : wallet.status;
839
850
  if (status === "connecting") return false;
840
851
  if (status === "disconnected") return wallet.error !== void 0 || !live.remembered.includes(cached.id);