cauthy-auth 0.32.0 → 0.34.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/dist/react.js CHANGED
@@ -910,6 +910,7 @@ var TEXTS = {
910
910
  last_manager: "Somebody else must manage the members first: give that role to another member.",
911
911
  last_owner: "An organization keeps at least one owner: make another member owner first.",
912
912
  organization_not_found: "That organization no longer exists.",
913
+ member_exists: "That person already belongs to the organization; their role was not changed. Change it in the list instead.",
913
914
  already_member: "That person already belongs to the organization. Change their role instead.",
914
915
  unknown_role: "Choose one of the roles offered.",
915
916
  user_not_active: "That account is disabled.",
@@ -1090,6 +1091,7 @@ var TEXTS = {
1090
1091
  last_manager: "Primero alguien m\xE1s debe administrar los miembros: dale ese rol a otro miembro.",
1091
1092
  last_owner: "Una organizaci\xF3n conserva al menos un propietario: primero haz propietario a otro miembro.",
1092
1093
  organization_not_found: "Esa organizaci\xF3n ya no existe.",
1094
+ member_exists: "Esa persona ya pertenece a la organizaci\xF3n; su rol no cambi\xF3. C\xE1mbialo en la lista.",
1093
1095
  already_member: "Esa persona ya pertenece a la organizaci\xF3n. Cambia su rol.",
1094
1096
  unknown_role: "Escoge uno de los roles ofrecidos.",
1095
1097
  user_not_active: "Esa cuenta est\xE1 desactivada.",
@@ -1134,7 +1136,7 @@ var TEXTS = {
1134
1136
  }
1135
1137
  }
1136
1138
  };
1137
- var PlainButton = (p) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { ...p });
1139
+ var PlainButton = ({ variant: _variant, loading: _loading, ...p }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { ...p });
1138
1140
  var PlainInput = (p) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { ...p });
1139
1141
  var looks = /* @__PURE__ */ new Map();
1140
1142
  var loaded = /* @__PURE__ */ new Set();
@@ -1249,8 +1251,24 @@ function useForm(props) {
1249
1251
  const C = props.components ?? {};
1250
1252
  const Button = C.Button ?? PlainButton;
1251
1253
  const Input = C.Input ?? PlainInput;
1252
- const [error, setError] = (0, import_react.useState)(null);
1253
- const [busy, setBusy] = (0, import_react.useState)(false);
1254
+ const [error, setOnlyError] = (0, import_react.useState)(null);
1255
+ const [problems, setProblems] = (0, import_react.useState)({ list: [], round: 0 });
1256
+ const setError = (0, import_react.useCallback)((e) => {
1257
+ setOnlyError(e);
1258
+ setProblems((p) => p.list.length === 0 ? p : { list: [], round: p.round });
1259
+ }, []);
1260
+ const check = (...found) => {
1261
+ const list = found.filter((e) => Boolean(e));
1262
+ setOnlyError(null);
1263
+ setProblems((p) => ({ list, round: list.length > 0 ? p.round + 1 : p.round }));
1264
+ return list.length === 0;
1265
+ };
1266
+ const fieldError = (name) => problems.list.find((e) => e.field === name) ?? (error?.field === name ? error : null);
1267
+ const firstWrong = (name) => problems.list[0]?.field === name ? problems.round : 0;
1268
+ const [acting, setActing] = (0, import_react.useState)(null);
1269
+ const busy = acting !== null;
1270
+ const setBusy = (0, import_react.useCallback)((on) => setActing(on === false ? null : on === true ? SUBMIT : on), []);
1271
+ const loading = (key = SUBMIT) => acting === key;
1254
1272
  const [policy, setPolicy] = (0, import_react.useState)(void 0);
1255
1273
  const locale = props.locale ?? "en";
1256
1274
  const say = (e) => {
@@ -1263,7 +1281,28 @@ function useForm(props) {
1263
1281
  else window.location.assign(sitePath(to) ?? sameOrigin(props.returnTo) ?? "/");
1264
1282
  };
1265
1283
  const root = useRoot(props);
1266
- return { t, cx, own, C, Button, Input, error, setError, busy, setBusy, say, done, root, policy, setPolicy, passwordRule };
1284
+ return { t, cx, own, C, Button, Input, error, setError, check, fieldError, firstWrong, busy, setBusy, loading, say, done, root, policy, setPolicy, passwordRule };
1285
+ }
1286
+ var SUBMIT = "submit";
1287
+ function Wrap(p) {
1288
+ const { Form: OwnForm } = p.f.C;
1289
+ if (OwnForm) {
1290
+ const own = /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1291
+ OwnForm,
1292
+ {
1293
+ as: p.as === "form" ? "form" : "div",
1294
+ onSubmit: p.onSubmit,
1295
+ noValidate: p.noValidate,
1296
+ "aria-busy": p.busy,
1297
+ className: p.slot ? p.f.own(p.slot) : void 0,
1298
+ section: p.section,
1299
+ children: p.children
1300
+ }
1301
+ );
1302
+ return p.root?.["data-cauthy"] !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { ...p.root, children: own }) : own;
1303
+ }
1304
+ const Tag2 = p.as;
1305
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Tag2, { className: p.slot ? p.f.cx(p.slot) : void 0, ...p.root, onSubmit: p.onSubmit, noValidate: p.noValidate, "aria-busy": p.busy, children: p.children });
1267
1306
  }
