@bwalletx/connect 0.3.0 → 0.4.1

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/react.d.ts CHANGED
@@ -1,7 +1,18 @@
1
- import * as react from 'react';
2
- import { CSSProperties } from 'react';
1
+ import { ReactNode, ReactElement, CSSProperties } from 'react';
2
+ import { WalletInterface } from '@bsv/sdk';
3
+ import { C as ConnectedWallet, W as WalletOption, P as PayRequest, a as PayResult, B as BuyTokenRequest, b as PayOptions } from './wallets-VOKIKBbO.js';
4
+ export { H as HandCashAdapterOptions, c as PaymentError, d as brc100Option, e as discoverWallets, h as handcashAdapter, p as pairingOption } from './wallets-VOKIKBbO.js';
3
5
  import { SigninSize, SigninTheme, SigninLabel, SigninBrand } from './element.js';
4
6
 
7
+ /**
8
+ * React wrapper for <bwalletx-signin>. React is an optional peer dependency; the button itself is
9
+ * the web component, so markup and behaviour are identical everywhere.
10
+ *
11
+ * import { BwalletxSignin } from '@bwalletx/connect/react';
12
+ * <BwalletxSignin challengeUrl="/auth/bwalletx/challenge" verifyUrl="/auth/bwalletx/verify"
13
+ * onSignedIn={(d) => router.refresh()} />
14
+ */
15
+
5
16
  interface SignedInDetail {
6
17
  identityKey: string;
7
18
  method: string;
@@ -41,30 +52,65 @@ interface BwalletxSigninProps {
41
52
  onNotFound?: () => void;
42
53
  onError?: (error: unknown) => void;
43
54
  }
44
- declare function BwalletxSignin(props: BwalletxSigninProps): react.ReactElement<{
45
- ref: react.RefObject<HTMLElement | null>;
46
- id: string | undefined;
47
- class: string | undefined;
48
- style: CSSProperties | undefined;
49
- size: SigninSize | undefined;
50
- theme: SigninTheme | undefined;
51
- label: SigninLabel | undefined;
52
- brand: SigninBrand | undefined;
53
- subtitle: string | undefined;
54
- 'full-width': string | undefined;
55
- loading: string | undefined;
56
- disabled: string | undefined;
57
- 'challenge-url': string | undefined;
58
- 'verify-url': string | undefined;
59
- 'get-url': string | undefined;
60
- 'web-url': string | undefined;
61
- href: string | undefined;
62
- 'authorize-url': string | undefined;
63
- 'client-id': string | undefined;
64
- 'redirect-uri': string | undefined;
65
- scope: string | undefined;
66
- 'discover-ms': number | undefined;
67
- 'aria-label': string | undefined;
68
- }, string | react.JSXElementConstructor<any>>;
55
+ declare function BwalletxSignin(props: BwalletxSigninProps): ReactElement;
56
+
57
+ type BwalletXStatus = 'idle' | 'restoring' | 'connecting' | 'connected' | 'error';
58
+ interface BwalletXState {
59
+ status: BwalletXStatus;
60
+ identityKey: string | null;
61
+ handle: string | null;
62
+ paymail: string | null;
63
+ /** The BRC-100 wallet. null until connect() (a session restored from the cookie has no wallet yet). */
64
+ wallet: WalletInterface | null;
65
+ /** Which option connected (e.g. 'com.bwalletx.extension', 'bwalletx-pairing', 'handcash'). */
66
+ walletId: string | null;
67
+ /** True while the transitional HandCash adapter is in use. */
68
+ transitional: boolean;
69
+ /** The server session (verifyUrl/sessionUrl) is in place. */
70
+ signedIn: boolean;
71
+ error: Error | null;
72
+ }
73
+ interface BwalletXContextValue extends BwalletXState {
74
+ /** Find wallets, show the picker if there is a choice, connect, and sign in if verifyUrl is set. */
75
+ connect(opts?: {
76
+ walletId?: string;
77
+ }): Promise<ConnectedWallet | null>;
78
+ disconnect(): Promise<void>;
79
+ /** The wallets found on the last connect() (or listWallets()). */
80
+ wallets: WalletOption[];
81
+ listWallets(): Promise<WalletOption[]>;
82
+ pay(req: PayRequest): Promise<PayResult>;
83
+ tip(req: PayRequest): Promise<PayResult>;
84
+ buyToken(req: BuyTokenRequest): Promise<PayResult>;
85
+ }
86
+ interface BwalletXProviderProps {
87
+ children?: ReactNode;
88
+ /** POST { identityKey } → challenge. With verifyUrl, connect() also signs in. */
89
+ challengeUrl?: string;
90
+ /** POST { identityKey, nonce, signature } → { identityKey, handle?, paymail? } and sets the cookie. */
91
+ verifyUrl?: string;
92
+ /** POST to clear the cookie on disconnect(). */
93
+ logoutUrl?: string;
94
+ /** GET → { identityKey, handle?, paymail? } to restore a session on load. */
95
+ sessionUrl?: string;
96
+ /** Offer phone pairing with the built-in QR modal. Default true. */
97
+ pairing?: boolean;
98
+ /** Show the built-in picker when more than one wallet is available. Default true. */
99
+ picker?: boolean;
100
+ /** Extra options, e.g. [handcashAdapter({ login })]. */
101
+ extraWallets?: WalletOption[];
102
+ /** Probe desktop wallets on localhost. Default false. */
103
+ probeLocal?: boolean;
104
+ discoverMs?: number;
105
+ payOptions?: PayOptions;
106
+ onError?: (e: Error) => void;
107
+ }
108
+ /** Holds the connected wallet and session for the tree. Renders nothing itself; SSR-safe. */
109
+ declare function BwalletXProvider(p: BwalletXProviderProps): ReactElement;
110
+ /** The wallet + session state. Must be inside <BwalletXProvider>. */
111
+ declare function useBwalletX(): BwalletXContextValue;
112
+
113
+ /** Alias with the casing used by <BwalletxSignin>. */
114
+ declare const BwalletxProvider: typeof BwalletXProvider;
69
115
 
70
- export { BwalletxSignin, type BwalletxSigninProps, type SignedInDetail, BwalletxSignin as default };
116
+ export { BuyTokenRequest, type BwalletXContextValue, BwalletXProvider, type BwalletXProviderProps, type BwalletXState, type BwalletXStatus, BwalletxProvider, BwalletxSignin, type BwalletxSigninProps, ConnectedWallet, PayOptions, PayRequest, PayResult, type SignedInDetail, WalletOption, BwalletxSignin as default, useBwalletX };
package/dist/react.js CHANGED
@@ -1,9 +1,32 @@
1
1
  import {
2
- defineBwalletxSignin
2
+ PaymentError,
3
+ brc100Option,
4
+ buyToken,
5
+ discoverWallets,
6
+ handcashAdapter,
7
+ pairingModal,
8
+ pairingOption,
9
+ pay,
10
+ pickWallet,
11
+ tip
12
+ } from "./chunk-FUQ46RJV.js";
13
+ import {
14
+ clearPairing,
15
+ defineBwalletxSignin,
16
+ signInWithBwalletX
3
17
  } from "./chunk-NNMUSWPG.js";
4
18
 
5
19
  // src/react.ts
6
- import { createElement, useEffect, useRef } from "react";
20
+ import {
21
+ createContext,
22
+ createElement,
23
+ useCallback,
24
+ useContext,
25
+ useEffect,
26
+ useMemo,
27
+ useRef,
28
+ useState
29
+ } from "react";
7
30
  function BwalletxSignin(props) {
8
31
  const ref = useRef(null);
9
32
  const cb = useRef(props);
@@ -52,8 +75,157 @@ function BwalletxSignin(props) {
52
75
  });
53
76
  }
