@authowl/react-native 0.3.16 → 0.4.1

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/index.js CHANGED
@@ -168,6 +168,9 @@ function usePublicConfig() {
168
168
  isLoading: publicConfigState === "loading"
169
169
  };
170
170
  }
171
+ function usePrivacy() {
172
+ return useAuthOwlContext().client.privacy;
173
+ }
171
174
  function useSession() {
172
175
  const { client } = useAuthOwlContext();
173
176
  const store = client.sessionStore;
@@ -521,11 +524,15 @@ function SignIn({
521
524
  // src/components/SignUp.tsx
522
525
  import { useEffect as useEffect2, useState as useState3 } from "react";
523
526
  import { Linking, Pressable as Pressable2, Text as Text3, View as View3 } from "react-native";
524
- import { resolveProjectCapabilities as resolveProjectCapabilities2 } from "@authowl/core/native";
527
+ import {
528
+ buildPrivacySignUpEvidence,
529
+ createIdempotencyKey,
530
+ resolveProjectCapabilities as resolveProjectCapabilities2
531
+ } from "@authowl/core/native";
525
532
  import { Fragment, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
526
533
  function SignUp({ onSignedUp, structuredName = false, theme = defaultTheme }) {
527
534
  const t = useT();
528
- const { direction } = useLocale();
535
+ const { locale, direction } = useLocale();
529
536
  const toMessage = useServerError();
530
537
  const client = useAuthOwlClient();
531
538
  const config = usePublicConfig();
@@ -539,10 +546,18 @@ function SignUp({ onSignedUp, structuredName = false, theme = defaultTheme }) {
539
546
  const [busy, setBusy] = useState3(false);
540
547
  const [error, setError] = useState3(null);
541
548
  const [acceptedConsent, setAcceptedConsent] = useState3(false);
549
+ const [grantedPurposeCodes, setGrantedPurposeCodes] = useState3(
550
+ () => /* @__PURE__ */ new Set()
551
+ );
552
+ const [privacyCorrelationId] = useState3(createIdempotencyKey);
542
553
  const legal = config.data?.legal;
554
+ const privacy = config.data?.privacy;
543
555
  useEffect2(() => {
544
556
  setAcceptedConsent(false);
545
557
  }, [legal?.version]);
558
+ useEffect2(() => {
559
+ setGrantedPurposeCodes(/* @__PURE__ */ new Set());
560
+ }, [privacy]);
546
561
  const useStructuredName = structuredName || capabilities.firstLastName;
547
562
  const displayName = useStructuredName ? [firstName.trim(), lastName.trim()].filter(Boolean).join(" ") : name.trim();
548
563
  const canSubmit = email.trim().length > 0 && password.length >= capabilities.passwordMinLength && password.length <= capabilities.passwordMaxLength && displayName.length > 0 && (!legal?.required || acceptedConsent);
@@ -555,6 +570,14 @@ function SignUp({ onSignedUp, structuredName = false, theme = defaultTheme }) {
555
570
  password,
556
571
  name: displayName,
557
572
  ...legal?.required ? { consentVersion: legal.version } : {},
573
+ ...privacy && privacy.notices.length > 0 ? {
574
+ privacyEvidence: buildPrivacySignUpEvidence(
575
+ privacy,
576
+ locale,
577
+ grantedPurposeCodes,
578
+ privacyCorrelationId
579
+ )
580
+ } : {},
558
581
  ...useStructuredName ? { firstName: firstName.trim(), lastName: lastName.trim() } : {}
559
582
  });
560
583
  setBusy(false);
@@ -692,6 +715,25 @@ function SignUp({ onSignedUp, structuredName = false, theme = defaultTheme }) {
692
715
  consentSuffix
693
716
  ] })
694
717
  ] }) : null,
718
+ privacy && (privacy.notices.length > 0 || privacy.consentPurposes.length > 0) ? /* @__PURE__ */ jsx3(
719
+ NativePrivacySignUpEvidence,
720
+ {
721
+ privacy,
722
+ locale,
723
+ direction,
724
+ grantedPurposeCodes,
725
+ onPurposeChange: (purposeCode, granted) => {
726
+ setGrantedPurposeCodes((current) => {
727
+ const next = new Set(current);
728
+ if (granted) next.add(purposeCode);
729
+ else next.delete(purposeCode);
730
+ return next;
731
+ });
732
+ },
733
+ theme,
734
+ disabled: busy
735
+ }
736
+ ) : null,
695
737
  /* @__PURE__ */ jsx3(FormError, { message: error, theme }),
696
738
  /* @__PURE__ */ jsx3(
697
739
  SubmitButton,
@@ -707,21 +749,338 @@ function SignUp({ onSignedUp, structuredName = false, theme = defaultTheme }) {
707
749
  )
708
750
  ] });
709
751
  }
