@enbox/browser 0.3.49 → 0.3.51

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 (33) hide show
  1. package/dist/browser.mjs +39 -14
  2. package/dist/browser.mjs.map +3 -3
  3. package/dist/esm/dweb-connect-client.js +64 -9
  4. package/dist/esm/dweb-connect-client.js.map +1 -1
  5. package/dist/esm/dweb-connect-messages.js +7 -1
  6. package/dist/esm/dweb-connect-messages.js.map +1 -1
  7. package/dist/esm/dweb-connect-wallet.js +48 -2
  8. package/dist/esm/dweb-connect-wallet.js.map +1 -1
  9. package/dist/esm/index.js +1 -1
  10. package/dist/esm/index.js.map +1 -1
  11. package/dist/esm/relay-connect-runner.js +35 -2
  12. package/dist/esm/relay-connect-runner.js.map +1 -1
  13. package/dist/esm/ui/connect-modal.js +171 -51
  14. package/dist/esm/ui/connect-modal.js.map +1 -1
  15. package/dist/types/dweb-connect-client.d.ts +9 -0
  16. package/dist/types/dweb-connect-client.d.ts.map +1 -1
  17. package/dist/types/dweb-connect-messages.d.ts +14 -1
  18. package/dist/types/dweb-connect-messages.d.ts.map +1 -1
  19. package/dist/types/dweb-connect-wallet.d.ts +23 -1
  20. package/dist/types/dweb-connect-wallet.d.ts.map +1 -1
  21. package/dist/types/index.d.ts +2 -2
  22. package/dist/types/index.d.ts.map +1 -1
  23. package/dist/types/relay-connect-runner.d.ts +11 -1
  24. package/dist/types/relay-connect-runner.d.ts.map +1 -1
  25. package/dist/types/ui/connect-modal.d.ts +8 -7
  26. package/dist/types/ui/connect-modal.d.ts.map +1 -1
  27. package/package.json +6 -6
  28. package/src/dweb-connect-client.ts +61 -7
  29. package/src/dweb-connect-messages.ts +16 -1
  30. package/src/dweb-connect-wallet.ts +50 -1
  31. package/src/index.ts +2 -0
  32. package/src/relay-connect-runner.ts +42 -2
  33. package/src/ui/connect-modal.ts +196 -61
@@ -12,7 +12,9 @@
12
12
  * value-checks the `apv` origin binding against this wallet's origin.
13
13
  * 3. After the consent ceremony, the wallet posts the sealed response JWE via
14
14
  * `sendResponse()` — or the deny token via `deny()` — back to the opener,
15
- * again with a pinned `targetOrigin`.
15
+ * again with a pinned `targetOrigin`. `sendResponseAwaitingAck()` does the
16
+ * same but stays alive briefly for the dapp's completion ack, so the
17
+ * wallet can show a confirmed "connected" state before closing itself.
16
18
  *
17
19
  * The dapp origin is never assumed: it is taken from an explicit option or
18
20
  * derived from `document.referrer`, and `create()` fails when neither is