54
77
  var react_default = BwalletxSignin;
78
+ var empty = {
79
+ status: "idle",
80
+ identityKey: null,
81
+ handle: null,
82
+ paymail: null,
83
+ wallet: null,
84
+ walletId: null,
85
+ transitional: false,
86
+ signedIn: false,
87
+ error: null
88
+ };
89
+ var Ctx = createContext(null);
90
+ async function postJson(url, body) {
91
+ const r = await fetch(url, { method: "POST", credentials: "same-origin", headers: { "content-type": "application/json" }, body: JSON.stringify(body) });
92
+ const j = await r.json().catch(() => ({}));
93
+ if (!r.ok) throw Object.assign(new Error(String(j.error ?? `${url} answered ${r.status}`)), { status: r.status, code: j.code });
94
+ return j;
95
+ }
96
+ function BwalletXProvider(p) {
97
+ const [state, setState] = useState(empty);
98
+ const [wallets, setWallets] = useState([]);
99
+ const props = useRef(p);
100
+ props.current = p;
101
+ const payer = useRef(null);
102
+ const fail = useCallback((e) => {
103
+ const err = e instanceof Error ? e : new Error(String(e));
104
+ setState((s) => ({ ...s, status: "error", error: err }));
105
+ props.current.onError?.(err);
106
+ return err;
107
+ }, []);
108
+ useEffect(() => {
109
+ const url = p.sessionUrl;
110
+ if (!url) return;
111
+ let live = true;
112
+ setState((s) => ({ ...s, status: "restoring" }));
113
+ fetch(url, { credentials: "same-origin" }).then((r) => r.json()).then((j) => {
114
+ if (!live) return;
115
+ setState(
116
+ (s) => j.identityKey ? { ...s, status: "connected", identityKey: j.identityKey, handle: j.handle ?? null, paymail: j.paymail ?? null, signedIn: true } : { ...s, status: s.status === "restoring" ? "idle" : s.status }
117
+ );
118
+ }).catch(() => live && setState((s) => ({ ...s, status: s.status === "restoring" ? "idle" : s.status })));
119
+ return () => {
120
+ live = false;
121
+ };
122
+ }, [p.sessionUrl]);
123
+ const listWallets = useCallback(async () => {
124
+ const c = props.current;
125
+ const list = await discoverWallets({
126
+ ms: c.discoverMs,
127
+ probeLocal: c.probeLocal,
128
+ extra: c.extraWallets,
129
+ pairing: c.pairing === false ? void 0 : () => pairingModal()
130
+ });
131
+ setWallets(list);
132
+ return list;
133
+ }, []);
134
+ const connect = useCallback(
135
+ async (opts = {}) => {
136
+ const c = props.current;
137
+ setState((s) => ({ ...s, status: "connecting", error: null }));
138
+ try {
139
+ const list = await listWallets();
140
+ let choice = opts.walletId ? list.find((w) => w.id === opts.walletId) : void 0;
141
+ if (!choice) {
142
+ const direct = list.filter((w) => w.kind !== "pairing");
143
+ choice = c.picker !== false && list.length > 1 ? await pickWallet(list) : direct[0] ?? list[0] ?? null;
144
+ }
145
+ if (!choice) {
146
+ if (!list.length) throw Object.assign(new Error("No wallet found. Get bWalletX at bwalletx.com."), { code: "NOT_FOUND" });
147
+ setState((s) => ({ ...s, status: s.identityKey ? "connected" : "idle" }));
148
+ return null;
149
+ }
150
+ const cw = await choice.connect();
151
+ let { handle = null, paymail = null } = cw;
152
+ let signedIn = false;
153
+ if (cw.wallet && c.challengeUrl && c.verifyUrl) {
154
+ const r = await signInWithBwalletX({
155
+ wallet: cw.wallet,
156
+ challenge: (identityKey) => postJson(c.challengeUrl, { identityKey })
157
+ });
158
+ const v = await postJson(c.verifyUrl, { identityKey: r.identityKey, nonce: r.nonce, signature: r.signature });
159
+ handle = v.handle ?? handle;
160
+ paymail = v.paymail ?? paymail;
161
+ signedIn = true;
162
+ }
163
+ payer.current = cw.payer;
164
+ setState({
165
+ status: "connected",
166
+ identityKey: cw.identityKey,
167
+ handle,
168
+ paymail,
169
+ wallet: cw.wallet,
170
+ walletId: cw.optionId,
171
+ transitional: !!cw.transitional,
172
+ signedIn,
173
+ error: null
174
+ });
175
+ return cw;
176
+ } catch (e) {
177
+ if (e?.message === "cancelled") {
178
+ setState((s) => ({ ...s, status: s.identityKey ? "connected" : "idle" }));
179
+ return null;
180
+ }
181
+ throw fail(e);
182
+ }
183
+ },
184
+ [fail, listWallets]
185
+ );
186
+ const disconnect = useCallback(async () => {
187
+ const c = props.current;
188
+ if (c.logoutUrl) await postJson(c.logoutUrl, {}).catch(() => {
189
+ });
190
+ if (state.walletId === "bwalletx-pairing") await clearPairing();
191
+ payer.current = null;
192
+ setState(empty);
193
+ }, [state.walletId]);
194
+ const need = () => {
195
+ if (!payer.current) throw new PaymentError("NOT_SUPPORTED", "Connect a wallet first (connect()).");
196
+ return payer.current;
197
+ };
198
+ const value = useMemo(
199
+ () => ({
200
+ ...state,
201
+ wallets,
202
+ connect,
203
+ disconnect,
204
+ listWallets,
205
+ pay: (req) => pay(need(), req, props.current.payOptions),
206
+ tip: (req) => tip(need(), req, props.current.payOptions),
207
+ buyToken: (req) => buyToken(need(), req)
208
+ }),
209
+ [state, wallets, connect, disconnect, listWallets]
210
+ );
211
+ return createElement(Ctx.Provider, { value }, p.children);
212
+ }
213
+ function useBwalletX() {
214
+ const v = useContext(Ctx);
215
+ if (!v) throw new Error("useBwalletX() must be used inside <BwalletXProvider>");
216
+ return v;
217
+ }
218
+ var BwalletxProvider = BwalletXProvider;
55
219
  export {
220
+ BwalletXProvider,
221
+ BwalletxProvider,
56
222
  BwalletxSignin,
57
- react_default as default
223
+ PaymentError,
224
+ brc100Option,
225
+ react_default as default,
226
+ discoverWallets,
227
+ handcashAdapter,
228
+ pairingOption,
229
+ useBwalletX
58
230
  };