752
+ function NativePrivacySignUpEvidence({
753
+ privacy,
754
+ locale,
755
+ direction,
756
+ grantedPurposeCodes,
757
+ onPurposeChange,
758
+ theme,
759
+ disabled
760
+ }) {
761
+ const t = useT();
762
+ return /* @__PURE__ */ jsxs3(
763
+ View3,
764
+ {
765
+ style: {
766
+ gap: theme.spacing,
767
+ borderWidth: 1,
768
+ borderColor: theme.border,
769
+ borderRadius: theme.radius,
770
+ backgroundColor: theme.surface,
771
+ padding: theme.spacing
772
+ },
773
+ testID: "authowl-signup-privacy-evidence",
774
+ children: [
775
+ /* @__PURE__ */ jsxs3(View3, { style: { gap: 4 }, children: [
776
+ /* @__PURE__ */ jsx3(Text3, { style: { color: theme.text, fontSize: 16, fontWeight: "600" }, children: t("privacy.signup.title") }),
777
+ /* @__PURE__ */ jsx3(Text3, { style: { color: theme.mutedText, fontSize: 13, lineHeight: 19 }, children: t("privacy.signup.description") })
778
+ ] }),
779
+ privacy.notices.map((notice) => /* @__PURE__ */ jsxs3(
780
+ View3,
781
+ {
782
+ style: { gap: 4, borderTopWidth: 1, borderTopColor: theme.border, paddingTop: 10 },
783
+ children: [
784
+ /* @__PURE__ */ jsx3(Text3, { style: { color: theme.text, fontSize: 14, fontWeight: "600", writingDirection: direction }, children: notice.title[locale] }),
785
+ /* @__PURE__ */ jsx3(Text3, { style: { color: theme.mutedText, fontSize: 12, lineHeight: 18, writingDirection: direction }, children: notice.body[locale] })
786
+ ]
787
+ },
788
+ notice.noticeVersionId
789
+ )),
790
+ privacy.consentPurposes.length > 0 ? /* @__PURE__ */ jsxs3(View3, { style: { gap: 8 }, children: [
791
+ /* @__PURE__ */ jsx3(Text3, { style: { color: theme.text, fontSize: 13, fontWeight: "600" }, children: t("privacy.signup.optional") }),
792
+ privacy.consentPurposes.map((purpose) => {
793
+ const granted = grantedPurposeCodes.has(purpose.code);
794
+ return /* @__PURE__ */ jsxs3(
795
+ Pressable2,
796
+ {
797
+ testID: `authowl-signup-purpose-${purpose.code}`,
798
+ accessibilityRole: "checkbox",
799
+ accessibilityState: { checked: granted, disabled },
800
+ disabled,
801
+ onPress: () => onPurposeChange(purpose.code, !granted),
802
+ style: { flexDirection: "row", alignItems: "flex-start", gap: 8 },
803
+ children: [
804
+ /* @__PURE__ */ jsx3(
805
+ View3,
806
+ {
807
+ style: {
808
+ width: 18,
809
+ height: 18,
810
+ alignItems: "center",
811
+ justifyContent: "center",
812
+ borderWidth: 1,
813
+ borderColor: granted ? theme.accent : theme.border,
814
+ borderRadius: 4,
815
+ backgroundColor: granted ? theme.accent : theme.background
816
+ },
817
+ children: granted ? /* @__PURE__ */ jsx3(Text3, { style: { color: theme.accentText, fontSize: 13 }, children: "\u2713" }) : null
818
+ }
819
+ ),
820
+ /* @__PURE__ */ jsxs3(View3, { style: { flex: 1, gap: 2 }, children: [
821
+ /* @__PURE__ */ jsx3(Text3, { style: { color: theme.text, fontSize: 13, fontWeight: "600", writingDirection: direction }, children: purpose.title[locale] }),
822
+ /* @__PURE__ */ jsx3(Text3, { style: { color: theme.mutedText, fontSize: 12, lineHeight: 17, writingDirection: direction }, children: purpose.description[locale] })
823
+ ] })
824
+ ]
825
+ },
826
+ purpose.purposeVersionId
827
+ );
828
+ }),
829
+ /* @__PURE__ */ jsx3(Text3, { style: { color: theme.mutedText, fontSize: 12, lineHeight: 18 }, children: t("privacy.signup.choiceNote") })
830
+ ] }) : null
831
+ ]
832
+ }
833
+ );
834
+ }
710
835
 
711
- // src/components/EmailOtpForm.tsx
712
- import { useState as useState4 } from "react";
713
- import { Text as Text4, View as View4 } from "react-native";
836
+ // src/components/PrivacyCenter.tsx
837
+ import { useCallback as useCallback2, useEffect as useEffect3, useMemo as useMemo4, useState as useState4 } from "react";
838
+ import { ActivityIndicator as ActivityIndicator2, Pressable as Pressable3, Text as Text4, View as View4 } from "react-native";
714
839
  import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