1268
1307
  function Note({ f, children, footer }) {
1269
1308
  const { Text } = f.C;
@@ -1344,6 +1383,11 @@ function isPasswordRule(e) {
1344
1383
  if (e.code === "weak_password" || PASSWORD_RULES.includes(e.code)) return true;
1345
1384
  return (e.code === "too_short" || e.code === "too_long") && (e.field === "password" || e.field === "newPassword");
1346
1385
  }
1386
+ function newPasswordProblem(value, policy, field = "password") {
1387
+ const bad = checkNewPassword(value, policy);
1388
+ if (!bad) return null;
1389
+ return { code: bad.code, message: bad.message, field, ...bad.code === "required" ? {} : { reasons: passwordProblems(value, policy) } };
1390
+ }
1347
1391
  function usePasswordPolicy(f, methods) {
1348
1392
  const { setPolicy } = f;
1349
1393
  (0, import_react.useEffect)(() => {
@@ -1353,7 +1397,11 @@ function usePasswordPolicy(f, methods) {
1353
1397
  function Field(p) {
1354
1398
  const id = (0, import_react.useId)();
1355
1399
  const { f } = p;
1356
- const bad = f.error?.field === p.name ? f.error : null;
1400
+ const bad = f.fieldError(p.name);
1401
+ const focus = f.firstWrong(p.name);
1402
+ (0, import_react.useEffect)(() => {
1403
+ if (focus > 0 && typeof document !== "undefined") document.getElementById(id)?.focus();
1404
+ }, [focus, id]);
1357
1405
  const described = [bad ? `${id}-error` : "", p.hint ? `${id}-hint` : ""].filter(Boolean).join(" ");
1358
1406
  const input = /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1359
1407
  f.Input,
@@ -1429,7 +1477,7 @@ function SecondStepForm(p) {
1429
1477
  });
1430
1478
  }, []);
1431
1479
  const sendEmail = async () => {
1432
- f.setBusy(true);
1480
+ f.setBusy("email");
1433
1481
  f.setError(null);
1434
1482
  const r = await auth.signIn.mfaSendEmail();
1435
1483
  f.setBusy(false);
@@ -1440,7 +1488,7 @@ function SecondStepForm(p) {
1440
1488
  const pushWait = (0, import_react.useRef)(null);
1441
1489
  (0, import_react.useEffect)(() => () => pushWait.current?.abort(), []);
1442
1490
  const sendPush = async () => {
1443
- f.setBusy(true);
1491
+ f.setBusy("push");
1444
1492
  f.setError(null);
1445
1493
  const r = await auth.signIn.mfaPush();
1446
1494
  f.setBusy(false);
@@ -1456,7 +1504,7 @@ function SecondStepForm(p) {
1456
1504
  f.done();
1457
1505
  };
1458
1506
  const sendCode = async () => {
1459
- f.setBusy(true);
1507
+ f.setBusy("phone");
1460
1508
  f.setError(null);
1461
1509
  const r = await auth.signIn.mfaSendCode();
1462
1510
  f.setBusy(false);
@@ -1474,10 +1522,10 @@ function SecondStepForm(p) {
1474
1522
  };
1475
1523
  const general = f.error && !f.error.field ? f.error : null;
1476
1524
  const to = phoneWay?.phone?.number ?? "";
1477
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { className: f.cx("form"), ...f.root, onSubmit: submit, noValidate: true, children: [
1525
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "form", slot: "form", root: f.root, onSubmit: submit, noValidate: true, children: [
1478
1526
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Note, { f, children: phoneSent ? f.t.codeSent.replace("{to}", phoneSent) : phoneWay?.email ? f.t.codeHelpEmail.replace("{to}", phoneWay.email.to) : phoneWay?.phone && !phoneWay.totp ? f.t.codeHelpPhone : f.t.codeHelp }),
1479
1527
  general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "danger", children: f.say(general) }),
1480
- phoneWay?.email && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => void sendEmail(), children: f.t.sendEmailAgain }),
1528
+ phoneWay?.email && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", variant: "secondary", loading: f.loading("email"), disabled: f.busy, className: f.cx("providerButton"), onClick: () => void sendEmail(), children: f.t.sendEmailAgain }),
1481
1529
  pushNumber > 0 ? f.C.Text ? (
1482
1530
  // The application's text: the number to pick drawn large, by its own rules.
1483
1531
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { role: "status", "aria-live": "polite", children: [
@@ -1489,15 +1537,15 @@ function SecondStepForm(p) {
1489
1537
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: f.t.pushPick.replace("{n}", String(pushNumber)) }),
1490
1538
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: { fontSize: 48, fontWeight: 700, margin: "4px 0", textAlign: "center" }, children: pushNumber }),
1491
1539
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: f.t.pushWaiting })
1492
- ] }) : phoneWay?.push && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", disabled: f.busy, className: f.cx("button"), onClick: () => void sendPush(), children: f.t.pushSend }),
1493
- phoneWay?.phone && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => void sendCode(), children: (phoneWay.phone.channel === "whatsapp" ? f.t.sendCodeWhatsapp : f.t.sendCodeSms).replace("{to}", to) }),
1540
+ ] }) : phoneWay?.push && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", variant: "secondary", loading: f.loading("push"), disabled: f.busy, className: f.cx("button"), onClick: () => void sendPush(), children: f.t.pushSend }),
1541
+ phoneWay?.phone && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", variant: "secondary", loading: f.loading("phone"), disabled: f.busy, className: f.cx("providerButton"), onClick: () => void sendCode(), children: (phoneWay.phone.channel === "whatsapp" ? f.t.sendCodeWhatsapp : f.t.sendCodeSms).replace("{to}", to) }),
1494
1542
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "code", type: "text", label: f.t.code, value: code, autoComplete: "one-time-code", required: true, onChange: setCode }),
1495
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.verify })
1543
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", variant: "primary", loading: f.loading(), disabled: f.busy, className: f.cx("button"), children: f.t.verify })
1496
1544
  ] });
1497
1545
  }
1498
1546
  function ProviderButtons(p) {
1499
1547
  const { f } = p;
1500
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: p.providers.map((provider) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(f.Button, { type: "button", className: f.cx("providerButton"), onClick: () => p.onPick(provider.id), children: [
1548
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: p.providers.map((provider) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(f.Button, { type: "button", variant: "secondary", className: f.cx("providerButton"), onClick: () => p.onPick(provider.id), children: [
1501
1549
  provider.iconUrl && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("img", { src: provider.iconUrl, alt: "", width: 18, height: 18, className: f.cx("providerIcon") }),
1502
1550
  f.t.continueWith,
1503
1551
  " ",
@@ -1537,7 +1585,7 @@ function SignIn(props) {
1537
1585
  }, [email, methods]);
1538
1586
  const toCompany = () => {
1539
1587
  autofill.current?.abort();
1540
- f.setBusy(true);
1588
+ f.setBusy("sso");
1541
1589
  auth.signIn.sso({ email }, { returnTo: props.returnTo });
1542
1590
  };
1543
1591
  (0, import_react.useEffect)(() => {
@@ -1579,6 +1627,7 @@ function SignIn(props) {
1579
1627
  if (methods?.magicLink) return sendLink();
1580
1628
  return;
1581
1629
  }
1630
+ if (step === "password" && !f.check(checkEmail(email), checkPassword(password))) return;
1582
1631
  f.setBusy(true);
1583
1632
  f.setError(null);
1584
1633
  const r = step === "sent" ? await auth.signIn.emailCode({ code }) : await auth.signIn.email({ email, password, captchaToken: challenge.token });
@@ -1594,7 +1643,7 @@ function SignIn(props) {
1594
1643
  };
1595
1644
  const usePasskey = async () => {
1596
1645
  autofill.current?.abort();
1597
- f.setBusy(true);
1646
+ f.setBusy("passkey");
1598
1647
  f.setError(null);
1599
1648
  const r = await auth.signIn.passkey({ returnTo: props.returnTo, locale: props.locale, email });
1600
1649
  if (r.ok && r.status === "redirected") return;
@@ -1605,7 +1654,7 @@ function SignIn(props) {
1605
1654
  };
1606
1655
  const sendLink = async () => {
1607
1656
  autofill.current?.abort();
1608
- f.setBusy(true);
1657
+ f.setBusy("link");
1609
1658
  f.setError(null);
1610
1659
  const r = await auth.signIn.magicLink({ email, captchaToken: challenge.token, create: props.createAccounts, returnTo: props.returnTo });
1611
1660
  f.setBusy(false);
@@ -1616,24 +1665,24 @@ function SignIn(props) {
1616
1665
  };
1617
1666
  const general = f.error && !f.error.field ? f.error : null;
1618
1667
  const asksEmail = offers.email;
1619
- if (step === "opening") return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: f.cx("form"), ...f.root, "aria-busy": "true" });
1668
+ if (step === "opening") return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Wrap, { f, as: "div", slot: "form", root: f.root, busy: true });
1620
1669
  if (step === "sent") {
1621
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { className: f.cx("form"), ...f.root, onSubmit: submit, noValidate: true, children: [
1670
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "form", slot: "form", root: f.root, onSubmit: submit, noValidate: true, children: [
1622
1671
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Note, { f, children: f.t.signInLinkSent }),
1623
1672
  general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "danger", children: f.say(general) }),
1624
1673
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Note, { f, children: f.t.codeFromMail }),
1625
1674
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "code", type: "text", label: f.t.code, value: code, autoComplete: "one-time-code", required: true, onChange: setCode }),
1626
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.verify })
1675
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", variant: "primary", loading: f.loading(), disabled: f.busy, className: f.cx("button"), children: f.t.verify })
1627
1676
  ] });
1628
1677
  }
1629
1678
  if (step === "code") return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SecondStepForm, { f, auth });
1630
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { className: f.cx("form"), ...f.root, onSubmit: submit, noValidate: true, children: [
1679
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "form", slot: "form", root: f.root, onSubmit: submit, noValidate: true, children: [
1631
1680
  general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "danger", children: f.say(general) }),
1632
1681
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ProviderButtons, { f, providers: methods?.providers ?? [], onPick: (id) => auth.signIn.social(id, { returnTo: props.returnTo }) }),
1633
- offers.passkey && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => void usePasskey(), children: f.t.usePasskey }),
1682
+ offers.passkey && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", variant: "secondary", loading: f.loading("passkey"), disabled: f.busy, className: f.cx("providerButton"), onClick: () => void usePasskey(), children: f.t.usePasskey }),
1634
1683
  asksEmail && ((methods?.providers.length ?? 0) > 0 || offers.passkey) && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Or, { f, children: f.t.orWithEmail }),