@@ -59,6 +61,7 @@ export declare class WalletPostMessageTransport {
59
61
  private _requestReceived;
60
62
  private _timeoutId?;
61
63
  private _messageListener?;
64
+ private _resolveAck?;
62
65
  private readonly _requestPromise;
63
66
  private _resolveRequest?;
64
67
  private _rejectRequest?;
@@ -90,6 +93,25 @@ export declare class WalletPostMessageTransport {
90
93
  * @param idToken - The sealed response JWE, or the literal deny token.
91
94
  */
92
95
  sendResponse(idToken: string): void;
96
+ /**
97
+ * Posts the sealed response JWE back to the dapp like
98
+ * {@link sendResponse}, then keeps the channel open briefly for the dapp's
99
+ * completion ack — the signal that it opened the response successfully —
100
+ * before closing the transport.
101
+ *
102
+ * Lets the wallet's connect page show a confirmed "connected" state (and
103
+ * close itself with confidence) instead of asking the user to dismiss the
104
+ * screen blind. Dapps predating the ack simply never send one, so the
105
+ * wallet should keep a manual dismiss affordance for the `false` case.
106
+ *
107
+ * @param idToken - The sealed response JWE.
108
+ * @param options - The awaiting options.
109
+ * @param options.timeoutMs - Ack budget in milliseconds; defaults to 5 s.
110
+ * @returns Whether the dapp acknowledged completion within the budget.
111
+ */
112
+ sendResponseAwaitingAck(idToken: string, options?: {
113
+ timeoutMs?: number;
114
+ }): Promise<boolean>;
93
115
  /** Posts the deny token back to the dapp — the user rejected the request. */
94
116
  deny(): void;
95
117
  /** Stops listening and releases the session's timers. Idempotent. */
@@ -1 +1 @@
1
- {"version":3,"file":"dweb-connect-wallet.d.ts","sourceRoot":"","sources":["../../src/dweb-connect-wallet.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAgBrD,iEAAiE;AACjE,MAAM,MAAM,iCAAiC,GAAG;IAC9C;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,qBAAa,0BAA0B;IACrC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAM;IAE3C,OAAO,CAAC,gBAAgB,CAAS;IACjC,OAAO,CAAC,UAAU,CAAC,CAAgC;IACnD,OAAO,CAAC,gBAAgB,CAAC,CAAgC;IAEzD,OAAO,CAAC,QAAQ,CAAC,eAAe,CAA0B;IAC1D,OAAO,CAAC,eAAe,CAAC,CAAoC;IAC5D,OAAO,CAAC,cAAc,CAAC,CAAyB;IAEhD,OAAO;IAsBP;;;;;;;OAOG;WACiB,MAAM,CAAC,OAAO,GAAE,iCAAsC,GAAG,OAAO,CAAC,0BAA0B,CAAC;IAgChH,4EAA4E;IAC5E,IAAW,UAAU,IAAI,MAAM,CAE9B;IAED;;;;;;OAMG;IACU,YAAY,IAAI,OAAO,CAAC,cAAc,CAAC;IAIpD;;;;;;OAMG;IACI,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAQ1C,6EAA6E;IACtE,IAAI,IAAI,IAAI;IAInB,qEAAqE;IAC9D,KAAK,IAAI,IAAI;IAQpB,OAAO,CAAC,SAAS;YAeH,WAAW;IAsBzB,OAAO,CAAC,aAAa;CAStB"}
1
+ {"version":3,"file":"dweb-connect-wallet.d.ts","sourceRoot":"","sources":["../../src/dweb-connect-wallet.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAoBrD,iEAAiE;AACjE,MAAM,MAAM,iCAAiC,GAAG;IAC9C;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,qBAAa,0BAA0B;IACrC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAM;IAE3C,OAAO,CAAC,gBAAgB,CAAS;IACjC,OAAO,CAAC,UAAU,CAAC,CAAgC;IACnD,OAAO,CAAC,gBAAgB,CAAC,CAAgC;IACzD,OAAO,CAAC,WAAW,CAAC,CAA2B;IAE/C,OAAO,CAAC,QAAQ,CAAC,eAAe,CAA0B;IAC1D,OAAO,CAAC,eAAe,CAAC,CAAoC;IAC5D,OAAO,CAAC,cAAc,CAAC,CAAyB;IAEhD,OAAO;IAsBP;;;;;;;OAOG;WACiB,MAAM,CAAC,OAAO,GAAE,iCAAsC,GAAG,OAAO,CAAC,0BAA0B,CAAC;IAgChH,4EAA4E;IAC5E,IAAW,UAAU,IAAI,MAAM,CAE9B;IAED;;;;;;OAMG;IACU,YAAY,IAAI,OAAO,CAAC,cAAc,CAAC;IAIpD;;;;;;OAMG;IACI,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAQ1C;;;;;;;;;;;;;;;OAeG;IACU,uBAAuB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IAqB7G,6EAA6E;IACtE,IAAI,IAAI,IAAI;IAInB,qEAAqE;IAC9D,KAAK,IAAI,IAAI;IAQpB,OAAO,CAAC,SAAS;YAoBH,WAAW;IAsBzB,OAAO,CAAC,aAAa;CAStB"}
@@ -36,8 +36,8 @@ export { connectViaPopup, PopupClientTransport, PopupWindowClosedError } from '.
36
36
  export type { PopupClientTransportOptions, PopupConnectOptions } from './dweb-connect-client.js';
37
37
  export { WalletPostMessageTransport } from './dweb-connect-wallet.js';
38
38
  export type { WalletPostMessageTransportOptions } from './dweb-connect-wallet.js';
39
- export { DWEB_CONNECT_LOADED_MESSAGE_TYPE, DWEB_CONNECT_PATH, DWEB_CONNECT_REQUEST_MESSAGE_TYPE, DWEB_CONNECT_RESPONSE_MESSAGE_TYPE, } from './dweb-connect-messages.js';
40
- export type { DWebConnectLoadedMessage, DWebConnectRequestMessage, DWebConnectResponseMessage, } from './dweb-connect-messages.js';
39
+ export { DWEB_CONNECT_ACK_MESSAGE_TYPE, DWEB_CONNECT_LOADED_MESSAGE_TYPE, DWEB_CONNECT_PATH, DWEB_CONNECT_REQUEST_MESSAGE_TYPE, DWEB_CONNECT_RESPONSE_MESSAGE_TYPE, } from './dweb-connect-messages.js';
40
+ export type { DWebConnectAckMessage, DWebConnectLoadedMessage, DWebConnectRequestMessage, DWebConnectResponseMessage, } from './dweb-connect-messages.js';
41
41
  export { fetchWalletWellKnown, probeWalletWellKnown, WALLET_WELL_KNOWN_PATH } from './ui/wallet-well-known.js';
42
42
  export type { WalletWellKnownDocument } from './ui/wallet-well-known.js';
43
43
  export { discoverWalletConnectServerUrl, runConnectModal } from './ui/connect-modal.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAMH,OAAO,EACL,KAAK,EACL,cAAc,EACd,UAAU,GACX,MAAM,YAAY,CAAC;AAEpB,YAAY,EACV,qBAAqB,EACrB,mBAAmB,EACnB,kBAAkB,EAClB,WAAW,EACX,kBAAkB,GACnB,MAAM,YAAY,CAAC;AAMpB,OAAO,EACL,WAAW,EACX,WAAW,EACX,yBAAyB,EACzB,aAAa,GACd,MAAM,qBAAqB,CAAC;AAE7B,YAAY,EACV,SAAS,EACT,gBAAgB,EAChB,kBAAkB,EAClB,SAAS,EACT,cAAc,EACd,cAAc,EACd,aAAa,EACb,iBAAiB,EACjB,qBAAqB,EACrB,yBAAyB,EACzB,mBAAmB,EACnB,UAAU,EACV,gBAAgB,EAChB,eAAe,EACf,mBAAmB,EACnB,cAAc,EACd,UAAU,EACV,oBAAoB,GACrB,MAAM,qBAAqB,CAAC;AAO7B,OAAO,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACvE,YAAY,EAAE,eAAe,EAAE,wBAAwB,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAMhG,cAAc,mBAAmB,CAAC;AAClC,OAAO,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AACtF,YAAY,EAAE,4BAA4B,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAC/F,OAAO,EAAE,eAAe,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AACzG,YAAY,EAAE,2BAA2B,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AACjG,OAAO,EAAE,0BAA0B,EAAE,MAAM,0BAA0B,CAAC;AACtE,YAAY,EAAE,iCAAiC,EAAE,MAAM,0BAA0B,CAAC;AAClF,OAAO,EACL,gCAAgC,EAChC,iBAAiB,EACjB,iCAAiC,EACjC,kCAAkC,GACnC,MAAM,4BAA4B,CAAC;AACpC,YAAY,EACV,wBAAwB,EACxB,yBAAyB,EACzB,0BAA0B,GAC3B,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AAC/G,YAAY,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AACzE,OAAO,EAAE,8BAA8B,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxF,YAAY,EAAE,aAAa,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC1I,OAAO,EAAE,gBAAgB,EAAE,0BAA0B,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAC1G,YAAY,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AACrE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAC/C,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAWvD,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAIzC,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,YAAY,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAGlF,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAI5C,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAC9E,YAAY,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAMH,OAAO,EACL,KAAK,EACL,cAAc,EACd,UAAU,GACX,MAAM,YAAY,CAAC;AAEpB,YAAY,EACV,qBAAqB,EACrB,mBAAmB,EACnB,kBAAkB,EAClB,WAAW,EACX,kBAAkB,GACnB,MAAM,YAAY,CAAC;AAMpB,OAAO,EACL,WAAW,EACX,WAAW,EACX,yBAAyB,EACzB,aAAa,GACd,MAAM,qBAAqB,CAAC;AAE7B,YAAY,EACV,SAAS,EACT,gBAAgB,EAChB,kBAAkB,EAClB,SAAS,EACT,cAAc,EACd,cAAc,EACd,aAAa,EACb,iBAAiB,EACjB,qBAAqB,EACrB,yBAAyB,EACzB,mBAAmB,EACnB,UAAU,EACV,gBAAgB,EAChB,eAAe,EACf,mBAAmB,EACnB,cAAc,EACd,UAAU,EACV,oBAAoB,GACrB,MAAM,qBAAqB,CAAC;AAO7B,OAAO,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACvE,YAAY,EAAE,eAAe,EAAE,wBAAwB,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAMhG,cAAc,mBAAmB,CAAC;AAClC,OAAO,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AACtF,YAAY,EAAE,4BAA4B,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAC/F,OAAO,EAAE,eAAe,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AACzG,YAAY,EAAE,2BAA2B,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AACjG,OAAO,EAAE,0BAA0B,EAAE,MAAM,0BAA0B,CAAC;AACtE,YAAY,EAAE,iCAAiC,EAAE,MAAM,0BAA0B,CAAC;AAClF,OAAO,EACL,6BAA6B,EAC7B,gCAAgC,EAChC,iBAAiB,EACjB,iCAAiC,EACjC,kCAAkC,GACnC,MAAM,4BAA4B,CAAC;AACpC,YAAY,EACV,qBAAqB,EACrB,wBAAwB,EACxB,yBAAyB,EACzB,0BAA0B,GAC3B,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AAC/G,YAAY,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AACzE,OAAO,EAAE,8BAA8B,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxF,YAAY,EAAE,aAAa,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC1I,OAAO,EAAE,gBAAgB,EAAE,0BAA0B,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAC1G,YAAY,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AACrE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAC/C,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAWvD,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAIzC,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,YAAY,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAGlF,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAI5C,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAC9E,YAAY,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC"}
@@ -72,8 +72,18 @@ export interface RelayConnectOptions {
72
72
  timeoutMs?: number;
73
73
  pollIntervalMs?: number;
74
74
  onClaimed?: () => void;
75
- }) => Pick<RelayClientTransport, 'requestProfile' | 'deliverRequest' | 'awaitResponse' | 'requiresPin'>;
75
+ }) => Pick<RelayClientTransport, 'requestProfile' | 'deliverRequest' | 'awaitResponse' | 'requiresPin'> & Partial<Pick<RelayClientTransport, 'confirmComplete'>>;
76
76
  }
