@orangecheck/auth-client 2.33.4 → 2.35.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/dist/appearance.mjs +23 -0
  2. package/dist/appearance.mjs.map +1 -0
  3. package/dist/bond.mjs +20 -0
  4. package/dist/bond.mjs.map +1 -0
  5. package/dist/chunk-4E7GJ5IB.mjs +1184 -0
  6. package/dist/chunk-4E7GJ5IB.mjs.map +1 -0
  7. package/dist/chunk-7ST4Q5KR.mjs +346 -0
  8. package/dist/chunk-7ST4Q5KR.mjs.map +1 -0
  9. package/dist/chunk-7UYPMTAV.mjs +652 -0
  10. package/dist/chunk-7UYPMTAV.mjs.map +1 -0
  11. package/dist/chunk-BYAF2P2L.mjs +253 -0
  12. package/dist/chunk-BYAF2P2L.mjs.map +1 -0
  13. package/dist/chunk-CYR7J4UY.mjs +118 -0
  14. package/dist/chunk-CYR7J4UY.mjs.map +1 -0
  15. package/dist/chunk-H2TXOCXH.mjs +23 -0
  16. package/dist/chunk-H2TXOCXH.mjs.map +1 -0
  17. package/dist/chunk-HZ4FJMF3.mjs +490 -0
  18. package/dist/chunk-HZ4FJMF3.mjs.map +1 -0
  19. package/dist/chunk-TJJNTITX.mjs +187 -0
  20. package/dist/chunk-TJJNTITX.mjs.map +1 -0
  21. package/dist/chunk-UFRU3I3D.mjs +913 -0
  22. package/dist/chunk-UFRU3I3D.mjs.map +1 -0
  23. package/dist/chunk-VRLFZAGK.mjs +37 -0
  24. package/dist/chunk-VRLFZAGK.mjs.map +1 -0
  25. package/dist/chunk-XCZVSE7M.mjs +40 -0
  26. package/dist/chunk-XCZVSE7M.mjs.map +1 -0
  27. package/dist/components.mjs +19 -0
  28. package/dist/components.mjs.map +1 -0
  29. package/dist/index.js +77 -67
  30. package/dist/index.js.map +1 -1
  31. package/dist/index.mjs +112 -4001
  32. package/dist/index.mjs.map +1 -1
  33. package/dist/linked-identities.mjs +41 -0
  34. package/dist/linked-identities.mjs.map +1 -0
  35. package/dist/provider.mjs +17 -0
  36. package/dist/provider.mjs.map +1 -0
  37. package/dist/return-to.mjs +18 -0
  38. package/dist/return-to.mjs.map +1 -0
  39. package/dist/signin.mjs +23 -0
  40. package/dist/signin.mjs.map +1 -0
  41. package/dist/sudo.mjs +16 -0
  42. package/dist/sudo.mjs.map +1 -0
  43. package/dist/tab-session.mjs +29 -0
  44. package/dist/tab-session.mjs.map +1 -0
  45. package/dist/types.mjs +17 -0
  46. package/dist/types.mjs.map +1 -0
  47. package/dist/webauthn.mjs +15 -0
  48. package/dist/webauthn.mjs.map +1 -0
  49. package/package.json +2 -1
  50. package/dist/index.d.mts +0 -303