1635
1684
  asksEmail && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "email", type: "email", label: f.t.email, value: email, autoComplete: methods?.passkeyHere ? "username webauthn" : "username", required: true, onChange: setEmail }),
1636
- company && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(f.Button, { type: "button", disabled: f.busy, className: f.cx(company.enforced ? "button" : "providerButton"), onClick: toCompany, children: [
1685
+ company && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(f.Button, { type: "button", variant: company.enforced ? "primary" : "secondary", loading: f.loading("sso"), disabled: f.busy, className: f.cx(company.enforced ? "button" : "providerButton"), onClick: toCompany, children: [
1637
1686
  f.t.continueWith,
1638
1687
  " ",
1639
1688
  company.name
@@ -1655,10 +1704,10 @@ function SignIn(props) {
1655
1704
  }
1656
1705
  ),
1657
1706
  challenge.node,
1658
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.signIn })
1707
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", variant: "primary", loading: f.loading(), disabled: f.busy, className: f.cx("button"), children: f.t.signIn })
1659
1708
  ] }),
1660
1709
  !company?.enforced && !passwordOn && methods?.magicLink && challenge.node,
1661
- methods?.magicLink && !company?.enforced && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => void sendLink(), children: f.t.emailMeALink }),
1710
+ methods?.magicLink && !company?.enforced && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", variant: "secondary", loading: f.loading("link"), disabled: f.busy, className: f.cx("providerButton"), onClick: () => void sendLink(), children: f.t.emailMeALink }),
1662
1711
  methods?.signUp !== false && props.links?.signUp && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Note, { f, footer: true, children: [
1663
1712
  f.t.noAccount,
1664
1713
  " ",
@@ -1698,11 +1747,19 @@ function SignUp(props) {
1698
1747
  const [acceptLegal, setAcceptLegal] = (0, import_react.useState)(false);
1699
1748
  const [consents, setConsents] = (0, import_react.useState)({});
1700
1749
  const legal = methods?.legal;
1750
+ const guest = auth.session?.user.isAnonymous === true;
1701
1751
  const submit = async (e) => {
1702
1752
  e.preventDefault();
1753
+ const asksCompany = methods?.organization?.ask === true;
1754
+ if (!f.check(
1755
+ asksCompany && checkOrganization(organization, methods?.organization?.required === true),
1756
+ checkName(name),
1757
+ checkEmail(email),
1758
+ newPasswordProblem(password, f.policy)
1759
+ )) return;
1703
1760
  f.setBusy(true);
1704
1761
  f.setError(null);
1705
- const r = auth.session?.user.isAnonymous === true ? await auth.account.upgrade({ name, email, password }) : await auth.signUp.email({
1762
+ const r = guest ? await auth.account.upgrade({ name, email, password }) : await auth.signUp.email({
1706
1763
  name,
1707
1764
  email,
1708
1765
  password,
@@ -1718,30 +1775,31 @@ function SignUp(props) {
1718
1775
  };
1719
1776
  const general = f.error && !f.error.field ? f.error : null;
1720
1777
  const passwordOn = methods?.passwordSignIn !== false;
1721
- const providers = auth.session?.user.isAnonymous === true || methods?.organization?.required ? [] : methods?.providers ?? [];
1778
+ const providers = guest || methods?.organization?.required ? [] : methods?.providers ?? [];
1722
1779
  if (sent) {
1723
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: f.cx("form"), ...f.root, children: [
1780
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "div", slot: "form", root: f.root, children: [
1724
1781
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Note, { f, children: f.t.checkEmail }),
1725
1782
  props.links?.signIn && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(A, { f, href: props.links.signIn, children: f.t.backToSignIn })
1726
1783
  ] });
1727
1784
  }
1785
+ if (methods === null) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Wrap, { f, as: "div", slot: "form", root: f.root, busy: true });
1728
1786
  const footer = props.links?.signIn && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Note, { f, footer: true, children: [
1729
1787
  f.t.haveAccount,
1730
1788
  " ",
1731
1789
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(A, { f, href: props.links.signIn, children: f.t.signIn })
1732
1790
  ] });
1733
1791
  if (!passwordOn) {
1734
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: f.cx("form"), ...f.root, children: [
1792
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "div", slot: "form", root: f.root, children: [
1735
1793
  general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "danger", children: f.say(general) }),
1736
1794
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ProviderButtons, { f, providers, onPick: (id) => auth.signIn.social(id, { returnTo: props.returnTo }) }),
1737
1795
  footer
1738
1796
  ] });
1739
1797
  }
1740
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { className: f.cx("form"), ...f.root, onSubmit: submit, noValidate: true, children: [
1798
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "form", slot: "form", root: f.root, onSubmit: submit, noValidate: true, children: [
1741
1799
  general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "danger", children: f.say(general) }),
1742
1800
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ProviderButtons, { f, providers, onPick: (id) => auth.signIn.social(id, { returnTo: props.returnTo }) }),
1743
1801
  providers.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Or, { f, children: f.t.orWithEmail }),
1744
- methods?.organization?.ask && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "organization", type: "text", label: f.t.company, value: organization, autoComplete: "organization", required: methods.organization.required, onChange: setOrganization }),
1802
+ methods.organization?.ask && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "organization", type: "text", label: f.t.company, value: organization, autoComplete: "organization", required: methods.organization.required, onChange: setOrganization }),
1745
1803
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "name", type: "text", label: f.t.name, value: name, autoComplete: "name", onChange: setName }),
1746
1804
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "email", type: "email", label: f.t.email, value: email, autoComplete: "username", required: true, onChange: setEmail }),
1747
1805
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "password", type: "password", label: f.t.password, value: password, autoComplete: "new-password", required: true, onChange: setPassword, hint: f.passwordRule }),
@@ -1752,7 +1810,7 @@ function SignUp(props) {
1752
1810
  ] }),
1753
1811
  legal?.consentPurposes.map((p) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Check, { f, name: `consent_${p.key}`, checked: consents[p.key] === true, required: p.required, onChange: (on) => setConsents({ ...consents, [p.key]: on }), children: p.label }, p.key)),
1754
1812
  challenge.node,
1755
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.signUp }),
1813
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", variant: "primary", loading: f.loading(), disabled: f.busy, className: f.cx("button"), children: f.t.signUp }),
1756
1814
  footer
1757
1815
  ] });
1758
1816
  }
@@ -1774,16 +1832,16 @@ function ForgotPassword(props) {
1774
1832
  };
1775
1833
  const general = f.error && !f.error.field ? f.error : null;
1776
1834
  if (sent) {
1777
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: f.cx("form"), ...f.root, children: [
1835
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "div", slot: "form", root: f.root, children: [
1778
1836
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Note, { f, children: f.t.linkSent }),
1779
1837
  props.links?.signIn && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(A, { f, href: props.links.signIn, children: f.t.backToSignIn })
1780
1838
  ] });
1781
1839
  }
1782
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { className: f.cx("form"), ...f.root, onSubmit: submit, noValidate: true, children: [
1840
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "form", slot: "form", root: f.root, onSubmit: submit, noValidate: true, children: [
1783
1841
  general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "danger", children: f.say(general) }),
1784
1842
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "email", type: "email", label: f.t.email, value: email, autoComplete: "username", required: true, onChange: setEmail }),
1785
1843
  challenge.node,
1786
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.sendLink }),
1844
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", variant: "primary", loading: f.loading(), disabled: f.busy, className: f.cx("button"), children: f.t.sendLink }),
1787
1845
  props.links?.signIn && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Note, { f, footer: true, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(A, { f, href: props.links.signIn, children: f.t.backToSignIn }) })
1788
1846
  ] });
