@bwalletx/connect 0.5.3 → 0.5.4

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/dist/index.d.cts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { WalletInterface, WalletProtocol } from '@bsv/sdk';
2
- import { f as PairingOptions, W as WalletOption } from './wallets-VOKIKBbO.cjs';
3
- export { g as BRC29_PROTOCOL, i as Brc29Remittance, B as BuyTokenRequest, C as ConnectedWallet, D as DiscoverWalletsOptions, H as HandCashAdapterOptions, L as LegacyPayer, j as PairConnectionOptions, k as PairStorage, l as PairedWallet, b as PayOptions, m as PayOutput, P as PayRequest, a as PayResult, n as PaySource, c as PaymentError, o as PaymentErrorCode, S as SocketFactory, q as SocketLike, T as TokenQuote, r as WalletKind, s as amountInSats, d as brc100Option, t as buyToken, u as clearPairing, e as discoverWallets, h as handcashAdapter, v as isIdentityKeyHex, w as isPaymail, x as pairBwalletX, p as pairingOption, y as pay, z as resolvePaymailP2P, A as restorePairing, E as tip } from './wallets-VOKIKBbO.cjs';
2
+ import { f as PairingOptions, W as WalletOption } from './wallets-CbzMtxrf.cjs';
3
+ export { g as BRC29_PROTOCOL, i as Brc29Remittance, B as BuyTokenRequest, C as ConnectedWallet, D as DiscoverWalletsOptions, H as HandCashAdapterOptions, L as LegacyPayer, j as PairConnectionOptions, k as PairStorage, l as PairedWallet, b as PayOptions, m as PayOutput, P as PayRequest, a as PayResult, n as PaySource, c as PaymentError, o as PaymentErrorCode, S as SocketFactory, q as SocketLike, T as TokenQuote, r as WalletKind, s as amountInSats, d as brc100Option, t as buyToken, u as clearPairing, e as discoverWallets, h as handcashAdapter, v as isIdentityKeyHex, w as isPaymail, x as pairBwalletX, p as pairingOption, y as pay, z as resolvePaymailP2P, A as restorePairing, E as tip } from './wallets-CbzMtxrf.cjs';
4
4
  export { AuthorizeParams, GET_URL, SIGNIN_CSS, SIGNIN_TAG, SigninBrand, SigninLabel, SigninSize, SigninTheme, WEB_WALLET_URL, buildAuthorizeUrl, defineBwalletxSignin, markSvg, signinText } from './element.cjs';
5
5
  import { b as AccessResponse, H as HoldingProof, A as AccessError } from './access-Dp0eKIOc.cjs';
6
6
  export { f as ACCESS_ERROR_STATUS, d as ACCESS_PROTOCOL, g as ACCESS_VERSION, h as AccessChallenge, i as AccessErrorBody, j as AccessErrorCode, c as AccessKind, a as AccessRequirement, P as PROTOCOL_MEDIA, T as TokenRequirement, k as accessErrorMessage, m as canonicalJson, r as protocolMediaUrl } from './access-Dp0eKIOc.cjs';
@@ -16,10 +16,17 @@ interface FoundWallet {
16
16
  method: FoundBy;
17
17
  rdns: string | null;
18
18
  }
19
+ /**
20
+ * Default wait for a wallet that has not answered yet, ms. bWalletX for Chrome answers `brc100:requestWallet`
21
+ * synchronously and the in-app browser sets `window.CWI` before the page runs, so a found wallet resolves at once;
22
+ * this is only how long "nothing here" takes before the pairing QR shows. Was 1200 (0.5.3 and earlier).
23
+ */
24
+ declare const DEFAULT_DISCOVER_MS = 300;
19
25
  /**
20
26
  * Like findBwalletX, but also says which bWalletX answered.
21
- * Order: a BRC-100 announcement with a bWalletX rdns; then `window.CWI` (set by the in-app
22
- * browser, and by older builds that do not announce); otherwise null.
27
+ * Order: a BRC-100 announcement with a bWalletX rdns (answered at once by the extension); then `window.CWI` (set by
28
+ * the in-app browser, and by older builds that do not announce), taken straight away; then, only when framed, the
29
+ * parent's XDM answer (probed in parallel with the wait). Otherwise null after `ms`.
23
30
  */
24
31
  declare function discoverBwalletX(ms?: number): Promise<FoundWallet | null>;
25
32
  /**
@@ -27,6 +34,15 @@ declare function discoverBwalletX(ms?: number): Promise<FoundWallet | null>;
27
34
  * open in bWalletX's in-app browser. Resolves null if nothing answered within `ms`.
28
35
  */
29
36
  declare function findBwalletX(ms?: number): Promise<WalletInterface | null>;
37
+ /**
38
+ * Wake the wallet before the click (call on pointerenter / focus / touchstart of your Connect button). Chrome stops an
39
+ * idle extension worker after ~30 s, and a cold worker needs 1-3 s to reopen the wallet; this sends a liveness call
40
+ * (isAuthenticated: never shows a prompt, never reveals a key) so that work starts while the pointer is on its way.
41
+ * At most once every 20 s. Does nothing when no bWalletX is in the page.
42
+ */
43
+ declare function prewarmBwalletX(): void;
44
+ /** Prewarm on hover, keyboard focus and touch. Returns a function that removes the listeners. */
45
+ declare function bindPrewarm(el: EventTarget): () => void;
30
46
 
31
47
  /** Security level 2, a protocol name used for nothing else. */
32
48
  declare const LOGIN_PROTOCOL: WalletProtocol;
@@ -38,6 +54,12 @@ interface Challenge {
38
54
  protocolID?: WalletProtocol;
39
55
  }
40
56
  type SignInMethod = 'extension' | 'in-app' | 'pairing' | 'wallet';