@@ -0,0 +1,490 @@
1
+ import {
2
+ ErrorLine,
3
+ FormButtons,
4
+ LazyWalletButton,
5
+ SectionLabel,
6
+ V,
7
+ bodyStyle,
8
+ dangerBtnStyle,
9
+ ghostBtnStyle,
10
+ hintStyle,
11
+ inputStyle,
12
+ panelStyle,
13
+ primaryBtnStyle
14
+ } from "./chunk-UFRU3I3D.mjs";
15
+ import {
16
+ SIGNIN_FONT
17
+ } from "./chunk-H2TXOCXH.mjs";
18
+ import {
19
+ readTabSession,
20
+ writeTabSession
21
+ } from "./chunk-TJJNTITX.mjs";
22
+
23
+ // src/bond.tsx
24
+ import * as React from "react";
25
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
26
+ var DEFAULT_AUTH_ORIGIN = "https://ochk.io";
27
+ var MIN_CONFIRMATIONS = 6;
28
+ var MIN_SATS = 1e3;
29
+ var n = (v) => v.toLocaleString("en-US");
30
+ function bondFloorsText(f) {
31
+ const sats = f.sats_floor > 0 ? `at least ${n(f.sats_floor)} sats` : "under 1,000 sats";
32
+ const days = f.days_floor > 0 ? `held ${f.days_floor} days or more` : "held under 7 days";
33
+ return `${sats}, ${days}`;
34
+ }
35
+ var short = (outpoint) => {
36
+ const [txid = "", vout = ""] = outpoint.split(":");
37
+ return `${txid.slice(0, 8)}\u2026${txid.slice(-4)}:${vout}`;
38
+ };
39
+ var usable = (c) => c.sats >= MIN_SATS && c.age_blocks >= MIN_CONFIRMATIONS;
40
+ var dustText = (count) => `${n(count)} ${count === 1 ? "coin" : "coins"} under ${n(MIN_SATS)} sats, too small to back an account`;
41
+ var REFUSAL = {
42
+ invalid_address: "that isn't a Bitcoin address we can read",
43
+ chain_unavailable: "couldn't read the chain just now \xB7 nothing changed, try again in a minute",
44
+ sig_invalid: "that signature isn't from this coin's address \xB7 sign with the wallet that holds it",
45
+ bond_expired: "the message expired \xB7 start again",
46
+ bond_not_yet_valid: "your device's clock looks wrong \xB7 check it and start again",
47
+ bond_subject_mismatch: "that message was for another account \xB7 start again",
48
+ bond_malformed: "the signed message changed on the way \xB7 start again",
49
+ bond_window_too_long: "that message's signing window is too long \xB7 start again",
50
+ coin_not_found: "that coin doesn't exist on-chain",
51
+ coin_address_mismatch: "that coin isn't at this address",
52
+ coin_unconfirmed: "that coin isn't confirmed yet",
53
+ coin_too_recent: "that coin needs six confirmations first, about an hour",
54
+ coin_spent: "that coin has been spent \xB7 pick one still in your wallet",
55
+ coin_too_small: `that coin is under ${n(MIN_SATS)} sats, too small to back an account`,
56
+ coin_backs_another_account: "that coin already backs another orangecheck account",
57
+ coin_backed_another_recently: "that coin backed another account until recently \xB7 it can back this one 30 days after it was released",
58
+ not_authenticated: "your session ended \xB7 sign in again",
59
+ rate_limited: "too many tries \xB7 wait a minute, then try again"
60
+ };
61
+ var bondRefusal = (reason) => reason && REFUSAL[reason] || "your backing didn't save \xB7 nothing changed, try again";
62
+ var smallStyle = { ...bodyStyle, fontSize: 12, margin: 0 };
63
+ var explainerStyle = {
64
+ ...hintStyle,
65
+ fontSize: 12,
66
+ lineHeight: 1.55,
67
+ opacity: 1,
68
+ margin: 0
69
+ };
70
+ var fieldLabelStyle = {
71
+ display: "block",
72
+ color: V.foreground,
73
+ fontSize: 13,
74
+ fontWeight: 500,
75
+ margin: "0 0 4px"
76
+ };
77
+ var addressStyle = { wordBreak: "break-all", color: V.foreground };
78
+ function coinStyle(picked, young) {
79
+ return {
80
+ background: V.card,
81
+ border: `1px ${young ? "dashed" : "solid"} ${picked ? V.primary : V.border}`,
82
+ padding: "0.75rem",
83
+ display: "flex",
84
+ gap: 10,
85
+ alignItems: "flex-start",
86
+ cursor: young ? "not-allowed" : "pointer"
87
+ };
88
+ }
89
+ async function fetchOcBond(opts) {
90
+ const r = await fetch(`${opts?.authOrigin ?? DEFAULT_AUTH_ORIGIN}/api/auth/bond`, {
91
+ credentials: "include"
92
+ });
93
+ const j = await r.json().catch(() => ({}));
94
+ if (!r.ok || !j.ok) throw new Error(`http_${r.status}`);
95
+ return j.bond ?? null;
96
+ }
97
+ function OcBond({
98
+ authOrigin = DEFAULT_AUTH_ORIGIN,
99
+ onChange,
100
+ className
101
+ }) {
102
+ const [bond, setBond] = React.useState(void 0);
103
+ const [loadErr, setLoadErr] = React.useState(false);
104
+ const [nonce, setNonce] = React.useState(0);
105
+ const [adding, setAdding] = React.useState(false);
106
+ const [confirming, setConfirming] = React.useState(false);
107
+ const [busy, setBusy] = React.useState(false);
108
+ const [err, setErr] = React.useState(null);
109
+ React.useEffect(() => {
110
+ let alive = true;
111
+ fetchOcBond({ authOrigin }).then(
112
+ (b) => alive && setBond(b),
113
+ () => alive && setLoadErr(true)
114
+ );
115
+ return () => {
116
+ alive = false;
117
+ };
118
+ }, [authOrigin, nonce]);
119
+ const call = React.useCallback(
120
+ async (method, body) => {
121
+ const r = await fetch(`${authOrigin}/api/auth/bond`, {
122
+ method,
123
+ credentials: "include",
124
+ headers: body ? { "Content-Type": "application/json" } : void 0,
125
+ body: body ? JSON.stringify(body) : void 0
126
+ });
127
+ const j = await r.json().catch(() => ({}));
128
+ const pin = readTabSession();
129
+ if (pin && r.ok && typeof j.token === "string") {
130
+ writeTabSession({ token: j.token, didOc: pin.didOc });
131
+ }
132
+ return { ok: r.ok && j.ok === true, ...j };
133
+ },
134
+ [authOrigin]
135
+ );
136
+ async function release() {
137
+ setBusy(true);
138
+ setErr(null);
139
+ const j = await call("DELETE").catch(() => ({ ok: false, reason: "network" }));
140
+ setBusy(false);
141
+ if (!j.ok) return setErr(bondRefusal(j.reason));
142
+ setConfirming(false);
143
+ setBond(null);
144
+ onChange?.(null);
145
+ }
146
+ if (loadErr) {
147
+ return /* @__PURE__ */ jsxs("div", { className, "data-oc-bond": "", style: panelStyle("warn"), role: "alert", children: [
148
+ /* @__PURE__ */ jsx("p", { style: bodyStyle, children: "couldn't read your backing \xB7 nothing was changed" }),
149
+ /* @__PURE__ */ jsx(
150
+ "button",
151
+ {
152
+ type: "button",
153
+ style: ghostBtnStyle(false),
154
+ onClick: () => {
155
+ setLoadErr(false);
156
+ setNonce((x) => x + 1);
157
+ },
158
+ children: "try again"
159
+ }
160
+ )
161
+ ] });
162
+ }
163
+ if (bond === void 0) {
164
+ return /* @__PURE__ */ jsx("div", { className, "data-oc-bond": "", style: panelStyle(), children: /* @__PURE__ */ jsx("span", { style: hintStyle, children: "reading your backing\u2026" }) });
165
+ }
166
+ return /* @__PURE__ */ jsxs("div", { className, "data-oc-bond": "", style: { display: "grid", gap: 16 }, children: [
167
+ bond && !adding ? /* @__PURE__ */ jsxs("div", { style: panelStyle("success"), children: [
168
+ /* @__PURE__ */ jsx(SectionLabel, { tone: "success", children: "\xA7 backed by bitcoin" }),
169
+ /* @__PURE__ */ jsxs("p", { style: { ...bodyStyle, color: V.foreground }, children: [
170
+ "apps see a coin ",
171
+ bondFloorsText(bond),
172
+ "."
173
+ ] }),
174
+ /* @__PURE__ */ jsxs("p", { style: bodyStyle, children: [
175
+ "coin ",
176
+ short(bond.outpoint),
177
+ ", ",
178
+ n(bond.sats),
179
+ " sats \xB7 last found unspent at block ",
180
+ n(bond.checked_height),
181
+ " \xB7 only you and orangecheck see this line"
182
+ ] }),
183
+ confirming ? /* @__PURE__ */ jsxs("div", { role: "group", "aria-label": "stop backing", "data-oc-bond-confirm": "", children: [
184
+ /* @__PURE__ */ jsx("p", { style: { ...bodyStyle, color: V.foreground, margin: 0 }, children: "stop backing? apps that ask for a backed account may stop letting you in." }),
185
+ /* @__PURE__ */ jsxs(FormButtons, { children: [
186
+ /* @__PURE__ */ jsx(
187
+ "button",
188
+ {
189
+ type: "button",
190
+ style: dangerBtnStyle(busy),
191
+ disabled: busy,
192
+ onClick: () => void release(),
193
+ children: "stop backing"
194
+ }
195
+ ),
196
+ /* @__PURE__ */ jsx(
197
+ "button",
198
+ {
199
+ type: "button",
200
+ style: ghostBtnStyle(busy),
201
+ disabled: busy,
202
+ autoFocus: true,
203
+ onClick: () => {
204
+ setErr(null);
205
+ setConfirming(false);
206
+ },
207
+ children: "keep"
208
+ }
209
+ )
210
+ ] })
211
+ ] }) : /* @__PURE__ */ jsxs(FormButtons, { children: [
212
+ /* @__PURE__ */ jsx(
213
+ "button",
214
+ {
215
+ type: "button",
216
+ style: ghostBtnStyle(false),
217
+ onClick: () => {
218
+ setErr(null);
219
+ setAdding(true);
220
+ },
221
+ children: "use another coin"
222
+ }
223
+ ),
224
+ /* @__PURE__ */ jsx(
225
+ "button",
226
+ {
227
+ type: "button",
228
+ style: dangerBtnStyle(false),
229
+ onClick: () => setConfirming(true),
230
+ children: "stop backing"
231
+ }
232
+ )
233
+ ] }),
234
+ err ? /* @__PURE__ */ jsx(ErrorLine, { children: err }) : null
235
+ ] }) : null,
236
+ !bond && !adding ? /* @__PURE__ */ jsxs("div", { style: panelStyle(), children: [
237
+ /* @__PURE__ */ jsx(SectionLabel, { children: "\xA7 not backed" }),
238
+ /* @__PURE__ */ jsx("p", { style: bodyStyle, children: "your account works everywhere as it is. some apps let in only accounts backed by a coin you hold. it stays in your wallet, and apps never see which coin." }),
239
+ /* @__PURE__ */ jsx(
240
+ "button",
241
+ {
242
+ type: "button",
243
+ style: primaryBtnStyle(false),
244
+ onClick: () => setAdding(true),
245
+ children: "back your account"
246
+ }
247
+ )
248
+ ] }) : null,
249
+ adding ? /* @__PURE__ */ jsx(
250
+ BondForm,
251
+ {
252
+ call,
253
+ onCancel: () => setAdding(false),
254
+ onDone: (b) => {
255
+ setBond(b);
256
+ setAdding(false);
257
+ onChange?.(b);
258
+ }
259
+ }
260
+ ) : null,
261
+ /* @__PURE__ */ jsx("p", { style: explainerStyle, children: "Apps see only rough floors, like \u201Cat least 100,000 sats, held 30 days or more\u201D, and a number unique to that app. Never the coin, your address or your balance. An app can block that number; only moving the coin gets past it, and a moved coin starts its age again." })
262
+ ] });
263
+ }
264
+ function BondForm({
265
+ call,
266
+ onCancel,
267
+ onDone
268
+ }) {
269
+ const [address, setAddress] = React.useState("");
270
+ const [coins, setCoins] = React.useState(null);
271
+ const [chosen, setChosen] = React.useState("");
272
+ const [message, setMessage] = React.useState(null);
273
+ const [busy, setBusy] = React.useState(false);
274
+ const [err, setErr] = React.useState(null);
275
+ const id = React.useId();
276
+ const addr = address.trim();
277
+ const canFind = addr.length >= 14;
278
+ async function run(fn) {
279
+ setBusy(true);
280
+ setErr(null);
281
+ try {
282
+ await fn();
283
+ } catch {
284
+ setErr(bondRefusal("chain_unavailable"));
285
+ } finally {
286
+ setBusy(false);
287
+ }
288
+ }
289
+ const findCoins = () => run(async () => {
290
+ const j = await call("POST", { action: "coins", address: addr });
291
+ if (!j.ok) return setErr(bondRefusal(j.reason));
292
+ const list = j.coins ?? [];
293
+ setCoins(list);
294
+ setChosen(list.find(usable)?.outpoint ?? "");
295
+ });
296
+ const getMessage = () => run(async () => {
297
+ const j = await call("POST", { action: "message", address: addr, outpoint: chosen });
298
+ if (!j.ok || typeof j.message !== "string") return setErr(bondRefusal(j.reason));
299
+ setMessage(j.message);
300
+ });
301
+ const submit = (signature) => run(async () => {
302
+ const j = await call("POST", { action: "bond", message, signature });
303
+ if (!j.ok || !j.bond) return setErr(bondRefusal(j.reason));
304
+ onDone(j.bond);
305
+ });
306
+ function back() {
307
+ setErr(null);
308
+ if (message) setMessage(null);
309
+ else setCoins(null);
310
+ }
311
+ const error = err ? /* @__PURE__ */ jsx(ErrorLine, { children: err }) : null;
312
+ const backButton = /* @__PURE__ */ jsx("button", { type: "button", style: ghostBtnStyle(busy), disabled: busy, onClick: back, children: "back" });
313
+ const cancelButton = /* @__PURE__ */ jsx("button", { type: "button", style: ghostBtnStyle(busy), disabled: busy, onClick: onCancel, children: "cancel" });
314
+ let step;
315
+ if (message) {
316
+ step = /* @__PURE__ */ jsxs(Fragment, { children: [
317
+ /* @__PURE__ */ jsxs("p", { style: bodyStyle, children: [
318
+ "sign with the wallet that holds ",
319
+ /* @__PURE__ */ jsx("span", { style: addressStyle, children: addr }),
320
+ ". signing spends nothing, and the coin stays in your wallet."
321
+ ] }),
322
+ /* @__PURE__ */ jsx(
323
+ LazyWalletButton,
324
+ {
325
+ address: addr,
326
+ message,
327
+ showManual: true,
328
+ onSigned: (sig) => void submit(sig),
329
+ onError: (e) => setErr(e.message ?? "the wallet refused to sign")
330
+ }
331
+ ),
332
+ error,
333
+ /* @__PURE__ */ jsxs(FormButtons, { children: [
334
+ backButton,
335
+ cancelButton
336
+ ] })
337
+ ] });
338
+ } else if (coins) {
339
+ const eligible = coins.filter((c) => c.sats >= MIN_SATS);
340
+ const dust = coins.length - eligible.length;
341
+ step = /* @__PURE__ */ jsxs(Fragment, { children: [
342
+ coins.length === 0 ? /* @__PURE__ */ jsxs("p", { style: bodyStyle, children: [
343
+ "no confirmed coins at that address \xB7 send at least ",
344
+ n(MIN_SATS),
345
+ " sats there and wait for six confirmations, about an hour. the longer a coin sits, the more it says."
346
+ ] }) : eligible.length === 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
347
+ /* @__PURE__ */ jsx("p", { style: { ...bodyStyle, color: V.foreground }, children: dustText(dust) }),
348
+ /* @__PURE__ */ jsxs("p", { style: bodyStyle, children: [
349
+ "send at least ",
350
+ n(MIN_SATS),
351
+ " sats there in one payment and wait for six confirmations, about an hour. coins aren't added together."
352
+ ] })
353
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
354
+ /* @__PURE__ */ jsx("p", { id: `${id}-pick`, style: bodyStyle, children: "pick one coin \xB7 coins aren't added together" }),
355
+ /* @__PURE__ */ jsx(
356
+ "div",
357
+ {
358
+ role: "radiogroup",
359
+ "aria-labelledby": `${id}-pick`,
360
+ style: { display: "grid", gap: 8 },
361
+ children: eligible.map((c) => {
362
+ const young = c.age_blocks < MIN_CONFIRMATIONS;
363
+ return /* @__PURE__ */ jsxs(
364
+ "label",
365
+ {
366
+ style: coinStyle(chosen === c.outpoint, young),
367
+ children: [
368
+ /* @__PURE__ */ jsx(
369
+ "input",
370
+ {
371
+ type: "radio",
372
+ name: "oc-bond-coin",
373
+ value: c.outpoint,
374
+ checked: chosen === c.outpoint,
375
+ disabled: young,
376
+ onChange: () => setChosen(c.outpoint),
377
+ style: { accentColor: V.primary, margin: "2px 0 0" }
378
+ }
379
+ ),
380
+ /* @__PURE__ */ jsxs("span", { style: { display: "grid", gap: 2, minWidth: 0 }, children: [
381
+ /* @__PURE__ */ jsxs(
382
+ "span",
383
+ {
384
+ style: {
385
+ color: young ? V.muted : V.foreground,
386
+ fontSize: 13
387
+ },
388
+ children: [
389
+ n(c.sats),
390
+ " sats, held",
391
+ " ",
392
+ Math.floor(c.age_blocks / 144),
393
+ " days"
394
+ ]
395
+ }
396
+ ),
397
+ /* @__PURE__ */ jsx("span", { style: smallStyle, children: young ? "too recent: needs six confirmations" : `apps would see ${bondFloorsText(c)}` }),
398
+ /* @__PURE__ */ jsx(
399
+ "span",
400
+ {
401
+ style: {
402
+ ...smallStyle,
403
+ fontFamily: SIGNIN_FONT,
404
+ wordBreak: "break-all"
405
+ },
406
+ children: short(c.outpoint)
407
+ }
408
+ )
409
+ ] })
410
+ ]
411
+ },
412
+ c.outpoint
413
+ );
414
+ })
415
+ }
416
+ ),
417
+ dust > 0 ? /* @__PURE__ */ jsx("p", { style: { ...smallStyle, marginTop: 8 }, children: dustText(dust) }) : null
418
+ ] }),
419
+ error,
420
+ /* @__PURE__ */ jsxs(FormButtons, { children: [
421
+ eligible.length > 0 ? /* @__PURE__ */ jsx(
422
+ "button",
423
+ {
424
+ type: "button",
425
+ style: primaryBtnStyle(busy || !chosen),
426
+ disabled: busy || !chosen,
427
+ onClick: () => void getMessage(),
428
+ children: "continue"
429
+ }
430
+ ) : null,
431
+ backButton,
432
+ cancelButton
433
+ ] })
434
+ ] });
435
+ } else {
436
+ step = /* @__PURE__ */ jsxs(
437
+ "form",
438
+ {
439
+ style: { margin: 0 },
440
+ onSubmit: (e) => {
441
+ e.preventDefault();
442
+ if (canFind && !busy) void findCoins();
443
+ },
444
+ children: [
445
+ /* @__PURE__ */ jsx("label", { htmlFor: `${id}-address`, style: fieldLabelStyle, children: "bitcoin address that holds the coin" }),
446
+ /* @__PURE__ */ jsx("p", { id: `${id}-advice`, style: { ...bodyStyle, margin: "0 0 8px" }, children: "use one that holds nothing else of yours: orangecheck learns its address and amount. apps never do." }),
447
+ /* @__PURE__ */ jsx(
448
+ "input",
449
+ {
450
+ id: `${id}-address`,
451
+ "aria-describedby": `${id}-advice`,
452
+ style: inputStyle,
453
+ value: address,
454
+ onChange: (e) => setAddress(e.target.value),
455
+ placeholder: "bc1q\u2026",
456
+ spellCheck: false,
457
+ autoComplete: "off",
458
+ autoCapitalize: "off"
459
+ }
460
+ ),
461
+ error,
462
+ /* @__PURE__ */ jsxs(FormButtons, { children: [
463
+ /* @__PURE__ */ jsx(
464
+ "button",
465
+ {
466
+ type: "submit",
467
+ style: primaryBtnStyle(busy || !canFind),
468
+ disabled: busy || !canFind,
469
+ children: "find its coins"
470
+ }
471
+ ),
472
+ cancelButton
473
+ ] })
474
+ ]
475
+ }
476
+ );
477
+ }
478
+ return /* @__PURE__ */ jsxs("div", { style: panelStyle("accent"), "data-oc-bond-form": "", children: [
479
+ /* @__PURE__ */ jsx(SectionLabel, { children: "\xA7 back your account" }),
480
+ step
481
+ ] });
482
+ }
483
+
484
+ export {
485
+ bondFloorsText,
486
+ bondRefusal,
487
+ fetchOcBond,
488
+ OcBond
489
+ };
490
+ //# sourceMappingURL=chunk-HZ4FJMF3.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/bond.tsx"],"sourcesContent":["'use client';\n\n/**\n * OcBond · back the signed-in account with bitcoin (oc-attest-protocol\n * BOND.md). The person signs for one coin they hold. It stays in their wallet,\n * and spending it ends the backing. Apps they sign in to see floors and a\n * number unique to that app, never the coin. Optional: every account signs in\n * without one.\n *\n * Runs against the auth host's /api/auth/bond with the family cookie, like\n * OcLinkedIdentities, so any *.ochk.io site can mount it. A change re-mints\n * the session there, and a tab pinned to this account re-pins with the fresh\n * token; `onChange` lets the site refresh its session view.\n */\nimport * as React from 'react';\n\nimport { SIGNIN_FONT } from './appearance';\nimport {\n bodyStyle,\n dangerBtnStyle,\n ErrorLine,\n FormButtons,\n ghostBtnStyle,\n hintStyle,\n inputStyle,\n LazyWalletButton,\n panelStyle,\n primaryBtnStyle,\n SectionLabel,\n V,\n} from './linked-identities';\nimport { readTabSession, writeTabSession } from './tab-session';\n\nconst DEFAULT_AUTH_ORIGIN = 'https://ochk.io';\nconst MIN_CONFIRMATIONS = 6;\n/** Smaller coins meet no floor, so the auth host refuses them (`coin_too_small`). */\nconst MIN_SATS = 1_000;\n\ninterface Floors {\n sats_floor: number;\n days_floor: number;\n}\n\n/** The account's bond as the auth host shows it to the account itself. */\nexport interface OcBondView extends Floors {\n address: string;\n outpoint: string;\n sats: number;\n confirmed_height: number;\n checked_at: string;\n checked_height: number;\n created_at: string;\n}\n\ninterface Coin extends Floors {\n outpoint: string;\n sats: number;\n age_blocks: number;\n}\n\nexport interface OcBondProps {\n /** The auth host. Default https://ochk.io; pass the current origin on ochk.io itself. */\n authOrigin?: string;\n /** After a bond is made or released, so the site can refresh its session. */\n onChange?: (bond: OcBondView | null) => void;\n className?: string;\n}\n\nconst n = (v: number) => v.toLocaleString('en-US');\n\n/** \"at least 100,000 sats, held 90 days or more\". */\nexport function bondFloorsText(f: Floors): string {\n const sats = f.sats_floor > 0 ? `at least ${n(f.sats_floor)} sats` : 'under 1,000 sats';\n const days = f.days_floor > 0 ? `held ${f.days_floor} days or more` : 'held under 7 days';\n return `${sats}, ${days}`;\n}\n\nconst short = (outpoint: string) => {\n const [txid = '', vout = ''] = outpoint.split(':');\n return `${txid.slice(0, 8)}…${txid.slice(-4)}:${vout}`;\n};\n\nconst usable = (c: Coin) => c.sats >= MIN_SATS && c.age_blocks >= MIN_CONFIRMATIONS;\n\nconst dustText = (count: number) =>\n `${n(count)} ${count === 1 ? 'coin' : 'coins'} under ${n(MIN_SATS)} sats, ` +\n 'too small to back an account';\n\nconst REFUSAL: Record<string, string> = {\n invalid_address: \"that isn't a Bitcoin address we can read\",\n chain_unavailable: \"couldn't read the chain just now · nothing changed, try again in a minute\",\n sig_invalid:\n \"that signature isn't from this coin's address · sign with the wallet that holds it\",\n bond_expired: 'the message expired · start again',\n bond_not_yet_valid: \"your device's clock looks wrong · check it and start again\",\n bond_subject_mismatch: 'that message was for another account · start again',\n bond_malformed: 'the signed message changed on the way · start again',\n bond_window_too_long: \"that message's signing window is too long · start again\",\n coin_not_found: \"that coin doesn't exist on-chain\",\n coin_address_mismatch: \"that coin isn't at this address\",\n coin_unconfirmed: \"that coin isn't confirmed yet\",\n coin_too_recent: 'that coin needs six confirmations first, about an hour',\n coin_spent: 'that coin has been spent · pick one still in your wallet',\n coin_too_small: `that coin is under ${n(MIN_SATS)} sats, too small to back an account`,\n coin_backs_another_account: 'that coin already backs another orangecheck account',\n coin_backed_another_recently:\n 'that coin backed another account until recently · ' +\n 'it can back this one 30 days after it was released',\n not_authenticated: 'your session ended · sign in again',\n rate_limited: 'too many tries · wait a minute, then try again',\n};\n\n/** A refusal from /api/auth/bond, in the component's voice. Not exported from the index. */\nexport const bondRefusal = (reason?: string): string =>\n (reason && REFUSAL[reason]) || \"your backing didn't save · nothing changed, try again\";\n\n/* Small print stays legible: 12px or more, and never dimmed below the muted tone. */\nconst smallStyle: React.CSSProperties = { ...bodyStyle, fontSize: 12, margin: 0 };\nconst explainerStyle: React.CSSProperties = {\n ...hintStyle,\n fontSize: 12,\n lineHeight: 1.55,\n opacity: 1,\n margin: 0,\n};\nconst fieldLabelStyle: React.CSSProperties = {\n display: 'block',\n color: V.foreground,\n fontSize: 13,\n fontWeight: 500,\n margin: '0 0 4px',\n};\nconst addressStyle: React.CSSProperties = { wordBreak: 'break-all', color: V.foreground };\n\nfunction coinStyle(picked: boolean, young: boolean): React.CSSProperties {\n return {\n background: V.card,\n border: `1px ${young ? 'dashed' : 'solid'} ${picked ? V.primary : V.border}`,\n padding: '0.75rem',\n display: 'flex',\n gap: 10,\n alignItems: 'flex-start',\n cursor: young ? 'not-allowed' : 'pointer',\n };\n}\n\n/** The account's bond, or null. Throws when the auth host can't be read. */\nexport async function fetchOcBond(opts?: { authOrigin?: string }): Promise<OcBondView | null> {\n const r = await fetch(`${opts?.authOrigin ?? DEFAULT_AUTH_ORIGIN}/api/auth/bond`, {\n credentials: 'include',\n });\n const j = (await r.json().catch(() => ({}))) as { ok?: boolean; bond?: OcBondView | null };\n if (!r.ok || !j.ok) throw new Error(`http_${r.status}`);\n return j.bond ?? null;\n}\n\nexport function OcBond({\n authOrigin = DEFAULT_AUTH_ORIGIN,\n onChange,\n className,\n}: OcBondProps): React.ReactElement {\n const [bond, setBond] = React.useState<OcBondView | null | undefined>(undefined);\n const [loadErr, setLoadErr] = React.useState(false);\n const [nonce, setNonce] = React.useState(0);\n const [adding, setAdding] = React.useState(false);\n const [confirming, setConfirming] = React.useState(false);\n const [busy, setBusy] = React.useState(false);\n const [err, setErr] = React.useState<string | null>(null);\n\n React.useEffect(() => {\n let alive = true;\n fetchOcBond({ authOrigin }).then(\n (b) => alive && setBond(b),\n () => alive && setLoadErr(true)\n );\n return () => {\n alive = false;\n };\n }, [authOrigin, nonce]);\n\n const call = React.useCallback(\n async (method: string, body?: unknown) => {\n const r = await fetch(`${authOrigin}/api/auth/bond`, {\n method,\n credentials: 'include',\n headers: body ? { 'Content-Type': 'application/json' } : undefined,\n body: body ? JSON.stringify(body) : undefined,\n });\n const j = (await r.json().catch(() => ({}))) as Record<string, unknown> & {\n ok?: boolean;\n reason?: string;\n token?: unknown;\n };\n // The host minted for the account this tab shows; keep the pin current.\n const pin = readTabSession();\n if (pin && r.ok && typeof j.token === 'string') {\n writeTabSession({ token: j.token, didOc: pin.didOc });\n }\n return { ok: r.ok && j.ok === true, ...j };\n },\n [authOrigin]\n );\n\n async function release(): Promise<void> {\n setBusy(true);\n setErr(null);\n const j = await call('DELETE').catch(() => ({ ok: false, reason: 'network' }));\n setBusy(false);\n if (!j.ok) return setErr(bondRefusal(j.reason));\n setConfirming(false);\n setBond(null);\n onChange?.(null);\n }\n\n if (loadErr) {\n return (\n <div className={className} data-oc-bond=\"\" style={panelStyle('warn')} role=\"alert\">\n <p style={bodyStyle}>couldn&apos;t read your backing · nothing was changed</p>\n <button\n type=\"button\"\n style={ghostBtnStyle(false)}\n onClick={() => {\n setLoadErr(false);\n setNonce((x) => x + 1);\n }}\n >\n try again\n </button>\n </div>\n );\n }\n if (bond === undefined) {\n return (\n <div className={className} data-oc-bond=\"\" style={panelStyle()}>\n <span style={hintStyle}>reading your backing…</span>\n </div>\n );\n }\n\n return (\n <div className={className} data-oc-bond=\"\" style={{ display: 'grid', gap: 16 }}>\n {bond && !adding ? (\n <div style={panelStyle('success')}>\n <SectionLabel tone=\"success\">§ backed by bitcoin</SectionLabel>\n <p style={{ ...bodyStyle, color: V.foreground }}>\n apps see a coin {bondFloorsText(bond)}.\n </p>\n <p style={bodyStyle}>\n coin {short(bond.outpoint)}, {n(bond.sats)} sats · last found unspent at\n block {n(bond.checked_height)} · only you and orangecheck see this line\n </p>\n {confirming ? (\n <div role=\"group\" aria-label=\"stop backing\" data-oc-bond-confirm=\"\">\n <p style={{ ...bodyStyle, color: V.foreground, margin: 0 }}>\n stop backing? apps that ask for a backed account may stop letting\n you in.\n </p>\n <FormButtons>\n <button\n type=\"button\"\n style={dangerBtnStyle(busy)}\n disabled={busy}\n onClick={() => void release()}\n >\n stop backing\n </button>\n <button\n type=\"button\"\n style={ghostBtnStyle(busy)}\n disabled={busy}\n autoFocus\n onClick={() => {\n setErr(null);\n setConfirming(false);\n }}\n >\n keep\n </button>\n </FormButtons>\n </div>\n ) : (\n <FormButtons>\n <button\n type=\"button\"\n style={ghostBtnStyle(false)}\n onClick={() => {\n setErr(null);\n setAdding(true);\n }}\n >\n use another coin\n </button>\n <button\n type=\"button\"\n style={dangerBtnStyle(false)}\n onClick={() => setConfirming(true)}\n >\n stop backing\n </button>\n </FormButtons>\n )}\n {err ? <ErrorLine>{err}</ErrorLine> : null}\n </div>\n ) : null}\n {!bond && !adding ? (\n <div style={panelStyle()}>\n <SectionLabel>§ not backed</SectionLabel>\n <p style={bodyStyle}>\n your account works everywhere as it is. some apps let in only accounts\n backed by a coin you hold. it stays in your wallet, and apps never see\n which coin.\n </p>\n <button\n type=\"button\"\n style={primaryBtnStyle(false)}\n onClick={() => setAdding(true)}\n >\n back your account\n </button>\n </div>\n ) : null}\n {adding ? (\n <BondForm\n call={call}\n onCancel={() => setAdding(false)}\n onDone={(b) => {\n setBond(b);\n setAdding(false);\n onChange?.(b);\n }}\n />\n ) : null}\n <p style={explainerStyle}>\n Apps see only rough floors, like “at least 100,000 sats, held 30 days or\n more”, and a number unique to that app. Never the coin, your address or your\n balance. An app can block that number; only moving the coin gets past it, and a\n moved coin starts its age again.\n </p>\n </div>\n );\n}\n\ntype Call = (\n method: string,\n body?: unknown\n) => Promise<Record<string, unknown> & { ok: boolean; reason?: string }>;\n\nfunction BondForm({\n call,\n onCancel,\n onDone,\n}: {\n call: Call;\n onCancel: () => void;\n onDone: (bond: OcBondView) => void;\n}): React.ReactElement {\n const [address, setAddress] = React.useState('');\n const [coins, setCoins] = React.useState<Coin[] | null>(null);\n const [chosen, setChosen] = React.useState('');\n const [message, setMessage] = React.useState<string | null>(null);\n const [busy, setBusy] = React.useState(false);\n const [err, setErr] = React.useState<string | null>(null);\n const id = React.useId();\n\n const addr = address.trim();\n const canFind = addr.length >= 14;\n\n async function run(fn: () => Promise<void>): Promise<void> {\n setBusy(true);\n setErr(null);\n try {\n await fn();\n } catch {\n setErr(bondRefusal('chain_unavailable'));\n } finally {\n setBusy(false);\n }\n }\n\n const findCoins = () =>\n run(async () => {\n const j = await call('POST', { action: 'coins', address: addr });\n if (!j.ok) return setErr(bondRefusal(j.reason));\n const list = (j.coins as Coin[]) ?? [];\n setCoins(list);\n setChosen(list.find(usable)?.outpoint ?? '');\n });\n\n const getMessage = () =>\n run(async () => {\n const j = await call('POST', { action: 'message', address: addr, outpoint: chosen });\n if (!j.ok || typeof j.message !== 'string') return setErr(bondRefusal(j.reason));\n setMessage(j.message);\n });\n\n const submit = (signature: string) =>\n run(async () => {\n const j = await call('POST', { action: 'bond', message, signature });\n if (!j.ok || !j.bond) return setErr(bondRefusal(j.reason));\n onDone(j.bond as OcBondView);\n });\n\n /** Back one step: from signing to the coin list, or from the coins to the address. */\n function back(): void {\n setErr(null);\n if (message) setMessage(null);\n else setCoins(null);\n }\n\n const error = err ? <ErrorLine>{err}</ErrorLine> : null;\n const backButton = (\n <button type=\"button\" style={ghostBtnStyle(busy)} disabled={busy} onClick={back}>\n back\n </button>\n );\n const cancelButton = (\n <button type=\"button\" style={ghostBtnStyle(busy)} disabled={busy} onClick={onCancel}>\n cancel\n </button>\n );\n\n let step: React.ReactNode;\n if (message) {\n step = (\n <>\n <p style={bodyStyle}>\n sign with the wallet that holds <span style={addressStyle}>{addr}</span>.\n signing spends nothing, and the coin stays in your wallet.\n </p>\n <LazyWalletButton\n address={addr}\n message={message}\n showManual\n onSigned={(sig) => void submit(sig)}\n onError={(e) => setErr(e.message ?? 'the wallet refused to sign')}\n />\n {error}\n <FormButtons>\n {backButton}\n {cancelButton}\n </FormButtons>\n </>\n );\n } else if (coins) {\n const eligible = coins.filter((c) => c.sats >= MIN_SATS);\n const dust = coins.length - eligible.length;\n step = (\n <>\n {coins.length === 0 ? (\n <p style={bodyStyle}>\n no confirmed coins at that address · send at least {n(MIN_SATS)} sats\n there and wait for six confirmations, about an hour. the longer a coin\n sits, the more it says.\n </p>\n ) : eligible.length === 0 ? (\n <>\n <p style={{ ...bodyStyle, color: V.foreground }}>{dustText(dust)}</p>\n <p style={bodyStyle}>\n send at least {n(MIN_SATS)} sats there in one payment and wait for six\n confirmations, about an hour. coins aren&apos;t added together.\n </p>\n </>\n ) : (\n <>\n <p id={`${id}-pick`} style={bodyStyle}>\n pick one coin · coins aren&apos;t added together\n </p>\n <div\n role=\"radiogroup\"\n aria-labelledby={`${id}-pick`}\n style={{ display: 'grid', gap: 8 }}\n >\n {eligible.map((c) => {\n const young = c.age_blocks < MIN_CONFIRMATIONS;\n return (\n <label\n key={c.outpoint}\n style={coinStyle(chosen === c.outpoint, young)}\n >\n <input\n type=\"radio\"\n name=\"oc-bond-coin\"\n value={c.outpoint}\n checked={chosen === c.outpoint}\n disabled={young}\n onChange={() => setChosen(c.outpoint)}\n style={{ accentColor: V.primary, margin: '2px 0 0' }}\n />\n <span style={{ display: 'grid', gap: 2, minWidth: 0 }}>\n <span\n style={{\n color: young ? V.muted : V.foreground,\n fontSize: 13,\n }}\n >\n {n(c.sats)} sats, held{' '}\n {Math.floor(c.age_blocks / 144)} days\n </span>\n <span style={smallStyle}>\n {young\n ? 'too recent: needs six confirmations'\n : `apps would see ${bondFloorsText(c)}`}\n </span>\n <span\n style={{\n ...smallStyle,\n fontFamily: SIGNIN_FONT,\n wordBreak: 'break-all',\n }}\n >\n {short(c.outpoint)}\n </span>\n </span>\n </label>\n );\n })}\n </div>\n {dust > 0 ? (\n <p style={{ ...smallStyle, marginTop: 8 }}>{dustText(dust)}</p>\n ) : null}\n </>\n )}\n {error}\n <FormButtons>\n {eligible.length > 0 ? (\n <button\n type=\"button\"\n style={primaryBtnStyle(busy || !chosen)}\n disabled={busy || !chosen}\n onClick={() => void getMessage()}\n >\n continue\n </button>\n ) : null}\n {backButton}\n {cancelButton}\n </FormButtons>\n </>\n );\n } else {\n step = (\n <form\n style={{ margin: 0 }}\n onSubmit={(e) => {\n e.preventDefault();\n if (canFind && !busy) void findCoins();\n }}\n >\n <label htmlFor={`${id}-address`} style={fieldLabelStyle}>\n bitcoin address that holds the coin\n </label>\n <p id={`${id}-advice`} style={{ ...bodyStyle, margin: '0 0 8px' }}>\n use one that holds nothing else of yours: orangecheck learns its address and\n amount. apps never do.\n </p>\n <input\n id={`${id}-address`}\n aria-describedby={`${id}-advice`}\n style={inputStyle}\n value={address}\n onChange={(e) => setAddress(e.target.value)}\n placeholder=\"bc1q…\"\n spellCheck={false}\n autoComplete=\"off\"\n autoCapitalize=\"off\"\n />\n {error}\n <FormButtons>\n <button\n type=\"submit\"\n style={primaryBtnStyle(busy || !canFind)}\n disabled={busy || !canFind}\n >\n find its coins\n </button>\n {cancelButton}\n </FormButtons>\n </form>\n );\n }\n\n return (\n <div style={panelStyle('accent')} data-oc-bond-form=\"\">\n <SectionLabel>§ back your account</SectionLabel>\n {step}\n </div>\n );\n}\n"],"mappings":"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","names":[]}