@bwalletx/connect 0.5.4 → 0.7.0

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 (50) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/README.md +97 -9
  3. package/dist/bwalletx-connect.global.js +97 -25
  4. package/dist/bwalletx-connect.global.js.map +1 -1
  5. package/dist/choice-B97AjqF4.d.cts +442 -0
  6. package/dist/choice-B97AjqF4.d.ts +442 -0
  7. package/dist/chunk-6EFFOIHK.js +2441 -0
  8. package/dist/chunk-6EFFOIHK.js.map +1 -0
  9. package/dist/{chunk-QRXFL66W.js → chunk-A2WLGS25.js} +6 -4
  10. package/dist/chunk-A2WLGS25.js.map +1 -0
  11. package/dist/chunk-DIKJFKND.js +239 -0
  12. package/dist/chunk-DIKJFKND.js.map +1 -0
  13. package/dist/chunk-TNRI7ARM.js +206 -0
  14. package/dist/chunk-TNRI7ARM.js.map +1 -0
  15. package/dist/element-BXEoOdXY.d.cts +53 -0
  16. package/dist/element-BXEoOdXY.d.ts +53 -0
  17. package/dist/element.cjs +1576 -60
  18. package/dist/element.cjs.map +1 -1
  19. package/dist/element.d.cts +26 -26
  20. package/dist/element.d.ts +26 -26
  21. package/dist/element.js +35 -2
  22. package/dist/element.js.map +1 -1
  23. package/dist/index.cjs +2046 -454
  24. package/dist/index.cjs.map +1 -1
  25. package/dist/index.d.cts +323 -16
  26. package/dist/index.d.ts +323 -16
  27. package/dist/index.js +186 -12
  28. package/dist/index.js.map +1 -1
  29. package/dist/next.cjs +5 -3
  30. package/dist/next.cjs.map +1 -1
  31. package/dist/next.js +1 -1
  32. package/dist/react.cjs +1601 -210
  33. package/dist/react.cjs.map +1 -1
  34. package/dist/react.d.cts +115 -9
  35. package/dist/react.d.ts +115 -9
  36. package/dist/react.js +262 -46
  37. package/dist/react.js.map +1 -1
  38. package/dist/server.cjs +5 -3
  39. package/dist/server.cjs.map +1 -1
  40. package/dist/server.d.cts +8 -3
  41. package/dist/server.d.ts +8 -3
  42. package/dist/server.js +1 -1
  43. package/package.json +10 -9
  44. package/dist/chunk-EXRQJTXF.js +0 -910
  45. package/dist/chunk-EXRQJTXF.js.map +0 -1
  46. package/dist/chunk-QRXFL66W.js.map +0 -1
  47. package/dist/chunk-RIOHG5JH.js +0 -459
  48. package/dist/chunk-RIOHG5JH.js.map +0 -1
  49. package/dist/wallets-CbzMtxrf.d.cts +0 -250
  50. package/dist/wallets-CbzMtxrf.d.ts +0 -250
package/dist/react.js CHANGED
@@ -1,22 +1,38 @@
1
1
  import {
2
2
  PaymentError,
3
- brc100Option,
4
3
  buyToken,
5
4
  declareWalletTheme,
6
- discoverWallets,
7
- handcashAdapter,
8
- pairingModal,
9
- pairingOption,
10
5
  pay,
11
- pickWallet,
12
6
  tip
13
- } from "./chunk-RIOHG5JH.js";
7
+ } from "./chunk-TNRI7ARM.js";
14
8
  import {
15
- atStage,
9
+ METANET_TYPE,
10
+ accountLabel,
11
+ attachRemembered,
12
+ autoChoice,
13
+ avatarUrlFor,
14
+ brc100Option,
15
+ chooseWallet,
16
16
  clearPairing,
17
17
  defineBwalletxSignin,
18
- signInWithBwalletX
19
- } from "./chunk-EXRQJTXF.js";
18
+ discoverWallets,
19
+ forgetWalletChoice,
20
+ handcashAdapter,
21
+ lookupPaymail,
22
+ metanetOffered,
23
+ needsBwalletX,
24
+ pairingModal,
25
+ pairingOption,
26
+ pickWallet,
27
+ planAutoConnect,
28
+ rememberedWalletChoice,
29
+ runConnect,
30
+ runSilentMetanet,
31
+ signInErrorText,
32
+ silentAttemptUsed,
33
+ walletTypeOf,
34
+ warnIfNoLogout
35
+ } from "./chunk-6EFFOIHK.js";
20
36
  import "./chunk-MWD4BCO5.js";
21
37
 
22
38
  // src/react.ts
@@ -30,19 +46,62 @@ import {
30
46
  useRef,
31
47
  useState
32
48
  } from "react";