1789
1847
  }
@@ -1810,21 +1868,21 @@ function ResetPassword(props) {
1810
1868
  const general = f.error && !f.error.field ? f.error : null;
1811
1869
  const back = props.links?.signIn && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(A, { f, href: props.links.signIn, children: f.t.backToSignIn });
1812
1870
  if (changed) {
1813
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: f.cx("form"), ...f.root, children: [
1871
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "div", slot: "form", root: f.root, children: [
1814
1872
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Note, { f, children: f.t.passwordChanged }),
1815
1873
  back
1816
1874
  ] });
1817
1875
  }
1818
1876
  if (token === "") {
1819
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: f.cx("form"), ...f.root, children: [
1877
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "div", slot: "form", root: f.root, children: [
1820
1878
  general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "danger", children: f.say(general) }),
1821
1879
  props.links?.forgotPassword && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(A, { f, href: props.links.forgotPassword, children: f.t.forgotPassword })
1822
1880
  ] });
1823
1881
  }
1824
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { className: f.cx("form"), ...f.root, onSubmit: submit, noValidate: true, children: [
1882
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "form", slot: "form", root: f.root, onSubmit: submit, noValidate: true, children: [
1825
1883
  general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "danger", children: f.say(general) }),
1826
1884
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "password", type: "password", label: f.t.newPassword, value: password, autoComplete: "new-password", required: true, onChange: setPassword, hint: f.passwordRule }),
1827
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", disabled: f.busy || token === null, className: f.cx("button"), children: f.t.savePassword })
1885
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", variant: "primary", loading: f.loading(), disabled: f.busy || token === null, className: f.cx("button"), children: f.t.savePassword })
1828
1886
  ] });
1829
1887
  }
1830
1888
  function VerifyEmail(props) {
@@ -1843,7 +1901,7 @@ function VerifyEmail(props) {
1843
1901
  setState("failed");
1844
1902
  });
1845
1903
  });
1846
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: f.cx("form"), ...f.root, "aria-busy": state === "confirming", children: [
1904
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "div", slot: "form", root: f.root, busy: state === "confirming", children: [
1847
1905
  state === "confirming" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Note, { f, children: f.t.confirming }),
1848
1906
  state === "confirmed" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Note, { f, children: f.t.emailConfirmed }),
1849
1907
  state === "failed" && f.error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "danger", children: f.say(f.error) }),
@@ -1887,10 +1945,10 @@ function AcceptInvite(props) {
1887
1945
  const general = f.error && !f.error.field ? f.error : null;
1888
1946
  const back = props.links?.signIn && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(A, { f, href: props.links.signIn, children: f.t.backToSignIn });
1889
1947
  if (state === "opening") {
1890
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: f.cx("form"), ...f.root, "aria-busy": "true", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Note, { f, children: f.t.openingInvitation }) });
1948
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Wrap, { f, as: "div", slot: "form", root: f.root, busy: true, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Note, { f, children: f.t.openingInvitation }) });
1891
1949
  }
1892
1950
  if (state === "invalid") {
1893
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: f.cx("form"), ...f.root, children: [
1951
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "div", slot: "form", root: f.root, children: [
1894
1952
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "danger", children: general ? f.say(general) : f.t.invitationInvalid }),
1895
1953
  back
1896
1954
  ] });
@@ -1898,13 +1956,13 @@ function AcceptInvite(props) {
1898
1956
  if (state === "code") return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SecondStepForm, { f, auth });
1899
1957
  const workspace = invitation?.workspace ?? "";
1900
1958
  const heading = invitation?.organization ? f.t.invitedToOrganization.replace("{organization}", invitation.organization).replace("{workspace}", workspace) : f.t.invitedTo.replace("{workspace}", workspace);
1901
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { className: f.cx("form"), ...f.root, onSubmit: submit, noValidate: true, children: [
1959
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "form", slot: "form", root: f.root, onSubmit: submit, noValidate: true, children: [
1902
1960
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Heading, { f, children: heading }),
1903
1961
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Note, { f, children: f.t.invitedAs.replace("{email}", invitation?.email ?? "") }),
1904
1962
  general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "danger", children: f.say(general) }),
1905
1963
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { type: "email", name: "username", autoComplete: "username", value: invitation?.email ?? "", readOnly: true, hidden: true }),
1906
1964
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "password", type: "password", label: f.t.choosePassword, value: password, autoComplete: "new-password", required: true, onChange: setPassword, hint: f.passwordRule }),
1907
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.acceptInvite })
1965
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", variant: "primary", loading: f.loading(), disabled: f.busy, className: f.cx("button"), children: f.t.acceptInvite })
1908
1966
  ] });
1909
1967
  }
1910
1968
  function PendingTerms(props) {
@@ -1924,7 +1982,7 @@ function PendingTerms(props) {
1924
1982
  setOwed([]);
1925
1983
  props.onSuccess?.();
1926
1984
  };
1927
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: f.cx("form"), ...f.root, children: [
1985
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "div", slot: "form", root: f.root, children: [
1928
1986
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Heading, { f, children: f.t.termsChanged }),
1929
1987
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Note, { f, children: [
1930
1988
  f.t.termsChangedHelp,
@@ -1932,15 +1990,15 @@ function PendingTerms(props) {
1932
1990
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Documents, { f, documents: owed })
1933
1991
  ] }),
1934
1992
  f.error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "danger", children: f.say(f.error) }),
1935
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", disabled: f.busy, className: f.cx("button"), onClick: () => void accept(), children: f.t.acceptTerms })
1993
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", variant: "primary", loading: f.loading(), disabled: f.busy, className: f.cx("button"), onClick: () => void accept(), children: f.t.acceptTerms })
1936
1994
  ] });
1937
1995
  }
1938
1996
  var ACCOUNT_SECTIONS = ["profile", "email", "password", "linked", "secondStep", "phone", "passkeys", "agents", "sessions"];
1939
1997
  function useSection(props) {
1940
1998
  const f = useForm({ ...props, unstyled: true });
1941
1999
  const [done, setDone] = (0, import_react.useState)(null);
1942
- const run = async (action) => {
1943
- f.setBusy(true);
2000
+ const run = async (action, key = SUBMIT) => {
2001
+ f.setBusy(key);
1944
2002
  f.setError(null);
1945
2003
  setDone(null);
1946
2004
  const r = await action();
@@ -1954,13 +2012,12 @@ function useSection(props) {
1954
2012
  function Account(props) {
1955
2013
  const auth = useAuth();
1956
2014
  const { accountUrl } = useSession();
1957
- const { root } = useForm(props);
1958
- const cx = (slot) => props.classNames?.[slot] ?? `cauthy-${slot}`;
2015
+ const f = useForm(props);
1959
2016
  const [account, setAccount] = (0, import_react.useState)(null);
1960
2017
  (0, import_react.useEffect)(() => {
1961
2018
  void auth.account.get().then(setAccount);
1962
2019
  }, []);
1963
- if (account === null) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: cx("account"), ...root, "aria-busy": "true" });
2020
+ if (account === null) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Wrap, { f, as: "div", slot: "account", root: f.root, busy: true });
1964
2021
  const when = props.formatDate ?? ((iso) => new Date(iso).toLocaleString());
1965
2022
  const mfa = (mfaEnabled) => setAccount({ ...account, mfaEnabled });
1966
2023
  const part = (section) => {
@@ -1985,7 +2042,7 @@ function Account(props) {
1985
2042
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SessionsPart, { props, when });
1986
2043
  }
1987
2044
  };
1988
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: cx("account"), ...root, children: (props.sections ?? ACCOUNT_SECTIONS).map((section) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react.Fragment, { children: part(section) }, section)) });
2045
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Wrap, { f, as: "div", slot: "account", root: f.root, children: (props.sections ?? ACCOUNT_SECTIONS).map((section) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react.Fragment, { children: part(section) }, section)) });
1989
2046
  }
