cauthy-auth 0.28.1 → 0.31.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 (46) hide show
  1. package/README.md +61 -3
  2. package/dist/{cauthy-CTVg4kvo.d.mts → cauthy-2jtP2LVQ.d.mts} +19 -1
  3. package/dist/{cauthy-BAwAJChs.d.ts → cauthy-CKfvvv_i.d.ts} +19 -1
  4. package/dist/cli.js +163 -59
  5. package/dist/client.d.mts +91 -10
  6. package/dist/client.d.ts +91 -10
  7. package/dist/client.js +39 -3
  8. package/dist/client.js.map +1 -1
  9. package/dist/client.mjs +39 -3
  10. package/dist/client.mjs.map +1 -1
  11. package/dist/express.d.mts +2 -2
  12. package/dist/express.d.ts +2 -2
  13. package/dist/fastify.d.mts +2 -2
  14. package/dist/fastify.d.ts +2 -2
  15. package/dist/hono.d.mts +2 -2
  16. package/dist/hono.d.ts +2 -2
  17. package/dist/index.d.mts +2 -2
  18. package/dist/index.d.ts +2 -2
  19. package/dist/index.js +387 -148
  20. package/dist/index.js.map +1 -1
  21. package/dist/index.mjs +385 -148
  22. package/dist/index.mjs.map +1 -1
  23. package/dist/next.d.mts +2 -2
  24. package/dist/next.d.ts +2 -2
  25. package/dist/react.d.mts +275 -23
  26. package/dist/react.d.ts +275 -23
  27. package/dist/react.js +312 -78
  28. package/dist/react.js.map +1 -1
  29. package/dist/react.mjs +311 -78
  30. package/dist/react.mjs.map +1 -1
  31. package/dist/styles.css +8 -0
  32. package/dist/svelte.d.mts +2 -2
  33. package/dist/svelte.d.ts +2 -2
  34. package/dist/svelte.js +53 -4
  35. package/dist/svelte.js.map +1 -1
  36. package/dist/svelte.mjs +53 -4
  37. package/dist/svelte.mjs.map +1 -1
  38. package/dist/{system-CVBW_AV8.d.mts → system-BrrJsrFW.d.mts} +321 -109
  39. package/dist/{system-Bn8uckvQ.d.ts → system-BvvoNs2y.d.ts} +321 -109
  40. package/dist/vue.d.mts +2 -2
  41. package/dist/vue.d.ts +2 -2
  42. package/dist/vue.js +53 -4
  43. package/dist/vue.js.map +1 -1
  44. package/dist/vue.mjs +53 -4
  45. package/dist/vue.mjs.map +1 -1
  46. package/package.json +1 -1
package/dist/react.js CHANGED
@@ -21,6 +21,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  // src/react.tsx
22
22
  var react_exports = {};