77
+ /**
78
+ * Sleep used between relay polls that ends early when the page returns to
79
+ * the foreground. Browsers throttle — and mobile browsers freeze — timers in
80
+ * background tabs, so a plain interval sleep can leave the user staring at a
81
+ * stale stage after approving in the wallet tab; cutting the sleep short on
82
+ * `visibilitychange` makes the next poll (and with it the pairing-code
83
+ * prompt) land the moment they switch back. Falls back to a plain sleep
84
+ * outside DOM environments.
85
+ */
86
+ export declare function visibilityAwareSleep(ms: number): Promise<void>;
77
87
  /**
78
88
  * Runs one relay-mediated connect handshake end-to-end and returns the
79
89
  * delegated credentials, or `undefined` when the user denied in the wallet.
@@ -1 +1 @@
1
- {"version":3,"file":"relay-connect-runner.d.ts","sourceRoot":"","sources":["../../src/relay-connect-runner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAGH,OAAO,KAAK,EACV,qBAAqB,EACrB,wBAAwB,EAExB,aAAa,EACb,gBAAgB,EACjB,MAAM,gBAAgB,CAAC;AAIxB,OAAO,EAIL,oBAAoB,EAErB,MAAM,gBAAgB,CAAC;AAExB,mEAAmE;AACnE,eAAO,MAAM,gBAAgB,IAAI,CAAC;AAElC,uEAAuE;AACvE,qBAAa,0BAA2B,SAAQ,KAAK;;CAKpD;AAED,2CAA2C;AAC3C,MAAM,WAAW,mBAAmB;IAClC,wDAAwD;IACxD,gBAAgB,EAAE,MAAM,CAAC;IAEzB,6EAA6E;IAC7E,SAAS,EAAE,MAAM,CAAC;IAElB,yEAAyE;IACzE,OAAO,EAAE,MAAM,CAAC;IAEhB,qEAAqE;IACrE,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,uEAAuE;IACvE,cAAc,CAAC,EAAE,qBAAqB,CAAC;IAEvC,2DAA2D;IAC3D,kBAAkB,EAAE,wBAAwB,EAAE,CAAC;IAE/C,kEAAkE;IAClE,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,mDAAmD;IACnD,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB;;;OAGG;IACH,gBAAgB,EAAE,CAAC,OAAO,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAEtD;;;;OAIG;IACH,UAAU,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,KAAK,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAExE;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC,KAAK,CAAC,GAAG,SAAS,CAAC;IAEvC;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IAEvB,0CAA0C;IAC1C,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE;QAC1B,gBAAgB,EAAE,MAAM,CAAC;QACzB,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;KACxB,KAAK,IAAI,CAAC,oBAAoB,EAAE,gBAAgB,GAAG,gBAAgB,GAAG,eAAe,GAAG,aAAa,CAAC,CAAC;CACzG;AAED;;;GAGG;AACH,wBAAsB,eAAe,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,aAAa,GAAG,SAAS,CAAC,CA4FtG"}
1
+ {"version":3,"file":"relay-connect-runner.d.ts","sourceRoot":"","sources":["../../src/relay-connect-runner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAGH,OAAO,KAAK,EACV,qBAAqB,EACrB,wBAAwB,EAExB,aAAa,EACb,gBAAgB,EACjB,MAAM,gBAAgB,CAAC;AAIxB,OAAO,EAIL,oBAAoB,EAErB,MAAM,gBAAgB,CAAC;AAExB,mEAAmE;AACnE,eAAO,MAAM,gBAAgB,IAAI,CAAC;AAElC,uEAAuE;AACvE,qBAAa,0BAA2B,SAAQ,KAAK;;CAKpD;AAED,2CAA2C;AAC3C,MAAM,WAAW,mBAAmB;IAClC,wDAAwD;IACxD,gBAAgB,EAAE,MAAM,CAAC;IAEzB,6EAA6E;IAC7E,SAAS,EAAE,MAAM,CAAC;IAElB,yEAAyE;IACzE,OAAO,EAAE,MAAM,CAAC;IAEhB,qEAAqE;IACrE,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,uEAAuE;IACvE,cAAc,CAAC,EAAE,qBAAqB,CAAC;IAEvC,2DAA2D;IAC3D,kBAAkB,EAAE,wBAAwB,EAAE,CAAC;IAE/C,kEAAkE;IAClE,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,mDAAmD;IACnD,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB;;;OAGG;IACH,gBAAgB,EAAE,CAAC,OAAO,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAEtD;;;;OAIG;IACH,UAAU,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,KAAK,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAExE;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC,KAAK,CAAC,GAAG,SAAS,CAAC;IAEvC;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IAEvB,0CAA0C;IAC1C,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE;QAC1B,gBAAgB,EAAE,MAAM,CAAC;QACzB,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;KACxB,KAAK,IAAI,CAAC,oBAAoB,EAAE,gBAAgB,GAAG,gBAAgB,GAAG,eAAe,GAAG,aAAa,CAAC,GACnG,OAAO,CAAC,IAAI,CAAC,oBAAoB,EAAE,iBAAiB,CAAC,CAAC,CAAC;CAC5D;AAED;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAoB9D;AAED;;;GAGG;AACH,wBAAsB,eAAe,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,aAAa,GAAG,SAAS,CAAC,CAoGtG"}
@@ -2,17 +2,18 @@
2
2
  * Connect modal — the single surface that owns a whole connect session.
3
3
  *
4
4
  * One Shadow-DOM dialog whose stage morphs in place through the session:
5
- * QR (phone path) → pairing code → connected, with the in-browser popup
6
- * path and the wallet catalog folded in as in-place alternates. The modal
7
- * never chains into a second modal and stays mounted until the session
8
- * resolves (connected / denied / cancelled).
5
+ * QR / deep-link handoff (phone path) → pairing code → connected, with the
6
+ * in-browser popup path and the wallet catalog folded in as in-place
7
+ * alternates. The modal never chains into a second modal and stays mounted
8
+ * until the session resolves (connected / denied / cancelled).
9
9
  *
10
10
  * Layout: three zones —
11
11
  *
12
12
  * 1. **Header** — requesting app identity + close.
13
- * 2. **Stage** — a height-stable region where states crossfade: the QR
14
- * (or deep link on phones), the pairing-code input, progress, success,
15
- * and error states.
13
+ * 2. **Stage** — a height-stable region where states crossfade: the
14
+ * clickable QR (joined by a Continue deep-link button on phones; both
15
+ * open the wallet in a new tab so this session stays alive underneath),
16
+ * the pairing-code input, progress, success, and error states.
16
17
  * 3. **Footer** — the wallet identity row ("Connecting with" + the selected
17
18
  * wallet and the next catalog wallets as tiles), a More tile that expands
18
19
  * the remaining wallets as a matching grid (search + custom URL) in
@@ -1 +1 @@
1
- {"version":3,"file":"connect-modal.d.ts","sourceRoot":"","sources":["../../../src/ui/connect-modal.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"connect-modal.d.ts","sourceRoot":"","sources":["../../../src/ui/connect-modal.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@enbox/browser",
3
- "version": "0.3.49",
3
+ "version": "0.3.51",
4
4
  "description": "Enbox tools and features to use in the browser",
5
5
  "type": "module",
6
6
  "main": "./dist/esm/index.js",
@@ -66,14 +66,14 @@
66
66
  "access": "public"
67
67
  },
68
68
  "dependencies": {
69
- "@enbox/agent": "0.8.20",
70
- "@enbox/api": "0.6.58",
71
- "@enbox/auth": "0.6.66",
69
+ "@enbox/agent": "0.8.22",
70
+ "@enbox/api": "0.6.60",
71
+ "@enbox/auth": "0.6.68",
72
72
  "@enbox/common": "0.1.3",
73
- "@enbox/connect": "0.1.2",
73
+ "@enbox/connect": "0.1.4",
74
74
  "@enbox/crypto": "0.1.5",
75
75
  "@enbox/dids": "0.1.5",
76
- "@enbox/dwn-sdk-js": "0.4.10"
76
+ "@enbox/dwn-sdk-js": "0.4.11"
77
77
  },
78
78
  "devDependencies": {
79
79
  "@types/qrcode": "^1.5.6",
@@ -32,6 +32,7 @@ import type {
32
32
  import { assertX25519PublicJwk, CONNECT_DENIED_TOKEN, ConnectClient } from '@enbox/connect';
33
33
 
34
34
  import {
35
+ DWEB_CONNECT_ACK_MESSAGE_TYPE,
35
36
  DWEB_CONNECT_LOADED_MESSAGE_TYPE,
36
37
  DWEB_CONNECT_PATH,
37
38
  DWEB_CONNECT_REQUEST_MESSAGE_TYPE,
@@ -45,11 +46,42 @@ const DEFAULT_TIMEOUT_MS = 300_000;
45
46
  /** Interval at which the transport checks whether the user closed the popup. */