1990
2047
  function AccountProfile(props) {
1991
2048
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Account, { ...props, sections: ["profile"] });
@@ -2058,15 +2115,20 @@ function OrganizationMembers(props) {
2058
2115
  (0, import_react.useEffect)(() => {
2059
2116
  void load();
2060
2117
  }, [id]);
2061
- if (gone !== null) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: f.cx("account"), ...root, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "success", children: gone }) });
2062
- if (seat === void 0) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: f.cx("account"), ...root, "aria-busy": "true" });
2063
- if (seat === null) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: f.cx("account"), ...root, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Note, { f, children: f.t.organizationMissing }) });
2118
+ if (gone !== null) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Wrap, { f, as: "div", slot: "account", root, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "success", children: gone }) });
2119
+ if (seat === void 0) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Wrap, { f, as: "div", slot: "account", root, busy: true });
2120
+ if (seat === null) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Wrap, { f, as: "div", slot: "account", root, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Note, { f, children: f.t.organizationMissing }) });
2064
2121
  const manage = seat.can.manageMembers;
2065
2122
  const giveable = rolesToGive(seat, roles, props.roles);
2066
- const label = (r) => roles.find((c) => c.role === r)?.name || r;
2123
+ const roleName = (c) => props.roleLabel?.(c) || c.name || c.role;
2124
+ const label = (r) => {
2125
+ const c = roles.find((x) => x.role === r);
2126
+ return c ? roleName(c) : r;
2127
+ };
2067
2128
  const add = async (e) => {
2068
2129
  e.preventDefault();
2069
- const r = await run(() => auth.organizations.add(id, { email, role, locale: props.locale }));
2130
+ if (!f.check(checkEmail(email))) return;
2131
+ const r = await run(() => auth.organizations.add(id, { email, role, locale: props.locale }), "add");
2070
2132
  if (!r.ok) return;
2071
2133
  setEmail("");
2072
2134
  setDone(f.t.memberAdded.replace("{email}", r.member.email));
@@ -2074,28 +2136,28 @@ function OrganizationMembers(props) {
2074
2136
  await load();
2075
2137
  };
2076
2138
  const change = async (m, next) => {
2077
- const r = await run(() => auth.organizations.setRole(id, m.userId, next));
2139
+ const r = await run(() => auth.organizations.setRole(id, m.userId, next), `role:${m.userId}`);
2078
2140
  if (!r.ok) return;
2079
2141
  saved({ kind: "member.role_changed", organizationId: id, userId: m.userId, role: next, previousRole: m.role });
2080
2142
  await load();
2081
2143
  };
2082
2144
  const remove = async (m) => {
2083
2145
  setAsking(null);
2084
- const r = await run(() => auth.organizations.remove(id, m.userId));
2146
+ const r = await run(() => auth.organizations.remove(id, m.userId), `remove:${m.userId}`);
2085
2147
  if (!r.ok) return;
2086
2148
  saved({ kind: m.you ? "member.left" : "member.removed", organizationId: id, userId: m.userId });
2087
2149
  if (m.you) setSeat(null);
2088
2150
  else await load();
2089
2151
  };
2090
2152
  const inviteAgain = async (m) => {
2091
- const r = await run(() => auth.organizations.inviteAgain(id, m.userId, { locale: props.locale }));
2153
+ const r = await run(() => auth.organizations.inviteAgain(id, m.userId, { locale: props.locale }), `invite:${m.userId}`);
2092
2154
  if (!r.ok) return;
2093
2155
  setDone(f.t.memberInvitationSent);
2094
2156
  saved({ kind: "member.invited_again", organizationId: id, userId: m.userId });
2095
2157
  };
2096
2158
  const rename = async (e) => {
2097
2159
  e.preventDefault();
2098
- const r = await run(() => auth.organizations.rename(id, name));
2160
+ const r = await run(() => auth.organizations.rename(id, name), "rename");
2099
2161
  if (!r.ok) return;
2100
2162
  setSeat(r.organization);
2101
2163
  setName(r.organization.name);
@@ -2104,7 +2166,7 @@ function OrganizationMembers(props) {
2104
2166
  };
2105
2167
  const close = async (how) => {
2106
2168
  setAsking(null);
2107
- const r = await run(() => how === "archive" ? auth.organizations.archive(id) : auth.organizations.delete(id));
2169
+ const r = await run(() => how === "archive" ? auth.organizations.archive(id) : auth.organizations.delete(id), how);
2108
2170
  if (!r.ok) return;
2109
2171
  setGone((how === "archive" ? f.t.organizationArchived : f.t.organizationDeleted).replace("{org}", seat.name));
2110
2172
  saved({ kind: how === "archive" ? "organization.archived" : "organization.deleted", organizationId: id });
@@ -2119,8 +2181,8 @@ function OrganizationMembers(props) {
2119
2181
  const confirming = (text2, label2, yes) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { role: "group", "aria-label": label2, children: [
2120
2182
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: text2 }),
2121
2183
  " ",
2122
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: yes, children: f.t.yes }),
2123
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => setAsking(null), children: f.t.cancel })
2184
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", variant: "danger", disabled: f.busy, className: f.cx("providerButton"), onClick: yes, children: f.t.yes }),
2185
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", variant: "secondary", disabled: f.busy, className: f.cx("providerButton"), onClick: () => setAsking(null), children: f.t.cancel })
2124
2186
  ] });
2125
2187
  const leaveQuestion = f.t.memberLeaveConfirm.replace("{org}", seat.name);
2126
2188
  const archiveQuestion = f.t.organizationArchiveConfirm.replace("{org}", seat.name);
@@ -2128,8 +2190,8 @@ function OrganizationMembers(props) {
2128
2190
  const archiving = seat.can.archive && !hidden("archive");
2129
2191
  const deleting = seat.can.delete && !hidden("delete");
2130
2192
  const renaming = seat.can.update && !hidden("rename");
2131
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: f.cx("account"), ...root, children: [
2132
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: f.cx("section"), children: [
2193
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "div", slot: "account", root, children: [
2194
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "section", slot: "section", section: "members", children: [
2133
2195
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Heading, { f, children: [
2134
2196
  f.t.members,
2135
2197
  " \xB7 ",
@@ -2142,6 +2204,8 @@ function OrganizationMembers(props) {
2142
2204
  f.Button,
2143
2205
  {
2144
2206
  type: "button",
2207
+ variant: "danger",
2208
+ loading: f.loading(`remove:${auth.session.user.id}`),
2145
2209
  disabled: f.busy,
2146
2210
  className: f.cx("providerButton"),
2147
2211
  onClick: () => ask(auth.session.user.id, f.t.memberLeave, leaveQuestion, () => void remove({ userId: auth.session.user.id, you: true })),
@@ -2176,34 +2240,34 @@ function OrganizationMembers(props) {
2176
2240
  label: `${f.t.memberRole}: ${who}`,
2177
2241
  value: m.role,
2178
2242
  disabled: f.busy,
2179
- options: choices.map((c) => ({ value: c.role, label: c.name || c.role })),
2243
+ options: choices.map((c) => ({ value: c.role, label: roleName(c) })),
2180
2244
  onChange: (next) => void change(m, next)
2181
2245
  }
2182
2246
  ) : f.C.Badge ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.C.Badge, { tone: "neutral", children: label(m.role) }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: label(m.role) }),
2183
2247
  " ",
2184
2248
  asking === m.userId ? confirming(question, action, () => void remove(m)) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
2185
- may.inviteAgain && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => void inviteAgain(m), children: f.t.memberInviteAgain }),
2186
- removable && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => ask(m.userId, action, question, () => void remove(m)), children: action })
2249
+ may.inviteAgain && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", variant: "secondary", loading: f.loading(`invite:${m.userId}`), disabled: f.busy, className: f.cx("providerButton"), onClick: () => void inviteAgain(m), children: f.t.memberInviteAgain }),
2250
+ removable && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", variant: "danger", loading: f.loading(`remove:${m.userId}`), disabled: f.busy, className: f.cx("providerButton"), onClick: () => ask(m.userId, action, question, () => void remove(m)), children: action })
2187
2251
  ] })