59
231
  //# sourceMappingURL=react.js.map
package/dist/react.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/react.ts"],"sourcesContent":["/**\n * React wrapper for <bwalletx-signin>. React is an optional peer dependency; the button itself is\n * the web component, so markup and behaviour are identical everywhere.\n *\n * import { BwalletxSignin } from '@bwalletx/connect/react';\n * <BwalletxSignin challengeUrl=\"/auth/bwalletx/challenge\" verifyUrl=\"/auth/bwalletx/verify\"\n * onSignedIn={(d) => router.refresh()} />\n */\nimport { createElement, useEffect, useRef, type CSSProperties } from 'react';\nimport { defineBwalletxSignin, type SigninBrand, type SigninLabel, type SigninSize, type SigninTheme } from './element.js';\n\nexport interface SignedInDetail {\n identityKey: string;\n method: string;\n nonce: string;\n signature: number[];\n response: unknown;\n}\n\nexport interface BwalletxSigninProps {\n size?: SigninSize;\n theme?: SigninTheme;\n label?: SigninLabel;\n brand?: SigninBrand;\n /** true/undefined = default, false = hidden, string = custom text. */\n subtitle?: boolean | string;\n fullWidth?: boolean;\n loading?: boolean;\n disabled?: boolean;\n challengeUrl?: string;\n verifyUrl?: string;\n getUrl?: string;\n webUrl?: string;\n href?: string;\n authorizeUrl?: string;\n clientId?: string;\n redirectUri?: string;\n scope?: string;\n discoverMs?: number;\n 'aria-label'?: string;\n className?: string;\n style?: CSSProperties;\n id?: string;\n /** Every click. Call e.preventDefault() to skip the built-in flow. */\n onClick?: (e: CustomEvent<{ element: HTMLElement }>) => void;\n onSignedIn?: (d: SignedInDetail) => void;\n onNotFound?: () => void;\n onError?: (error: unknown) => void;\n}\n\nexport function BwalletxSignin(props: BwalletxSigninProps) {\n const ref = useRef<HTMLElement>(null);\n const cb = useRef(props);\n cb.current = props;\n\n useEffect(() => {\n defineBwalletxSignin();\n const el = ref.current;\n if (!el) return;\n const on: [string, EventListener][] = [\n ['bwalletx-signin', (e) => cb.current.onClick?.(e as CustomEvent<{ element: HTMLElement }>)],\n ['bwalletx-signed-in', (e) => cb.current.onSignedIn?.((e as CustomEvent<SignedInDetail>).detail)],\n ['bwalletx-not-found', () => cb.current.onNotFound?.()],\n ['bwalletx-error', (e) => cb.current.onError?.((e as CustomEvent<{ error: unknown }>).detail.error)],\n ];\n for (const [t, f] of on) el.addEventListener(t, f);\n return () => { for (const [t, f] of on) el.removeEventListener(t, f); };\n }, []);\n\n const p = props;\n const flag = (v: boolean | undefined) => (v ? '' : undefined);\n return createElement('bwalletx-signin', {\n ref,\n id: p.id,\n class: p.className,\n style: p.style,\n size: p.size,\n theme: p.theme,\n label: p.label,\n brand: p.brand,\n subtitle: p.subtitle === false ? 'off' : typeof p.subtitle === 'string' ? p.subtitle : undefined,\n 'full-width': flag(p.fullWidth),\n loading: flag(p.loading),\n disabled: flag(p.disabled),\n 'challenge-url': p.challengeUrl,\n 'verify-url': p.verifyUrl,\n 'get-url': p.getUrl,\n 'web-url': p.webUrl,\n href: p.href,\n 'authorize-url': p.authorizeUrl,\n 'client-id': p.clientId,\n 'redirect-uri': p.redirectUri,\n scope: p.scope,\n 'discover-ms': p.discoverMs,\n 'aria-label': p['aria-label'],\n });\n}\n\nexport default BwalletxSignin;\n"],"mappings":";;;;;AAQA,SAAS,eAAe,WAAW,cAAkC;AA0C9D,SAAS,eAAe,OAA4B;AACzD,QAAM,MAAM,OAAoB,IAAI;AACpC,QAAM,KAAK,OAAO,KAAK;AACvB,KAAG,UAAU;AAEb,YAAU,MAAM;AACd,yBAAqB;AACrB,UAAM,KAAK,IAAI;AACf,QAAI,CAAC,GAAI;AACT,UAAM,KAAgC;AAAA,MACpC,CAAC,mBAAmB,CAAC,MAAM,GAAG,QAAQ,UAAU,CAA0C,CAAC;AAAA,MAC3F,CAAC,sBAAsB,CAAC,MAAM,GAAG,QAAQ,aAAc,EAAkC,MAAM,CAAC;AAAA,MAChG,CAAC,sBAAsB,MAAM,GAAG,QAAQ,aAAa,CAAC;AAAA,MACtD,CAAC,kBAAkB,CAAC,MAAM,GAAG,QAAQ,UAAW,EAAsC,OAAO,KAAK,CAAC;AAAA,IACrG;AACA,eAAW,CAAC,GAAG,CAAC,KAAK,GAAI,IAAG,iBAAiB,GAAG,CAAC;AACjD,WAAO,MAAM;AAAE,iBAAW,CAAC,GAAG,CAAC,KAAK,GAAI,IAAG,oBAAoB,GAAG,CAAC;AAAA,IAAG;AAAA,EACxE,GAAG,CAAC,CAAC;AAEL,QAAM,IAAI;AACV,QAAM,OAAO,CAAC,MAA4B,IAAI,KAAK;AACnD,SAAO,cAAc,mBAAmB;AAAA,IACtC;AAAA,IACA,IAAI,EAAE;AAAA,IACN,OAAO,EAAE;AAAA,IACT,OAAO,EAAE;AAAA,IACT,MAAM,EAAE;AAAA,IACR,OAAO,EAAE;AAAA,IACT,OAAO,EAAE;AAAA,IACT,OAAO,EAAE;AAAA,IACT,UAAU,EAAE,aAAa,QAAQ,QAAQ,OAAO,EAAE,aAAa,WAAW,EAAE,WAAW;AAAA,IACvF,cAAc,KAAK,EAAE,SAAS;AAAA,IAC9B,SAAS,KAAK,EAAE,OAAO;AAAA,IACvB,UAAU,KAAK,EAAE,QAAQ;AAAA,IACzB,iBAAiB,EAAE;AAAA,IACnB,cAAc,EAAE;AAAA,IAChB,WAAW,EAAE;AAAA,IACb,WAAW,EAAE;AAAA,IACb,MAAM,EAAE;AAAA,IACR,iBAAiB,EAAE;AAAA,IACnB,aAAa,EAAE;AAAA,IACf,gBAAgB,EAAE;AAAA,IAClB,OAAO,EAAE;AAAA,IACT,eAAe,EAAE;AAAA,IACjB,cAAc,EAAE,YAAY;AAAA,EAC9B,CAAC;AACH;AAEA,IAAO,gBAAQ;","names":[]}
1
+ {"version":3,"sources":["../src/react.ts"],"sourcesContent":["/**\n * React wrapper for <bwalletx-signin>. React is an optional peer dependency; the button itself is\n * the web component, so markup and behaviour are identical everywhere.\n *\n * import { BwalletxSignin } from '@bwalletx/connect/react';\n * <BwalletxSignin challengeUrl=\"/auth/bwalletx/challenge\" verifyUrl=\"/auth/bwalletx/verify\"\n * onSignedIn={(d) => router.refresh()} />\n */\nimport {\n createContext,\n createElement,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n type CSSProperties,\n type ReactElement,\n type ReactNode,\n} from 'react';\nimport type { WalletInterface } from '@bsv/sdk';\nimport { clearPairing } from './pair.js';\nimport { signInWithBwalletX, type Challenge } from './signin.js';\nimport { pairingModal } from './modal.js';\nimport { pickWallet } from './modal.js';\nimport { discoverWallets, type ConnectedWallet, type WalletOption } from './wallets.js';\nimport { PaymentError, buyToken, pay, tip, type BuyTokenRequest, type PayOptions, type PayRequest, type PayResult } from './pay.js';\nimport { defineBwalletxSignin, type SigninBrand, type SigninLabel, type SigninSize, type SigninTheme } from './element.js';\n\nexport interface SignedInDetail {\n identityKey: string;\n method: string;\n nonce: string;\n signature: number[];\n response: unknown;\n}\n\nexport interface BwalletxSigninProps {\n size?: SigninSize;\n theme?: SigninTheme;\n label?: SigninLabel;\n brand?: SigninBrand;\n /** true/undefined = default, false = hidden, string = custom text. */\n subtitle?: boolean | string;\n fullWidth?: boolean;\n loading?: boolean;\n disabled?: boolean;\n challengeUrl?: string;\n verifyUrl?: string;\n getUrl?: string;\n webUrl?: string;\n href?: string;\n authorizeUrl?: string;\n clientId?: string;\n redirectUri?: string;\n scope?: string;\n discoverMs?: number;\n 'aria-label'?: string;\n className?: string;\n style?: CSSProperties;\n id?: string;\n /** Every click. Call e.preventDefault() to skip the built-in flow. */\n onClick?: (e: CustomEvent<{ element: HTMLElement }>) => void;\n onSignedIn?: (d: SignedInDetail) => void;\n onNotFound?: () => void;\n onError?: (error: unknown) => void;\n}\n\nexport function BwalletxSignin(props: BwalletxSigninProps): ReactElement {\n const ref = useRef<HTMLElement>(null);\n const cb = useRef(props);\n cb.current = props;\n\n useEffect(() => {\n defineBwalletxSignin();\n const el = ref.current;\n if (!el) return;\n const on: [string, EventListener][] = [\n ['bwalletx-signin', (e) => cb.current.onClick?.(e as CustomEvent<{ element: HTMLElement }>)],\n ['bwalletx-signed-in', (e) => cb.current.onSignedIn?.((e as CustomEvent<SignedInDetail>).detail)],\n ['bwalletx-not-found', () => cb.current.onNotFound?.()],\n ['bwalletx-error', (e) => cb.current.onError?.((e as CustomEvent<{ error: unknown }>).detail.error)],\n ];\n for (const [t, f] of on) el.addEventListener(t, f);\n return () => { for (const [t, f] of on) el.removeEventListener(t, f); };\n }, []);\n\n const p = props;\n const flag = (v: boolean | undefined) => (v ? '' : undefined);\n return createElement('bwalletx-signin', {\n ref,\n id: p.id,\n class: p.className,\n style: p.style,\n size: p.size,\n theme: p.theme,\n label: p.label,\n brand: p.brand,\n subtitle: p.subtitle === false ? 'off' : typeof p.subtitle === 'string' ? p.subtitle : undefined,\n 'full-width': flag(p.fullWidth),\n loading: flag(p.loading),\n disabled: flag(p.disabled),\n 'challenge-url': p.challengeUrl,\n 'verify-url': p.verifyUrl,\n 'get-url': p.getUrl,\n 'web-url': p.webUrl,\n href: p.href,\n 'authorize-url': p.authorizeUrl,\n 'client-id': p.clientId,\n 'redirect-uri': p.redirectUri,\n scope: p.scope,\n 'discover-ms': p.discoverMs,\n 'aria-label': p['aria-label'],\n });\n}\n\nexport default BwalletxSignin;\n\nexport type BwalletXStatus = 'idle' | 'restoring' | 'connecting' | 'connected' | 'error';\n\nexport interface BwalletXState {\n status: BwalletXStatus;\n identityKey: string | null;\n handle: string | null;\n paymail: string | null;\n /** The BRC-100 wallet. null until connect() (a session restored from the cookie has no wallet yet). */\n wallet: WalletInterface | null;\n /** Which option connected (e.g. 'com.bwalletx.extension', 'bwalletx-pairing', 'handcash'). */\n walletId: string | null;\n /** True while the transitional HandCash adapter is in use. */\n transitional: boolean;\n /** The server session (verifyUrl/sessionUrl) is in place. */\n signedIn: boolean;\n error: Error | null;\n}\n\nexport interface BwalletXContextValue extends BwalletXState {\n /** Find wallets, show the picker if there is a choice, connect, and sign in if verifyUrl is set. */\n connect(opts?: { walletId?: string }): Promise<ConnectedWallet | null>;\n disconnect(): Promise<void>;\n /** The wallets found on the last connect() (or listWallets()). */\n wallets: WalletOption[];\n listWallets(): Promise<WalletOption[]>;\n pay(req: PayRequest): Promise<PayResult>;\n tip(req: PayRequest): Promise<PayResult>;\n buyToken(req: BuyTokenRequest): Promise<PayResult>;\n}\n\nexport interface BwalletXProviderProps {\n children?: ReactNode;\n /** POST { identityKey } → challenge. With verifyUrl, connect() also signs in. */\n challengeUrl?: string;\n /** POST { identityKey, nonce, signature } → { identityKey, handle?, paymail? } and sets the cookie. */\n verifyUrl?: string;\n /** POST to clear the cookie on disconnect(). */\n logoutUrl?: string;\n /** GET → { identityKey, handle?, paymail? } to restore a session on load. */\n sessionUrl?: string;\n /** Offer phone pairing with the built-in QR modal. Default true. */\n pairing?: boolean;\n /** Show the built-in picker when more than one wallet is available. Default true. */\n picker?: boolean;\n /** Extra options, e.g. [handcashAdapter({ login })]. */\n extraWallets?: WalletOption[];\n /** Probe desktop wallets on localhost. Default false. */\n probeLocal?: boolean;\n discoverMs?: number;\n payOptions?: PayOptions;\n onError?: (e: Error) => void;\n}\n\nconst empty: BwalletXState = {\n status: 'idle', identityKey: null, handle: null, paymail: null, wallet: null, walletId: null, transitional: false, signedIn: false, error: null,\n};\n\nconst Ctx = createContext<BwalletXContextValue | null>(null);\n\nasync function postJson(url: string, body: unknown): Promise<Record<string, unknown>> {\n const r = await fetch(url, { method: 'POST', credentials: 'same-origin', headers: { 'content-type': 'application/json' }, body: JSON.stringify(body) });\n const j = (await r.json().catch(() => ({}))) as Record<string, unknown>;\n if (!r.ok) throw Object.assign(new Error(String(j.error ?? `${url} answered ${r.status}`)), { status: r.status, code: j.code });\n return j;\n}\n\n/** Holds the connected wallet and session for the tree. Renders nothing itself; SSR-safe. */\nexport function BwalletXProvider(p: BwalletXProviderProps): ReactElement {\n const [state, setState] = useState<BwalletXState>(empty);\n const [wallets, setWallets] = useState<WalletOption[]>([]);\n const props = useRef(p);\n props.current = p;\n const payer = useRef<ConnectedWallet['payer']>(null);\n\n const fail = useCallback((e: unknown) => {\n const err = e instanceof Error ? e : new Error(String(e));\n setState((s) => ({ ...s, status: 'error', error: err }));\n props.current.onError?.(err);\n return err;\n }, []);\n\n useEffect(() => {\n const url = p.sessionUrl;\n if (!url) return;\n let live = true;\n setState((s) => ({ ...s, status: 'restoring' }));\n fetch(url, { credentials: 'same-origin' })\n .then((r) => r.json())\n .then((j: { identityKey?: string | null; handle?: string; paymail?: string }) => {\n if (!live) return;\n setState((s) =>\n j.identityKey\n ? { ...s, status: 'connected', identityKey: j.identityKey, handle: j.handle ?? null, paymail: j.paymail ?? null, signedIn: true }\n : { ...s, status: s.status === 'restoring' ? 'idle' : s.status },\n );\n })\n .catch(() => live && setState((s) => ({ ...s, status: s.status === 'restoring' ? 'idle' : s.status })));\n return () => {\n live = false;\n };\n }, [p.sessionUrl]);\n\n const listWallets = useCallback(async () => {\n const c = props.current;\n const list = await discoverWallets({\n ms: c.discoverMs,\n probeLocal: c.probeLocal,\n extra: c.extraWallets,\n pairing: c.pairing === false ? undefined : () => pairingModal(),\n });\n setWallets(list);\n return list;\n }, []);\n\n const connect = useCallback(\n async (opts: { walletId?: string } = {}) => {\n const c = props.current;\n setState((s) => ({ ...s, status: 'connecting', error: null }));\n try {\n const list = await listWallets();\n let choice: WalletOption | null | undefined = opts.walletId ? list.find((w) => w.id === opts.walletId) : undefined;\n if (!choice) {\n const direct = list.filter((w) => w.kind !== 'pairing');\n choice = c.picker !== false && list.length > 1 ? await pickWallet(list) : (direct[0] ?? list[0] ?? null);\n }\n if (!choice) {\n if (!list.length) throw Object.assign(new Error('No wallet found. Get bWalletX at bwalletx.com.'), { code: 'NOT_FOUND' });\n setState((s) => ({ ...s, status: s.identityKey ? 'connected' : 'idle' }));\n return null;\n }\n const cw = await choice.connect();\n let { handle = null, paymail = null } = cw as { handle?: string | null; paymail?: string | null };\n let signedIn = false;\n if (cw.wallet && c.challengeUrl && c.verifyUrl) {\n const r = await signInWithBwalletX({\n wallet: cw.wallet,\n challenge: (identityKey) => postJson(c.challengeUrl!, { identityKey }) as unknown as Promise<Challenge>,\n });\n const v = await postJson(c.verifyUrl, { identityKey: r.identityKey, nonce: r.nonce, signature: r.signature });\n handle = (v.handle as string | undefined) ?? handle;\n paymail = (v.paymail as string | undefined) ?? paymail;\n signedIn = true;\n }\n payer.current = cw.payer;\n setState({\n status: 'connected',\n identityKey: cw.identityKey,\n handle,\n paymail,\n wallet: cw.wallet,\n walletId: cw.optionId,\n transitional: !!cw.transitional,\n signedIn,\n error: null,\n });\n return cw;\n } catch (e) {\n if ((e as Error)?.message === 'cancelled') {\n setState((s) => ({ ...s, status: s.identityKey ? 'connected' : 'idle' }));\n return null;\n }\n throw fail(e);\n }\n },\n [fail, listWallets],\n );\n\n const disconnect = useCallback(async () => {\n const c = props.current;\n if (c.logoutUrl) await postJson(c.logoutUrl, {}).catch(() => {});\n if (state.walletId === 'bwalletx-pairing') await clearPairing();\n payer.current = null;\n setState(empty);\n }, [state.walletId]);\n\n const need = () => {\n if (!payer.current) throw new PaymentError('NOT_SUPPORTED', 'Connect a wallet first (connect()).');\n return payer.current;\n };\n\n const value = useMemo<BwalletXContextValue>(\n () => ({\n ...state,\n wallets,\n connect,\n disconnect,\n listWallets,\n pay: (req) => pay(need(), req, props.current.payOptions),\n tip: (req) => tip(need(), req, props.current.payOptions),\n buyToken: (req) => buyToken(need(), req),\n }),\n [state, wallets, connect, disconnect, listWallets],\n );\n\n return createElement(Ctx.Provider, { value }, p.children);\n}\n\n/** The wallet + session state. Must be inside <BwalletXProvider>. */\nexport function useBwalletX(): BwalletXContextValue {\n const v = useContext(Ctx);\n if (!v) throw new Error('useBwalletX() must be used inside <BwalletXProvider>');\n return v;\n}\n\nexport { handcashAdapter, brc100Option, pairingOption, discoverWallets } from './wallets.js';\nexport type { WalletOption, ConnectedWallet, HandCashAdapterOptions } from './wallets.js';\nexport { PaymentError } from './pay.js';\nexport type { PayRequest, PayResult, BuyTokenRequest, PayOptions } from './pay.js';\n/** Alias with the casing used by <BwalletxSignin>. */\nexport const BwalletxProvider = BwalletXProvider;\n"],"mappings":"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","names":[]}