840
+ var RIGHT_KEYS = {
841
+ access: "privacy.right.access",
842
+ correction: "privacy.right.correction",
843
+ portability: "privacy.right.portability",
844
+ erasure: "privacy.right.erasure",
845
+ restriction: "privacy.right.restriction",
846
+ objection: "privacy.right.objection",
847
+ consent_withdrawal: "privacy.right.consentWithdrawal"
848
+ };
849
+ var STATE_KEYS = {
850
+ received: "privacy.state.received",
851
+ identity_pending: "privacy.state.identityPending",
852
+ in_progress: "privacy.state.inProgress",
853
+ restricted: "privacy.state.restricted",
854
+ completed: "privacy.state.completed",
855
+ denied: "privacy.state.denied",
856
+ withdrawn: "privacy.state.withdrawn"
857
+ };
858
+ function PrivacyCenter({ theme = defaultTheme } = {}) {
859
+ const t = useT();
860
+ const { locale, direction } = useLocale();
861
+ const toMessage = useServerError();
862
+ const client = useAuthOwlClient();
863
+ const config = usePublicConfig();
864
+ const session = useSession();
865
+ const [preferences, setPreferences] = useState4([]);
866
+ const [requests, setRequests] = useState4([]);
867
+ const [loading, setLoading] = useState4(true);
868
+ const [error, setError] = useState4(null);
869
+ const [pendingPurpose, setPendingPurpose] = useState4(null);
870
+ const [pendingRight, setPendingRight] = useState4(null);
871
+ const signedIn = session.data !== null;
872
+ const refresh = useCallback2(async (showLoading = false) => {
873
+ if (!signedIn) {
874
+ setLoading(false);
875
+ return;
876
+ }
877
+ if (showLoading) setLoading(true);
878
+ const [consent, rights] = await Promise.all([
879
+ client.privacy.listConsentPreferences(),
880
+ client.privacy.listRightsRequests()
881
+ ]);
882
+ const failure = consent.error ?? rights.error;
883
+ if (failure) setError(toMessage(failure, "privacy.error.load"));
884
+ else {
885
+ setPreferences(consent.data?.preferences ?? []);
886
+ setRequests(rights.data?.requests ?? []);
887
+ setError(null);
888
+ }
889
+ setLoading(false);
890
+ }, [client, signedIn, toMessage]);
891
+ useEffect3(() => {
892
+ if (!session.isPending) void refresh(true);
893
+ }, [refresh, session.data?.user.id, session.isPending]);
894
+ const preferencesByCode = useMemo4(
895
+ () => new Map(preferences.map((preference) => [preference.code, preference])),
896
+ [preferences]
897
+ );
898
+ async function updateConsent(purposeCode, granted) {
899
+ const privacy2 = config.data?.privacy;
900
+ const purpose = privacy2?.consentPurposes.find((item) => item.code === purposeCode);
901
+ const notice = privacy2?.notices.find((item) => item.purposeCodes.includes(purposeCode));
902
+ if (!purpose || !notice) {
903
+ setError(t("privacy.error.unavailable"));
904
+ return;
905
+ }
906
+ setPendingPurpose(purposeCode);
907
+ setError(null);
908
+ const result = await client.privacy.recordConsent({
909
+ purposeCode,
910
+ purposeVersionId: purpose.purposeVersionId,
911
+ noticeVersionId: notice.noticeVersionId,
912
+ decision: granted ? "granted" : "withdrawn",
913
+ locale
914
+ });
915
+ if (result.error) setError(toMessage(result.error, "privacy.error.save"));
916
+ else await refresh();
917
+ setPendingPurpose(null);
918
+ }
919
+ async function createRequest(rightType) {
920
+ setPendingRight(rightType);
921
+ setError(null);
922
+ const result = await client.privacy.createRightsRequest({ rightType, locale });
923
+ if (result.error) setError(toMessage(result.error, "privacy.error.request"));
924
+ else await refresh();
925
+ setPendingRight(null);
926
+ }
927
+ if (session.isPending || loading) {
928
+ return /* @__PURE__ */ jsx4(ActivityIndicator2, { color: theme.accent, testID: "authowl-privacy-loading" });
929
+ }
930
+ if (!signedIn) return /* @__PURE__ */ jsx4(Text4, { style: { color: theme.mutedText }, children: t("privacy.signedOut") });
931
+ const privacy = config.data?.privacy;
932
+ return /* @__PURE__ */ jsxs4(
933
+ View4,
934
+ {
935
+ testID: "authowl-privacy-center",
936
+ style: { gap: theme.spacing, backgroundColor: theme.background },
937
+ children: [
938
+ /* @__PURE__ */ jsxs4(View4, { style: { gap: 4 }, children: [
939
+ /* @__PURE__ */ jsx4(Text4, { style: { color: theme.mutedText, fontSize: 12, fontWeight: "700" }, children: t("privacy.dataUse") }),
940
+ /* @__PURE__ */ jsx4(Text4, { style: { color: theme.text, fontSize: 22, fontWeight: "600" }, children: t("privacy.title") }),
941
+ /* @__PURE__ */ jsx4(Text4, { style: { color: theme.mutedText, fontSize: 14, lineHeight: 20 }, children: t("privacy.description") })
942
+ ] }),
943
+ /* @__PURE__ */ jsx4(FormError, { message: error, theme, testID: "authowl-privacy-error" }),
944
+ /* @__PURE__ */ jsx4(
945
+ PrivacyBlock,
946
+ {
947
+ title: t("privacy.choices.title"),
948
+ description: t("privacy.choices.description"),
949
+ theme,
950
+ children: (privacy?.consentPurposes.length ?? 0) === 0 ? /* @__PURE__ */ jsx4(Text4, { style: { color: theme.mutedText }, children: t("privacy.choices.empty") }) : privacy.consentPurposes.map((purpose) => {
951
+ const granted = preferencesByCode.get(purpose.code)?.state === "granted";
952
+ const pending = pendingPurpose === purpose.code;
953
+ return /* @__PURE__ */ jsxs4(
954
+ View4,
955
+ {
956
+ style: { flexDirection: "row", alignItems: "center", gap: 10 },
957
+ children: [
958
+ /* @__PURE__ */ jsxs4(View4, { style: { flex: 1, gap: 2 }, children: [
959
+ /* @__PURE__ */ jsx4(Text4, { style: { color: theme.text, fontWeight: "600", writingDirection: direction }, children: purpose.title[locale] }),
960
+ /* @__PURE__ */ jsx4(Text4, { style: { color: theme.mutedText, fontSize: 12, lineHeight: 18, writingDirection: direction }, children: purpose.description[locale] })
961
+ ] }),
962
+ /* @__PURE__ */ jsx4(
963
+ Pressable3,
964
+ {
965
+ testID: `authowl-privacy-purpose-${purpose.code}`,
966
+ accessibilityRole: "switch",
967
+ accessibilityLabel: purpose.title[locale],
968
+ accessibilityState: { checked: granted, disabled: pending },
969
+ disabled: pending,
970
+ onPress: () => void updateConsent(purpose.code, !granted),
971
+ style: {
972
+ minWidth: 58,
973
+ borderRadius: theme.radius,
974
+ paddingHorizontal: 10,
975
+ paddingVertical: 8,
976
+ alignItems: "center",
977
+ backgroundColor: granted ? theme.accent : theme.surface,
978
+ borderWidth: 1,
979
+ borderColor: granted ? theme.accent : theme.border,
980
+ opacity: pending ? 0.55 : 1
981
+ },
982
+ children: /* @__PURE__ */ jsx4(Text4, { style: { color: granted ? theme.accentText : theme.text, fontWeight: "600" }, children: pending ? t("common.working") : t(granted ? "privacy.on" : "privacy.off") })
983
+ }
984
+ )
985
+ ]
986
+ },
987
+ purpose.purposeVersionId
988
+ );
989
+ })
990
+ }
991
+ ),
992
+ /* @__PURE__ */ jsx4(
993
+ PrivacyBlock,
994
+ {
995
+ title: t("privacy.rights.title"),
996
+ description: t("privacy.rights.description"),
997
+ theme,
998
+ children: Object.keys(RIGHT_KEYS).map((right) => /* @__PURE__ */ jsx4(
999
+ Pressable3,
1000
+ {
1001
+ testID: `authowl-privacy-right-${right}`,
1002
+ accessibilityRole: "button",
1003
+ accessibilityState: { disabled: pendingRight !== null, busy: pendingRight === right },
1004
+ disabled: pendingRight !== null,
1005
+ onPress: () => void createRequest(right),
1006
+ style: {
1007
+ borderWidth: 1,
1008
+ borderColor: right === "erasure" ? theme.danger : theme.border,
1009
+ borderRadius: theme.radius,
1010
+ padding: 12,
1011
+ opacity: pendingRight !== null ? 0.55 : 1
1012
+ },
1013
+ children: /* @__PURE__ */ jsx4(Text4, { style: { color: right === "erasure" ? theme.danger : theme.text, fontWeight: "600" }, children: pendingRight === right ? t("common.working") : t(RIGHT_KEYS[right]) })
1014
+ },
1015
+ right
1016
+ ))
1017
+ }
1018
+ ),
1019
+ /* @__PURE__ */ jsx4(
1020
+ PrivacyBlock,
1021
+ {
1022
+ title: t("privacy.requests.title"),
1023
+ description: t("privacy.requests.description"),
1024
+ theme,
1025
+ children: requests.length === 0 ? /* @__PURE__ */ jsx4(Text4, { style: { color: theme.mutedText }, children: t("privacy.requests.empty") }) : requests.map((request) => /* @__PURE__ */ jsxs4(
1026
+ View4,
1027
+ {
1028
+ style: { flexDirection: "row", justifyContent: "space-between", gap: 12 },
1029
+ children: [
1030
+ /* @__PURE__ */ jsx4(Text4, { style: { flex: 1, color: theme.text }, children: t(RIGHT_KEYS[request.rightType]) }),
1031
+ /* @__PURE__ */ jsx4(Text4, { style: { color: theme.mutedText, fontWeight: "600" }, children: t(STATE_KEYS[request.state]) })
1032
+ ]
1033
+ },
1034
+ request.id
1035
+ ))
1036
+ }
1037
+ )
1038
+ ]
1039
+ }
1040
+ );
1041
+ }
1042
+ function PrivacyBlock({
1043
+ title,
1044
+ description,
1045
+ theme,
1046
+ children
1047
+ }) {
1048
+ return /* @__PURE__ */ jsxs4(
1049
+ View4,
1050
+ {
1051
+ style: {
1052
+ gap: theme.spacing,
1053
+ borderWidth: 1,
1054
+ borderColor: theme.border,
1055
+ borderRadius: theme.radius,
1056
+ backgroundColor: theme.surface,
1057
+ padding: theme.spacing
1058
+ },
1059
+ children: [
1060
+ /* @__PURE__ */ jsxs4(View4, { style: { gap: 3 }, children: [
1061
+ /* @__PURE__ */ jsx4(Text4, { style: { color: theme.text, fontSize: 16, fontWeight: "600" }, children: title }),
1062
+ /* @__PURE__ */ jsx4(Text4, { style: { color: theme.mutedText, fontSize: 13, lineHeight: 19 }, children: description })
1063
+ ] }),
1064
+ children
1065
+ ]
1066
+ }
1067
+ );
1068
+ }
1069
+
1070
+ // src/components/EmailOtpForm.tsx
1071
+ import { useState as useState5 } from "react";
1072
+ import { Text as Text5, View as View5 } from "react-native";
1073
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
715
1074
  function EmailOtpForm({ onSignedIn, theme = defaultTheme }) {
716
1075
  const t = useT();
717
1076
  const toMessage = useServerError();
718
1077
  const client = useAuthOwlClient();
719
1078
  const styles = useStyles(theme);
720
- const [stage, setStage] = useState4("email");
721
- const [email, setEmail] = useState4("");
722
- const [code, setCode] = useState4("");
723
- const [busy, setBusy] = useState4(false);
724
- const [error, setError] = useState4(null);
1079
+ const [stage, setStage] = useState5("email");
1080
+ const [email, setEmail] = useState5("");
1081
+ const [code, setCode] = useState5("");
1082
+ const [busy, setBusy] = useState5(false);
1083
+ const [error, setError] = useState5(null);
725
1084
  async function requestCode() {
726
1085
  if (busy || email.trim().length === 0) return;
727
1086
  setBusy(true);
@@ -755,8 +1114,8 @@ function EmailOtpForm({ onSignedIn, theme = defaultTheme }) {
755
1114
  setError(null);
756
1115
  }
757
1116
  if (stage === "email") {
758
- return /* @__PURE__ */ jsxs4(View4, { style: styles.container, testID: "authowl-emailotp", children: [
759
- /* @__PURE__ */ jsx4(
1117
+ return /* @__PURE__ */ jsxs5(View5, { style: styles.container, testID: "authowl-emailotp", children: [
1118
+ /* @__PURE__ */ jsx5(
760
1119
  Field,
761
1120
  {
762
1121
  label: t("common.emailLabel"),
@@ -770,8 +1129,8 @@ function EmailOtpForm({ onSignedIn, theme = defaultTheme }) {
770
1129
  onSubmitEditing: requestCode
771
1130
  }
772
1131
  ),
773
- /* @__PURE__ */ jsx4(FormError, { message: error, theme }),
774
- /* @__PURE__ */ jsx4(
1132
+ /* @__PURE__ */ jsx5(FormError, { message: error, theme }),
1133
+ /* @__PURE__ */ jsx5(
775
1134
  SubmitButton,
776
1135
  {
777
1136
  label: t("emailOtp.requestSubmit"),
@@ -785,8 +1144,8 @@ function EmailOtpForm({ onSignedIn, theme = defaultTheme }) {
785
1144
  )
786
1145
  ] });
787
1146
  }
788
- return /* @__PURE__ */ jsxs4(View4, { style: styles.container, testID: "authowl-emailotp", children: [
789
- /* @__PURE__ */ jsx4(
1147
+ return /* @__PURE__ */ jsxs5(View5, { style: styles.container, testID: "authowl-emailotp", children: [
1148
+ /* @__PURE__ */ jsx5(
790
1149
  Field,
791
1150
  {
792
1151
  label: t("emailOtp.codeLabel", { email: email.trim() }),
@@ -800,8 +1159,8 @@ function EmailOtpForm({ onSignedIn, theme = defaultTheme }) {
800
1159
  onSubmitEditing: verifyCode
801
1160
  }
802
1161
  ),
803
- /* @__PURE__ */ jsx4(FormError, { message: error, theme }),
804
- /* @__PURE__ */ jsx4(
1162
+ /* @__PURE__ */ jsx5(FormError, { message: error, theme }),
1163
+ /* @__PURE__ */ jsx5(
805
1164
  SubmitButton,
806
1165
  {
807
1166
  label: t("emailOtp.verifySubmit"),
@@ -813,14 +1172,14 @@ function EmailOtpForm({ onSignedIn, theme = defaultTheme }) {
813
1172
  testID: "authowl-emailotp-verify"
814
1173
  }
815
1174
  ),
816
- /* @__PURE__ */ jsx4(Text4, { style: styles.link, onPress: changeEmail, testID: "authowl-emailotp-change", children: t("emailOtp.changeEmail") })
1175
+ /* @__PURE__ */ jsx5(Text5, { style: styles.link, onPress: changeEmail, testID: "authowl-emailotp-change", children: t("emailOtp.changeEmail") })
817
1176
  ] });
818
1177
  }
819
1178
 
820
1179
  // src/components/OrganizationSwitcher.tsx
821
- import { useCallback as useCallback2, useEffect as useEffect3, useRef, useState as useState5 } from "react";
822
- import { Pressable as Pressable3, Text as Text5, View as View5 } from "react-native";
823
- import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
1180
+ import { useCallback as useCallback3, useEffect as useEffect4, useRef, useState as useState6 } from "react";
1181
+ import { Pressable as Pressable4, Text as Text6, View as View6 } from "react-native";
1182
+ import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
824
1183
  function OrganizationSwitcher({
825
1184
  onSwitched,
826
1185
  allowPersonal = false,
@@ -832,14 +1191,14 @@ function OrganizationSwitcher({
832
1191
  const config = usePublicConfig();
833
1192
  const session = useSession();
834
1193
  const styles = useStyles(theme);
835
- const [organizations, setOrganizations] = useState5(null);
836
- const [switching, setSwitching] = useState5(null);
837
- const [error, setError] = useState5(null);
1194
+ const [organizations, setOrganizations] = useState6(null);
1195
+ const [switching, setSwitching] = useState6(null);
1196
+ const [error, setError] = useState6(null);
838
1197
  const activeId = session.data?.session.activeOrganizationId ?? null;
839
1198
  const userId = session.data?.user.id ?? null;
840
1199
  const signedIn = userId !== null;
841
1200
  const requestGeneration = useRef(0);
842
- const load = useCallback2(async () => {
1201
+ const load = useCallback3(async () => {
843
1202
  const generation = ++requestGeneration.current;
844
1203
  setError(null);
845
1204
  const result = await client.organization.list();
@@ -850,7 +1209,7 @@ function OrganizationSwitcher({
850
1209
  }
851
1210
  setOrganizations(result.data ?? []);
852
1211
  }, [client, toMessage]);
853
- useEffect3(() => {
1212
+ useEffect4(() => {
854
1213
  requestGeneration.current += 1;
855
1214
  setOrganizations(null);
856
1215
  setError(null);
@@ -876,14 +1235,14 @@ function OrganizationSwitcher({
876
1235
  }
877
1236
  if (config.isLoading || config.data !== null && !config.data.organizations) return null;
878
1237
  if (!signedIn) {
879
- return /* @__PURE__ */ jsx5(View5, { style: styles.container, testID: "authowl-orgswitcher", children: /* @__PURE__ */ jsx5(Text5, { style: styles.label, children: t("organization.signedOut") }) });
1238
+ return /* @__PURE__ */ jsx6(View6, { style: styles.container, testID: "authowl-orgswitcher", children: /* @__PURE__ */ jsx6(Text6, { style: styles.label, children: t("organization.signedOut") }) });
880
1239
  }
881
1240
  if (organizations === null && error === null) {
882
- return /* @__PURE__ */ jsx5(View5, { style: styles.container, testID: "authowl-orgswitcher", children: /* @__PURE__ */ jsx5(Text5, { style: styles.label, children: t("organization.loading") }) });
1241
+ return /* @__PURE__ */ jsx6(View6, { style: styles.container, testID: "authowl-orgswitcher", children: /* @__PURE__ */ jsx6(Text6, { style: styles.label, children: t("organization.loading") }) });
883
1242
  }
884
- return /* @__PURE__ */ jsxs5(View5, { style: styles.container, testID: "authowl-orgswitcher", children: [
885
- /* @__PURE__ */ jsx5(Text5, { style: styles.label, children: t("organization.switcher.label") }),
886
- allowPersonal ? /* @__PURE__ */ jsx5(
1243
+ return /* @__PURE__ */ jsxs6(View6, { style: styles.container, testID: "authowl-orgswitcher", children: [
1244
+ /* @__PURE__ */ jsx6(Text6, { style: styles.label, children: t("organization.switcher.label") }),
1245
+ allowPersonal ? /* @__PURE__ */ jsx6(
887
1246
  OrganizationRow,
888
1247
  {
889
1248
  label: t("organization.personal"),
@@ -895,7 +1254,7 @@ function OrganizationSwitcher({
895
1254
  theme
896
1255
  }
897
1256
  ) : null,
898
- (organizations ?? []).map((organization) => /* @__PURE__ */ jsx5(
1257
+ (organizations ?? []).map((organization) => /* @__PURE__ */ jsx6(
899
1258
  OrganizationRow,
900
1259
  {
901
1260
  label: organization.name,
@@ -908,9 +1267,9 @@ function OrganizationSwitcher({
908
1267
  },
909
1268
  organization.id
910
1269
  )),
911
- /* @__PURE__ */ jsx5(FormError, { message: error, theme, testID: "authowl-orgswitcher-error" }),
912
- error !== null ? /* @__PURE__ */ jsx5(
913
- Text5,
1270
+ /* @__PURE__ */ jsx6(FormError, { message: error, theme, testID: "authowl-orgswitcher-error" }),
1271
+ error !== null ? /* @__PURE__ */ jsx6(
1272
+ Text6,
914
1273
  {
915
1274
  style: styles.link,
916
1275
  onPress: () => void load(),
@@ -930,24 +1289,24 @@ function OrganizationRow({
930
1289
  theme
931
1290
  }) {
932
1291
  const styles = useStyles(theme);
933
- return /* @__PURE__ */ jsx5(
934
- Pressable3,
1292
+ return /* @__PURE__ */ jsx6(
1293
+ Pressable4,
935
1294
  {
936
1295
  onPress,
937
1296
  disabled: disabled || selected,
938
1297
  testID,
939
1298
  accessibilityRole: "button",
940
1299
  accessibilityState: { disabled: disabled || selected, busy },
941
- children: /* @__PURE__ */ jsx5(Text5, { style: selected ? [styles.label, { color: theme.accent }] : styles.label, children: label })
1300
+ children: /* @__PURE__ */ jsx6(Text6, { style: selected ? [styles.label, { color: theme.accent }] : styles.label, children: label })
942
1301
  }
943
1302
  );
944
1303
  }
945
1304
 
946
1305
  // src/components/PasskeyEnrollment.tsx
947
- import { useState as useState6 } from "react";
948
- import { Text as Text6, View as View6 } from "react-native";
1306
+ import { useState as useState7 } from "react";
1307
+ import { Text as Text7, View as View7 } from "react-native";
949
1308
  import { resolveProjectCapabilities as resolveProjectCapabilities3 } from "@authowl/core/native";
950
- import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
1309
+ import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
951
1310
  function usePasskeyClient() {
952
1311
  const client = useAuthOwlClient();
953
1312
  return "addPasskey" in client.passkey ? client : null;
@@ -964,8 +1323,8 @@ function PasskeyEnrollment({
964
1323
  const config = usePublicConfig();
965
1324
  const capabilities = resolveProjectCapabilities3(config.data);
966
1325
  const styles = useStyles(theme);
967
- const [busy, setBusy] = useState6(false);
968
- const [error, setError] = useState6(null);
1326
+ const [busy, setBusy] = useState7(false);
1327
+ const [error, setError] = useState7(null);
969
1328
  if (client === null || config.isLoading || config.data !== null && !capabilities.passkeyAdd) {
970
1329
  return null;
971
1330
  }
@@ -981,11 +1340,11 @@ function PasskeyEnrollment({
981
1340
  }
982
1341
  onEnrolled?.();
983
1342
  }
984
- return /* @__PURE__ */ jsxs6(View6, { style: styles.container, testID: "authowl-passkey-enrollment", children: [
985
- /* @__PURE__ */ jsx6(Text6, { style: styles.title, children: t("signUp.passkeyTitle") }),
986
- /* @__PURE__ */ jsx6(Text6, { style: styles.label, children: t("signUp.passkeyDescription") }),
987
- /* @__PURE__ */ jsx6(FormError, { message: error, theme, testID: "authowl-passkey-error" }),
988
- /* @__PURE__ */ jsx6(
1343
+ return /* @__PURE__ */ jsxs7(View7, { style: styles.container, testID: "authowl-passkey-enrollment", children: [
1344
+ /* @__PURE__ */ jsx7(Text7, { style: styles.title, children: t("signUp.passkeyTitle") }),
1345
+ /* @__PURE__ */ jsx7(Text7, { style: styles.label, children: t("signUp.passkeyDescription") }),
1346
+ /* @__PURE__ */ jsx7(FormError, { message: error, theme, testID: "authowl-passkey-error" }),
1347
+ /* @__PURE__ */ jsx7(
989
1348
  SubmitButton,
990
1349
  {
991
1350
  label: t("signUp.passkeySubmit"),
@@ -996,7 +1355,7 @@ function PasskeyEnrollment({
996
1355
  testID: "authowl-passkey-submit"
997
1356
  }
998
1357
  ),
999
- onSkip ? /* @__PURE__ */ jsx6(Text6, { style: styles.link, onPress: onSkip, testID: "authowl-passkey-skip", children: t("signUp.passkeySkip") }) : null
1358
+ onSkip ? /* @__PURE__ */ jsx7(Text7, { style: styles.link, onPress: onSkip, testID: "authowl-passkey-skip", children: t("signUp.passkeySkip") }) : null
1000
1359
  ] });
1001
1360
  }
1002
1361
  function PasskeySignInButton({
@@ -1009,8 +1368,8 @@ function PasskeySignInButton({
1009
1368
  const config = usePublicConfig();
1010
1369
  const capabilities = resolveProjectCapabilities3(config.data);
1011
1370
  const styles = useStyles(theme);
1012
- const [busy, setBusy] = useState6(false);
1013
- const [error, setError] = useState6(null);
1371
+ const [busy, setBusy] = useState7(false);
1372
+ const [error, setError] = useState7(null);
1014
1373
  if (client === null || config.isLoading || config.data !== null && !capabilities.passkeySignIn) return null;
1015
1374
  async function signIn() {
1016
1375
  if (busy || client === null) return;
@@ -1024,8 +1383,8 @@ function PasskeySignInButton({
1024
1383
  }
1025
1384
  onSignedIn?.();
1026
1385
  }
1027
- return /* @__PURE__ */ jsxs6(View6, { style: styles.container, testID: "authowl-passkey-signin", children: [
1028
- /* @__PURE__ */ jsx6(
1386
+ return /* @__PURE__ */ jsxs7(View7, { style: styles.container, testID: "authowl-passkey-signin", children: [
1387
+ /* @__PURE__ */ jsx7(
1029
1388
  SubmitButton,
1030
1389
  {
1031
1390
  label: t("passkey.signInButton"),
@@ -1036,7 +1395,7 @@ function PasskeySignInButton({
1036
1395
  testID: "authowl-passkey-signin-submit"
1037
1396
  }
1038
1397
  ),
1039
- /* @__PURE__ */ jsx6(FormError, { message: error, theme, testID: "authowl-passkey-signin-error" })
1398
+ /* @__PURE__ */ jsx7(FormError, { message: error, theme, testID: "authowl-passkey-signin-error" })
1040
1399
  ] });
1041
1400
  }
1042
1401
 
@@ -1053,9 +1412,9 @@ function createNativePasskeys(adapter) {
1053
1412
  }
1054
1413
 
1055
1414
  // src/components/SocialButtons.tsx
1056
- import { useState as useState7 } from "react";
1057
- import { View as View7 } from "react-native";
1058
- import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
1415
+ import { useState as useState8 } from "react";
1416
+ import { View as View8 } from "react-native";
1417
+ import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
1059
1418
  function SocialButtons({
1060
1419
  providers,
1061
1420
  onSignedIn,
@@ -1065,8 +1424,8 @@ function SocialButtons({
1065
1424
  const toMessage = useServerError();
1066
1425
  const client = useAuthOwlClient();
1067
1426
  const styles = useStyles(theme);
1068
- const [pending, setPending] = useState7(null);
1069
- const [error, setError] = useState7(null);
1427
+ const [pending, setPending] = useState8(null);
1428
+ const [error, setError] = useState8(null);
1070
1429
  async function signIn(provider) {
1071
1430
  if (pending !== null) return;
1072
1431
  setPending(provider.id);
@@ -1087,8 +1446,8 @@ function SocialButtons({
1087
1446
  }
1088
1447
  }
1089
1448
  if (providers.length === 0) return null;
1090
- return /* @__PURE__ */ jsxs7(View7, { style: styles.container, testID: "authowl-social", children: [
1091
- providers.map((provider) => /* @__PURE__ */ jsx7(
1449
+ return /* @__PURE__ */ jsxs8(View8, { style: styles.container, testID: "authowl-social", children: [
1450
+ providers.map((provider) => /* @__PURE__ */ jsx8(
1092
1451
  SubmitButton,
1093
1452
  {
1094
1453
  label: t("social.continueWith", { provider: provider.label }),
@@ -1103,7 +1462,7 @@ function SocialButtons({
1103
1462
  },
1104
1463
  provider.id
1105
1464
  )),
1106
- /* @__PURE__ */ jsx7(FormError, { message: error, theme, testID: "authowl-social-error" })
1465
+ /* @__PURE__ */ jsx8(FormError, { message: error, theme, testID: "authowl-social-error" })
1107
1466
  ] });
1108
1467
  }
1109
1468
 
@@ -1132,6 +1491,7 @@ export {
1132
1491
  OrganizationSwitcher,
1133
1492
  PasskeyEnrollment,
1134
1493
  PasskeySignInButton,
1494
+ PrivacyCenter,
1135
1495
  SignIn,
1136
1496
  SignUp,
1137
1497
  SocialButtons,
@@ -1150,6 +1510,7 @@ export {
1150
1510
  useAuthOwlClient,
1151
1511
  useAuthOwlLocale,
1152
1512
  useLocale,
1513
+ usePrivacy,
1153
1514
  usePublicConfig,
1154
1515
  useServerError,
1155
1516
  useSession,