2188
2252
  ] }, m.userId);
2189
2253
  }) }),
2190
2254
  done && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "success", children: done })
2191
2255
  ] }),
2192
- manage && giveable.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { className: f.cx("section"), onSubmit: add, noValidate: true, children: [
2256
+ manage && giveable.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "form", slot: "section", section: "add-member", onSubmit: add, noValidate: true, children: [
2193
2257
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Heading, { f, children: f.t.memberAdd }),
2194
2258
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "email", type: "email", label: f.t.email, value: email, autoComplete: "off", required: true, onChange: setEmail }),
2195
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AddRole, { f, value: role, options: giveable.map((c) => ({ value: c.role, label: c.name || c.role })), onChange: setRole }),
2196
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.memberAdd })
2259
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AddRole, { f, value: role, options: giveable.map((c) => ({ value: c.role, label: roleName(c) })), onChange: setRole }),
2260
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", variant: "primary", loading: f.loading("add"), disabled: f.busy, className: f.cx("button"), children: f.t.memberAdd })
2197
2261
  ] }),
2198
- (renaming || archiving || deleting) && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: f.cx("section"), children: [
2262
+ (renaming || archiving || deleting) && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "section", slot: "section", section: "organization", children: [
2199
2263
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Heading, { f, children: f.t.organization }),
2200
- renaming && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { onSubmit: rename, noValidate: true, children: [
2264
+ renaming && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "form", section: "rename", onSubmit: rename, noValidate: true, children: [
2201
2265
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "name", type: "text", label: f.t.name, value: name, autoComplete: "organization", required: true, onChange: setName }),
2202
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.organizationRename })
2266
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", variant: "primary", loading: f.loading("rename"), disabled: f.busy, className: f.cx("button"), children: f.t.organizationRename })
2203
2267
  ] }),
2204
2268
  asking === "archive" ? confirming(archiveQuestion, f.t.organizationArchive, () => void close("archive")) : asking === "delete" ? confirming(deleteQuestion, f.t.organizationDelete, () => void close("delete")) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
2205
- archiving && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => ask("archive", f.t.organizationArchive, archiveQuestion, () => void close("archive")), children: f.t.organizationArchive }),
2206
- deleting && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => ask("delete", f.t.organizationDelete, deleteQuestion, () => void close("delete")), children: f.t.organizationDelete })
2269
+ archiving && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", variant: "danger", loading: f.loading("archive"), disabled: f.busy, className: f.cx("providerButton"), onClick: () => ask("archive", f.t.organizationArchive, archiveQuestion, () => void close("archive")), children: f.t.organizationArchive }),
2270
+ deleting && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", variant: "danger", loading: f.loading("delete"), disabled: f.busy, className: f.cx("providerButton"), onClick: () => ask("delete", f.t.organizationDelete, deleteQuestion, () => void close("delete")), children: f.t.organizationDelete })
2207
2271
  ] })
2208
2272
  ] })
2209
2273
  ] });
@@ -2228,14 +2292,15 @@ function PasskeysPart({ props, account, accountUrl, when }) {
2228
2292
  }, []);
2229
2293
  if (here === null) return null;
2230
2294
  if (!here) {
2231
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: f.cx("section"), children: [
2295
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "section", slot: "section", section: "passkeys", children: [
2232
2296
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Heading, { f, children: f.t.passkeys }),
2233
2297
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(A, { f, href: accountUrl, children: f.t.managePasskeys })
2234
2298
  ] });
2235
2299
  }
2236
2300
  const add = async (e, kind = "device") => {
2237
2301
  e?.preventDefault();
2238
- const r = await run(() => auth.account.addPasskey({ name, password, kind }));
2302
+ if (account.hasPassword && !f.check(checkPassword(password))) return;
2303
+ const r = await run(() => auth.account.addPasskey({ name, password, kind }), kind === "security_key" ? "security_key" : SUBMIT);
2239
2304
  if (!r.ok) {
2240
2305
  if (r.error.code === "cancelled") f.setError(null);
2241
2306
  return;
@@ -2247,12 +2312,12 @@ function PasskeysPart({ props, account, accountUrl, when }) {
2247
2312
  props.onSaved?.({ kind: "passkey.added", id: r.passkey.id });
2248
2313
  };
2249
2314
  const remove = async (id) => {
2250
- const r = await run(() => auth.account.removePasskey(id));
2315
+ const r = await run(() => auth.account.removePasskey(id), `remove:${id}`);
2251
2316
  if (!r.ok) return;
2252
2317
  setList(list.filter((p) => p.id !== id));
2253
2318
  props.onSaved?.({ kind: "passkey.removed", id });
2254
2319
  };
2255
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { className: f.cx("section"), onSubmit: add, noValidate: true, children: [
2320
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "form", slot: "section", section: "passkeys", onSubmit: add, noValidate: true, children: [
2256
2321
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Heading, { f, children: f.t.passkeys }),
2257
2322
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Note, { f, children: f.t.passkeysHelp }),
2258
2323
  general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "danger", children: f.say(general) }),
@@ -2262,12 +2327,12 @@ function PasskeysPart({ props, account, accountUrl, when }) {
2262
2327
  " \xB7 ",
2263
2328
  when(p.createdAt),
2264
2329
  " ",
2265
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => void remove(p.id), children: f.t.removePasskey })
2330
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", variant: "danger", loading: f.loading(`remove:${p.id}`), disabled: f.busy, className: f.cx("providerButton"), onClick: () => void remove(p.id), children: f.t.removePasskey })
2266
2331
  ] }, p.id)) }),
2267
2332
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "name", type: "text", label: f.t.passkeyName, value: name, autoComplete: "off", onChange: setName }),
2268
2333
  account.hasPassword && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "password", type: "password", label: f.t.currentPassword, value: password, autoComplete: "current-password", required: true, onChange: setPassword }),
2269
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.addPasskeyDevice }),
2270
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => void add(null, "security_key"), children: f.t.addSecurityKey }),
2334
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", variant: "primary", loading: f.loading(), disabled: f.busy, className: f.cx("button"), children: f.t.addPasskeyDevice }),
2335
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", variant: "secondary", loading: f.loading("security_key"), disabled: f.busy, className: f.cx("providerButton"), onClick: () => void add(null, "security_key"), children: f.t.addSecurityKey }),
2271
2336
  done && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "success", children: done })
2272
2337
  ] });
2273
2338
  }
@@ -2283,12 +2348,12 @@ function ProfilePart({ props, account, onSaved }) {
2283
2348
  setDone(f.t.saved);
2284
2349
  props.onSaved?.({ kind: "profile.saved", name: r.account.name });
2285
2350
  };
2286
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { className: f.cx("section"), onSubmit: submit, noValidate: true, children: [
2351
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "form", slot: "section", section: "profile", onSubmit: submit, noValidate: true, children: [
2287
2352
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Heading, { f, children: f.t.profile }),
2288
2353
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Note, { f, children: account.email }),
2289
2354
  general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "danger", children: f.say(general) }),
2290
2355
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "name", type: "text", label: f.t.name, value: name, autoComplete: "name", onChange: setName }),
2291
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.save }),
2356
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", variant: "primary", loading: f.loading(), disabled: f.busy, className: f.cx("button"), children: f.t.save }),
2292
2357
  done && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "success", children: done })
2293
2358
  ] });
2294
2359
  }