57
+ /**
58
+ * Progress for the button label. 'finding': looking for a wallet (sub-second); 'opening': a wallet was found and its
59
+ * approval is on the way ("Opening your wallet…"); 'pairing': no wallet in this browser, the QR is showing;
60
+ * 'signing': the identity key arrived, the sign-in signature is being asked for.
61
+ */
62
+ type SignInStatus = 'finding' | 'opening' | 'pairing' | 'signing';
41
63
  interface SignInOptions {
42
64
  /** Ask your server for a challenge for this identity key. */
43
65
  challenge: (identityKey: string) => Promise<Challenge>;
@@ -47,8 +69,10 @@ interface SignInOptions {
47
69
  pairing?: PairingOptions;
48
70
  /** Reuse a pairing stored by an earlier sign-in before showing a new QR. Default true. */
49
71
  reusePairing?: boolean;
50
- /** How long to wait for the extension or in-app browser to answer, in ms. Default 1200. */
72
+ /** How long to wait for a wallet that has not answered, in ms, before pairing. Default 300 (a present wallet answers at once). */
51
73
  discoverMs?: number;
74
+ /** Called as the sign-in moves on, so the button can say "Opening your wallet…" straight away. */
75
+ onStatus?: (status: SignInStatus) => void;
52
76
  }
53
77
  interface SignInResult {
54
78
  identityKey: string;
@@ -60,7 +84,7 @@ interface SignInResult {
60
84
  wallet: WalletInterface;
61
85
  }
62
86
  /** Find a wallet: extension or in-app browser first, then a stored pairing, then a new pairing. */
63
- declare function connectBwalletX(opts?: Pick<SignInOptions, 'wallet' | 'pairing' | 'reusePairing' | 'discoverMs'>): Promise<{
87
+ declare function connectBwalletX(opts?: Pick<SignInOptions, 'wallet' | 'pairing' | 'reusePairing' | 'discoverMs' | 'onStatus'>): Promise<{
64
88
  wallet: WalletInterface;
65
89
  method: SignInMethod;
66
90
  }>;
@@ -177,4 +201,4 @@ declare const WALLET_THEME_META = "bwalletx-theme";
177
201
  /** Declare (or with null, withdraw) the theme the wallet should wear for this page. No-op outside a browser. */
178
202
  declare function declareWalletTheme(theme: string | null | undefined): void;
179
203
 
180
- export { type AccessChallengeLike, AccessError, AccessResponse, BWALLETX_RDNS, type ButtonOptions, type Challenge, type FoundBy, type FoundWallet, HoldingProof, LOGIN_PROTOCOL, LOGO_URL, type PairingModalOptions, PairingOptions, RDNS_EXTENSION, RDNS_MOBILE, type RequestAccessOptions, type RequestAccessResult, type SignInMethod, type SignInOptions, type SignInResult, type SignInStage, WALLET_THEME_META, WalletOption, connectBwalletX, declareWalletTheme, discoverBwalletX, findBwalletX, inOrder, isTouchDevice, pairingModal, pickWallet, proveHoldings, qrSvg, renderButton, requestAccess, signInWithBwalletX, throwIfAccessError, toAccessError };
204
+ export { type AccessChallengeLike, AccessError, AccessResponse, BWALLETX_RDNS, type ButtonOptions, type Challenge, DEFAULT_DISCOVER_MS, type FoundBy, type FoundWallet, HoldingProof, LOGIN_PROTOCOL, LOGO_URL, type PairingModalOptions, PairingOptions, RDNS_EXTENSION, RDNS_MOBILE, type RequestAccessOptions, type RequestAccessResult, type SignInMethod, type SignInOptions, type SignInResult, type SignInStage, type SignInStatus, WALLET_THEME_META, WalletOption, bindPrewarm, connectBwalletX, declareWalletTheme, discoverBwalletX, findBwalletX, inOrder, isTouchDevice, pairingModal, pickWallet, prewarmBwalletX, proveHoldings, qrSvg, renderButton, requestAccess, signInWithBwalletX, throwIfAccessError, toAccessError };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { WalletInterface, WalletProtocol } from '@bsv/sdk';
2
- import { f as PairingOptions, W as WalletOption } from './wallets-VOKIKBbO.js';
3
- export { g as BRC29_PROTOCOL, i as Brc29Remittance, B as BuyTokenRequest, C as ConnectedWallet, D as DiscoverWalletsOptions, H as HandCashAdapterOptions, L as LegacyPayer, j as PairConnectionOptions, k as PairStorage, l as PairedWallet, b as PayOptions, m as PayOutput, P as PayRequest, a as PayResult, n as PaySource, c as PaymentError, o as PaymentErrorCode, S as SocketFactory, q as SocketLike, T as TokenQuote, r as WalletKind, s as amountInSats, d as brc100Option, t as buyToken, u as clearPairing, e as discoverWallets, h as handcashAdapter, v as isIdentityKeyHex, w as isPaymail, x as pairBwalletX, p as pairingOption, y as pay, z as resolvePaymailP2P, A as restorePairing, E as tip } from './wallets-VOKIKBbO.js';
2
+ import { f as PairingOptions, W as WalletOption } from './wallets-CbzMtxrf.js';
3
+ export { g as BRC29_PROTOCOL, i as Brc29Remittance, B as BuyTokenRequest, C as ConnectedWallet, D as DiscoverWalletsOptions, H as HandCashAdapterOptions, L as LegacyPayer, j as PairConnectionOptions, k as PairStorage, l as PairedWallet, b as PayOptions, m as PayOutput, P as PayRequest, a as PayResult, n as PaySource, c as PaymentError, o as PaymentErrorCode, S as SocketFactory, q as SocketLike, T as TokenQuote, r as WalletKind, s as amountInSats, d as brc100Option, t as buyToken, u as clearPairing, e as discoverWallets, h as handcashAdapter, v as isIdentityKeyHex, w as isPaymail, x as pairBwalletX, p as pairingOption, y as pay, z as resolvePaymailP2P, A as restorePairing, E as tip } from './wallets-CbzMtxrf.js';
4
4
  export { AuthorizeParams, GET_URL, SIGNIN_CSS, SIGNIN_TAG, SigninBrand, SigninLabel, SigninSize, SigninTheme, WEB_WALLET_URL, buildAuthorizeUrl, defineBwalletxSignin, markSvg, signinText } from './element.js';
5
5
  import { b as AccessResponse, H as HoldingProof, A as AccessError } from './access-Dp0eKIOc.js';
6
6
  export { f as ACCESS_ERROR_STATUS, d as ACCESS_PROTOCOL, g as ACCESS_VERSION, h as AccessChallenge, i as AccessErrorBody, j as AccessErrorCode, c as AccessKind, a as AccessRequirement, P as PROTOCOL_MEDIA, T as TokenRequirement, k as accessErrorMessage, m as canonicalJson, r as protocolMediaUrl } from './access-Dp0eKIOc.js';
@@ -16,10 +16,17 @@ interface FoundWallet {
16
16
  method: FoundBy;
17
17
  rdns: string | null;
18
18
  }
19
+ /**
20
+ * Default wait for a wallet that has not answered yet, ms. bWalletX for Chrome answers `brc100:requestWallet`
21
+ * synchronously and the in-app browser sets `window.CWI` before the page runs, so a found wallet resolves at once;
22
+ * this is only how long "nothing here" takes before the pairing QR shows. Was 1200 (0.5.3 and earlier).
23
+ */
24
+ declare const DEFAULT_DISCOVER_MS = 300;
19
25
  /**
20
26
  * Like findBwalletX, but also says which bWalletX answered.
21
- * Order: a BRC-100 announcement with a bWalletX rdns; then `window.CWI` (set by the in-app
22
- * browser, and by older builds that do not announce); otherwise null.
27
+ * Order: a BRC-100 announcement with a bWalletX rdns (answered at once by the extension); then `window.CWI` (set by
28
+ * the in-app browser, and by older builds that do not announce), taken straight away; then, only when framed, the
29
+ * parent's XDM answer (probed in parallel with the wait). Otherwise null after `ms`.
23
30
  */
24
31
  declare function discoverBwalletX(ms?: number): Promise<FoundWallet | null>;
25
32
  /**
@@ -27,6 +34,15 @@ declare function discoverBwalletX(ms?: number): Promise<FoundWallet | null>;
27
34
  * open in bWalletX's in-app browser. Resolves null if nothing answered within `ms`.
28
35
  */
29
36
  declare function findBwalletX(ms?: number): Promise<WalletInterface | null>;
37
+ /**
38
+ * Wake the wallet before the click (call on pointerenter / focus / touchstart of your Connect button). Chrome stops an
39
+ * idle extension worker after ~30 s, and a cold worker needs 1-3 s to reopen the wallet; this sends a liveness call
40
+ * (isAuthenticated: never shows a prompt, never reveals a key) so that work starts while the pointer is on its way.
41
+ * At most once every 20 s. Does nothing when no bWalletX is in the page.
42
+ */
43
+ declare function prewarmBwalletX(): void;
44
+ /** Prewarm on hover, keyboard focus and touch. Returns a function that removes the listeners. */
45
+ declare function bindPrewarm(el: EventTarget): () => void;
30
46
 
31
47
  /** Security level 2, a protocol name used for nothing else. */
32
48
  declare const LOGIN_PROTOCOL: WalletProtocol;
@@ -38,6 +54,12 @@ interface Challenge {
38
54
  protocolID?: WalletProtocol;
39
55
  }
40
56
  type SignInMethod = 'extension' | 'in-app' | 'pairing' | 'wallet';
57
+ /**
58
+ * Progress for the button label. 'finding': looking for a wallet (sub-second); 'opening': a wallet was found and its
59
+ * approval is on the way ("Opening your wallet…"); 'pairing': no wallet in this browser, the QR is showing;
60
+ * 'signing': the identity key arrived, the sign-in signature is being asked for.
61
+ */
62
+ type SignInStatus = 'finding' | 'opening' | 'pairing' | 'signing';
41
63
  interface SignInOptions {
42
64
  /** Ask your server for a challenge for this identity key. */
43
65
  challenge: (identityKey: string) => Promise<Challenge>;
@@ -47,8 +69,10 @@ interface SignInOptions {
47
69
  pairing?: PairingOptions;
48
70
  /** Reuse a pairing stored by an earlier sign-in before showing a new QR. Default true. */
49
71
  reusePairing?: boolean;
50
- /** How long to wait for the extension or in-app browser to answer, in ms. Default 1200. */
72
+ /** How long to wait for a wallet that has not answered, in ms, before pairing. Default 300 (a present wallet answers at once). */
51
73
  discoverMs?: number;
74
+ /** Called as the sign-in moves on, so the button can say "Opening your wallet…" straight away. */
75
+ onStatus?: (status: SignInStatus) => void;
52
76
  }
53
77
  interface SignInResult {
54
78
  identityKey: string;
@@ -60,7 +84,7 @@ interface SignInResult {
60
84
  wallet: WalletInterface;
61
85
  }
62
86
  /** Find a wallet: extension or in-app browser first, then a stored pairing, then a new pairing. */
63
- declare function connectBwalletX(opts?: Pick<SignInOptions, 'wallet' | 'pairing' | 'reusePairing' | 'discoverMs'>): Promise<{
87
+ declare function connectBwalletX(opts?: Pick<SignInOptions, 'wallet' | 'pairing' | 'reusePairing' | 'discoverMs' | 'onStatus'>): Promise<{
64
88
  wallet: WalletInterface;
65
89
  method: SignInMethod;
66
90
  }>;
@@ -177,4 +201,4 @@ declare const WALLET_THEME_META = "bwalletx-theme";
177
201
  /** Declare (or with null, withdraw) the theme the wallet should wear for this page. No-op outside a browser. */
178
202
  declare function declareWalletTheme(theme: string | null | undefined): void;
179
203
 
180
- export { type AccessChallengeLike, AccessError, AccessResponse, BWALLETX_RDNS, type ButtonOptions, type Challenge, type FoundBy, type FoundWallet, HoldingProof, LOGIN_PROTOCOL, LOGO_URL, type PairingModalOptions, PairingOptions, RDNS_EXTENSION, RDNS_MOBILE, type RequestAccessOptions, type RequestAccessResult, type SignInMethod, type SignInOptions, type SignInResult, type SignInStage, WALLET_THEME_META, WalletOption, connectBwalletX, declareWalletTheme, discoverBwalletX, findBwalletX, inOrder, isTouchDevice, pairingModal, pickWallet, proveHoldings, qrSvg, renderButton, requestAccess, signInWithBwalletX, throwIfAccessError, toAccessError };
204
+ export { type AccessChallengeLike, AccessError, AccessResponse, BWALLETX_RDNS, type ButtonOptions, type Challenge, DEFAULT_DISCOVER_MS, type FoundBy, type FoundWallet, HoldingProof, LOGIN_PROTOCOL, LOGO_URL, type PairingModalOptions, PairingOptions, RDNS_EXTENSION, RDNS_MOBILE, type RequestAccessOptions, type RequestAccessResult, type SignInMethod, type SignInOptions, type SignInResult, type SignInStage, type SignInStatus, WALLET_THEME_META, WalletOption, bindPrewarm, connectBwalletX, declareWalletTheme, discoverBwalletX, findBwalletX, inOrder, isTouchDevice, pairingModal, pickWallet, prewarmBwalletX, proveHoldings, qrSvg, renderButton, requestAccess, signInWithBwalletX, throwIfAccessError, toAccessError };
package/dist/index.js CHANGED
@@ -18,9 +18,10 @@ import {
18
18
  qrSvg,
19
19
  resolvePaymailP2P,
20
20
  tip
21
- } from "./chunk-PI42H3XY.js";
21
+ } from "./chunk-RIOHG5JH.js";
22
22
  import {
23
23
  BWALLETX_RDNS,
24
+ DEFAULT_DISCOVER_MS,
24
25
  GET_URL,
25
26
  LOGIN_PROTOCOL,
26
27
  RDNS_EXTENSION,
@@ -28,6 +29,7 @@ import {
28
29
  SIGNIN_CSS,
29
30
  SIGNIN_TAG,
30
31
  WEB_WALLET_URL,
32
+ bindPrewarm,
31
33
  buildAuthorizeUrl,
32
34
  clearPairing,
33
35
  connectBwalletX,
@@ -37,6 +39,7 @@ import {
37
39
  inOrder,
38
40
  markSvg,
39
41
  pairBwalletX,
42
+ prewarmBwalletX,
40
43
  proveHoldings,
41
44
  requestAccess,
42
45
  restorePairing,
@@ -44,7 +47,7 @@ import {
44
47
  signinText,
45
48
  throwIfAccessError,
46
49
  toAccessError
47
- } from "./chunk-IUDD7YTQ.js";
50
+ } from "./chunk-EXRQJTXF.js";
48
51
  import {
49
52
  ACCESS_ERROR_STATUS,
50
53
  ACCESS_PROTOCOL,
@@ -106,6 +109,7 @@ export {
106
109
  AccessError,
107
110
  BRC29_PROTOCOL,
108
111
  BWALLETX_RDNS,
112
+ DEFAULT_DISCOVER_MS,
109
113
  GET_URL,
110
114
  LOGIN_PROTOCOL,
111
115
  LOGO_URL,
@@ -119,6 +123,7 @@ export {
119
123
  WEB_WALLET_URL,
120
124
  accessErrorMessage,
121
125
  amountInSats,
126
+ bindPrewarm,
122
127
  brc100Option,
123
128
  buildAuthorizeUrl,
124
129
  buyToken,
@@ -141,6 +146,7 @@ export {
141
146
  pairingOption,
142
147
  pay,
143
148
  pickWallet,
149
+ prewarmBwalletX,
144
150
  protocolMediaUrl,
145
151
  proveHoldings,
146
152
  qrSvg,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/button.ts"],"sourcesContent":["const CSS = `\n.bwx-signin{display:inline-flex;align-items:center;gap:12px;min-height:48px;padding:10px 22px 10px 10px;border:0;border-radius:12px;cursor:pointer;background:linear-gradient(180deg,#fcd34d,#f59e0b);color:#000;text-align:left;font:700 16px/1.2 system-ui,-apple-system,\"Segoe UI\",sans-serif;box-shadow:0 8px 24px rgba(245,158,11,.2)}\n.bwx-signin:hover{background:linear-gradient(180deg,#fde68a,#fbbf24)}\n.bwx-signin:focus-visible{outline:2px solid #ffd24d;outline-offset:3px}\n.bwx-signin:disabled{opacity:.6;cursor:wait}\n.bwx-signin .bwx-mark{display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;border-radius:50%;background:#000;flex:none}\n.bwx-signin small{display:block;font-size:11px;font-weight:500;color:rgba(0,0,0,.6)}`;\n\nexport const LOGO_URL = 'https://bwalletx.com/logo.svg';\n\nexport interface ButtonOptions {\n /** Second line under the label. Default \"also works with bWallet\". false hides it. */\n subtitle?: string | false;\n onClick?: (button: HTMLButtonElement) => void;\n}\n\n/** Put the gold \"Sign in with bWalletX\" button into `el` (replacing its contents). */\nexport function renderButton(el: HTMLElement | string, opts: ButtonOptions = {}): HTMLButtonElement {\n const host = typeof el === 'string' ? document.querySelector<HTMLElement>(el) : el;\n if (!host) throw new Error(`renderButton: no element ${String(el)}`);\n if (!document.getElementById('bwx-signin-css')) {\n const style = document.createElement('style');\n style.id = 'bwx-signin-css';\n style.textContent = CSS;\n document.head.appendChild(style);\n }\n const b = document.createElement('button');\n b.type = 'button';\n b.className = 'bwx-signin';\n const mark = document.createElement('span');\n mark.className = 'bwx-mark';\n const img = document.createElement('img');\n img.src = LOGO_URL;\n img.alt = '';\n img.width = 15;\n img.height = 20;\n mark.appendChild(img);\n const label = document.createElement('span');\n label.className = 'bwx-label';\n label.textContent = 'Sign in with bWalletX';\n const sub = opts.subtitle === undefined ? 'also works with bWallet' : opts.subtitle;\n if (sub) {\n const small = document.createElement('small');\n small.textContent = sub;\n label.appendChild(small);\n }\n b.append(mark, label);\n if (opts.onClick) b.addEventListener('click', () => opts.onClick!(b));\n host.replaceChildren(b);\n return b;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,IAAM,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAQL,IAAM,WAAW;AASjB,SAAS,aAAa,IAA0B,OAAsB,CAAC,GAAsB;AAClG,QAAM,OAAO,OAAO,OAAO,WAAW,SAAS,cAA2B,EAAE,IAAI;AAChF,MAAI,CAAC,KAAM,OAAM,IAAI,MAAM,4BAA4B,OAAO,EAAE,CAAC,EAAE;AACnE,MAAI,CAAC,SAAS,eAAe,gBAAgB,GAAG;AAC9C,UAAM,QAAQ,SAAS,cAAc,OAAO;AAC5C,UAAM,KAAK;AACX,UAAM,cAAc;AACpB,aAAS,KAAK,YAAY,KAAK;AAAA,EACjC;AACA,QAAM,IAAI,SAAS,cAAc,QAAQ;AACzC,IAAE,OAAO;AACT,IAAE,YAAY;AACd,QAAM,OAAO,SAAS,cAAc,MAAM;AAC1C,OAAK,YAAY;AACjB,QAAM,MAAM,SAAS,cAAc,KAAK;AACxC,MAAI,MAAM;AACV,MAAI,MAAM;AACV,MAAI,QAAQ;AACZ,MAAI,SAAS;AACb,OAAK,YAAY,GAAG;AACpB,QAAM,QAAQ,SAAS,cAAc,MAAM;AAC3C,QAAM,YAAY;AAClB,QAAM,cAAc;AACpB,QAAM,MAAM,KAAK,aAAa,SAAY,4BAA4B,KAAK;AAC3E,MAAI,KAAK;AACP,UAAM,QAAQ,SAAS,cAAc,OAAO;AAC5C,UAAM,cAAc;AACpB,UAAM,YAAY,KAAK;AAAA,EACzB;AACA,IAAE,OAAO,MAAM,KAAK;AACpB,MAAI,KAAK,QAAS,GAAE,iBAAiB,SAAS,MAAM,KAAK,QAAS,CAAC,CAAC;AACpE,OAAK,gBAAgB,CAAC;AACtB,SAAO;AACT;","names":[]}
1
+ {"version":3,"sources":["../src/button.ts"],"sourcesContent":["const CSS = `\n.bwx-signin{display:inline-flex;align-items:center;gap:12px;min-height:48px;padding:10px 22px 10px 10px;border:0;border-radius:12px;cursor:pointer;background:linear-gradient(180deg,#fcd34d,#f59e0b);color:#000;text-align:left;font:700 16px/1.2 system-ui,-apple-system,\"Segoe UI\",sans-serif;box-shadow:0 8px 24px rgba(245,158,11,.2)}\n.bwx-signin:hover{background:linear-gradient(180deg,#fde68a,#fbbf24)}\n.bwx-signin:focus-visible{outline:2px solid #ffd24d;outline-offset:3px}\n.bwx-signin:disabled{opacity:.6;cursor:wait}\n.bwx-signin .bwx-mark{display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;border-radius:50%;background:#000;flex:none}\n.bwx-signin small{display:block;font-size:11px;font-weight:500;color:rgba(0,0,0,.6)}`;\n\nexport const LOGO_URL = 'https://bwalletx.com/logo.svg';\n\nexport interface ButtonOptions {\n /** Second line under the label. Default \"also works with bWallet\". false hides it. */\n subtitle?: string | false;\n onClick?: (button: HTMLButtonElement) => void;\n}\n\n/** Put the gold \"Sign in with bWalletX\" button into `el` (replacing its contents). */\nexport function renderButton(el: HTMLElement | string, opts: ButtonOptions = {}): HTMLButtonElement {\n const host = typeof el === 'string' ? document.querySelector<HTMLElement>(el) : el;\n if (!host) throw new Error(`renderButton: no element ${String(el)}`);\n if (!document.getElementById('bwx-signin-css')) {\n const style = document.createElement('style');\n style.id = 'bwx-signin-css';\n style.textContent = CSS;\n document.head.appendChild(style);\n }\n const b = document.createElement('button');\n b.type = 'button';\n b.className = 'bwx-signin';\n const mark = document.createElement('span');\n mark.className = 'bwx-mark';\n const img = document.createElement('img');\n img.src = LOGO_URL;\n img.alt = '';\n img.width = 15;\n img.height = 20;\n mark.appendChild(img);\n const label = document.createElement('span');\n label.className = 'bwx-label';\n label.textContent = 'Sign in with bWalletX';\n const sub = opts.subtitle === undefined ? 'also works with bWallet' : opts.subtitle;\n if (sub) {\n const small = document.createElement('small');\n small.textContent = sub;\n label.appendChild(small);\n }\n b.append(mark, label);\n if (opts.onClick) b.addEventListener('click', () => opts.onClick!(b));\n host.replaceChildren(b);\n return b;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,IAAM,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAQL,IAAM,WAAW;AASjB,SAAS,aAAa,IAA0B,OAAsB,CAAC,GAAsB;AAClG,QAAM,OAAO,OAAO,OAAO,WAAW,SAAS,cAA2B,EAAE,IAAI;AAChF,MAAI,CAAC,KAAM,OAAM,IAAI,MAAM,4BAA4B,OAAO,EAAE,CAAC,EAAE;AACnE,MAAI,CAAC,SAAS,eAAe,gBAAgB,GAAG;AAC9C,UAAM,QAAQ,SAAS,cAAc,OAAO;AAC5C,UAAM,KAAK;AACX,UAAM,cAAc;AACpB,aAAS,KAAK,YAAY,KAAK;AAAA,EACjC;AACA,QAAM,IAAI,SAAS,cAAc,QAAQ;AACzC,IAAE,OAAO;AACT,IAAE,YAAY;AACd,QAAM,OAAO,SAAS,cAAc,MAAM;AAC1C,OAAK,YAAY;AACjB,QAAM,MAAM,SAAS,cAAc,KAAK;AACxC,MAAI,MAAM;AACV,MAAI,MAAM;AACV,MAAI,QAAQ;AACZ,MAAI,SAAS;AACb,OAAK,YAAY,GAAG;AACpB,QAAM,QAAQ,SAAS,cAAc,MAAM;AAC3C,QAAM,YAAY;AAClB,QAAM,cAAc;AACpB,QAAM,MAAM,KAAK,aAAa,SAAY,4BAA4B,KAAK;AAC3E,MAAI,KAAK;AACP,UAAM,QAAQ,SAAS,cAAc,OAAO;AAC5C,UAAM,cAAc;AACpB,UAAM,YAAY,KAAK;AAAA,EACzB;AACA,IAAE,OAAO,MAAM,KAAK;AACpB,MAAI,KAAK,QAAS,GAAE,iBAAiB,SAAS,MAAM,KAAK,QAAS,CAAC,CAAC;AACpE,OAAK,gBAAgB,CAAC;AACtB,SAAO;AACT;","names":[]}
package/dist/react.cjs CHANGED
@@ -83,10 +83,22 @@ function pairUrl(link) {
83
83
  const q = new URLSearchParams({ v: link.v, r: link.r, c: link.c, k: link.k, o: link.o, e: String(link.e) });
84
84
  return `https://${PAIR_HOST}/pair?${q}`;
85
85
  }
86
- var relaySocketUrl = (relay, channel, role, expiry) => {
86
+ var newClaim = () => b64url(crypto.getRandomValues(new Uint8Array(16)));
87
+ var WALLET_CLAIM_INFO = "bwallet-pair-wallet-claim-v1";
88
+ var hex = (b) => Array.from(b, (x) => x.toString(16).padStart(2, "0")).join("");
89
+ async function walletClaimFor(channel, sitePub) {
90
+ const d = await crypto.subtle.digest("SHA-256", enc.encode(`${WALLET_CLAIM_INFO}|${channel}|${sitePub}`));
91
+ return b64url(new Uint8Array(d));
92
+ }
93
+ async function claimDigest(claim) {
94
+ return hex(new Uint8Array(await crypto.subtle.digest("SHA-256", enc.encode(claim))));
95
+ }
96
+ var relaySocketUrl = (relay, channel, role, expiry, claim, walletClaimHash) => {
87
97
  const local = /^(localhost|127\.0\.0\.1)(:\d+)?$/.test(relay);
88
98
  const q = new URLSearchParams({ role });
89
99
  if (expiry) q.set("e", String(expiry));
100
+ if (claim) q.set("claim", claim);
101
+ if (walletClaimHash && role === "site") q.set("wc", walletClaimHash);
90
102
  return `${local ? "ws" : "wss"}://${relay}/v1/c/${channel}?${q}`;
91
103
  };
92
104
  async function deriveSession(mine, theirs, channel) {
@@ -184,6 +196,7 @@ function parseStored(raw) {
184
196
  if (typeof p.s !== "string" || !/^[0-9a-f]{64}$/i.test(p.s)) return null;
185
197
  if (typeof p.k !== "string" || !/^0[23][0-9a-f]{64}$/i.test(p.k)) return null;
186
198
  if (!n(p.sent) || !n(p.lastSeen) || !n(p.pairedAt)) return null;
199
+ if (p.sc !== void 0 && (typeof p.sc !== "string" || !/^[A-Za-z0-9_-]{22,64}$/.test(p.sc))) return null;
187
200
  return p;
188
201
  } catch {
189
202
  return null;
@@ -241,7 +254,12 @@ var Link = class {
241
254
  await this.opening;
242
255
  }
243
256
  reopen() {
244
- const ws = this.socket(relaySocketUrl(this.p.r, this.p.c, "site", Math.floor(Date.now() / 1e3) + QR_LIFETIME_S - 10));
257
+ if (!this.p.sc) {
258
+ this.p = { ...this.p, sc: newClaim() };
259
+ this.persist();
260
+ }
261
+ const e = Math.floor(Date.now() / 1e3) + QR_LIFETIME_S - 10;
262
+ const ws = this.socket(relaySocketUrl(this.p.r, this.p.c, "site", e, this.p.sc));
245
263
  return new Promise((resolve, reject) => {
246
264
  ws.onopen = () => {
247
265
  this.ws = ws;
@@ -335,11 +353,13 @@ function pairBwalletX(opts) {
335
353
  const siteKey = import_sdk2.PrivateKey.fromRandom();
336
354
  const channel = newChannel();
337
355
  const expiry = Math.floor(Date.now() / 1e3) + QR_LIFETIME_S;
338
- return new Promise((resolve, reject) => {
356
+ const siteClaim = newClaim();
357
+ const sitePub = siteKey.toPublicKey().toString();
358
+ return walletClaimFor(channel, sitePub).then(claimDigest).then((wc) => new Promise((resolve, reject) => {
339
359
  if (opts.signal?.aborted) return reject(new Error("cancelled"));
340
360
  let ws;
341
361
  try {
342
- ws = socket(relaySocketUrl(relay, channel, "site", expiry));
362
+ ws = socket(relaySocketUrl(relay, channel, "site", expiry, siteClaim, wc));
343
363
  } catch (e) {
344
364
  return reject(e);
345
365
  }
@@ -378,7 +398,7 @@ function pairBwalletX(opts) {
378
398
  if (msg.t === "ready") {
379
399
  settled = true;
380
400
  clearTimeout(timer);
381
- const p = { v: 1, c: channel, r: relay, s: siteKey.toHex(), k: walletKey, ...sealer.counters, pairedAt: Date.now() };
401
+ const p = { v: 1, c: channel, r: relay, s: siteKey.toHex(), k: walletKey, ...sealer.counters, pairedAt: Date.now(), sc: siteClaim };
382
402
  await Promise.resolve(storage.setItem(key, JSON.stringify(p))).catch(() => {
383
403
  });
384
404
  const link = new Link(p, sealer, storage, key, socket, opts.callTimeoutMs ?? 12e4);
@@ -388,8 +408,8 @@ function pairBwalletX(opts) {
388
408
  fail(new Error("Pairing was declined in bWalletX"));
389
409
  }
390
410
  });
391
- opts.onLink(pairUrl({ v: PAIR_VERSION, r: relay, c: channel, k: siteKey.toPublicKey().toString(), o: origin, e: expiry }));
392
- });
411
+ opts.onLink(pairUrl({ v: PAIR_VERSION, r: relay, c: channel, k: sitePub, o: origin, e: expiry }));
412
+ }));
393
413
  }
394
414
  async function restorePairing(opts = {}) {
395
415
  const storage = opts.storage === void 0 ? defaultStorage() : opts.storage ?? memoryStorage();
@@ -431,13 +451,16 @@ var import_sdk3 = require("@bsv/sdk");
431
451
  var RDNS_EXTENSION = "com.bwalletx.extension";
432
452
  var RDNS_MOBILE = "space.bwallet.mobile";
433
453
  var BWALLETX_RDNS = [RDNS_EXTENSION, RDNS_MOBILE];
434
- function discoverBwalletX(ms = 1200) {
454
+ var DEFAULT_DISCOVER_MS = 300;
455
+ function discoverBwalletX(ms = DEFAULT_DISCOVER_MS) {
435
456
  if (typeof window === "undefined") return Promise.resolve(null);
436
457
  return new Promise((resolve) => {
437
458
  let done = false;
459
+ let timer;
438
460
  const finish = (r) => {
439
461
  if (done) return;
440
462
  done = true;
463
+ if (timer !== void 0) clearTimeout(timer);
441
464
  window.removeEventListener("brc100:announceWallet", onAnnounce);
442
465
  resolve(r);
443
466
  };
@@ -448,13 +471,21 @@ function discoverBwalletX(ms = 1200) {
448
471
  finish({ wallet: d.wallet, method: rdns === RDNS_EXTENSION ? "extension" : "in-app", rdns });
449
472
  }
450
473
  };
474
+ const cwiNow = () => {
475
+ const cwi = window.CWI;
476
+ if (cwi) finish({ wallet: cwi, method: "in-app", rdns: null });
477
+ return !!cwi;
478
+ };
451
479
  window.addEventListener("brc100:announceWallet", onAnnounce);
452
480
  window.dispatchEvent(new Event("brc100:requestWallet"));
453
- setTimeout(() => {
454
- const cwi = window.CWI;
455
- if (cwi) return finish({ wallet: cwi, method: "in-app", rdns: null });
456
- if (!isFramed()) return finish(null);
457
- void probeXdm(ms).then((w) => finish(w ? { wallet: w, method: "in-app", rdns: null } : null));
481
+ if (done || cwiNow()) return;
482
+ if (isFramed()) {
483
+ void probeXdm(Math.max(ms, 1200)).then((w) => finish(w ? { wallet: w, method: "in-app", rdns: null } : null));
484
+ return;
485
+ }
486
+ timer = setTimeout(() => {
487
+ timer = void 0;
488
+ if (!cwiNow()) finish(null);
458
489
  }, ms);
459
490
  });
460
491
  }
@@ -510,20 +541,52 @@ async function probeXdm(ms = 1200, allowed = BWALLETX_PARENT_ORIGINS) {
510
541
  return null;
511
542
  }
512
543
  }
544
+ var warmedAt = 0;
545
+ function prewarmBwalletX() {
546
+ if (typeof window === "undefined" || Date.now() - warmedAt < 2e4) return;
547
+ warmedAt = Date.now();
548
+ void discoverBwalletX(0).then((f) => {
549
+ if (f && f.method === "extension") void f.wallet.isAuthenticated({}).catch(() => {
550
+ });
551
+ });
552
+ }
553
+ function bindPrewarm(el2) {
554
+ const on = () => prewarmBwalletX();
555
+ const evs = ["pointerenter", "focus", "touchstart"];
556
+ for (const e of evs) el2.addEventListener(e, on, { passive: true });
557
+ return () => evs.forEach((e) => el2.removeEventListener(e, on));
558
+ }
513
559
 
514
560
  // src/signin.ts
515
561
  var LOGIN_PROTOCOL = [2, "bwallet sign in"];
516
562
  async function connectBwalletX(opts = {}) {
517
- if (opts.wallet) return { wallet: opts.wallet, method: "wallet" };
518
- const found = await discoverBwalletX(opts.discoverMs);
519
- if (found) return { wallet: found.wallet, method: found.method };
563
+ const status = (s) => {
564
+ try {
565
+ opts.onStatus?.(s);
566
+ } catch {
567
+ }
568
+ };
569
+ if (opts.wallet) {
570
+ status("opening");
571
+ return { wallet: opts.wallet, method: "wallet" };
572
+ }
573
+ status("finding");
574
+ const found = await discoverBwalletX(opts.discoverMs ?? DEFAULT_DISCOVER_MS);
575
+ if (found) {
576
+ status("opening");
577
+ return { wallet: found.wallet, method: found.method };
578
+ }
520
579
  if (opts.reusePairing !== false) {
521
580
  const restored = await restorePairing(opts.pairing ?? {});
522
- if (restored) return { wallet: restored, method: "pairing" };
581
+ if (restored) {
582
+ status("opening");
583
+ return { wallet: restored, method: "pairing" };
584
+ }
523
585
  }
524
586
  if (!opts.pairing) {
525
587
  throw Object.assign(new Error("bWalletX was not found in this browser"), { code: "NOT_FOUND" });
526
588
  }
589
+ status("pairing");
527
590
  return { wallet: await pairBwalletX(opts.pairing), method: "pairing" };
528
591
  }
529
592
  async function signInWithBwalletX(opts) {
@@ -531,6 +594,10 @@ async function signInWithBwalletX(opts) {
531
594
  if (method !== "pairing") await wallet.waitForAuthentication({}).catch(() => {
532
595
  });
533
596
  const { publicKey: identityKey } = await atStage("identity", () => wallet.getPublicKey({ identityKey: true }));
597
+ try {
598
+ opts.onStatus?.("signing");
599
+ } catch {
600
+ }
534
601
  const c = await atStage("challenge", () => opts.challenge(identityKey));
535
602
  const { signature } = await atStage(
536
603
  "signature",
@@ -891,6 +958,7 @@ function createClass() {
891
958
  const nf = this.#notFound ? `<p class="nf" part="not-found" role="status">bWalletX wasn't found in this browser. <a href="${esc(getUrl)}" target="_blank" rel="noopener">Get ${brand}</a> or <a href="${esc(webUrl)}" target="_blank" rel="noopener">use the web wallet</a>.</p>` : "";
892
959
  this.#root.innerHTML = `<style>${SIGNIN_CSS}</style><button type="button" part="button" aria-label="${esc(aria)}" title="${esc(subtitle ? `${text} (${subtitle})` : text)}"${disabled ? " disabled" : ""}${loading ? ' aria-busy="true"' : ""}><span class="mark" part="mark">${mark}</span>${label}</button>${loading ? '<span class="sr" role="status">Connecting to bWalletX\u2026</span>' : ""}${nf}`;
893
960
  this.button.addEventListener("click", () => void this.#click());
961
+ bindPrewarm(this.button);
894
962
  }
895
963
  #emit(type, detail, cancelable = false) {
896
964
  return this.dispatchEvent(new CustomEvent(type, { detail, bubbles: true, composed: true, cancelable }));
@@ -917,7 +985,7 @@ function createClass() {
917
985
  this.#notFound = false;
918
986
  this.#setBusy(true);
919
987
  try {
920
- const ms = Number(this.#attr("discover-ms")) || 1200;
988
+ const ms = Number(this.#attr("discover-ms")) || DEFAULT_DISCOVER_MS;
921
989
  const found = await discoverBwalletX(ms);
922
990
  if (!found) {
923
991
  this.#notFound = true;
@@ -1182,7 +1250,7 @@ async function probe(substrate, ms) {
1182
1250
  }
1183
1251
  async function discoverWallets(o = {}) {
1184
1252
  const out = [];
1185
- const ms = o.ms ?? 1200;
1253
+ const ms = o.ms ?? 300;
1186
1254
  if (typeof window !== "undefined") {
1187
1255
  const seen = /* @__PURE__ */ new Map();
1188
1256
  const onAnnounce = (e) => {
@@ -1196,7 +1264,7 @@ async function discoverWallets(o = {}) {
1196
1264
  window.addEventListener("brc100:announceWallet", onAnnounce);
1197
1265
  window.dispatchEvent(new Event("brc100:requestWallet"));
1198
1266
  const local = o.probeLocal ? Promise.all([probe("json-api", ms), probe("Cicada", ms)]) : Promise.resolve([null, null]);
1199
- await new Promise((r) => setTimeout(r, ms));
1267
+ await new Promise((r) => setTimeout(r, seen.size > 0 || window.CWI ? Math.min(ms, 50) : ms));
1200
1268
  window.removeEventListener("brc100:announceWallet", onAnnounce);
1201
1269
  out.push(...seen.values());
1202
1270
  const cwi = window.CWI;
@@ -1293,11 +1361,11 @@ async function resolvePaymailP2P(paymail, sats, f = fetch) {
1293
1361
  return {
1294
1362
  outputs: body.outputs.map((o) => ({ script: o.script, satoshis: o.satoshis })),
1295
1363
  reference: body.reference,
1296
- submit: receive ? async (hex) => {
1364
+ submit: receive ? async (hex2) => {
1297
1365
  await f(fill(receive), {
1298
1366
  method: "POST",
1299
1367
  headers: { "content-type": "application/json" },
1300
- body: JSON.stringify({ hex, reference: body.reference, metadata: {} })
1368
+ body: JSON.stringify({ hex: hex2, reference: body.reference, metadata: {} })
1301
1369
  });
1302
1370
  } : void 0
1303
1371
  };