@tezos-x/octez.connect-dapp 4.8.4 → 4.8.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -21,3 +21,44 @@ This package is published from the Trilitech-maintained octez.connect repository
21
21
 
22
22
  - Trilitech publishes these packages under the `@tezos-x/octez.connect-*` scope
23
23
  - Release notes, package policy, and the current package list live in the repository README
24
+
25
+ ## Restoring persisted dApp sessions
26
+
27
+ Applications should restore Beacon connection state from the SDK, for example by
28
+ calling `client.getActiveAccount()`, instead of relying only on app-local markers
29
+ such as `wallet-provider=beacon`.
30
+
31
+ Treat a missing `beacon:active-account` storage entry as disconnected. Do not use
32
+ checks like `localStorage.getItem('beacon:active-account') !== 'undefined'` as a
33
+ valid restore guard, because `null` and corrupt stored state are not valid active
34
+ accounts.
35
+
36
+ When Beacon deactivates invalid stored active-account state, it emits
37
+ `BeaconEvent.INVALID_ACCOUNT_DEACTIVATED` with this payload:
38
+
39
+ ```ts
40
+ {
41
+ reason:
42
+ | 'missing_active_account'
43
+ | 'invalid_active_account_storage'
44
+ | 'storage_validation_failed'
45
+ }
46
+ ```
47
+
48
+ For backward compatibility, event handler types still allow this event data to
49
+ be `undefined`. Beacon's invalid active-account deactivation path emits the
50
+ object above; consumers that inspect the reason should handle an absent payload
51
+ defensively if they also support direct event emission or older Beacon versions.
52
+
53
+ Reason meanings:
54
+
55
+ - `missing_active_account`: `beacon:active-account` pointed at an account
56
+ identifier that was not present in Beacon's persisted account list.
57
+ - `invalid_active_account_storage`: Beacon could not read or parse the persisted
58
+ active-account/account storage needed to restore the account.
59
+ - `storage_validation_failed`: Beacon restored an active account, but a later
60
+ storage validation pass still found invalid persisted session state.
61
+
62
+ The default UI intentionally keeps a generic "session expired" message for end
63
+ users, while the event payload and debug logs expose the specific restore failure
64
+ reason for applications and diagnostics.
@@ -6,6 +6,20 @@ import { DAppClientOptions } from './DAppClientOptions';
6
6
  import { DappPostMessageTransport } from '../transports/DappPostMessageTransport';
7
7
  import { DappP2PTransport } from '../transports/DappP2PTransport';
8
8
  import { DappWalletConnectTransport } from '../transports/DappWalletConnectTransport';
9
+ export interface DAppClientDisconnectOptions {
10
+ /**
11
+ * Send Beacon disconnect messages to stored wallet peers before tearing down the transport.
12
+ *
13
+ * Defaults to true because DAppClient.disconnect is the user-facing wallet logout path.
14
+ */
15
+ notifyPeers?: boolean;
16
+ }
17
+ export interface DAppClientRemoveAllPeersOptions {
18
+ /**
19
+ * Continue removing local peer/account state even if notifying one peer fails.
20
+ */
21
+ ignoreDisconnectErrors?: boolean;
22
+ }
9
23
  /**
10
24
  * @publicapi
11
25
  *
@@ -39,12 +53,20 @@ export declare class DAppClient extends Client {
39
53
  protected walletConnectTransport: DappWalletConnectTransport | undefined;
40
54
  protected wcProjectId?: string;
41
55
  protected wcRelayUrl?: string;
56
+ /**
57
+ * When true, the WalletConnect transport is not built or listened to. See
58
+ * DAppClientOptions.disableWalletConnect.
59
+ */
60
+ protected disableWalletConnect: boolean;
42
61
  private isGetActiveAccountHandled;
43
62
  private readonly openRequestsOtherTabs;
44
63
  /**
45
64
  * A map of requests that are currently "open", meaning we have sent them to a wallet and are still awaiting a response.
46
65
  */
47
66
  private readonly openRequests;
67
+ private readonly acknowledgedRequests;
68
+ private readonly openRequestTimeouts;
69
+ private readonly requestTimeoutMs;
48
70
  /**
49
71
  * The currently active account. For all requests that are associated to a specific request (operation request, signing request),
50
72
  * the active account is used to determine the network and destination wallet
@@ -55,8 +77,22 @@ export declare class DAppClient extends Client {
55
77
  */
