@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.
- package/CHANGELOG.md +56 -0
- package/README.md +97 -9
- package/dist/bwalletx-connect.global.js +97 -25
- package/dist/bwalletx-connect.global.js.map +1 -1
- package/dist/choice-B97AjqF4.d.cts +442 -0
- package/dist/choice-B97AjqF4.d.ts +442 -0
- package/dist/chunk-6EFFOIHK.js +2441 -0
- package/dist/chunk-6EFFOIHK.js.map +1 -0
- package/dist/{chunk-QRXFL66W.js → chunk-A2WLGS25.js} +6 -4
- package/dist/chunk-A2WLGS25.js.map +1 -0
- package/dist/chunk-DIKJFKND.js +239 -0
- package/dist/chunk-DIKJFKND.js.map +1 -0
- package/dist/chunk-TNRI7ARM.js +206 -0
- package/dist/chunk-TNRI7ARM.js.map +1 -0
- package/dist/element-BXEoOdXY.d.cts +53 -0
- package/dist/element-BXEoOdXY.d.ts +53 -0
- package/dist/element.cjs +1576 -60
- package/dist/element.cjs.map +1 -1
- package/dist/element.d.cts +26 -26
- package/dist/element.d.ts +26 -26
- package/dist/element.js +35 -2
- package/dist/element.js.map +1 -1
- package/dist/index.cjs +2046 -454
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +323 -16
- package/dist/index.d.ts +323 -16
- package/dist/index.js +186 -12
- package/dist/index.js.map +1 -1
- package/dist/next.cjs +5 -3
- package/dist/next.cjs.map +1 -1
- package/dist/next.js +1 -1
- package/dist/react.cjs +1601 -210
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +115 -9
- package/dist/react.d.ts +115 -9
- package/dist/react.js +262 -46
- package/dist/react.js.map +1 -1
- package/dist/server.cjs +5 -3
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +8 -3
- package/dist/server.d.ts +8 -3
- package/dist/server.js +1 -1
- package/package.json +10 -9
- package/dist/chunk-EXRQJTXF.js +0 -910
- package/dist/chunk-EXRQJTXF.js.map +0 -1
- package/dist/chunk-QRXFL66W.js.map +0 -1
- package/dist/chunk-RIOHG5JH.js +0 -459
- package/dist/chunk-RIOHG5JH.js.map +0 -1
- package/dist/wallets-CbzMtxrf.d.cts +0 -250
- 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-
|
|
7
|
+
} from "./chunk-TNRI7ARM.js";
|
|
14
8
|
import {
|
|
15
|
-
|
|
9
|
+
METANET_TYPE,
|
|
10
|
+
accountLabel,
|
|
11
|
+
attachRemembered,
|
|
12
|
+
autoChoice,
|
|
13
|
+
avatarUrlFor,
|
|
14
|
+
brc100Option,
|
|
15
|
+
chooseWallet,
|
|
16
16
|
clearPairing,
|
|
17
17
|
defineBwalletxSignin,
|
|
18
|
-
|
|
19
|
-
|
|
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
|
-
[
|
|
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(() =>
|
|
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
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
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 =
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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;
|