@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/element.cjs CHANGED
@@ -36,13 +36,16 @@ var import_sdk = require("@bsv/sdk");
36
36
  var RDNS_EXTENSION = "com.bwalletx.extension";
37
37
  var RDNS_MOBILE = "space.bwallet.mobile";
38
38
  var BWALLETX_RDNS = [RDNS_EXTENSION, RDNS_MOBILE];
39
- function discoverBwalletX(ms = 1200) {
39
+ var DEFAULT_DISCOVER_MS = 300;
40
+ function discoverBwalletX(ms = DEFAULT_DISCOVER_MS) {
40
41
  if (typeof window === "undefined") return Promise.resolve(null);
41
42
  return new Promise((resolve) => {
42
43
  let done = false;
44
+ let timer;
43
45
  const finish = (r) => {
44
46
  if (done) return;
45
47
  done = true;
48
+ if (timer !== void 0) clearTimeout(timer);
46
49
  window.removeEventListener("brc100:announceWallet", onAnnounce);
47
50
  resolve(r);
48
51
  };
@@ -53,13 +56,21 @@ function discoverBwalletX(ms = 1200) {
53
56
  finish({ wallet: d.wallet, method: rdns === RDNS_EXTENSION ? "extension" : "in-app", rdns });
54
57
  }
55
58
  };
59
+ const cwiNow = () => {
60
+ const cwi = window.CWI;
61
+ if (cwi) finish({ wallet: cwi, method: "in-app", rdns: null });
62
+ return !!cwi;
63
+ };
56
64
  window.addEventListener("brc100:announceWallet", onAnnounce);
57
65
  window.dispatchEvent(new Event("brc100:requestWallet"));
58
- setTimeout(() => {
59
- const cwi = window.CWI;
60
- if (cwi) return finish({ wallet: cwi, method: "in-app", rdns: null });
61
- if (!isFramed()) return finish(null);
62
- void probeXdm(ms).then((w) => finish(w ? { wallet: w, method: "in-app", rdns: null } : null));
66
+ if (done || cwiNow()) return;
67
+ if (isFramed()) {
68
+ void probeXdm(Math.max(ms, 1200)).then((w) => finish(w ? { wallet: w, method: "in-app", rdns: null } : null));
69
+ return;
70
+ }
71
+ timer = setTimeout(() => {
72
+ timer = void 0;
73
+ if (!cwiNow()) finish(null);
63
74
  }, ms);
64
75
  });
65
76
  }
@@ -115,6 +126,21 @@ async function probeXdm(ms = 1200, allowed = BWALLETX_PARENT_ORIGINS) {
115
126
  return null;
116
127
  }
117
128
  }
129
+ var warmedAt = 0;
130
+ function prewarmBwalletX() {
131
+ if (typeof window === "undefined" || Date.now() - warmedAt < 2e4) return;
132
+ warmedAt = Date.now();
133
+ void discoverBwalletX(0).then((f) => {
134
+ if (f && f.method === "extension") void f.wallet.isAuthenticated({}).catch(() => {
135
+ });
136
+ });
137
+ }
138
+ function bindPrewarm(el) {
139
+ const on = () => prewarmBwalletX();
140
+ const evs = ["pointerenter", "focus", "touchstart"];
141
+ for (const e of evs) el.addEventListener(e, on, { passive: true });
142
+ return () => evs.forEach((e) => el.removeEventListener(e, on));
143
+ }
118
144
 
119
145
  // src/pair.ts
120
146
  var import_sdk3 = require("@bsv/sdk");
@@ -155,10 +181,22 @@ function pairUrl(link) {
155
181
  const q = new URLSearchParams({ v: link.v, r: link.r, c: link.c, k: link.k, o: link.o, e: String(link.e) });
156
182
  return `https://${PAIR_HOST}/pair?${q}`;
157
183
  }
158
- var relaySocketUrl = (relay, channel, role, expiry) => {
184
+ var newClaim = () => b64url(crypto.getRandomValues(new Uint8Array(16)));
185
+ var WALLET_CLAIM_INFO = "bwallet-pair-wallet-claim-v1";
186
+ var hex = (b) => Array.from(b, (x) => x.toString(16).padStart(2, "0")).join("");
187
+ async function walletClaimFor(channel, sitePub) {
188
+ const d = await crypto.subtle.digest("SHA-256", enc.encode(`${WALLET_CLAIM_INFO}|${channel}|${sitePub}`));
189
+ return b64url(new Uint8Array(d));
190
+ }
191
+ async function claimDigest(claim) {
192
+ return hex(new Uint8Array(await crypto.subtle.digest("SHA-256", enc.encode(claim))));
193
+ }
194
+ var relaySocketUrl = (relay, channel, role, expiry, claim, walletClaimHash) => {
159
195
  const local = /^(localhost|127\.0\.0\.1)(:\d+)?$/.test(relay);
160
196
  const q = new URLSearchParams({ role });
161
197
  if (expiry) q.set("e", String(expiry));
198
+ if (claim) q.set("claim", claim);
199
+ if (walletClaimHash && role === "site") q.set("wc", walletClaimHash);
162
200
  return `${local ? "ws" : "wss"}://${relay}/v1/c/${channel}?${q}`;
163
201
  };
164
202
  async function deriveSession(mine, theirs, channel) {
@@ -256,6 +294,7 @@ function parseStored(raw) {
256
294
  if (typeof p.s !== "string" || !/^[0-9a-f]{64}$/i.test(p.s)) return null;
257
295
  if (typeof p.k !== "string" || !/^0[23][0-9a-f]{64}$/i.test(p.k)) return null;
258
296
  if (!n(p.sent) || !n(p.lastSeen) || !n(p.pairedAt)) return null;
297
+ if (p.sc !== void 0 && (typeof p.sc !== "string" || !/^[A-Za-z0-9_-]{22,64}$/.test(p.sc))) return null;
259
298
  return p;
260
299
  } catch {
261
300
  return null;
@@ -313,7 +352,12 @@ var Link = class {
313
352
  await this.opening;
314
353
  }
315
354
  reopen() {
316
- const ws = this.socket(relaySocketUrl(this.p.r, this.p.c, "site", Math.floor(Date.now() / 1e3) + QR_LIFETIME_S - 10));
355
+ if (!this.p.sc) {
356
+ this.p = { ...this.p, sc: newClaim() };
357
+ this.persist();
358
+ }
359
+ const e = Math.floor(Date.now() / 1e3) + QR_LIFETIME_S - 10;
360
+ const ws = this.socket(relaySocketUrl(this.p.r, this.p.c, "site", e, this.p.sc));
317
361
  return new Promise((resolve, reject) => {
318
362
  ws.onopen = () => {
319
363
  this.ws = ws;
@@ -407,11 +451,13 @@ function pairBwalletX(opts) {
407
451
  const siteKey = import_sdk3.PrivateKey.fromRandom();
408
452
  const channel = newChannel();
409
453
  const expiry = Math.floor(Date.now() / 1e3) + QR_LIFETIME_S;
410
- return new Promise((resolve, reject) => {
454
+ const siteClaim = newClaim();
455
+ const sitePub = siteKey.toPublicKey().toString();
456
+ return walletClaimFor(channel, sitePub).then(claimDigest).then((wc) => new Promise((resolve, reject) => {
411
457
  if (opts.signal?.aborted) return reject(new Error("cancelled"));
412
458
  let ws;
413
459
  try {
414
- ws = socket(relaySocketUrl(relay, channel, "site", expiry));
460
+ ws = socket(relaySocketUrl(relay, channel, "site", expiry, siteClaim, wc));
415
461
  } catch (e) {
416
462
  return reject(e);
417
463
  }
@@ -450,7 +496,7 @@ function pairBwalletX(opts) {
450
496
  if (msg.t === "ready") {
451
497
  settled = true;
452
498
  clearTimeout(timer);
453
- const p = { v: 1, c: channel, r: relay, s: siteKey.toHex(), k: walletKey, ...sealer.counters, pairedAt: Date.now() };
499
+ const p = { v: 1, c: channel, r: relay, s: siteKey.toHex(), k: walletKey, ...sealer.counters, pairedAt: Date.now(), sc: siteClaim };
454
500
  await Promise.resolve(storage.setItem(key, JSON.stringify(p))).catch(() => {
455
501
  });
456
502
  const link = new Link(p, sealer, storage, key, socket, opts.callTimeoutMs ?? 12e4);
@@ -460,8 +506,8 @@ function pairBwalletX(opts) {
460
506
  fail(new Error("Pairing was declined in bWalletX"));
461
507
  }
462
508
  });
463
- opts.onLink(pairUrl({ v: PAIR_VERSION, r: relay, c: channel, k: siteKey.toPublicKey().toString(), o: origin, e: expiry }));
464
- });
509
+ opts.onLink(pairUrl({ v: PAIR_VERSION, r: relay, c: channel, k: sitePub, o: origin, e: expiry }));
510
+ }));
465
511
  }
466
512
  async function restorePairing(opts = {}) {
467
513
  const storage = opts.storage === void 0 ? defaultStorage() : opts.storage ?? memoryStorage();
@@ -477,16 +523,33 @@ async function restorePairing(opts = {}) {
477
523
  // src/signin.ts
478
524
  var LOGIN_PROTOCOL = [2, "bwallet sign in"];
479
525
  async function connectBwalletX(opts = {}) {
480
- if (opts.wallet) return { wallet: opts.wallet, method: "wallet" };
481
- const found = await discoverBwalletX(opts.discoverMs);
482
- if (found) return { wallet: found.wallet, method: found.method };
526
+ const status = (s) => {
527
+ try {
528
+ opts.onStatus?.(s);
529
+ } catch {
530
+ }
531
+ };
532
+ if (opts.wallet) {
533
+ status("opening");
534
+ return { wallet: opts.wallet, method: "wallet" };
535
+ }
536
+ status("finding");
537
+ const found = await discoverBwalletX(opts.discoverMs ?? DEFAULT_DISCOVER_MS);
538
+ if (found) {
539
+ status("opening");
540
+ return { wallet: found.wallet, method: found.method };
541
+ }
483
542
  if (opts.reusePairing !== false) {
484
543
  const restored = await restorePairing(opts.pairing ?? {});
485
- if (restored) return { wallet: restored, method: "pairing" };
544
+ if (restored) {
545
+ status("opening");
546
+ return { wallet: restored, method: "pairing" };
547
+ }
486
548
  }
487
549
  if (!opts.pairing) {
488
550
  throw Object.assign(new Error("bWalletX was not found in this browser"), { code: "NOT_FOUND" });
489
551
  }
552
+ status("pairing");
490
553
  return { wallet: await pairBwalletX(opts.pairing), method: "pairing" };
491
554
  }
492
555
  async function signInWithBwalletX(opts) {
@@ -494,6 +557,10 @@ async function signInWithBwalletX(opts) {
494
557
  if (method !== "pairing") await wallet.waitForAuthentication({}).catch(() => {
495
558
  });
496
559
  const { publicKey: identityKey } = await atStage("identity", () => wallet.getPublicKey({ identityKey: true }));
560
+ try {
561
+ opts.onStatus?.("signing");
562
+ } catch {
563
+ }
497
564
  const c = await atStage("challenge", () => opts.challenge(identityKey));
498
565
  const { signature } = await atStage(
499
566
  "signature",
@@ -851,6 +918,7 @@ function createClass() {
851
918
  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>` : "";
852
919
  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}`;
853
920
  this.button.addEventListener("click", () => void this.#click());
921
+ bindPrewarm(this.button);
854
922
  }
855
923
  #emit(type, detail, cancelable = false) {
856
924
  return this.dispatchEvent(new CustomEvent(type, { detail, bubbles: true, composed: true, cancelable }));
@@ -877,7 +945,7 @@ function createClass() {
877
945
  this.#notFound = false;
878
946
  this.#setBusy(true);
879
947
  try {
880
- const ms = Number(this.#attr("discover-ms")) || 1200;
948
+ const ms = Number(this.#attr("discover-ms")) || DEFAULT_DISCOVER_MS;
881
949
  const found = await discoverBwalletX(ms);
882
950
  if (!found) {
883
951
  this.#notFound = true;