56
78
  private _activePeer;
57
79
  private _initPromise;
58
- private isInitPending;
80
+ /**
81
+ * Rejector for the in-flight {@link _initPromise}. Populated while init is
82
+ * awaiting a peer pairing; cleared automatically when {@link _initPromise}
83
+ * settles. Abort paths (modal close, WC session-proposal rejection,
84
+ * transport-level connection failure) call this to unwedge any
85
+ * `await this.init()` caller, surfacing the error to handleRequestError
86
+ * instead of hanging.
87
+ */
88
+ private _initReject;
89
+ private _initSubstratePairing;
90
+ private _requestPermissionsPromise;
91
+ private _requestPermissionsKey;
92
+ private _sdkSecretSeedPromise;
93
+ private destroyed;
59
94
  private readonly activeAccountLoaded;
95
+ private readonly storageValidated;
60
96
  private readonly appMetadataManager;
61
97
  private readonly disclaimerText?;
62
98
  private readonly errorMessages;
@@ -64,15 +100,22 @@ export declare class DAppClient extends Client {
64
100
  private readonly storageValidator;
65
101
  private readonly beaconIDB;
66
102
  private debounceSetActiveAccount;
103
+ private hasEmittedInvalidAccountDeactivated;
104
+ private _disconnectPromise?;
105
+ private _disconnectNotifyPeers;
67
106
  private multiTabChannel;
68
107
  constructor(config: DAppClientOptions);
69
108
  private checkIfBCLeaderExists;
70
109
  private onElectedLeaderhandler;
71
110
  private onBCMessageHandler;
72
111
  private prepareRequest;
112
+ private buildDelegatedRequestError;
73
113
  private createStateSnapshot;
74
114
  private initUserID;
75
115
  initInternalTransports(): Promise<void>;
116
+ private getOrCreateSDKSecretSeed;
117
+ private loadOrCreateSDKSecretSeed;
118
+ private isValidSDKSecretSeed;
76
119
  private initEvents;
77
120
  private onRelayerError;
78
121
  private wcToastHandler;
@@ -85,11 +128,30 @@ export declare class DAppClient extends Client {
85
128
  * If you wish to disconnect your dApp, use `disconnect` instead.
86
129
  */
87
130
  destroy(): Promise<void>;
131
+ private destroyInternalTransports;
132
+ private abortPendingInit;
133
+ private createAbortedError;
134
+ /**
135
+ * @internal
136
+ */
137
+ isDestroyed(): boolean;
138
+ private assertNotDestroyed;
88
139
  init(transport?: Transport<any>, substratePairing?: boolean): Promise<TransportType>;
140
+ /**
141
+ * Attach a settle observer to {@link _initPromise} that drops
142
+ * {@link _initReject} once the promise settles (resolved or rejected).
143
+ * Errors on the observer chain are swallowed; only the original
144
+ * {@link _initPromise} surfaces them to its awaiters.
145
+ */
146
+ private clearInitRejectOnSettle;
89
147
  /**
90
148
  * Returns the active account
91
149
  */
92
150
  getActiveAccount(): Promise<AccountInfo | undefined>;
151
+ private retainStorageValidationPromise;
152
+ private deactivateInvalidAccountState;
153
+ private repairMissingActiveAccount;
154
+ private hasStoredActiveAccountPointer;
93
155
  private isInvalidState;
94
156
  private resetInvalidState;
95
157
  /**
@@ -98,6 +160,8 @@ export declare class DAppClient extends Client {
98
160
  * @param account The account that will be set as the active account
99
161
  */
100
162
  setActiveAccount(account?: AccountInfo): Promise<void>;
163
+ private isActiveAccountAlreadyCleared;
164
+ private recoverActiveAccountFromAccountsChange;
101
165
  /**
102
166
  * Clear the active account
103
167
  */
@@ -115,7 +179,6 @@ export declare class DAppClient extends Client {
115
179
  private tryToAppSwitch;
116
180
  private addQueryParam;
117
181
  private buildPayload;
118
- private updateMetricsStorage;
119
182
  private sendMetrics;
120
183
  private checkMakeRequest;
121
184
  /**
@@ -137,7 +200,7 @@ export declare class DAppClient extends Client {
137
200
  /**
138
201
  * Remove all peers and all accounts that have been connected through those peers
139
202
  */
140
- removeAllPeers(sendDisconnectToPeers?: boolean): Promise<void>;
203
+ removeAllPeers(sendDisconnectToPeers?: boolean, options?: DAppClientRemoveAllPeersOptions): Promise<void>;
141
204
  /**
142
205
  * Allows the user to subscribe to specific events that are fired in the SDK
143
206
  *
@@ -166,6 +229,9 @@ export declare class DAppClient extends Client {
166
229
  * @param input The message details we need to prepare the PermissionRequest message.
167
230
  */
168
231
  requestPermissions(input?: RequestPermissionInput): Promise<PermissionResponseOutput>;
232
+ private requestPermissionsInternal;
233
+ private getPermissionRequestScopes;
234
+ private getPermissionRequestKey;
169
235
  /**
170
236
  * Send a proof of event request to the wallet. The wallet will either accept or decline the challenge.
171
237
  * If it is accepted, the challenge will be stored, meaning that even if the user refresh the page, the DAppClient will keep checking if the challenge has been fulfilled.
@@ -230,6 +296,29 @@ export declare class DAppClient extends Client {
230
296
  */
231
297
  private removeAccountsForPeers;
232
298
  private removeAccountsForPeerIds;
299
+ /**
300
+ * Run the standard request-error side effects (abort/error metric,
301
+ * timer stop, {@link handleRequestError}) on a single awaited control
302
+ * flow so callers can re-throw the original {@link ErrorResponse} without
303
+ * leaking an UnhandledPromiseRejection.
304
+ *
305
+ * Why this exists: every request method historically used a fire-and-
306
+ * forget `res.catch(handler)` whose handler did `throw await
307
+ * this.handleRequestError(...)`. The catch returned a detached promise
308
+ * nobody awaited; `handleRequestError`'s thrown wrapped error became an
309
+ * unhandled rejection. The bug rarely surfaced before the dapp-init
310
+ * promise was made rejectable, because pre-pairing rejection paths used
311
+ * to hang instead of routing through these handlers. Once init started
312
+ * rejecting reliably, the unhandled rejection started firing on every
313
+ * WC session-proposal rejection. We swallow the wrapped throw here so
314
+ * callers can re-throw the original `ErrorResponse`, matching the
315
+ * post-pairing rejection contract at the openRequest path.
316
+ *
317
+ * @param request The request we sent
318
+ * @param requestError The error we received
319
+ * @param logId The {@link logger.time} label opened for this request
320
+ */
321
+ private runRequestErrorSideEffects;
233
322
  /**
234
323
  * This message handles errors that we receive from the wallet.
235
324
  *
@@ -268,7 +357,22 @@ export declare class DAppClient extends Client {
268
357
  */
269
358
  private makeRequestV3;
270
359
  private makeRequestBC;
271
- disconnect(): Promise<void>;
360
+ /**
361
+ * Disconnects all resolved transports (postMessage, P2P, WalletConnect), removes their peers,
362
+ * and clears active account state. After calling, the DAppClient remains usable for a new
363
+ * requestPermissions().
364
+ */
365
+ disconnect(options?: DAppClientDisconnectOptions): Promise<void>;
366
+ private disconnectInternal;
367
+ private getInitializedTransports;
368
+ private getResolvedTransports;
369
+ private clearInternalTransportReferences;
370
+ private removeAllPeersFromTransports;
371
+ private getUniquePeerNotifications;
372
+ private getPeerNotificationKey;
373
+ private disconnectResolvedTransports;
374
+ private hasClientCleanup;
375
+ private isTransportConnected;
272
376
  /**
273
377
  * Adds a requests to the "openRequests" set so we know what messages have already been answered/handled.
274
378
  *
@@ -276,6 +380,11 @@ export declare class DAppClient extends Client {
276
380
  * @param promise A promise that resolves once the response for that specific message is received
277
381
  */
278
382
  private addOpenRequest;
383
+ private addOpenRequestTimeout;
384
+ private clearOpenRequest;
385
+ private clearOpenRequestTimeout;
386
+ private clearAllOpenRequestTimeouts;
387
+ private abortOpenRequests;
279
388
  private sendNotificationWithAccessToken;
280
389
  private onNewAccount;
281
390
  }