46
47
  const CLOSED_POLL_INTERVAL_MS = 500;
47
48
 
49
+ /**
50
+ * Grace period after the popup is seen closed for an already-posted response
51
+ * to finish delivery before the close is treated as a denial.
52
+ */
53
+ const CLOSED_RESPONSE_GRACE_MS = 1_500;
54
+
48
55
  /** Window name given to the wallet popup. */
49
56
  const POPUP_WINDOW_NAME = 'enbox-dweb-connect';
50
57
 
51
- /** Window features given to the wallet popup. */
52
- const POPUP_WINDOW_FEATURES = 'width=500,height=700,menubar=no,toolbar=no,location=no,status=no';
58
+ /** Outer size of the wallet popup window. */
59
+ const POPUP_WIDTH = 500;
60
+ const POPUP_HEIGHT = 700;
61
+
62
+ /**
63
+ * Builds the wallet popup's window features, centring it over the opener
64
+ * window — without `left`/`top` browsers park the popup wherever they like
65
+ * (usually offset into a corner). Falls back to centring on the screen when
66
+ * the opener's metrics are unavailable, and lets the browser clamp
67
+ * off-screen coordinates. Mobile browsers ignore the features entirely and
68
+ * open a tab.
69
+ */
70
+ function popupWindowFeatures(): string {
71
+ const base = `width=${POPUP_WIDTH},height=${POPUP_HEIGHT},menubar=no,toolbar=no,location=no,status=no`;
72
+
73
+ // `screenX`/`outerWidth` describe the opener window; `screen.avail*`
74
+ // describe its monitor — so the popup lands on the same display.
75
+ const openerWidth = window.outerWidth > 0 ? window.outerWidth : (window.screen?.availWidth ?? 0);
76
+ const openerHeight = window.outerHeight > 0 ? window.outerHeight : (window.screen?.availHeight ?? 0);
77
+ if (openerWidth === 0 || openerHeight === 0) {
78
+ return base;
79
+ }
80
+
81
+ const left = Math.max(0, Math.round((window.screenX ?? 0) + (openerWidth - POPUP_WIDTH) / 2));
82
+ const top = Math.max(0, Math.round((window.screenY ?? 0) + (openerHeight - POPUP_HEIGHT) / 2));
83
+ return `${base},left=${left},top=${top}`;
84
+ }
53
85
 