@@ -2299,19 +2364,20 @@ function EmailPart({ props, account }) {
2299
2364
  const [password, setPassword] = (0, import_react.useState)("");
2300
2365
  const submit = async (e) => {
2301
2366
  e.preventDefault();
2367
+ if (!f.check(checkEmail(email), account.hasPassword && checkPassword(password))) return;
2302
2368
  const r = await run(() => auth.account.changeEmail({ email, password }));
2303
2369
  if (!r.ok) return;
2304
2370
  setPassword("");
2305
2371
  setDone(f.t.emailSent.replace("{email}", r.email));
2306
2372
  props.onSaved?.({ kind: "email.change_requested", email: r.email });
2307
2373
  };
2308
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { className: f.cx("section"), onSubmit: submit, noValidate: true, children: [
2374
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "form", slot: "section", section: "email", onSubmit: submit, noValidate: true, children: [
2309
2375
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Heading, { f, children: f.t.emailAddress }),
2310
2376
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Note, { f, children: account.email }),
2311
2377
  general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "danger", children: f.say(general) }),
2312
2378
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "email", type: "email", label: f.t.newEmail, value: email, autoComplete: "email", required: true, onChange: setEmail }),
2313
2379
  account.hasPassword && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "password", type: "password", label: f.t.currentPassword, value: password, autoComplete: "current-password", required: true, onChange: setPassword }),
2314
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.changeEmail }),
2380
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", variant: "primary", loading: f.loading(), disabled: f.busy, className: f.cx("button"), children: f.t.changeEmail }),
2315
2381
  done && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "success", children: done })
2316
2382
  ] });
2317
2383
  }
@@ -2334,13 +2400,13 @@ function LinkedPart({ props }) {
2334
2400
  if (linked === null) return null;
2335
2401
  const name = (id) => offered.find((p) => p.id === id)?.name ?? id;
2336
2402
  const unlink = async (provider) => {
2337
- const r = await run(() => auth.account.unlink(provider));
2403
+ const r = await run(() => auth.account.unlink(provider), `unlink:${provider}`);
2338
2404
  if (!r.ok) return;
2339
2405
  setLinked(linked.filter((x) => x.provider !== provider));
2340
2406
  props.onSaved?.({ kind: "identity.unlinked", provider });
2341
2407
  };
2342
2408
  const here = typeof window === "undefined" ? void 0 : window.location.pathname;
2343
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: f.cx("section"), children: [
2409
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "section", slot: "section", section: "linked", children: [
2344
2410
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Heading, { f, children: f.t.linkedAccounts }),
2345
2411
  came && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "success", children: f.t.linked }),
2346
2412
  general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "danger", children: f.say(general) }),
@@ -2348,9 +2414,9 @@ function LinkedPart({ props }) {
2348
2414
  name(x.provider),
2349
2415
  x.email ? ` \xB7 ${x.email}` : "",
2350
2416
  " ",
2351
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => void unlink(x.provider), children: f.t.unlink })
2417
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", variant: "danger", loading: f.loading(`unlink:${x.provider}`), disabled: f.busy, className: f.cx("providerButton"), onClick: () => void unlink(x.provider), children: f.t.unlink })
2352
2418
  ] }, x.provider)) }),
2353
- offered.filter((p) => !linked.some((x) => x.provider === p.id)).map((p) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(f.Button, { type: "button", className: f.cx("providerButton"), onClick: () => auth.account.link(p.id, { returnTo: here }), children: [
2419
+ offered.filter((p) => !linked.some((x) => x.provider === p.id)).map((p) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(f.Button, { type: "button", variant: "secondary", className: f.cx("providerButton"), onClick: () => auth.account.link(p.id, { returnTo: here }), children: [
2354
2420
  f.t.linkWith,
2355
2421
  " ",
2356
2422
  p.name
@@ -2365,6 +2431,7 @@ function PasswordPart({ props }) {
2365
2431
  usePasswordPolicy(f, useMethods(auth));
2366
2432
  const submit = async (e) => {
2367
2433
  e.preventDefault();
2434
+ if (!f.check(checkPassword(password), newPasswordProblem(newPassword, f.policy, "newPassword"))) return;
2368
2435
  const r = await run(() => auth.account.changePassword({ password, newPassword }));
2369
2436
  if (!r.ok) return;
2370
2437
  setPassword("");
@@ -2372,12 +2439,12 @@ function PasswordPart({ props }) {
2372
2439
  setDone(f.t.passwordChangedHere);
2373
2440
  props.onSaved?.({ kind: "password.changed" });
2374
2441
  };
2375
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { className: f.cx("section"), onSubmit: submit, noValidate: true, children: [
2442
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "form", slot: "section", section: "password", onSubmit: submit, noValidate: true, children: [
2376
2443
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Heading, { f, children: f.t.changePassword }),
2377
2444
  general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "danger", children: f.say(general) }),
2378
2445
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "password", type: "password", label: f.t.currentPassword, value: password, autoComplete: "current-password", required: true, onChange: setPassword }),
2379
2446
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "newPassword", type: "password", label: f.t.newPassword, value: newPassword, autoComplete: "new-password", required: true, onChange: setNewPassword, hint: f.passwordRule }),
2380
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.changePassword }),
2447
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", variant: "primary", loading: f.loading(), disabled: f.busy, className: f.cx("button"), children: f.t.changePassword }),
2381
2448
  done && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "success", children: done })
2382
2449
  ] });
2383
2450
  }
@@ -2414,7 +2481,7 @@ function SecondStepPart({ props, account, onChanged }) {
2414
2481
  props.onSaved?.({ kind: "mfa.disabled" });
2415
2482
  };
2416
2483
  const renew = async () => {
2417
- const r = await run(() => auth.account.mfa.recoveryCodes({ password }));
2484
+ const r = await run(() => auth.account.mfa.recoveryCodes({ password }), "codes");
2418
2485
  if (!r.ok) return;
2419
2486
  setCodes(r.recoveryCodes);
2420
2487
  props.onSaved?.({ kind: "recovery_codes.regenerated" });
@@ -2425,24 +2492,24 @@ function SecondStepPart({ props, account, onChanged }) {
2425
2492
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { className: f.cx("codes"), children: codes.map((c) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", { children: c }) }, c)) })
2426
2493
  ] });
2427
2494
  if (setup) {
2428
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { className: f.cx("section"), onSubmit: confirm, noValidate: true, children: [
2495
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "form", slot: "section", section: "secondStep", onSubmit: confirm, noValidate: true, children: [
2429
2496
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Heading, { f, children: f.t.secondStep }),
2430
2497
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Note, { f, children: f.t.addToApp }),
2431
2498
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", { className: f.cx("secret"), children: setup.secret }),
2432
2499
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(A, { f, href: setup.otpauthUri, children: f.t.openInApp }),
2433
2500
  general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "danger", children: f.say(general) }),
2434
2501
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "code", type: "text", label: f.t.code, value: code, autoComplete: "one-time-code", required: true, onChange: setCode }),
2435
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.verify })
2502
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", variant: "primary", loading: f.loading(), disabled: f.busy, className: f.cx("button"), children: f.t.verify })
2436
2503
  ] });
2437
2504
  }
2438
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { className: f.cx("section"), onSubmit: account.mfaEnabled ? turnOff : start, noValidate: true, children: [
2505
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "form", slot: "section", section: "secondStep", onSubmit: account.mfaEnabled ? turnOff : start, noValidate: true, children: [
2439
2506
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Heading, { f, children: f.t.secondStep }),
2440
2507
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Note, { f, children: account.mfaEnabled ? f.t.secondStepOn : f.t.secondStepOff }),
2441
2508
  shown,
2442
2509
  general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "danger", children: f.say(general) }),
2443
2510
  passwordField,
2444
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: account.mfaEnabled ? f.t.turnOff : f.t.turnOn }),
2445
- account.mfaEnabled && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => void renew(), children: f.t.newRecoveryCodes })
2511
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", variant: account.mfaEnabled ? "danger" : "primary", loading: f.loading(), disabled: f.busy, className: f.cx("button"), children: account.mfaEnabled ? f.t.turnOff : f.t.turnOn }),
2512
+ account.mfaEnabled && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", variant: "secondary", loading: f.loading("codes"), disabled: f.busy, className: f.cx("providerButton"), onClick: () => void renew(), children: f.t.newRecoveryCodes })
2446
2513
  ] });
