@@ -5,7 +5,7 @@ import { type ExplicitSession, type ExplicitSessionConfig, type ImplicitSession,
55import { ChainSessionManager } from './ChainSessionManager.js'
66import { DappTransport } from './DappTransport.js'
77import { ConnectionError , InitializationError , SigningError , TransactionError } from './utils/errors.js'
8- import { SequenceStorage , WebStorage } from './utils/storage.js'
8+ import { SequenceStorage , WebStorage , type SessionlessConnectionData } from './utils/storage.js'
99import {
1010 CreateNewSessionResponse ,
1111 DappClientExplicitSessionEventListener ,
@@ -85,6 +85,7 @@ export class DappClient {
8585
8686 private walletAddress : Address . Address | null = null
8787 private hasSessionlessConnection = false
88+ private cachedSessionlessConnection : SessionlessConnectionData | null = null
8889 private eventListeners : {
8990 [ K in keyof DappClientEventMap ] ?: Set < DappClientEventMap [ K ] >
9091 } = { }
@@ -273,10 +274,14 @@ export class DappClient {
273274 private async _loadStateFromStorage ( ) : Promise < void > {
274275 const implicitSession = await this . sequenceStorage . getImplicitSession ( )
275276
276- const [ explicitSessions , sessionlessConnection ] = await Promise . all ( [
277+ const [ explicitSessions , sessionlessConnection , sessionlessSnapshot ] = await Promise . all ( [
277278 this . sequenceStorage . getExplicitSessions ( ) ,
278279 this . sequenceStorage . getSessionlessConnection ( ) ,
280+ this . sequenceStorage . getSessionlessConnectionSnapshot
281+ ? this . sequenceStorage . getSessionlessConnectionSnapshot ( )
282+ : Promise . resolve ( null ) ,
279283 ] )
284+ this . cachedSessionlessConnection = sessionlessSnapshot ?? null
280285 const chainIdsToInitialize = new Set ( [
281286 ...( implicitSession ?. chainId !== undefined ? [ implicitSession . chainId ] : [ ] ) ,
282287 ...explicitSessions . map ( ( s ) => s . chainId ) ,
@@ -316,6 +321,10 @@ export class DappClient {
316321 this . userEmail = result [ 0 ] ?. userEmail || null
317322 this . guard = implicitSession ?. guard || explicitSessions . find ( ( s ) => ! ! s . guard ) ?. guard
318323 await this . sequenceStorage . clearSessionlessConnection ( )
324+ if ( this . sequenceStorage . clearSessionlessConnectionSnapshot ) {
325+ await this . sequenceStorage . clearSessionlessConnectionSnapshot ( )
326+ }
327+ this . cachedSessionlessConnection = null
319328
320329 this . isInitialized = true
321330 this . emit ( 'sessionsUpdated' )
@@ -369,6 +378,63 @@ export class DappClient {
369378 }
370379 }
371380
381+ /**
382+ * Indicates if there is cached sessionless connection data that can be restored.
383+ */
384+ public async hasRestorableSessionlessConnection ( ) : Promise < boolean > {
385+ if ( this . cachedSessionlessConnection ) return true
386+ this . cachedSessionlessConnection = this . sequenceStorage . getSessionlessConnectionSnapshot
387+ ? await this . sequenceStorage . getSessionlessConnectionSnapshot ( )
388+ : null
389+ return this . cachedSessionlessConnection !== null
390+ }
391+
392+ /**
393+ * Returns the cached sessionless connection metadata without altering client state.
394+ * @returns The cached sessionless connection or null if none is available.
395+ */
396+ public async getSessionlessConnectionInfo ( ) : Promise < SessionlessConnectionData | null > {
397+ if ( ! this . cachedSessionlessConnection ) {
398+ this . cachedSessionlessConnection = this . sequenceStorage . getSessionlessConnectionSnapshot
399+ ? await this . sequenceStorage . getSessionlessConnectionSnapshot ( )
400+ : null
401+ }
402+ if ( ! this . cachedSessionlessConnection ) return null
403+ return {
404+ walletAddress : this . cachedSessionlessConnection . walletAddress ,
405+ loginMethod : this . cachedSessionlessConnection . loginMethod ,
406+ userEmail : this . cachedSessionlessConnection . userEmail ,
407+ guard : this . cachedSessionlessConnection . guard ,
408+ }
409+ }
410+
411+ /**
412+ * Restores a sessionless connection that was previously persisted via {@link disconnect} or a connect flow.
413+ * @returns A promise that resolves to true if a sessionless connection was applied.
414+ */
415+ public async restoreSessionlessConnection ( ) : Promise < boolean > {
416+ const sessionlessConnection =
417+ this . cachedSessionlessConnection ??
418+ ( this . sequenceStorage . getSessionlessConnectionSnapshot
419+ ? await this . sequenceStorage . getSessionlessConnectionSnapshot ( )
420+ : null )
421+ if ( ! sessionlessConnection ) {
422+ return false
423+ }
424+
425+ await this . applySessionlessConnectionState (
426+ sessionlessConnection . walletAddress ,
427+ sessionlessConnection . loginMethod ,
428+ sessionlessConnection . userEmail ,
429+ sessionlessConnection . guard ,
430+ )
431+ if ( this . sequenceStorage . clearSessionlessConnectionSnapshot ) {
432+ await this . sequenceStorage . clearSessionlessConnectionSnapshot ( )
433+ }
434+ this . cachedSessionlessConnection = null
435+ return true
436+ }
437+
372438 /**
373439 * Handles the redirect response from the Wallet.
374440 * This is called automatically on `initialize()` for web environments but can be called manually
@@ -881,17 +947,21 @@ export class DappClient {
881947 /**
882948 * Disconnects the client, clearing all session data from browser storage.
883949 * @remarks This action does not revoke the sessions on-chain. Sessions remain active until they expire or are manually revoked by the user in their wallet.
950+ * @param options Options to control the disconnection behavior.
951+ * @param options.keepSessionlessConnection When true, retains the latest wallet metadata so it can be restored later as a sessionless connection. Defaults to true.
884952 * @returns A promise that resolves when disconnection is complete.
885953 *
886954 * @example
887955 * const dappClient = new DappClient('http://localhost:5173');
888956 * await dappClient.initialize();
889957 *
890958 * if (dappClient.isInitialized) {
891- * await dappClient.disconnect();
959+ * await dappClient.disconnect({ keepSessionlessConnection: true } );
892960 * }
893961 */
894- async disconnect ( ) : Promise < void > {
962+ async disconnect ( options ?: { keepSessionlessConnection ?: boolean } ) : Promise < void > {
963+ const keepSessionlessConnection = options ?. keepSessionlessConnection ?? true
964+
895965 const transportMode = this . transport . mode
896966
897967 this . transport . destroy ( )
@@ -904,7 +974,30 @@ export class DappClient {
904974 )
905975
906976 this . chainSessionManagers . clear ( )
977+ const sessionlessSnapshot =
978+ keepSessionlessConnection && this . walletAddress
979+ ? {
980+ walletAddress : this . walletAddress ,
981+ loginMethod : this . loginMethod ?? undefined ,
982+ userEmail : this . userEmail ?? undefined ,
983+ guard : this . guard ,
984+ }
985+ : undefined
986+
907987 await this . sequenceStorage . clearAllData ( )
988+
989+ if ( sessionlessSnapshot ) {
990+ if ( this . sequenceStorage . saveSessionlessConnectionSnapshot ) {
991+ await this . sequenceStorage . saveSessionlessConnectionSnapshot ( sessionlessSnapshot )
992+ }
993+ this . cachedSessionlessConnection = sessionlessSnapshot
994+ } else {
995+ if ( this . sequenceStorage . clearSessionlessConnectionSnapshot ) {
996+ await this . sequenceStorage . clearSessionlessConnectionSnapshot ( )
997+ }
998+ this . cachedSessionlessConnection = null
999+ }
1000+
9081001 this . isInitialized = false
9091002 this . walletAddress = null
9101003 this . loginMethod = null
@@ -939,6 +1032,7 @@ export class DappClient {
9391032 this . guard = guard
9401033 this . hasSessionlessConnection = true
9411034 this . isInitialized = true
1035+ this . cachedSessionlessConnection = null
9421036 this . emit ( 'sessionsUpdated' )
9431037 if ( persist ) {
9441038 await this . sequenceStorage . saveSessionlessConnection ( {
0 commit comments