49
+ function signinStateFor(v) {
50
+ if (v.status === "restoring") return "restoring";
51
+ if (v.status === "connecting") return "pending";
52
+ if (v.status === "error") return "error";
53
+ if (v.status === "connected" && v.identityKey) return "signed-in";
54
+ return "signed-out";
55
+ }
33
56
  function BwalletxSignin(props) {
34
57
  const ref = useRef(null);
35
58
  const cb = useRef(props);
36
59
  cb.current = props;
60
+ const ctx = useContext(Ctx);
61
+ const ctxRef = useRef(ctx);
62
+ ctxRef.current = ctx;
63
+ const driven = !!ctx && props.stateful !== false;
37
64
  useEffect(() => {
38
65
  defineBwalletxSignin();
39
66
  const el = ref.current;
40
67
  if (!el) return;
41
68
  const on = [
42
- ["bwalletx-signin", (e) => cb.current.onClick?.(e)],
69
+ [
70
+ "bwalletx-signin",
71
+ (e) => {
72
+ cb.current.onClick?.(e);
73
+ const c = ctxRef.current;
74
+ if (!e.defaultPrevented && c && cb.current.stateful !== false) {
75
+ e.preventDefault();
76
+ const mode = e.detail?.mode;
77
+ c.connect(mode === "chooser" ? { chooser: true } : mode === "bwalletx" ? { bwalletx: true } : mode === "metanet" ? { metanet: true } : {}).catch(() => void 0);
78
+ }
79
+ }
80
+ ],
43
81
  ["bwalletx-signed-in", (e) => cb.current.onSignedIn?.(e.detail)],
44
82
  ["bwalletx-not-found", () => cb.current.onNotFound?.()],
45
- ["bwalletx-error", (e) => cb.current.onError?.(e.detail.error)]
83
+ ["bwalletx-error", (e) => cb.current.onError?.(e.detail.error)],
84
+ [
85
+ "bwalletx-disconnect",
86
+ (e) => {
87
+ const c = ctxRef.current;
88
+ if (c && cb.current.stateful !== false) {
89
+ e.preventDefault();
90
+ void c.disconnect().then(() => cb.current.onSignedOut?.());
91
+ }
92
+ }
93
+ ],
94
+ ["bwalletx-signed-out", () => cb.current.onSignedOut?.()],
95
+ [
96
+ "bwalletx-not-you",
97
+ (e) => {
98
+ const c = ctxRef.current;
99
+ if (c && cb.current.stateful !== false) {
100
+ e.preventDefault();
101
+ c.forgetWallet();
102
+ }
103
+ }
104
+ ]
46
105
  ];
47
106
  for (const [t, f] of on) el.addEventListener(t, f);
48
107
  return () => {
@@ -51,6 +110,7 @@ function BwalletxSignin(props) {
51
110
  }, []);
52
111
  const p = props;
53
112
  const flag = (v) => v ? "" : void 0;
113
+ const state = driven ? signinStateFor(ctx) : void 0;
54
114
  return createElement("bwalletx-signin", {
55
115
  ref,
56
116
  id: p.id,
@@ -66,6 +126,16 @@ function BwalletxSignin(props) {
66
126
  disabled: flag(p.disabled),
67
127
  "challenge-url": p.challengeUrl,
68
128
  "verify-url": p.verifyUrl,
129
+ "session-url": driven ? void 0 : p.sessionUrl,
130
+ "logout-url": driven ? void 0 : p.logoutUrl,
131
+ "lookup-url": p.lookupUrl,
132
+ stateless: p.stateful === false ? "" : void 0,
133
+ state,
134
+ "identity-key": driven ? ctx.identityKey ?? void 0 : void 0,
135
+ paymail: driven ? ctx.paymail ?? void 0 : void 0,
136
+ handle: driven ? ctx.handle ?? void 0 : void 0,
137
+ "avatar-url": driven ? avatarUrlFor(ctx.identityKey) ?? void 0 : void 0,
138
+ error: driven && ctx.status === "error" ? signInErrorText(ctx.error) : void 0,
69
139
  "get-url": p.getUrl,
70
140
  "web-url": p.webUrl,
71
141
  href: p.href,
@@ -74,6 +144,9 @@ function BwalletxSignin(props) {
74
144
  "redirect-uri": p.redirectUri,
75
145
  scope: p.scope,
76
146
  "discover-ms": p.discoverMs,
147
+ "other-wallets": p.otherWallets,
148
+ metanet: p.metanet,
149
+ "wallet-type": driven ? ctx.walletType ?? ctx.rememberedWallet?.type ?? void 0 : void 0,
77
150
  "aria-label": p["aria-label"]
78
151
  });
79
152
  }
@@ -85,9 +158,13 @@ var empty = {
85
158
  paymail: null,
86
159
  wallet: null,
87
160
  walletId: null,
161
+ walletType: null,
88
162
  transitional: false,
89
163
  signedIn: false,
90
- error: null
164
+ error: null,
165
+ rememberedWallet: null,
166
+ needsBwalletX: false,
167
+ metanetOffered: false
91
168
  };
92
169
  var Ctx = createContext(null);
93
170
  async function postJson(url, body) {
@@ -98,6 +175,10 @@ async function postJson(url, body) {
98
175
  }
99
176
  function BwalletXProvider(p) {
100
177
  const [state, setState] = useState(empty);
178
+ const stateRef = useRef(state);
179
+ stateRef.current = state;
180
+ const silentRun = useRef(0);
181
+ const sessionChecked = useRef(!p.sessionUrl);
101
182
  const [wallets, setWallets] = useState([]);
102
183
  const props = useRef(p);
103
184
  props.current = p;
@@ -108,6 +189,9 @@ function BwalletXProvider(p) {
108
189
  props.current.onError?.(err);
109
190
  return err;
110
191
  }, []);
192
+ useEffect(() => {
193
+ warnIfNoLogout({ verifyUrl: p.verifyUrl, logoutUrl: p.logoutUrl, where: "<BwalletXProvider>" });
194
+ }, [p.verifyUrl, p.logoutUrl]);
111
195
  const themeOn = !!p.theme && (state.status === "connecting" || state.status === "connected");
112
196
  useEffect(() => {
113
197
  if (!themeOn) return;
@@ -121,10 +205,15 @@ function BwalletXProvider(p) {
121
205
  setState((s) => ({ ...s, status: "restoring" }));
122
206
  fetch(url, { credentials: "same-origin" }).then((r) => r.json()).then((j) => {
123
207
  if (!live) return;
208
+ sessionChecked.current = true;
124
209
  setState(
125
210
  (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 }
126
211
  );
127
- }).catch(() => live && setState((s) => ({ ...s, status: s.status === "restoring" ? "idle" : s.status })));
212
+ }).catch(() => {
213
+ if (!live) return;
214
+ sessionChecked.current = true;
215
+ setState((s) => ({ ...s, status: s.status === "restoring" ? "idle" : s.status }));
216
+ });
128
217
  return () => {
129
218
  live = false;
130
219
  };
@@ -145,46 +234,50 @@ function BwalletXProvider(p) {
145
234
  const c = props.current;
146
235
  setState((s) => ({ ...s, status: "connecting", error: null }));
147
236
  try {
148
- const list = await listWallets();
149
- let choice = opts.walletId ? list.find((w) => w.id === opts.walletId) : void 0;
150
- if (!choice) {
151
- const direct = list.filter((w) => w.kind !== "pairing");
152
- choice = c.picker !== false && list.length > 1 ? await pickWallet(list) : direct[0] ?? list[0] ?? null;
153
- }
154
- if (!choice) {
155
- if (!list.length) throw Object.assign(new Error("No wallet found. Get bWalletX at bwalletx.com."), { code: "NOT_FOUND" });
156
- setState((s) => ({ ...s, status: s.identityKey ? "connected" : "idle" }));
237
+ const list = opts.metanet ? [] : await listWallets();
238
+ const fast = c.fastPath !== false;
239
+ const r = await runConnect({
240
+ list,
241
+ walletId: opts.walletId,
242
+ chooser: opts.chooser,
243
+ picker: c.picker,
244
+ fastPath: fast,
245
+ pick: fast ? (l) => chooseWallet(l) : pickWallet,
246
+ onAttempt: c.onAttempt,
247
+ remembered: fast && c.rememberChoice !== false ? rememberedWalletChoice() : null,
248
+ remember: fast && c.rememberChoice !== false,
249
+ preferBwalletX: opts.bwalletx,
250
+ metanet: opts.metanet,
251
+ challengeUrl: c.challengeUrl,
252
+ verifyUrl: c.verifyUrl,
253
+ challengeNeedsIdentityKey: c.challengeNeedsIdentityKey,
254
+ post: postJson
255
+ });
256
+ if (!r) {
257
+ setState((s) => ({ ...s, status: s.identityKey && s.signedIn ? "connected" : "idle" }));
157
258
  return null;
158
259
  }
159
- const cw = await choice.connect();
160
- let { handle = null, paymail = null } = cw;
161
- let signedIn = false;
162
- if (cw.wallet && c.challengeUrl && c.verifyUrl) {
163
- const r = await signInWithBwalletX({
164
- wallet: cw.wallet,
165
- challenge: (identityKey) => postJson(c.challengeUrl, { identityKey })
166
- });
167
- const v = await atStage("verify", () => postJson(c.verifyUrl, { identityKey: r.identityKey, nonce: r.nonce, signature: r.signature }));
168
- handle = v.handle ?? handle;
169
- paymail = v.paymail ?? paymail;
170
- signedIn = true;
171
- }
260
+ const cw = r.connected;
172
261
  payer.current = cw.payer;
173
262
  setState({
174
263
  status: "connected",
175
- identityKey: cw.identityKey,
176
- handle,
177
- paymail,
264
+ identityKey: r.identityKey,
265
+ handle: r.handle,
266
+ paymail: r.paymail,
178
267
  wallet: cw.wallet,
179
268
  walletId: cw.optionId,
269
+ walletType: r.walletType,
180
270
  transitional: !!cw.transitional,
181
- signedIn,
182
- error: null
271
+ signedIn: r.signedIn,
272
+ error: null,
273
+ rememberedWallet: c.rememberChoice !== false ? rememberedWalletChoice() : null,
274
+ needsBwalletX: needsBwalletX(r.walletType),
275
+ metanetOffered: metanetOffered()
183
276
  });
184
- return cw;
277
+ return { ...cw, identityKey: r.identityKey, handle: r.handle ?? void 0, paymail: r.paymail ?? void 0 };
185
278
  } catch (e) {
186
279
  if (e?.message === "cancelled") {
187
- setState((s) => ({ ...s, status: s.identityKey ? "connected" : "idle" }));
280
+ setState((s) => ({ ...s, status: s.identityKey && s.signedIn ? "connected" : "idle" }));
188
281
  return null;
189
282
  }
190
283
  throw fail(e);
@@ -197,9 +290,107 @@ function BwalletXProvider(p) {
197
290
  if (c.logoutUrl) await postJson(c.logoutUrl, {}).catch(() => {
198
291
  });
199
292
  if (state.walletId === "bwalletx-pairing") await clearPairing();
293
+ if (c.rememberChoice !== false) forgetWalletChoice();
294
+ silentRun.current++;
200
295
  payer.current = null;
201
- setState(empty);
296
+ setState({ ...empty, metanetOffered: metanetOffered() });
202
297
  }, [state.walletId]);
298
+ const switchToBwalletX = useCallback(() => connect({ bwalletx: true }), [connect]);
299
+ const connectMetanet = useCallback(() => connect({ metanet: true }), [connect]);
300
+ const forgetWallet = useCallback(() => {
301
+ if (props.current.rememberChoice !== false) forgetWalletChoice();
302
+ silentRun.current++;
303
+ setState((s) => ({
304
+ ...s,
305
+ rememberedWallet: null,
306
+ needsBwalletX: s.walletType ? needsBwalletX(s.walletType) : false,
307
+ // A silent Metanet sign-in on screen goes back to the normal choice.
308
+ status: s.status === "connecting" && !s.wallet ? s.signedIn && s.identityKey ? "connected" : "idle" : s.status
309
+ }));
310
+ }, []);
311
+ useEffect(() => {
312
+ setState((s) => s.metanetOffered === metanetOffered() ? s : { ...s, metanetOffered: metanetOffered() });
313
+ if (p.rememberChoice === false) return;
314
+ const r = rememberedWalletChoice();
315
+ if (r) setState((s) => s.rememberedWallet ? s : { ...s, rememberedWallet: r, needsBwalletX: s.walletType ? s.needsBwalletX : needsBwalletX(r.type) && r.type !== METANET_TYPE });
316
+ }, [p.rememberChoice]);
317
+ const sessionOnly = state.status === "connected" && state.signedIn && !state.wallet;
318
+ useEffect(() => {
319
+ const c = props.current;
320
+ if (!sessionOnly || c.autoConnect === false || c.rememberChoice === false || c.fastPath === false) return;
321
+ const plan = planAutoConnect({ remembered: rememberedWalletChoice(), signedIn: true });
322
+ if (plan.action !== "attach" || !plan.remembered) return;
323
+ let live = true;
324
+ void attachRemembered(plan.remembered, { ms: c.discoverMs }).then((opt) => {
325
+ if (!live || !opt?.wallet) return;
326
+ payer.current = opt.wallet;
327
+ setState(
328
+ (s) => s.status === "connected" && !s.wallet ? { ...s, wallet: opt.wallet, walletId: opt.id, walletType: walletTypeOf(opt), needsBwalletX: needsBwalletX(walletTypeOf(opt)) } : s
329
+ );
330
+ });
331
+ return () => {
332
+ live = false;
333
+ };
334
+ }, [sessionOnly]);
335
+ const silentReady = state.status === "idle" || sessionOnly;
336
+ useEffect(() => {
337
+ const c = props.current;
338
+ if (!silentReady || !sessionChecked.current || c.autoConnect === false || c.rememberChoice === false || c.fastPath === false || silentAttemptUsed()) return;
339
+ const plan = planAutoConnect({ remembered: rememberedWalletChoice(), signedIn: sessionOnly });
340
+ if (plan.action !== "silent-metanet") return;
341
+ const run = ++silentRun.current;
342
+ const live = () => run === silentRun.current;
343
+ let shown = false;
344
+ const session = stateRef.current;
345
+ void runSilentMetanet({
346
+ sessionKey: sessionOnly ? session.identityKey : null,
347
+ challengeUrl: c.challengeUrl,
348
+ verifyUrl: c.verifyUrl,
349
+ post: postJson,
350
+ onAttempt: c.onAttempt,
351
+ isAbandoned: () => !live(),
352
+ logout: () => props.current.logoutUrl ? postJson(props.current.logoutUrl, {}) : Promise.resolve(),
353
+ onSignIn: () => {
354
+ if (!live()) return;
355
+ shown = true;
356
+ setState((s) => s.status === "idle" ? { ...s, status: "connecting", error: null } : s);
357
+ }
358
+ }).then((r) => {
359
+ if (!live()) return;
360
+ if (!r) {
361
+ if (shown) setState((s) => s.status === "connecting" && !s.wallet ? { ...s, status: s.signedIn && s.identityKey ? "connected" : "idle" } : s);
362
+ return;
363
+ }
364
+ const cw = r.connected;
365
+ payer.current = cw.payer;
366
+ setState((s) => ({
367
+ ...s,
368
+ status: "connected",
369
+ identityKey: r.identityKey,
370
+ handle: r.handle ?? s.handle,
371
+ paymail: r.paymail ?? s.paymail,
372
+ wallet: cw.wallet,
373
+ walletId: cw.optionId,
374
+ walletType: r.walletType,
375
+ transitional: false,
376
+ signedIn: r.signedIn || s.signedIn,
377
+ error: null,
378
+ rememberedWallet: rememberedWalletChoice(),
379
+ needsBwalletX: true
380
+ }));
381
+ });
382
+ }, [silentReady, sessionOnly]);
383
+ useEffect(() => {
384
+ const key = state.identityKey;
385
+ if (!key || state.paymail || p.lookupUrl === "off") return;
386
+ let live = true;
387
+ void lookupPaymail(key, p.lookupUrl).then((pm) => {
388
+ if (live && pm) setState((s) => s.identityKey === key && !s.paymail ? { ...s, paymail: pm } : s);
389
+ });
390
+ return () => {
391
+ live = false;
392
+ };
393
+ }, [state.identityKey, state.paymail, p.lookupUrl]);
203
394
  const need = () => {
204
395
  if (!payer.current) throw new PaymentError("NOT_SUPPORTED", "Connect a wallet first (connect()).");
205
396
  return payer.current;
@@ -210,12 +401,15 @@ function BwalletXProvider(p) {
210
401
  wallets,
211
402
  connect,
212
403
  disconnect,
404
+ switchToBwalletX,
405
+ connectMetanet,
406
+ forgetWallet,
213
407
  listWallets,
214
408
  pay: (req) => pay(need(), req, props.current.payOptions),
215
409
  tip: (req) => tip(need(), req, props.current.payOptions),
216
410
  buyToken: (req) => buyToken(need(), req)
217
411
  }),
218
- [state, wallets, connect, disconnect, listWallets]
412
+ [state, wallets, connect, disconnect, switchToBwalletX, connectMetanet, forgetWallet, listWallets]
219
413
  );
220
414
  return createElement(Ctx.Provider, { value }, p.children);
221
415
  }
@@ -224,17 +418,39 @@ function useBwalletX() {
224
418
  if (!v) throw new Error("useBwalletX() must be used inside <BwalletXProvider>");
225
419
  return v;
226
420
  }
421
+ function accountFor(v) {
422
+ const status = signinStateFor(v);
423
+ const signedIn = status === "signed-in";
424
+ return {
425
+ status,
426
+ identityKey: signedIn ? v.identityKey : null,
427
+ paymail: signedIn ? v.paymail : null,
428
+ handle: signedIn ? v.handle : null,
429
+ label: signedIn ? accountLabel(v) : null,
430
+ avatarUrl: signedIn ? avatarUrlFor(v.identityKey) : null,
431
+ error: status === "error" ? signInErrorText(v.error) : null,
432
+ login: () => v.connect().then(() => void 0, () => void 0),
433
+ logout: () => v.disconnect()
434
+ };
435
+ }
436
+ function useBwalletXAccount() {
437
+ return accountFor(useBwalletX());
438
+ }
227
439
  var BwalletxProvider = BwalletXProvider;
228
440
  export {
229
441
  BwalletXProvider,
230
442
  BwalletxProvider,
231
443
  BwalletxSignin,
232
444
  PaymentError,
445
+ accountFor,
446
+ autoChoice,
233
447
  brc100Option,
234
448
  react_default as default,
235
449
  discoverWallets,
236
450
  handcashAdapter,
237
451
  pairingOption,
238
- useBwalletX
452
+ signinStateFor,
453
+ useBwalletX,
454
+ useBwalletXAccount
239
455
  };
240
456
  //# 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 {\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 { declareWalletTheme } from './theme.js';\nimport { atStage, 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 /**\n * A theme the wallet already bundles (e.g. 'npgx') for bWalletX to wear while this site is connected and is the\n * active tab. Declared only while connecting or connected; withdrawn on disconnect. See declareWalletTheme.\n */\n theme?: string;\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 const themeOn = !!p.theme && (state.status === 'connecting' || state.status === 'connected');\n useEffect(() => {\n if (!themeOn) return;\n declareWalletTheme(p.theme);\n return () => declareWalletTheme(null);\n }, [themeOn, p.theme]);\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 atStage('verify', () => 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":[]}
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 { declareWalletTheme } from './theme.js';\nimport { runConnect, runSilentMetanet } from './connect-flow.js';\nimport { metanetOffered, silentAttemptUsed, warnIfNoLogout, METANET_TYPE } from './metanet.js';\nimport { chooseWallet, pairingModal, pickWallet } from './modal.js';\nimport {\n attachRemembered, forgetWalletChoice, needsBwalletX, planAutoConnect, rememberedWalletChoice, walletTypeOf,\n type AttemptHook, type RememberedWallet,\n} from './choice.js';\nimport { discoverWallets, type ConnectedWallet, type WalletOption } from './wallets.js';\nimport { accountLabel, avatarUrlFor, lookupPaymail, signInErrorText } from './account.js';\nimport { PaymentError, buyToken, pay, tip, type BuyTokenRequest, type PayOptions, type PayRequest, type PayResult } from './pay.js';\nimport { defineBwalletxSignin, type SigninBrand, type SigninLabel, type SigninMode, type SigninSize, type SigninState, 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 /** \"Other wallets\" link under the button: 'auto' (default: lg/md), 'on' or 'off'. */\n otherWallets?: 'auto' | 'on' | 'off';\n /**\n * (0.7.0) \"Use Metanet Desktop or another BRC-100 wallet\" under the button: 'auto' (default: lg/md on a computer,\n * outside any frame), 'on' or 'off'. Connects to localhost:3321 on its own tap only.\n */\n metanet?: 'auto' | 'on' | 'off';\n /** Without a provider: GET → { identityKey, handle?, paymail? } restores the signed-in badge on load. */\n sessionUrl?: string;\n /** Without a provider: POST on Disconnect. */\n logoutUrl?: string;\n /** Paymail lookup for the badge; 'off' to skip. */\n lookupUrl?: string;\n /**\n * Show the signed-in badge and the pending / error states (default true). Inside <BwalletXProvider> the button\n * follows the provider: click → connect() (one wallet sheet), badge when signed in, Disconnect → disconnect()\n * (logoutUrl). Set false for the pre-0.5.5 plain button.\n */\n stateful?: boolean;\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 /** After Disconnect in the badge menu. */\n onSignedOut?: () => void;\n}\n\n/** The element state for the provider's state. */\nexport function signinStateFor(v: Pick<BwalletXState, 'status' | 'identityKey'>): SigninState {\n if (v.status === 'restoring') return 'restoring';\n if (v.status === 'connecting') return 'pending';\n if (v.status === 'error') return 'error';\n if (v.status === 'connected' && v.identityKey) return 'signed-in';\n return 'signed-out';\n}\n\nexport function BwalletxSignin(props: BwalletxSigninProps): ReactElement {\n const ref = useRef<HTMLElement>(null);\n const cb = useRef(props);\n cb.current = props;\n const ctx = useContext(Ctx);\n const ctxRef = useRef(ctx);\n ctxRef.current = ctx;\n const driven = !!ctx && props.stateful !== false;\n\n useEffect(() => {\n defineBwalletxSignin();\n const el = ref.current;\n if (!el) return;\n const on: [string, EventListener][] = [\n [\n 'bwalletx-signin',\n (e) => {\n cb.current.onClick?.(e as CustomEvent<{ element: HTMLElement }>);\n // Inside a provider the click is the provider's connect(): one wallet sheet, and this button follows it.\n // \"Other wallets\" arrives here too, as mode 'chooser'.\n const c = ctxRef.current;\n if (!e.defaultPrevented && c && cb.current.stateful !== false) {\n e.preventDefault();\n const mode = (e as CustomEvent<{ mode?: SigninMode }>).detail?.mode;\n c.connect(mode === 'chooser' ? { chooser: true } : mode === 'bwalletx' ? { bwalletx: true } : mode === 'metanet' ? { metanet: true } : {}).catch(() => undefined);\n }\n },\n ],\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 'bwalletx-disconnect',\n (e) => {\n const c = ctxRef.current;\n if (c && cb.current.stateful !== false) {\n e.preventDefault();\n void c.disconnect().then(() => cb.current.onSignedOut?.());\n }\n },\n ],\n ['bwalletx-signed-out', () => cb.current.onSignedOut?.()],\n [\n 'bwalletx-not-you',\n (e) => {\n const c = ctxRef.current;\n if (c && cb.current.stateful !== false) {\n e.preventDefault();\n c.forgetWallet();\n }\n },\n ],\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 const state = driven ? signinStateFor(ctx) : 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 'session-url': driven ? undefined : p.sessionUrl,\n 'logout-url': driven ? undefined : p.logoutUrl,\n 'lookup-url': p.lookupUrl,\n stateless: p.stateful === false ? '' : undefined,\n state,\n 'identity-key': driven ? (ctx.identityKey ?? undefined) : undefined,\n paymail: driven ? (ctx.paymail ?? undefined) : undefined,\n handle: driven ? (ctx.handle ?? undefined) : undefined,\n 'avatar-url': driven ? (avatarUrlFor(ctx.identityKey) ?? undefined) : undefined,\n error: driven && ctx.status === 'error' ? signInErrorText(ctx.error) : undefined,\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 'other-wallets': p.otherWallets,\n metanet: p.metanet,\n 'wallet-type': driven ? (ctx.walletType ?? ctx.rememberedWallet?.type ?? undefined) : undefined,\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 /** 'bwalletx' for any bWalletX, else the wallet's rdns / option id (0.5.6). null until connect(). */\n walletType: string | null;\n /** (0.6.0) The wallet type picked on this origin before (no keys), or null. Read after mount. */\n rememberedWallet: RememberedWallet | null;\n /** (0.6.0) The connected (or remembered) wallet isn't bWalletX: show \"Some features need bWalletX · Switch\". */\n needsBwalletX: boolean;\n /** (0.7.0) \"Use Metanet Desktop or another BRC-100 wallet\" is offered here (a computer, outside any frame). */\n metanetOffered: boolean;\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 /**\n * Find wallets, connect, and sign in if verifyUrl is set. bWalletX in this browser is used straight away (0.5.6);\n * without it the chooser opens with \"Get bWalletX\" first. `chooser: true` is the \"Other wallets\" link: the chooser\n * even when bWalletX is here.\n */\n connect(opts?: { walletId?: string; chooser?: boolean; bwalletx?: boolean; metanet?: boolean }): Promise<ConnectedWallet | null>;\n /** (0.6.0) \"Switch\": sign in with bWalletX (straight to it when here, else the chooser with Get bWalletX first). */\n switchToBwalletX(): Promise<ConnectedWallet | null>;\n /**\n * (0.7.0) \"Use Metanet Desktop or another BRC-100 wallet\": connect to localhost:3321 (pinned json-api) and sign in.\n * Call it from the user's tap only.\n */\n connectMetanet(): Promise<ConnectedWallet | null>;\n /**\n * (0.7.0) \"Not you? Use another wallet\": forget the wallet remembered on this origin and drop a silent Metanet\n * sign-in in flight (a session its verify already set is ended through logoutUrl).\n */\n forgetWallet(): void;\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 {} (or { identityKey }) → challenge. With verifyUrl, connect() also signs in, in ONE wallet sheet. */\n challengeUrl?: string;\n /** POST { identityKey, nonce, signature } → { identityKey, handle?, paymail? } and sets the cookie. */\n verifyUrl?: string;\n /**\n * POST to clear the cookie on disconnect(). REQUIRED whenever verifyUrl is set (0.7.0): \"Not you? Use another wallet\"\n * and disconnect() end a session the verify route created through it. Missing → a console.warn in development.\n */\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 /**\n * Show the built-in picker when there is a real choice. Default true. One wallet found in this browser (and no\n * app-supplied option such as HandCash) is used straight away, with no picker; phone pairing is then not offered.\n */\n picker?: boolean;\n /**\n * Your challenge route is older than 0.5.5 and needs the identity key first (two wallet sheets). Normally not\n * needed: such a server is detected and retried. Upgrade the server to @bwalletx/connect 0.5.5 for one sheet.\n */\n challengeNeedsIdentityKey?: boolean;\n /** Paymail lookup by identity key for the signed-in badge (best effort). Default pay.bwallet.space; 'off' to skip. */\n lookupUrl?: string;\n /** Extra options, e.g. [handcashAdapter({ login })]. */\n extraWallets?: WalletOption[];\n /**\n * Probe desktop wallets on localhost while listing. Default false, and best left off (Chrome's Local Network Access\n * prompt): the chooser offers \"Desktop wallet\", which probes only when tapped.\n */\n probeLocal?: boolean;\n /**\n * bWalletX first (0.5.6, default true): with bWalletX in this browser connect() goes straight to its sheet, no\n * chooser; without it the chooser opens, \"Get bWalletX\" first, and another wallet is connected only when picked.\n * false: the 0.5.5 picker rule (one detected wallet of any brand was used without asking).\n */\n fastPath?: boolean;\n /** Called once per connect attempt with { walletType, substrate, outcome, ms }. No keys, addresses or identity data. */\n onAttempt?: AttemptHook;\n /**\n * (0.6.0) Remember the wallet TYPE the user explicitly picked on this origin (localStorage `bwx:wallet`; never a key)\n * and go straight to it on the next tap, with no chooser. Disconnect forgets it. Default true.\n */\n rememberChoice?: boolean;\n /**\n * (0.6.0) On load, for a returning visitor with a server session, pick up the remembered wallet by discovery alone\n * (no wallet call, so no prompt; never a localhost probe) so pay/tip work without another tap. Without a session\n * nothing fires on load: a connect would ask the wallet, so it waits for the tap. Default true.\n */\n autoConnect?: boolean;\n /**\n * A theme the wallet already bundles (e.g. 'npgx') for bWalletX to wear while this site is connected and is the\n * active tab. Declared only while connecting or connected; withdrawn on disconnect. See declareWalletTheme.\n */\n theme?: string;\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, walletType: null, transitional: false, signedIn: false, error: null,\n rememberedWallet: null, needsBwalletX: false, metanetOffered: false,\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 stateRef = useRef(state);\n stateRef.current = state;\n /** (0.7.0) Bumped by forgetWallet (\"Not you?\") and disconnect: a silent Metanet sign-in started before lands nobody. */\n const silentRun = useRef(0);\n /** The session check (sessionUrl) has answered, so the silent attempt knows whether a session is there. */\n const sessionChecked = useRef(!p.sessionUrl);\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 warnIfNoLogout({ verifyUrl: p.verifyUrl, logoutUrl: p.logoutUrl, where: '<BwalletXProvider>' });\n }, [p.verifyUrl, p.logoutUrl]);\n\n const themeOn = !!p.theme && (state.status === 'connecting' || state.status === 'connected');\n useEffect(() => {\n if (!themeOn) return;\n declareWalletTheme(p.theme);\n return () => declareWalletTheme(null);\n }, [themeOn, p.theme]);\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 sessionChecked.current = true;\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(() => {\n if (!live) return;\n sessionChecked.current = true;\n setState((s) => ({ ...s, status: s.status === 'restoring' ? 'idle' : s.status }));\n });\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; chooser?: boolean; bwalletx?: boolean; metanet?: boolean } = {}) => {\n const c = props.current;\n setState((s) => ({ ...s, status: 'connecting', error: null }));\n try {\n const list = opts.metanet ? [] : await listWallets();\n const fast = c.fastPath !== false;\n const r = await runConnect({\n list,\n walletId: opts.walletId,\n chooser: opts.chooser,\n picker: c.picker,\n fastPath: fast,\n pick: fast ? (l) => chooseWallet(l) : pickWallet,\n onAttempt: c.onAttempt,\n remembered: fast && c.rememberChoice !== false ? rememberedWalletChoice() : null,\n remember: fast && c.rememberChoice !== false,\n preferBwalletX: opts.bwalletx,\n metanet: opts.metanet,\n challengeUrl: c.challengeUrl,\n verifyUrl: c.verifyUrl,\n challengeNeedsIdentityKey: c.challengeNeedsIdentityKey,\n post: postJson,\n });\n if (!r) {\n setState((s) => ({ ...s, status: s.identityKey && s.signedIn ? 'connected' : 'idle' }));\n return null;\n }\n const cw = r.connected;\n payer.current = cw.payer;\n setState({\n status: 'connected',\n identityKey: r.identityKey,\n handle: r.handle,\n paymail: r.paymail,\n wallet: cw.wallet,\n walletId: cw.optionId,\n walletType: r.walletType,\n transitional: !!cw.transitional,\n signedIn: r.signedIn,\n error: null,\n rememberedWallet: c.rememberChoice !== false ? rememberedWalletChoice() : null,\n needsBwalletX: needsBwalletX(r.walletType),\n metanetOffered: metanetOffered(),\n });\n return { ...cw, identityKey: r.identityKey, handle: r.handle ?? undefined, paymail: r.paymail ?? undefined };\n } catch (e) {\n if ((e as Error)?.message === 'cancelled') {\n setState((s) => ({ ...s, status: s.identityKey && s.signedIn ? '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 // A Disconnect means \"don't connect me automatically\": the remembered wallet goes too.\n if (c.rememberChoice !== false) forgetWalletChoice();\n silentRun.current++;\n payer.current = null;\n setState({ ...empty, metanetOffered: metanetOffered() });\n }, [state.walletId]);\n\n const switchToBwalletX = useCallback(() => connect({ bwalletx: true }), [connect]);\n const connectMetanet = useCallback(() => connect({ metanet: true }), [connect]);\n\n const forgetWallet = useCallback(() => {\n if (props.current.rememberChoice !== false) forgetWalletChoice();\n silentRun.current++;\n setState((s) => ({\n ...s,\n rememberedWallet: null,\n needsBwalletX: s.walletType ? needsBwalletX(s.walletType) : false,\n // A silent Metanet sign-in on screen goes back to the normal choice.\n status: s.status === 'connecting' && !s.wallet ? (s.signedIn && s.identityKey ? 'connected' : 'idle') : s.status,\n }));\n }, []);\n\n // 0.6.0: the remembered wallet, read after mount (SSR-safe), for the \"Switch\" note.\n useEffect(() => {\n setState((s) => (s.metanetOffered === metanetOffered() ? s : { ...s, metanetOffered: metanetOffered() }));\n if (p.rememberChoice === false) return;\n const r = rememberedWalletChoice();\n // A remembered Metanet isn't where the button's tap goes, so it never shows the Switch note (0.7.0).\n if (r) setState((s) => (s.rememberedWallet ? s : { ...s, rememberedWallet: r, needsBwalletX: s.walletType ? s.needsBwalletX : needsBwalletX(r.type) && r.type !== METANET_TYPE }));\n }, [p.rememberChoice]);\n\n // 0.6.0 auto-connect: signed in from the session cookie, wallet not attached yet → pick up the remembered wallet by\n // discovery alone (no wallet call, no prompt, no localhost). Anything that would ask the wallet waits for a tap.\n const sessionOnly = state.status === 'connected' && state.signedIn && !state.wallet;\n useEffect(() => {\n const c = props.current;\n if (!sessionOnly || c.autoConnect === false || c.rememberChoice === false || c.fastPath === false) return;\n const plan = planAutoConnect({ remembered: rememberedWalletChoice(), signedIn: true });\n if (plan.action !== 'attach' || !plan.remembered) return;\n let live = true;\n void attachRemembered(plan.remembered, { ms: c.discoverMs }).then((opt) => {\n if (!live || !opt?.wallet) return;\n payer.current = opt.wallet;\n setState((s) =>\n s.status === 'connected' && !s.wallet\n ? { ...s, wallet: opt.wallet!, walletId: opt.id, walletType: walletTypeOf(opt), needsBwalletX: needsBwalletX(walletTypeOf(opt)) }\n : s,\n );\n });\n return () => {\n live = false;\n };\n }, [sessionOnly]);\n\n // 0.7.0 rule 4: Metanet Desktop remembered here → ONE silent attempt per page load (2 s, a known identity key only).\n // With a session: attach the wallet if it is the session's key. Without: sign in with the signature alone. Any stop\n // or failure is silent (no error, no onError, no retry); the normal choice stays and waits for a tap.\n const silentReady = state.status === 'idle' || sessionOnly;\n useEffect(() => {\n const c = props.current;\n if (!silentReady || !sessionChecked.current || c.autoConnect === false || c.rememberChoice === false || c.fastPath === false || silentAttemptUsed()) return;\n const plan = planAutoConnect({ remembered: rememberedWalletChoice(), signedIn: sessionOnly });\n if (plan.action !== 'silent-metanet') return;\n const run = ++silentRun.current;\n const live = () => run === silentRun.current;\n let shown = false;\n const session = stateRef.current;\n void runSilentMetanet({\n sessionKey: sessionOnly ? session.identityKey : null,\n challengeUrl: c.challengeUrl,\n verifyUrl: c.verifyUrl,\n post: postJson,\n onAttempt: c.onAttempt,\n isAbandoned: () => !live(),\n logout: () => (props.current.logoutUrl ? postJson(props.current.logoutUrl, {}) : Promise.resolve()),\n onSignIn: () => {\n if (!live()) return;\n shown = true;\n setState((s) => (s.status === 'idle' ? { ...s, status: 'connecting', error: null } : s));\n },\n }).then((r) => {\n if (!live()) return;\n if (!r) {\n if (shown) setState((s) => (s.status === 'connecting' && !s.wallet ? { ...s, status: s.signedIn && s.identityKey ? 'connected' : 'idle' } : s));\n return;\n }\n const cw = r.connected;\n payer.current = cw.payer;\n setState((s) => ({\n ...s,\n status: 'connected',\n identityKey: r.identityKey,\n handle: r.handle ?? s.handle,\n paymail: r.paymail ?? s.paymail,\n wallet: cw.wallet,\n walletId: cw.optionId,\n walletType: r.walletType,\n transitional: false,\n signedIn: r.signedIn || s.signedIn,\n error: null,\n rememberedWallet: rememberedWalletChoice(),\n needsBwalletX: true,\n }));\n });\n }, [silentReady, sessionOnly]);\n\n // Best-effort paymail for the badge when the server did not send one (pay.bwallet.space lookup by key).\n useEffect(() => {\n const key = state.identityKey;\n if (!key || state.paymail || p.lookupUrl === 'off') return;\n let live = true;\n void lookupPaymail(key, p.lookupUrl).then((pm) => {\n if (live && pm) setState((s) => (s.identityKey === key && !s.paymail ? { ...s, paymail: pm } : s));\n });\n return () => {\n live = false;\n };\n }, [state.identityKey, state.paymail, p.lookupUrl]);\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 switchToBwalletX,\n connectMetanet,\n forgetWallet,\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, switchToBwalletX, connectMetanet, forgetWallet, 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 type BwalletXAccountStatus = 'restoring' | 'signed-out' | 'pending' | 'signed-in' | 'error';\n\nexport interface BwalletXAccount {\n status: BwalletXAccountStatus;\n identityKey: string | null;\n paymail: string | null;\n handle: string | null;\n /** $alice, a site handle, the paymail, or a short key: what the badge shows. */\n label: string | null;\n /** The bAvatar picture for the identity key (gold by default), or null when signed out. */\n avatarUrl: string | null;\n /** One plain line for the last error (\"Sign-in cancelled\" for a no in the wallet), or null. */\n error: string | null;\n /** Sign in (the provider's connect(): one wallet sheet). Never throws; failures show in `status`/`error`. */\n login(): Promise<void>;\n /** Sign out: the provider's disconnect() (posts logoutUrl). */\n logout(): Promise<void>;\n}\n\n/** The account view of a provider state (what useBwalletXAccount returns). */\nexport function accountFor(v: BwalletXContextValue): BwalletXAccount {\n const status = signinStateFor(v) as BwalletXAccountStatus;\n const signedIn = status === 'signed-in';\n return {\n status,\n identityKey: signedIn ? v.identityKey : null,\n paymail: signedIn ? v.paymail : null,\n handle: signedIn ? v.handle : null,\n label: signedIn ? accountLabel(v) : null,\n avatarUrl: signedIn ? avatarUrlFor(v.identityKey) : null,\n error: status === 'error' ? signInErrorText(v.error) : null,\n login: () => v.connect().then(() => undefined, () => undefined),\n logout: () => v.disconnect(),\n };\n}\n\n/** The signed-in account for your own UI: { status, identityKey, paymail, handle, avatarUrl, logout, … }. Inside <BwalletXProvider>. */\nexport function useBwalletXAccount(): BwalletXAccount {\n return accountFor(useBwalletX());\n}\n\nexport { handcashAdapter, brc100Option, pairingOption, discoverWallets, autoChoice } 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":[]}
package/dist/server.cjs CHANGED
@@ -787,12 +787,14 @@ function randomNonce2() {
787
787
  return btoa(s).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
788
788
  }
789
789
  async function createChallenge(opts) {
790
- if (!isIdentityKey(opts.identityKey)) throw new Error("identityKey must be a compressed public key (66 hex chars)");
790
+ const given = opts.identityKey !== void 0 && opts.identityKey !== null && opts.identityKey !== "";
791
+ if (given && !isIdentityKey(opts.identityKey)) throw new Error("identityKey must be a compressed public key (66 hex chars)");
792
+ const boundKey = given ? opts.identityKey.toLowerCase() : "";
791
793
  const origin = normalOrigin2(opts.origin);
792
794
  const nonce = randomNonce2();
793
795
  const expiresAt = Date.now() + CHALLENGE_LIFETIME_MS;
794
796
  const message = loginMessage(origin, nonce, expiresAt);
795
- await storeOrDefault(opts.store).put(nonce, { identityKey: opts.identityKey.toLowerCase(), origin, message, expiresAt });
797
+ await storeOrDefault(opts.store).put(nonce, { identityKey: boundKey, origin, message, expiresAt });
796
798
  return { nonce, message, protocolID: LOGIN_PROTOCOL, keyID: nonce, expiresAt };
797
799
  }
798
800
  async function verifySignIn(opts) {
@@ -808,7 +810,7 @@ async function verifySignIn(opts) {
808
810
  if (c.origin !== origin) return null;
809
811
  if (!isIdentityKey(opts.identityKey)) return null;
810
812
  const identityKey = opts.identityKey.toLowerCase();
811
- if (c.identityKey !== identityKey) return null;
813
+ if (c.identityKey && c.identityKey !== identityKey) return null;
812
814
  const sig = opts.signature;
813
815
  if (!Array.isArray(sig) || sig.length < 8 || sig.length > 80 || !sig.every((b) => Number.isInteger(b) && b >= 0 && b <= 255)) {
814
816
  return null;