54
86
  /** Guards against calling browser-only entry points outside a browser. */
55
87
  function assertBrowserEnvironment(): void {
@@ -137,7 +169,7 @@ export class PopupClientTransport implements ConnectTransport {
137
169
  // the stack has yielded to a prior `await`, so opening it here — instead of
138
170
  // inside the awaited `requestProfile()` — is what keeps the flow usable.
139
171
  const popupUrl = new URL(DWEB_CONNECT_PATH, options.walletUrl).toString();
140
- const popup = window.open(popupUrl, POPUP_WINDOW_NAME, POPUP_WINDOW_FEATURES);
172
+ const popup = window.open(popupUrl, POPUP_WINDOW_NAME, popupWindowFeatures());
141
173
  if (!popup) {
142
174
  throw new Error('[@enbox/browser] Popup blocked. Allow popups for this site to connect to a wallet.');
143
175
  }
@@ -212,6 +244,19 @@ export class PopupClientTransport implements ConnectTransport {
212
244
  return await this._responsePromise;
213
245
  }
214
246
 
247
+ /**
248
+ * {@inheritDoc ConnectTransport.confirmComplete}
249
+ *
250
+ * Posts the payload-less ack to the wallet popup (pinned `targetOrigin`)
251
+ * so a wallet still holding its "finishing up" screen can flip to a
252
+ * confirmed "connected" state before closing itself. A popup the user
253
+ * already closed is skipped silently.
254
+ */
255
+ public async confirmComplete(): Promise<void> {
256
+ if (this._popup.closed === true) { return; }
257
+ this._popup.postMessage({ type: DWEB_CONNECT_ACK_MESSAGE_TYPE }, this._walletOrigin);
258
+ }
259
+
215
260
  private onMessage(event: MessageEvent): void {
216
261
  const message = getTrustedMessage(event, this._walletOrigin, this._popup);
217
262
  if (message === undefined) { return; }
@@ -275,10 +320,19 @@ export class PopupClientTransport implements ConnectTransport {
275
320
  return;
276
321
  }
277
322
 
278
- // Closed after the handshake was established — treat as a user denial.
279
- this._settled = true;
280
- this.cleanup();
281
- this._resolveResponse?.(CONNECT_DENIED_TOKEN);
323
+ // Closed after the handshake was established. A wallet that posts its
324
+ // response and immediately closes itself can lose the race between that
325
+ // final message and this poll — especially when this page was
326
+ // backgrounded (mobile tabs) and both signals arrive together on
327
+ // resume — so give a queued response a grace window before treating the
328
+ // close as a user denial.
329
+ clearInterval(this._closedPollId);
330
+ setTimeout((): void => {
331
+ if (this._settled) { return; }
332
+ this._settled = true;
333
+ this.cleanup();
334
+ this._resolveResponse?.(CONNECT_DENIED_TOKEN);
335
+ }, CLOSED_RESPONSE_GRACE_MS);
282
336
  }
283
337
 
284
338
  private fail(error: Error): void {
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Wire shapes for the DWeb Connect popup/postMessage channel.
3
3
  *
4
- * The dapp and the wallet exchange exactly three message kinds over
4
+ * The dapp and the wallet exchange exactly four message kinds over
5
5
  * `window.postMessage`:
6
6
  *
7
7
  * 1. `loaded` beacon (wallet → dapp): announces the wallet page is ready and
@@ -11,6 +11,10 @@
11
11
  * bound into the JWE `apv` header.
12
12
  * 3. `response` (wallet → dapp): the sealed connect response JWE, or the
13
13
  * literal deny token — again ciphertext only.
14
+ * 4. `ack` (dapp → wallet): payload-less completion signal — the dapp opened
15
+ * the response successfully, so the wallet can flip to a confirmed
16
+ * "connected" state before closing itself. Optional and observational;
17
+ * wallets unaware of it simply ignore the message.
14
18
  *
15
19
  * Plaintext never crosses the channel, and both sides pin `targetOrigin`
16
20
  * and verify `event.origin` / `event.source` on every message.
@@ -32,6 +36,9 @@ export const DWEB_CONNECT_REQUEST_MESSAGE_TYPE = 'enbox-connect-request';
32
36
  /** Message type carrying the sealed connect response JWE or deny token (wallet → dapp). */
33
37
  export const DWEB_CONNECT_RESPONSE_MESSAGE_TYPE = 'enbox-connect-response';
34
38
 
39
+ /** Message type of the dapp's completion acknowledgement (dapp → wallet). */
40
+ export const DWEB_CONNECT_ACK_MESSAGE_TYPE = 'enbox-connect-ack';
41
+
35
42
  /**
36
43
  * The wallet's readiness beacon. Emitted to `window.opener` once the wallet's
37
44
  * DWeb Connect page has generated its per-session ephemeral X25519 key.
@@ -62,6 +69,14 @@ export type DWebConnectResponseMessage = {
62
69
  payload: string;
63
70
  };
64
71
 
72
+ /**
73
+ * The dapp's completion acknowledgement: it opened the wallet's response
74
+ * successfully. Carries no payload by design.
75
+ */
76
+ export type DWebConnectAckMessage = {
77
+ type: typeof DWEB_CONNECT_ACK_MESSAGE_TYPE;
78
+ };
79
+
65
80
  /**
66
81
  * Applies the shared inbound-message trust filter for the popup channel:
67
82
  * pins the message to the expected `event.origin`, pins its `event.source` to
@@ -12,7 +12,9 @@
12
12
  * value-checks the `apv` origin binding against this wallet's origin.
13
13
  * 3. After the consent ceremony, the wallet posts the sealed response JWE via
14
14
  * `sendResponse()` — or the deny token via `deny()` — back to the opener,
15
- * again with a pinned `targetOrigin`.
15
+ * again with a pinned `targetOrigin`. `sendResponseAwaitingAck()` does the
16
+ * same but stays alive briefly for the dapp's completion ack, so the
17
+ * wallet can show a confirmed "connected" state before closing itself.
16
18
  *
17
19
  * The dapp origin is never assumed: it is taken from an explicit option or
18
20
  * derived from `document.referrer`, and `create()` fails when neither is
@@ -28,6 +30,7 @@ import { X25519 } from '@enbox/crypto';
28
30
  import { CONNECT_DENIED_TOKEN, ConnectProvider } from '@enbox/connect';
29
31
 
30
32
  import {
33
+ DWEB_CONNECT_ACK_MESSAGE_TYPE,
31
34
  DWEB_CONNECT_LOADED_MESSAGE_TYPE,
32
35
  DWEB_CONNECT_REQUEST_MESSAGE_TYPE,
33
36
  DWEB_CONNECT_RESPONSE_MESSAGE_TYPE,
@@ -37,6 +40,9 @@ import {
37
40
  /** Default budget for awaiting the dapp's sealed request. */
38
41
  const DEFAULT_REQUEST_TIMEOUT_MS = 300_000;
39
42
 
43
+ /** Default budget for awaiting the dapp's completion ack after a response. */
44
+ const DEFAULT_ACK_TIMEOUT_MS = 5_000;
45
+
40
46
  /** Options for creating a {@link WalletPostMessageTransport}. */
41
47
  export type WalletPostMessageTransportOptions = {
42
48
  /**
@@ -79,6 +85,7 @@ export class WalletPostMessageTransport {
79
85
  private _requestReceived = false;
80
86
  private _timeoutId?: ReturnType<typeof setTimeout>;
81
87
  private _messageListener?: (event: MessageEvent) => void;
88
+ private _resolveAck?: (acked: boolean) => void;
82
89
 
83
90
  private readonly _requestPromise: Promise<ConnectRequest>;
84
91
  private _resolveRequest?: (request: ConnectRequest) => void;
@@ -177,6 +184,43 @@ export class WalletPostMessageTransport {
177
184
  this.close();
178
185
  }
179
186
 
187
+ /**
188
+ * Posts the sealed response JWE back to the dapp like
189
+ * {@link sendResponse}, then keeps the channel open briefly for the dapp's
190
+ * completion ack — the signal that it opened the response successfully —
191
+ * before closing the transport.
192
+ *
193
+ * Lets the wallet's connect page show a confirmed "connected" state (and
194
+ * close itself with confidence) instead of asking the user to dismiss the
195
+ * screen blind. Dapps predating the ack simply never send one, so the
196
+ * wallet should keep a manual dismiss affordance for the `false` case.
197
+ *
198
+ * @param idToken - The sealed response JWE.
199
+ * @param options - The awaiting options.
200
+ * @param options.timeoutMs - Ack budget in milliseconds; defaults to 5 s.
201
+ * @returns Whether the dapp acknowledged completion within the budget.
202
+ */
203
+ public async sendResponseAwaitingAck(idToken: string, options: { timeoutMs?: number } = {}): Promise<boolean> {
204
+ const ackPromise = new Promise<boolean>((resolve): void => {
205
+ this._resolveAck = resolve;
206
+ });
207
+
208
+ this._dappWindow.postMessage({
209
+ type : DWEB_CONNECT_RESPONSE_MESSAGE_TYPE,
210
+ payload : idToken,
211
+ }, this._dappOrigin);
212
+
213
+ const ackTimeoutId = setTimeout((): void => {
214
+ this._resolveAck?.(false);
215
+ }, options.timeoutMs ?? DEFAULT_ACK_TIMEOUT_MS);
216
+
217
+ const acked = await ackPromise;
218
+ clearTimeout(ackTimeoutId);
219
+ this._resolveAck = undefined;
220
+ this.close();
221
+ return acked;
222
+ }
223
+
180
224
  /** Posts the deny token back to the dapp — the user rejected the request. */
181
225
  public deny(): void {
182
226
  this.sendResponse(CONNECT_DENIED_TOKEN);
@@ -195,6 +239,11 @@ export class WalletPostMessageTransport {
195
239
  const message = getTrustedMessage(event, this._dappOrigin, this._dappWindow);
196
240
  if (message === undefined) { return; }
197
241
 
242
+ if (message.type === DWEB_CONNECT_ACK_MESSAGE_TYPE) {
243
+ this._resolveAck?.(true);
244
+ return;
245
+ }
246
+
198
247
  if (message.type !== DWEB_CONNECT_REQUEST_MESSAGE_TYPE || this._requestReceived) { return; }
199
248
  this._requestReceived = true;
200
249
 
package/src/index.ts CHANGED
@@ -94,12 +94,14 @@ export type { PopupClientTransportOptions, PopupConnectOptions } from './dweb-co
94
94
  export { WalletPostMessageTransport } from './dweb-connect-wallet.js';
95
95
  export type { WalletPostMessageTransportOptions } from './dweb-connect-wallet.js';
96
96
  export {
97
+ DWEB_CONNECT_ACK_MESSAGE_TYPE,
97
98
  DWEB_CONNECT_LOADED_MESSAGE_TYPE,
98
99
  DWEB_CONNECT_PATH,
99
100
  DWEB_CONNECT_REQUEST_MESSAGE_TYPE,
100
101
  DWEB_CONNECT_RESPONSE_MESSAGE_TYPE,
101
102
  } from './dweb-connect-messages.js';
102
103
  export type {
104
+ DWebConnectAckMessage,
103
105
  DWebConnectLoadedMessage,
104
106
  DWebConnectRequestMessage,
105
107
  DWebConnectResponseMessage,
@@ -107,7 +107,39 @@ export interface RelayConnectOptions {
107
107
  timeoutMs?: number;
108
108
  pollIntervalMs?: number;
109
109
  onClaimed?: () => void;
110
- }) => Pick<RelayClientTransport, 'requestProfile' | 'deliverRequest' | 'awaitResponse' | 'requiresPin'>;
110
+ }) => Pick<RelayClientTransport, 'requestProfile' | 'deliverRequest' | 'awaitResponse' | 'requiresPin'>
111
+ & Partial<Pick<RelayClientTransport, 'confirmComplete'>>;
112
+ }
113
+
114
+ /**
115
+ * Sleep used between relay polls that ends early when the page returns to
116
+ * the foreground. Browsers throttle — and mobile browsers freeze — timers in
117
+ * background tabs, so a plain interval sleep can leave the user staring at a
118
+ * stale stage after approving in the wallet tab; cutting the sleep short on
119
+ * `visibilitychange` makes the next poll (and with it the pairing-code
120
+ * prompt) land the moment they switch back. Falls back to a plain sleep
121
+ * outside DOM environments.
122
+ */
123
+ export function visibilityAwareSleep(ms: number): Promise<void> {
124
+ return new Promise((resolve) => {
125
+ if (typeof document === 'undefined') {
126
+ setTimeout(resolve, ms);
127
+ return;
128
+ }
129
+
130
+ const finish = (): void => {
131
+ clearTimeout(timer);
132
+ document.removeEventListener('visibilitychange', onVisibilityChange);
133
+ resolve();
134
+ };
135
+ const onVisibilityChange = (): void => {
136
+ if (document.visibilityState === 'visible') {
137
+ finish();
138
+ }
139
+ };
140
+ const timer = setTimeout(finish, ms);
141
+ document.addEventListener('visibilitychange', onVisibilityChange);
142
+ });
111
143
  }
112
144
 
113
145
  /**
@@ -116,7 +148,10 @@ export interface RelayConnectOptions {
116
148
  */
117
149
  export async function runRelayConnect(options: RelayConnectOptions): Promise<ConnectResult | undefined> {
118
150
  const createTransport = options.createTransport
119
- ?? ((transportOptions): RelayClientTransport => new RelayClientTransport(transportOptions));
151
+ ?? ((transportOptions): RelayClientTransport => new RelayClientTransport({
152
+ ...transportOptions,
153
+ sleep: visibilityAwareSleep,
154
+ }));
120
155
 
121
156
  const transport = createTransport({
122
157
  connectServerUrl : options.connectServerUrl,
@@ -189,6 +224,11 @@ export async function runRelayConnect(options: RelayConnectOptions): Promise<Con
189
224
  throw new Error('Connect: wallet response omitted `delegatePortableDid`.');
190
225
  }
191
226
 
227
+ // Best-effort completion signal so the wallet can flip its pairing
228
+ // screen to a confirmed "connected" state instead of leaving the user
229
+ // to dismiss it blind. Fire-and-forget — mirrors the kernel client.
230
+ void transport.confirmComplete?.().catch((): undefined => undefined);
231
+
192
232
  return {
193
233
  delegatePortableDid : response.delegatePortableDid,
194
234
  delegateGrants : response.delegateGrants,