2447
2514
  }
2448
2515
  function PhonePart({ props, account, onChanged }) {
@@ -2492,15 +2559,15 @@ function PhonePart({ props, account, onChanged }) {
2492
2559
  };
2493
2560
  const passwordField = account.hasPassword && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "password", type: "password", label: f.t.currentPassword, value: password, autoComplete: "current-password", required: true, onChange: setPassword });
2494
2561
  if (sentTo) {
2495
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { className: f.cx("section"), onSubmit: confirm, noValidate: true, children: [
2562
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "form", slot: "section", section: "phone", onSubmit: confirm, noValidate: true, children: [
2496
2563
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Heading, { f, children: f.t.phone }),
2497
2564
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Note, { f, children: f.t.codeSent.replace("{to}", sentTo) }),
2498
2565
  general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "danger", children: f.say(general) }),
2499
2566
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "code", type: "text", label: f.t.code, value: code, autoComplete: "one-time-code", required: true, onChange: setCode }),
2500
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.verify })
2567
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", variant: "primary", loading: f.loading(), disabled: f.busy, className: f.cx("button"), children: f.t.verify })
2501
2568
  ] });
2502
2569
  }
2503
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { className: f.cx("section"), onSubmit: ways.phone ? remove : add, noValidate: true, children: [
2570
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "form", slot: "section", section: "phone", onSubmit: ways.phone ? remove : add, noValidate: true, children: [
2504
2571
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Heading, { f, children: f.t.phone }),
2505
2572
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Note, { f, children: ways.phone ? f.t.phoneOn.replace("{to}", ways.phone.number) : `${f.t.phoneOff} ${f.t.phoneHelp}` }),
2506
2573
  codes && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
@@ -2516,7 +2583,7 @@ function PhonePart({ props, account, onChanged }) {
2516
2583
  c === "whatsapp" ? f.t.byWhatsapp : f.t.bySms
2517
2584
  ] }, c)) }),
2518
2585
  passwordField,
2519
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: ways.phone ? f.t.removePhone : f.t.addPhone })
2586
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", variant: ways.phone ? "danger" : "primary", loading: f.loading(), disabled: f.busy, className: f.cx("button"), children: ways.phone ? f.t.removePhone : f.t.addPhone })
2520
2587
  ] });
2521
2588
  }
2522
2589
  function AgentsPart({ props, when }) {
@@ -2528,12 +2595,12 @@ function AgentsPart({ props, when }) {
2528
2595
  }, []);
2529
2596
  if (list.length === 0) return null;
2530
2597
  const disconnect = async (id) => {
2531
- const r = await run(() => auth.account.disconnectAgent(id));
2598
+ const r = await run(() => auth.account.disconnectAgent(id), `disconnect:${id}`);
2532
2599
  if (!r.ok) return;
2533
2600
  setList(list.filter((a) => a.id !== id));
2534
2601
  props.onSaved?.({ kind: "agent.disconnected", id });
2535
2602
  };
2536
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: f.cx("section"), children: [
2603
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "section", slot: "section", section: "agents", children: [
2537
2604
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Heading, { f, children: f.t.agents }),
2538
2605
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Note, { f, children: f.t.agentsHelp }),
2539
2606
  general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "danger", children: f.say(general) }),
@@ -2546,7 +2613,7 @@ function AgentsPart({ props, when }) {
2546
2613
  " \xB7 ",
2547
2614
  when(a.lastUsedAt),
2548
2615
  " ",
2549
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => void disconnect(a.id), children: f.t.disconnect })
2616
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", variant: "danger", loading: f.loading(`disconnect:${a.id}`), disabled: f.busy, className: f.cx("providerButton"), onClick: () => void disconnect(a.id), children: f.t.disconnect })
2550
2617
  ] }, a.id)) })
2551
2618
  ] });
2552
2619
  }
@@ -2564,7 +2631,7 @@ function SessionsPart({ props, when }) {
2564
2631
  if (props.onSignedOut) props.onSignedOut();
2565
2632
  else window.location.assign("/");
2566
2633
  };
2567
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: f.cx("section"), children: [
2634
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Wrap, { f, as: "section", slot: "section", section: "sessions", children: [
2568
2635
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Heading, { f, children: f.t.sessions }),
2569
2636
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { className: f.cx("sessions"), children: sessions.map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("li", { className: f.cx("session"), children: [
2570
2637
  s.device || f.t.unknownDevice,
@@ -2574,7 +2641,7 @@ function SessionsPart({ props, when }) {
2574
2641
  when(s.createdAt)
2575
2642
  ] }, i)) }),
2576
2643
  general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Say, { f, tone: "danger", children: f.say(general) }),
2577
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", disabled: f.busy, className: f.cx("button"), onClick: () => void end(), children: f.t.signOutEverywhere })
2644
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", variant: "danger", loading: f.loading(), disabled: f.busy, className: f.cx("button"), onClick: () => void end(), children: f.t.signOutEverywhere })
2578
2645
  ] });
2579
2646
  }
2580
2647
  function ChannelPick({ f, value, channels, onChange }) {
@@ -2595,9 +2662,16 @@ function ChannelPick({ f, value, channels, onChange }) {
2595
2662
  // src/react.tsx
2596
2663
  var import_jsx_runtime2 = require("react/jsx-runtime");
2597
2664
  var Ctx = (0, import_react3.createContext)(null);
2665
+ var LocaleCtx = (0, import_react3.createContext)(() => void 0);
2598
2666
  var AHEAD_MS = 6e4;
2599
- function CauthyProvider({ children, basePath = "/api/auth", initialSession }) {
2667
+ function CauthyProvider({ children, basePath = "/api/auth", initialSession, locale }) {
2600
2668
  const base = withoutTrailingSlashes(basePath);
2669
+ const said = (0, import_react3.useRef)(locale);
2670
+ said.current = locale;
2671
+ const readLocale = (0, import_react3.useCallback)(() => {
2672
+ const l = said.current;
2673
+ return typeof l === "function" ? l() : l;
2674
+ }, []);
2601
2675
  const [session, setSession] = (0, import_react3.useState)(initialSession ?? null);
2602
2676
  const [status, setStatus] = (0, import_react3.useState)(
2603
2677
  initialSession === void 0 ? "loading" : initialSession === null ? "unauthenticated" : "authenticated"
@@ -2661,7 +2735,7 @@ function CauthyProvider({ children, basePath = "/api/auth", initialSession }) {
2661
2735
  }),
2662
2736
  [session, status, refresh, base]
2663
2737
  );
2664
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Ctx.Provider, { value, children });
2738
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Ctx.Provider, { value, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(LocaleCtx.Provider, { value: readLocale, children }) });
2665
2739
  }
2666
2740
  function useSession() {
2667
2741
  const ctx = (0, import_react3.useContext)(Ctx);
@@ -2670,8 +2744,9 @@ function useSession() {
2670
2744
  }
2671
2745
  function useAuth() {
2672
2746
  const { session, status, refresh, basePath } = useSession();
2747
+ const locale = (0, import_react3.useContext)(LocaleCtx);
2673
2748
  return (0, import_react3.useMemo)(() => {
2674
- const client = createAuthClient({ basePath });
2749
+ const client = createAuthClient({ basePath, locale });
2675
2750
  const then = async (running) => {
2676
2751
  const r = await running;
2677
2752
  await refresh();
@@ -2708,7 +2783,7 @@ function useAuth() {
2708
2783
  status,
2709
2784
  refresh
2710
2785
  };
2711
- }, [basePath, refresh, session, status]);
2786
+ }, [basePath, locale, refresh, session, status]);
2712
2787
  }
2713
2788
  // Annotate the CommonJS export names for ESM import in node:
2714
2789
  0 && (module.exports = {