23
23
  __export(react_exports, {
24
+ AcceptInvite: () => AcceptInvite,
24
25
  Account: () => Account,
25
26
  CauthyProvider: () => CauthyProvider,
26
27
  ForgotPassword: () => ForgotPassword,
@@ -321,6 +322,12 @@ function checkOrganization(value, required = false) {
321
322
  if (v.length > MAX_ORGANIZATION) return { field: "organization", code: "too_long", message: `Use at most ${MAX_ORGANIZATION} characters.` };
322
323
  return null;
323
324
  }
325
+ function checkOrganizationName(value) {
326
+ const v = typeof value === "string" ? value.trim() : "";
327
+ if (v === "") return { field: "name", code: "required", message: "Enter the organization's name." };
328
+ if (v.length > MAX_ORGANIZATION) return { field: "name", code: "too_long", message: `Use at most ${MAX_ORGANIZATION} characters.` };
329
+ return null;
330
+ }
324
331
  function checkCode(value) {
325
332
  const v = typeof value === "string" ? value.trim() : "";
326
333
  if (v === "") return { field: "code", code: "required", message: "Enter the code." };
@@ -405,11 +412,11 @@ function createAuthClient(options = {}) {
405
412
  if (res.ok) {
406
413
  const m = await res.json();
407
414
  if (m.password) policy = passwordPolicyOf(m.password);
408
- return { providers: [], signUp: true, magicLink: false, passkey: false, ...m, ...policy ? { password: policy } : {}, passkeyHere: m.passkeyHere === true, passkeyOnCauthy: m.passkeyOnCauthy ?? m.passkey === true, sso: m.sso === true };
415
+ return { providers: [], signUp: true, magicLink: false, passkey: false, ...m, ...policy ? { password: policy } : {}, passwordSignIn: m.passwordSignIn !== false, passkeyHere: m.passkeyHere === true, passkeyOnCauthy: m.passkeyOnCauthy ?? m.passkey === true, sso: m.sso === true };
409
416
  }
410
417
  } catch {
411
418
  }
412
- return { providers: [], signUp: true, magicLink: false, passkey: false, passkeyHere: false, passkeyOnCauthy: false, sso: false };
419
+ return { providers: [], signUp: true, passwordSignIn: true, magicLink: false, passkey: false, passkeyHere: false, passkeyOnCauthy: false, sso: false };
413
420
  }
414
421
  async function ceremony(begin, finish, form, run, more = {}) {
415
422
  const begun = await post(begin, form, null);
@@ -556,6 +563,20 @@ function createAuthClient(options = {}) {
556
563
  forgotPassword: (f) => post("/forgot-password", { email: f.email }, checkEmail(f.email), f.captchaToken),
557
564
  resetPassword: (f) => post("/reset-password", { token: f.token, password: f.password }, checkNewPassword(f.password, policy)),
558
565
  verifyEmail: (f) => post("/verify-email", { token: f.token }, null),
566
+ async invitation(token) {
567
+ const bad = { ok: false, error: { code: "invalid_token", message: "This invitation is not valid, or it has expired." } };
568
+ if (typeof token !== "string" || token.trim() === "") return bad;
569
+ try {
570
+ const res = await doFetch(`${base}/invitation?token=${encodeURIComponent(token.trim())}`, { credentials: "same-origin", headers: { accept: "application/json" } });
571
+ const body = await res.json().catch(() => null);
572
+ if (res.ok && body?.invitation) return { ok: true, status: "ok", invitation: body.invitation };
573
+ const e = body?.error;
574
+ return { ok: false, error: e && typeof e.code === "string" ? { code: e.code, message: String(e.message ?? "") } : UNAVAILABLE };
575
+ } catch {
576
+ return { ok: false, error: UNAVAILABLE };
577
+ }
578
+ },
579
+ acceptInvitation: (token, password) => post("/invitation/accept", { token, password }, checkNewPassword(password, policy)),
559
580
  signOut: () => post("/sign-out", {}, null),
560
581
  organizations: {
561
582
  async list(system) {
@@ -573,7 +594,23 @@ function createAuthClient(options = {}) {
573
594
  add: (id, f) => post("/me/organizations/members/add", { organizationId: id, email: f.email, role: f.role, name: f.name ?? "", locale: f.locale ?? "" }, checkEmail(f.email)),
574
595
  setRole: (id, userId, role) => post("/me/organizations/members/role", { organizationId: id, userId, role }, null),
575
596
  remove: (id, userId) => post("/me/organizations/members/remove", { organizationId: id, userId }, null),
576
- inviteAgain: (id, userId, f = {}) => post("/me/organizations/members/invite", { organizationId: id, userId, locale: f.locale ?? "" }, null)
597
+ inviteAgain: (id, userId, f = {}) => post("/me/organizations/members/invite", { organizationId: id, userId, locale: f.locale ?? "" }, null),
598
+ rename: (id, name) => post("/me/organizations/rename", { organizationId: id, name }, checkOrganizationName(name)),
599
+ archive: (id) => post("/me/organizations/archive", { organizationId: id }, null),
600
+ delete: (id) => post("/me/organizations/delete", { organizationId: id }, null),
601
+ async check(q) {
602
+ const organizationId = typeof q.organization === "string" ? q.organization : q.organization?.id ?? "";
603
+ const r = await post("/me/organizations/check", {
604
+ organizationId,
605
+ resource: q.resource,
606
+ action: q.action,
607
+ ...q.attributes !== void 0 ? { attributes: q.attributes } : {},
608
+ ...q.resourceId !== void 0 ? { resourceId: q.resourceId } : {},
609
+ ...q.ownerId !== void 0 ? { ownerId: q.ownerId } : {}
610
+ }, null);
611
+ if (!r.ok) return { allowed: false, reason: r.error.message, error: r.error };
612
+ return { allowed: r.allowed === true, reason: String(r.reason ?? ""), ...r.rule ? { rule: r.rule } : {} };
613
+ }
577
614
  },
578
615
  account: {
579
616
  async get() {
@@ -669,7 +706,9 @@ function can(organization, resource, action) {
669
706
  if ((organization.denied ?? []).some((d) => matches(d, resource, action))) return false;
670
707
  return organization.permissions.some((p) => matches(p, resource, action));
671
708
  }
672
- function definePolicy(roles) {
709
+ function definePolicy(given) {
710
+ const roles = Array.isArray(given) ? given : given.roles;
711
+ const resources = Array.isArray(given) ? [] : given.catalog ?? [];
673
712
  const entry = (p) => `${p.resource}:${p.action}`;
674
713
  const byName = new Map(roles.map((r) => [r.role, r]));
675
714
  const split = (permission) => {
@@ -684,6 +723,12 @@ function definePolicy(roles) {
684
723
  ...r.inherits ? { inherits: [...r.inherits] } : {},
685
724
  ...r.permissions ? { permissions: r.permissions.map((p) => ({ ...p })) } : {}
686
725
  })),
726
+ catalog: resources.map((c) => ({
727
+ resource: c.resource,
728
+ ...c.description !== void 0 ? { description: c.description } : {},
729
+ actions: [...c.actions],
730
+ ...c.attributes ? { attributes: { ...c.attributes } } : {}
731
+ })),
687
732
  roleNames: roles.map((r) => r.role),
688
733
  permissions: [...new Set(roles.flatMap((r) => (r.permissions ?? []).map(entry)))],
689
734
  can(organization, permission) {
@@ -705,6 +750,11 @@ function definePolicy(roles) {
705
750
  };
706
751
  walk(role);
707
752
  return [...out];
753
+ },
754
+ // The types did the work; this only hands the question on, as a method of
755
+ // the asker so it keeps its `this`.
756
+ check(asker, ...args) {
757
+ return asker.check(...args);
708
758
  }
709
759
  };
710
760
  }
@@ -763,6 +813,13 @@ var TEXTS = {
763
813
  passwordChanged: "Your password was changed. You can sign in with it now.",
764
814
  confirming: "Confirming your address\u2026",
765
815
  emailConfirmed: "Your address is confirmed. You can sign in now.",
816
+ invitedTo: "You've been invited to {workspace}",
817
+ invitedToOrganization: "You've been invited to {organization} on {workspace}",
818
+ invitedAs: "You will sign in as {email}.",
819
+ choosePassword: "Choose a password",
820
+ acceptInvite: "Accept and sign in",
821
+ openingInvitation: "Opening your invitation\u2026",
822
+ invitationInvalid: "This invitation is not valid: it was already used, or it has expired. Ask whoever invited you to send it again.",
766
823
  profile: "Profile",
767
824
  save: "Save",
768
825
  saved: "Saved.",
@@ -823,11 +880,22 @@ var TEXTS = {
823
880
  memberLeaveConfirm: "Leave {org}? You will need an invitation to come back.",
824
881
  memberInviteAgain: "Send the invitation again",
825
882
  memberInvitationSent: "The invitation is on its way.",
883
+ organization: "Organization",
884
+ organizationRename: "Rename",
885
+ organizationRenamed: "The organization is now called {name}.",
886
+ organizationArchive: "Archive",
887
+ organizationArchiveConfirm: "Archive {org}? Its members will no longer reach it, and only the workspace can restore it.",
888
+ organizationArchived: "{org} was archived.",
889
+ organizationDelete: "Delete organization",
890
+ organizationDeleteConfirm: "Delete {org} for good? Its members lose it and this cannot be undone.",
891
+ organizationDeleted: "{org} was deleted.",
826
892
  yes: "Yes",
827
893
  cancel: "Cancel",
828
894
  errors: {
829
895
  not_allowed: "Your role in this organization does not allow that.",
830
896
  last_manager: "Somebody else must manage the members first: give that role to another member.",
897
+ last_owner: "An organization keeps at least one owner: make another member owner first.",
898
+ organization_not_found: "That organization no longer exists.",
831
899
  already_member: "That person already belongs to the organization. Change their role instead.",
832
900
  unknown_role: "Choose one of the roles offered.",
833
901
  user_not_active: "That account is disabled.",
@@ -866,7 +934,9 @@ var TEXTS = {
866
934
  invalid_assertion: "Your company's answer could not be checked. Try again.",
867
935
  provisioning_disabled: "Your company's sign-in worked, but this workspace has no account for you. Ask its admin to add you.",
868
936
  connection_inactive: "Your company's sign-in is turned off for this workspace.",
869
- invalid_grant: "That sign-in expired. Try again."
937
+ invalid_grant: "That sign-in expired. Try again.",
938
+ password_disabled: "This workspace does not sign in with a password. Use one of the other ways.",
939
+ passkeys_disabled: "Passkeys are turned off for this workspace."
870
940
  }
871
941
  },
872
942
  es: {
@@ -919,6 +989,13 @@ var TEXTS = {
919
989
  passwordChanged: "Tu contrase\xF1a se cambi\xF3. Ya puedes entrar con ella.",
920
990
  confirming: "Confirmando tu correo\u2026",
921
991
  emailConfirmed: "Tu correo qued\xF3 confirmado. Ya puedes entrar.",
992
+ invitedTo: "Te invitaron a {workspace}",
993
+ invitedToOrganization: "Te invitaron a {organization} en {workspace}",
994
+ invitedAs: "Vas a entrar como {email}.",
995
+ choosePassword: "Elige una contrase\xF1a",
996
+ acceptInvite: "Aceptar y entrar",
997
+ openingInvitation: "Abriendo tu invitaci\xF3n\u2026",
998
+ invitationInvalid: "Esta invitaci\xF3n no es v\xE1lida: ya se us\xF3 o venci\xF3. P\xEDdele a quien te invit\xF3 que te la env\xEDe de nuevo.",
922
999
  profile: "Perfil",
923
1000
  save: "Guardar",
924
1001
  saved: "Guardado.",
@@ -979,11 +1056,22 @@ var TEXTS = {
979
1056
  memberLeaveConfirm: "\xBFSalir de {org}? Necesitar\xE1s una invitaci\xF3n para volver.",
980
1057
  memberInviteAgain: "Enviar la invitaci\xF3n de nuevo",
981
1058
  memberInvitationSent: "La invitaci\xF3n va en camino.",
1059
+ organization: "Organizaci\xF3n",
1060
+ organizationRename: "Cambiar nombre",
1061
+ organizationRenamed: "La organizaci\xF3n ahora se llama {name}.",
1062
+ organizationArchive: "Archivar",
1063
+ organizationArchiveConfirm: "\xBFArchivar {org}? Sus miembros ya no podr\xE1n entrar y solo el espacio de trabajo puede restaurarla.",
1064
+ organizationArchived: "{org} qued\xF3 archivada.",
1065
+ organizationDelete: "Eliminar organizaci\xF3n",
1066
+ organizationDeleteConfirm: "\xBFEliminar {org} para siempre? Sus miembros la pierden y no se puede deshacer.",
1067
+ organizationDeleted: "{org} qued\xF3 eliminada.",
982
1068
  yes: "S\xED",
983
1069
  cancel: "Cancelar",
984
1070
  errors: {
985
1071
  not_allowed: "Tu rol en esta organizaci\xF3n no permite eso.",
986
1072
  last_manager: "Primero alguien m\xE1s debe administrar los miembros: dale ese rol a otro miembro.",
1073
+ last_owner: "Una organizaci\xF3n conserva al menos un propietario: primero haz propietario a otro miembro.",
1074
+ organization_not_found: "Esa organizaci\xF3n ya no existe.",
987
1075
  already_member: "Esa persona ya pertenece a la organizaci\xF3n. Cambia su rol.",
988
1076
  unknown_role: "Escoge uno de los roles ofrecidos.",
989
1077
  user_not_active: "Esa cuenta est\xE1 desactivada.",
@@ -1022,7 +1110,9 @@ var TEXTS = {
1022
1110
  invalid_assertion: "No se pudo verificar la respuesta de tu empresa. Intenta de nuevo.",
1023
1111
  provisioning_disabled: "El ingreso con tu empresa funcion\xF3, pero este workspace no tiene una cuenta para ti. P\xEDdele a su admin que te agregue.",
1024
1112
  connection_inactive: "El inicio de sesi\xF3n de tu empresa est\xE1 apagado en este workspace.",
1025
- invalid_grant: "Ese ingreso venci\xF3. Intenta de nuevo."
1113
+ invalid_grant: "Ese ingreso venci\xF3. Intenta de nuevo.",
1114
+ password_disabled: "En este workspace no se entra con contrase\xF1a. Usa una de las otras formas.",
1115
+ passkeys_disabled: "Las llaves de acceso est\xE1n desactivadas en este workspace."
1026
1116
  }
1027
1117
  }
1028
1118
  };
@@ -1217,23 +1307,16 @@ function useMailedToken(name, open) {
1217
1307
  open(takeToken(name));
1218
1308
  }, []);
1219
1309
  }
1220
- function SignIn(props) {
1221
- const auth = useAuth();
1222
- const f = useForm(props);
1223
- const [methods, setMethods] = (0, import_react.useState)(null);
1224
- const [email, setEmail] = (0, import_react.useState)("");
1225
- const [password, setPassword] = (0, import_react.useState)("");
1310
+ function SecondStepForm(p) {
1311
+ const { f, auth } = p;
1226
1312
  const [code, setCode] = (0, import_react.useState)("");
1227
- const [step, setStep] = (0, import_react.useState)("password");
1228
- const challenge = useChallenge(methods?.captchaSiteKey, props.locale);
1229
1313
  const [phoneWay, setPhoneWay] = (0, import_react.useState)(null);
1230
1314
  const [phoneSent, setPhoneSent] = (0, import_react.useState)("");
1231
1315
  (0, import_react.useEffect)(() => {
1232
- if (step !== "code") return;
1233
1316
  void auth.signIn.mfaMethods().then((r) => {
1234
1317
  if (r.ok) setPhoneWay({ totp: r.totp, phone: r.phone, push: r.push, email: r.email });
1235
1318
  });
1236
- }, [step]);
1319
+ }, []);
1237
1320
  const sendEmail = async () => {
1238
1321
  f.setBusy(true);
1239
1322
  f.setError(null);
@@ -1269,6 +1352,55 @@ function SignIn(props) {
1269
1352
  if (!r.ok) return f.setError(r.error);
1270
1353
  setPhoneSent(r.sentTo);
1271
1354
  };
1355
+ const submit = async (e) => {
1356
+ e.preventDefault();
1357
+ f.setBusy(true);
1358
+ f.setError(null);
1359
+ const r = await auth.signIn.mfa({ code });
1360
+ f.setBusy(false);
1361
+ if (!r.ok) return f.setError(r.error);
1362
+ f.done();
1363
+ };
1364
+ const general = f.error && !f.error.field ? f.error : null;
1365
+ const to = phoneWay?.phone?.number ?? "";
1366
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { className: f.cx("form"), ...f.root, onSubmit: submit, noValidate: true, children: [
1367
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: f.cx("notice"), 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 }),
1368
+ general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "alert", className: f.cx("error"), children: f.say(general) }),
1369
+ 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 }),
1370
+ pushNumber > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: f.cx("notice"), role: "status", "aria-live": "polite", children: [
1371
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: f.t.pushPick.replace("{n}", String(pushNumber)) }),
1372
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: { fontSize: 48, fontWeight: 700, margin: "4px 0", textAlign: "center" }, children: pushNumber }),
1373
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: f.t.pushWaiting })
1374
+ ] }) : 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 }),
1375
+ 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) }),
1376
+ /* @__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 }),
1377
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.verify })
1378
+ ] });
1379
+ }
1380
+ function ProviderButtons(p) {
1381
+ const { f } = p;
1382
+ 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: [
1383
+ provider.iconUrl && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("img", { src: provider.iconUrl, alt: "", width: 18, height: 18, className: f.cx("providerIcon") }),
1384
+ f.t.continueWith,
1385
+ " ",
1386
+ provider.name
1387
+ ] }, provider.id)) });
1388
+ }
1389
+ function signInOffers(methods) {
1390
+ const password = methods?.passwordSignIn !== false;
1391
+ return { password, email: password || methods?.magicLink === true || methods?.sso === true, passkey: methods?.passkey === true };
1392
+ }
1393
+ function SignIn(props) {
1394
+ const auth = useAuth();
1395
+ const f = useForm(props);
1396
+ const [methods, setMethods] = (0, import_react.useState)(null);
1397
+ const [email, setEmail] = (0, import_react.useState)("");
1398
+ const [password, setPassword] = (0, import_react.useState)("");
1399
+ const [code, setCode] = (0, import_react.useState)("");
1400
+ const [step, setStep] = (0, import_react.useState)("password");
1401
+ const challenge = useChallenge(methods?.captchaSiteKey, props.locale);
1402
+ const offers = signInOffers(methods);
1403
+ const passwordOn = offers.password;
1272
1404
  const autofill = (0, import_react.useRef)(null);
1273
1405
  const [offer, setOffer] = (0, import_react.useState)(0);
1274
1406
  const [company, setCompany] = (0, import_react.useState)(null);
@@ -1320,10 +1452,18 @@ function SignIn(props) {
1320
1452
  const submit = async (e) => {
1321
1453
  e.preventDefault();
1322
1454
  autofill.current?.abort();
1455
+ if (step === "password" && company?.enforced) {
1456
+ f.setError(null);
1457
+ return toCompany();
1458
+ }
1459
+ if (step === "password" && !passwordOn) {
1460
+ if (company) return toCompany();
1461
+ if (methods?.magicLink) return sendLink();
1462
+ return;
1463
+ }
1323
1464
  f.setBusy(true);
1324
1465
  f.setError(null);
1325
- if (step === "password" && company?.enforced) return toCompany();
1326
- const r = step === "code" ? await auth.signIn.mfa({ code }) : step === "sent" ? await auth.signIn.emailCode({ code }) : await auth.signIn.email({ email, password, captchaToken: challenge.token });
1466
+ const r = step === "sent" ? await auth.signIn.emailCode({ code }) : await auth.signIn.email({ email, password, captchaToken: challenge.token });
1327
1467
  f.setBusy(false);
1328
1468
  if (step === "password") challenge.renew();
1329
1469
  if (!r.ok && r.error.code === "sso_required") return toCompany();
@@ -1357,6 +1497,7 @@ function SignIn(props) {
1357
1497
  setStep("sent");
1358
1498
  };
1359
1499
  const general = f.error && !f.error.field ? f.error : null;
1500
+ const asksEmail = offers.email;
1360
1501
  if (step === "opening") return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: f.cx("form"), ...f.root, "aria-busy": "true" });
1361
1502
  if (step === "sent") {
1362
1503
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { className: f.cx("form"), ...f.root, onSubmit: submit, noValidate: true, children: [
@@ -1367,39 +1508,20 @@ function SignIn(props) {
1367
1508
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.verify })
1368
1509
  ] });
1369
1510
  }
1370
- if (step === "code") {
1371
- const to = phoneWay?.phone?.number ?? "";
1372
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { className: f.cx("form"), ...f.root, onSubmit: submit, noValidate: true, children: [
1373
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: f.cx("notice"), 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 }),
1374
- general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "alert", className: f.cx("error"), children: f.say(general) }),
1375
- 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 }),
1376
- pushNumber > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: f.cx("notice"), role: "status", "aria-live": "polite", children: [
1377
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: f.t.pushPick.replace("{n}", String(pushNumber)) }),
1378
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: { fontSize: 48, fontWeight: 700, margin: "4px 0", textAlign: "center" }, children: pushNumber }),
1379
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: f.t.pushWaiting })
1380
- ] }) : 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 }),
1381
- 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) }),
1382
- /* @__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 }),
1383
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.verify })
1384
- ] });
1385
- }
1511
+ if (step === "code") return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SecondStepForm, { f, auth });
1386
1512
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { className: f.cx("form"), ...f.root, onSubmit: submit, noValidate: true, children: [
1387
1513
  general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "alert", className: f.cx("error"), children: f.say(general) }),
1388
- (methods?.providers ?? []).map((p) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(f.Button, { type: "button", className: f.cx("providerButton"), onClick: () => auth.signIn.social(p.id, { returnTo: props.returnTo }), children: [
1389
- f.t.continueWith,
1390
- " ",
1391
- p.name
1392
- ] }, p.id)),
1393
- methods?.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 }),
1394
- ((methods?.providers.length ?? 0) > 0 || methods?.passkey) && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: f.cx("separator"), children: f.t.orWithEmail }),
1395
- /* @__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 }),
1514
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ProviderButtons, { f, providers: methods?.providers ?? [], onPick: (id) => auth.signIn.social(id, { returnTo: props.returnTo }) }),
1515
+ 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 }),
1516
+ asksEmail && ((methods?.providers.length ?? 0) > 0 || offers.passkey) && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: f.cx("separator"), children: f.t.orWithEmail }),
1517
+ 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 }),
1396
1518
  company && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(f.Button, { type: "button", disabled: f.busy, className: f.cx(company.enforced ? "button" : "providerButton"), onClick: toCompany, children: [
1397
1519
  f.t.continueWith,
1398
1520
  " ",
1399
1521
  company.name
1400
1522
  ] }),
1401
1523
  company?.enforced && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: f.cx("notice"), children: f.t.ssoOnly }),
1402
- !company?.enforced && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1524
+ !company?.enforced && passwordOn && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1403
1525
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1404
1526
  Field,
1405
1527
  {
@@ -1417,6 +1539,7 @@ function SignIn(props) {
1417
1539
  challenge.node,
1418
1540
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.signIn })
1419
1541
  ] }),
1542
+ !company?.enforced && !passwordOn && methods?.magicLink && challenge.node,
1420
1543
  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 }),
1421
1544
  methods?.signUp !== false && props.links?.signUp && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: f.cx("footer"), children: [
1422
1545
  f.t.noAccount,
@@ -1476,14 +1599,30 @@ function SignUp(props) {
1476
1599
  f.done();
1477
1600
  };
1478
1601
  const general = f.error && !f.error.field ? f.error : null;
1602
+ const passwordOn = methods?.passwordSignIn !== false;
1603
+ const providers = auth.session?.user.isAnonymous === true || methods?.organization?.required ? [] : methods?.providers ?? [];
1479
1604
  if (sent) {
1480
1605
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: f.cx("form"), ...f.root, children: [
1481
1606
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: f.cx("notice"), children: f.t.checkEmail }),
1482
1607
  props.links?.signIn && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", { className: f.cx("link"), href: props.links.signIn, children: f.t.backToSignIn })
1483
1608
  ] });
1484
1609
  }
1610
+ const footer = props.links?.signIn && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: f.cx("footer"), children: [
1611
+ f.t.haveAccount,
1612
+ " ",
1613
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", { className: f.cx("link"), href: props.links.signIn, children: f.t.signIn })
1614
+ ] });
1615
+ if (!passwordOn) {
1616
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: f.cx("form"), ...f.root, children: [
1617
+ general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "alert", className: f.cx("error"), children: f.say(general) }),
1618
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ProviderButtons, { f, providers, onPick: (id) => auth.signIn.social(id, { returnTo: props.returnTo }) }),
1619
+ footer
1620
+ ] });
1621
+ }
1485
1622
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { className: f.cx("form"), ...f.root, onSubmit: submit, noValidate: true, children: [
1486
1623
  general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "alert", className: f.cx("error"), children: f.say(general) }),
1624
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ProviderButtons, { f, providers, onPick: (id) => auth.signIn.social(id, { returnTo: props.returnTo }) }),
1625
+ providers.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: f.cx("separator"), children: f.t.orWithEmail }),
1487
1626
  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 }),
1488
1627
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "name", type: "text", label: f.t.name, value: name, autoComplete: "name", onChange: setName }),
1489
1628
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "email", type: "email", label: f.t.email, value: email, autoComplete: "username", required: true, onChange: setEmail }),
@@ -1509,11 +1648,7 @@ function SignUp(props) {
1509
1648
  ] }, p.key)),
1510
1649
  challenge.node,
1511
1650
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.signUp }),
1512
- props.links?.signIn && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: f.cx("footer"), children: [
1513
- f.t.haveAccount,
1514
- " ",
1515
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", { className: f.cx("link"), href: props.links.signIn, children: f.t.signIn })
1516
- ] })
1651
+ footer
1517
1652
  ] });
1518
1653
  }
1519
1654
  function ForgotPassword(props) {
@@ -1610,6 +1745,62 @@ function VerifyEmail(props) {
1610
1745
  state !== "confirming" && props.links?.signIn && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", { className: f.cx("link"), href: props.links.signIn, children: f.t.backToSignIn })
1611
1746
  ] });
1612
1747
  }
1748
+ function AcceptInvite(props) {
1749
+ const auth = useAuth();
1750
+ const f = useForm(props);
1751
+ const [token, setToken] = (0, import_react.useState)("");
1752
+ const [invitation, setInvitation] = (0, import_react.useState)(null);
1753
+ const [state, setState] = (0, import_react.useState)("opening");
1754
+ const [password, setPassword] = (0, import_react.useState)("");
1755
+ usePasswordPolicy(f, useMethods(auth));
1756
+ useMailedToken("token", (t) => {
1757
+ if (!t) return setState("invalid");
1758
+ setToken(t);
1759
+ void auth.invitation(t).then((r) => {
1760
+ if (r.ok) {
1761
+ setInvitation(r.invitation);
1762
+ return setState("ready");
1763
+ }
1764
+ if (r.error.code !== "invalid_token") f.setError(r.error);
1765
+ setState("invalid");
1766
+ });
1767
+ });
1768
+ const submit = async (e) => {
1769
+ e.preventDefault();
1770
+ f.setBusy(true);
1771
+ f.setError(null);
1772
+ const r = await auth.acceptInvitation(token, password);
1773
+ f.setBusy(false);
1774
+ if (!r.ok) {
1775
+ if (r.error.code === "invalid_token") return setState("invalid");
1776
+ return f.setError(r.error);
1777
+ }
1778
+ if (r.status === "mfa_required") return setState("code");
1779
+ f.done();
1780
+ };
1781
+ const general = f.error && !f.error.field ? f.error : null;
1782
+ const back = props.links?.signIn && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", { className: f.cx("link"), href: props.links.signIn, children: f.t.backToSignIn });
1783
+ if (state === "opening") {
1784
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: f.cx("form"), ...f.root, "aria-busy": "true", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: f.cx("notice"), children: f.t.openingInvitation }) });
1785
+ }
1786
+ if (state === "invalid") {
1787
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: f.cx("form"), ...f.root, children: [
1788
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "alert", className: f.cx("error"), children: general ? f.say(general) : f.t.invitationInvalid }),
1789
+ back
1790
+ ] });
1791
+ }
1792
+ if (state === "code") return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SecondStepForm, { f, auth });
1793
+ const workspace = invitation?.workspace ?? "";
1794
+ const heading = invitation?.organization ? f.t.invitedToOrganization.replace("{organization}", invitation.organization).replace("{workspace}", workspace) : f.t.invitedTo.replace("{workspace}", workspace);
1795
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { className: f.cx("form"), ...f.root, onSubmit: submit, noValidate: true, children: [
1796
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { className: f.cx("heading"), children: heading }),
1797
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: f.cx("notice"), children: f.t.invitedAs.replace("{email}", invitation?.email ?? "") }),
1798
+ general && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "alert", className: f.cx("error"), children: f.say(general) }),
1799
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { type: "email", name: "username", autoComplete: "username", value: invitation?.email ?? "", readOnly: true, hidden: true }),
1800
+ /* @__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 }),
1801
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.acceptInvite })
1802
+ ] });
1803
+ }
1613
1804
  function useSection(props) {
1614
1805
  const f = useForm({ ...props, unstyled: true });
1615
1806
  const [done, setDone] = (0, import_react.useState)(null);
@@ -1660,32 +1851,52 @@ function Account(props) {
1660
1851
  ] })
1661
1852
  ] });
1662
1853
  }
1854
+ var OWNER = "owner";
1855
+ function memberActions(seat, m) {
1856
+ const reachable = m.role !== OWNER || seat.role === OWNER;
1857
+ return {
1858
+ changeRole: seat.can.manageMembers && reachable,
1859
+ // Leaving is anybody's; removing somebody else is cauthy:members:remove.
1860
+ remove: m.you || seat.can.removeMembers && reachable,
1861
+ inviteAgain: seat.can.manageMembers && m.invited
1862
+ };
1863
+ }
1864
+ function rolesToGive(seat, roles) {
1865
+ return seat.role === OWNER ? roles : roles.filter((c) => c.role !== OWNER);
1866
+ }
1663
1867
  function OrganizationMembers(props) {
1664
1868
  const auth = useAuth();
1665
1869
  const { root } = useForm(props);
1666
1870
  const { f, done, setDone, run } = useSection(props);
1667
- const problem = f.error && f.error.field !== "email" ? f.error : null;
1871
+ const problem = f.error && f.error.field !== "email" && f.error.field !== "name" ? f.error : null;
1668
1872
  const [seat, setSeat] = (0, import_react.useState)(void 0);
1669
1873
  const [members, setMembers] = (0, import_react.useState)(null);
1670
1874
  const [roles, setRoles] = (0, import_react.useState)([]);
1671
1875
  const [email, setEmail] = (0, import_react.useState)("");
1672
1876
  const [role, setRole] = (0, import_react.useState)("");
1877
+ const [name, setName] = (0, import_react.useState)("");
1673
1878
  const [asking, setAsking] = (0, import_react.useState)(null);
1879
+ const [gone, setGone] = (0, import_react.useState)(null);
1674
1880
  const id = props.organizationId;
1675
1881
  const when = props.formatDate ?? ((iso) => new Date(iso).toLocaleDateString());
1676
1882
  const load = async () => {
1677
1883
  const [list, people, choices] = await Promise.all([auth.organizations.list(), auth.organizations.members(id), auth.organizations.roles(id)]);
1678
- setSeat(list.find((o) => o.id === id) ?? null);
1884
+ const mine = list.find((o) => o.id === id) ?? null;
1885
+ setSeat(mine);
1886
+ if (mine) setName(mine.name);
1679
1887
  setMembers(people);
1680
1888
  setRoles(choices ?? []);
1681
- if (choices && choices.length > 0 && !choices.some((c) => c.role === role)) setRole(choices[0].role);
1889
+ const offered = mine ? rolesToGive(mine, choices ?? []) : [];
1890
+ if (offered.length > 0 && !offered.some((c) => c.role === role)) setRole(offered[0].role);
1682
1891
  };
1683
1892
  (0, import_react.useEffect)(() => {
1684
1893
  void load();
1685
1894
  }, [id]);
1895
+ if (gone !== null) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: f.cx("account"), ...root, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "status", className: f.cx("notice"), children: gone }) });
1686
1896
  if (seat === void 0) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: f.cx("account"), ...root, "aria-busy": "true" });
1687
1897
  if (seat === null) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: f.cx("account"), ...root, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: f.cx("notice"), children: f.t.organizationMissing }) });
1688
1898
  const manage = seat.can.manageMembers;
1899
+ const giveable = rolesToGive(seat, roles);
1689
1900
  const label = (r) => roles.find((c) => c.role === r)?.name || r;
1690
1901
  const add = async (e) => {
1691
1902
  e.preventDefault();
@@ -1710,6 +1921,25 @@ function OrganizationMembers(props) {
1710
1921
  const r = await run(() => auth.organizations.inviteAgain(id, m.userId, { locale: props.locale }));
1711
1922
  if (r.ok) setDone(f.t.memberInvitationSent);
1712
1923
  };
1924
+ const rename = async (e) => {
1925
+ e.preventDefault();
1926
+ const r = await run(() => auth.organizations.rename(id, name));
1927
+ if (!r.ok) return;
1928
+ setSeat(r.organization);
1929
+ setName(r.organization.name);
1930
+ setDone(f.t.organizationRenamed.replace("{name}", r.organization.name));
1931
+ };
1932
+ const close = async (how) => {
1933
+ setAsking(null);
1934
+ const r = await run(() => how === "archive" ? auth.organizations.archive(id) : auth.organizations.delete(id));
1935
+ if (r.ok) setGone((how === "archive" ? f.t.organizationArchived : f.t.organizationDeleted).replace("{org}", seat.name));
1936
+ };
1937
+ const confirming = (text2, label2, yes) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { role: "group", "aria-label": label2, children: [
1938
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: text2 }),
1939
+ " ",
1940
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: yes, children: f.t.yes }),
1941
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => setAsking(null), children: f.t.cancel })
1942
+ ] });
1713
1943
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: f.cx("account"), ...root, children: [
1714
1944
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: f.cx("section"), children: [
1715
1945
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("h2", { className: f.cx("heading"), children: [
@@ -1720,23 +1950,14 @@ function OrganizationMembers(props) {
1720
1950
  problem && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "alert", className: f.cx("error"), children: f.say(problem) }),
1721
1951
  members === null ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1722
1952
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: f.cx("notice"), children: f.t.membersHidden }),
1723
- auth.session && (asking === auth.session.user.id ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { role: "group", "aria-label": f.t.memberLeave, children: [
1724
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: f.t.memberLeaveConfirm.replace("{org}", seat.name) }),
1725
- " ",
1726
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1727
- f.Button,
1728
- {
1729
- type: "button",
1730
- disabled: f.busy,
1731
- className: f.cx("providerButton"),
1732
- onClick: () => void remove({ userId: auth.session.user.id, you: true }),
1733
- children: f.t.yes
1734
- }
1735
- ),
1736
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => setAsking(null), children: f.t.cancel })
1737
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => setAsking(auth.session.user.id), children: f.t.memberLeave }))
1953
+ auth.session && (asking === auth.session.user.id ? confirming(
1954
+ f.t.memberLeaveConfirm.replace("{org}", seat.name),
1955
+ f.t.memberLeave,
1956
+ () => void remove({ userId: auth.session.user.id, you: true })
1957
+ ) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => setAsking(auth.session.user.id), children: f.t.memberLeave }))
1738
1958
  ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { className: f.cx("sessions"), children: members.map((m) => {
1739
1959
  const who = m.name || m.email;
1960
+ const may = memberActions(seat, m);
1740
1961
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("li", { className: f.cx("session"), children: [
1741
1962
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
1742
1963
  who,
@@ -1748,7 +1969,7 @@ function OrganizationMembers(props) {
1748
1969
  when(m.since)
1749
1970
  ] }),
1750
1971
  " ",
1751
- manage && roles.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1972
+ may.changeRole && giveable.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1752
1973
  "select",
1753
1974
  {
1754
1975
  "aria-label": `${f.t.memberRole}: ${who}`,
@@ -1756,31 +1977,41 @@ function OrganizationMembers(props) {
1756
1977
  value: m.role,
1757
1978
  disabled: f.busy,
1758
1979
  onChange: (e) => void change(m, e.target.value),
1759
- children: roles.map((c) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: c.role, children: c.name || c.role }, c.role))
1980
+ children: (giveable.some((c) => c.role === m.role) ? giveable : [...roles.filter((c) => c.role === m.role), ...giveable]).map((c) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: c.role, children: c.name || c.role }, c.role))
1760
1981
  }
1761
1982
  ) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: label(m.role) }),
1762
1983
  " ",
1763
- asking === m.userId ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { role: "group", "aria-label": m.you ? f.t.memberLeave : f.t.memberRemove, children: [
1764
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: m.you ? f.t.memberLeaveConfirm.replace("{org}", seat.name) : f.t.memberRemoveConfirm.replace("{who}", who) }),
1765
- " ",
1766
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => void remove(m), children: f.t.yes }),
1767
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => setAsking(null), children: f.t.cancel })
1768
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1769
- manage && m.invited && /* @__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 }),
1770
- (manage || m.you) && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => setAsking(m.userId), children: m.you ? f.t.memberLeave : f.t.memberRemove })
1984
+ asking === m.userId ? confirming(
1985
+ m.you ? f.t.memberLeaveConfirm.replace("{org}", seat.name) : f.t.memberRemoveConfirm.replace("{who}", who),
1986
+ m.you ? f.t.memberLeave : f.t.memberRemove,
1987
+ () => void remove(m)
1988
+ ) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1989
+ 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 }),
1990
+ may.remove && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => setAsking(m.userId), children: m.you ? f.t.memberLeave : f.t.memberRemove })
1771
1991
  ] })
1772
1992
  ] }, m.userId);
1773
1993
  }) }),
1774
1994
  done && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { role: "status", className: f.cx("notice"), children: done })
1775
1995
  ] }),
1776
- manage && roles.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { className: f.cx("section"), onSubmit: add, noValidate: true, children: [
1996
+ manage && giveable.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { className: f.cx("section"), onSubmit: add, noValidate: true, children: [
1777
1997
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { className: f.cx("heading"), children: f.t.memberAdd }),
1778
1998
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "email", type: "email", label: f.t.email, value: email, autoComplete: "off", required: true, onChange: setEmail }),
1779
1999
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: f.cx("field"), children: [
1780
2000
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: f.cx("label"), children: f.t.memberRole }),
1781
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("select", { name: "role", className: f.cx("input"), value: role, onChange: (e) => setRole(e.target.value), children: roles.map((c) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: c.role, children: c.name || c.role }, c.role)) })
2001
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("select", { name: "role", className: f.cx("input"), value: role, onChange: (e) => setRole(e.target.value), children: giveable.map((c) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: c.role, children: c.name || c.role }, c.role)) })
1782
2002
  ] }),
1783
2003
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.memberAdd })
2004
+ ] }),
2005
+ (seat.can.update || seat.can.archive || seat.can.delete) && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: f.cx("section"), children: [
2006
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { className: f.cx("heading"), children: f.t.organization }),
2007
+ seat.can.update && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { onSubmit: rename, noValidate: true, children: [
2008
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Field, { f, name: "name", type: "text", label: f.t.name, value: name, autoComplete: "organization", required: true, onChange: setName }),
2009
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "submit", disabled: f.busy, className: f.cx("button"), children: f.t.organizationRename })
2010
+ ] }),
2011
+ asking === "archive" ? confirming(f.t.organizationArchiveConfirm.replace("{org}", seat.name), f.t.organizationArchive, () => void close("archive")) : asking === "delete" ? confirming(f.t.organizationDeleteConfirm.replace("{org}", seat.name), f.t.organizationDelete, () => void close("delete")) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
2012
+ seat.can.archive && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => setAsking("archive"), children: f.t.organizationArchive }),
2013
+ seat.can.delete && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(f.Button, { type: "button", disabled: f.busy, className: f.cx("providerButton"), onClick: () => setAsking("delete"), children: f.t.organizationDelete })
2014
+ ] })
1784
2015
  ] })
1785
2016
  ] });
1786
2017
  }
@@ -2217,6 +2448,8 @@ function useAuth() {
2217
2448
  googleOneTap: (o) => then(client.signIn.googleOneTap(o))
2218
2449
  },
2219
2450
  signUp: { email: (f) => then(client.signUp.email(f)) },
2451
+ // The invited person is signed in once they accept.
2452
+ acceptInvitation: (token, password) => then(client.acceptInvitation(token, password)),
2220
2453
  signOut: () => then(client.signOut()),
2221
2454
  account: {
2222
2455
  ...client.account,
@@ -2235,6 +2468,7 @@ function useAuth() {
2235
2468
  }
2236
2469
  // Annotate the CommonJS export names for ESM import in node:
2237
2470
  0 && (module.exports = {
2471
+ AcceptInvite,
2238
2472
  Account,
2239
2473
  CauthyProvider,
2240
2474
  ForgotPassword,