@entropy-softworks/ui 2026.8.30 → 2026.8.32

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.
@@ -1 +1 @@
1
- {"version":3,"file":"password-generator.d.ts","sourceRoot":"","sources":["../../src/components/password-generator.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmD,MAAM,OAAO,CAAC;AAoBxE,MAAM,WAAW,sBAAsB;IACrC,yFAAyF;IACzF,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,+DAA+D;IAC/D,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,oDAkS5B,CAAC;AAEH,eAAe,iBAAiB,CAAC"}
1
+ {"version":3,"file":"password-generator.d.ts","sourceRoot":"","sources":["../../src/components/password-generator.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAQ1B,MAAM,WAAW,sBAAsB;IACrC,yFAAyF;IACzF,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,+DAA+D;IAC/D,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,oDAgM5B,CAAC;AAEH,eAAe,iBAAiB,CAAC"}
@@ -1,140 +1,19 @@
1
- import React, { useCallback, useEffect, useRef, useState } from "react";
2
- import { ActivityIndicator, Platform, Pressable, View } from "react-native";
1
+ import React from "react";
2
+ import { ActivityIndicator, Pressable, View } from "react-native";
3
3
  import { Ionicons } from "@expo/vector-icons";
4
4
  import { useColorScheme } from "nativewind";
5
- import * as Haptics from "expo-haptics";
6
5
  import Slider from "@react-native-community/slider";
7
- import Toast from "react-native-toast-message";
8
6
  import { Text } from "./text";
9
- import { copyToClipboard } from "../utils/clipboard";
10
- import { generatePassword } from "../utils/password";
11
- import { checkPasswordPwned } from "../services/hibp";
12
- // A randomly drawn password being in a breach corpus is astronomically
13
- // unlikely, but if it ever hits, regenerate until clean — capped so a
14
- // misbehaving API can't loop forever.
15
- const MAX_BREACH_REGEN = 5;
16
- const isWeb = Platform.OS === "web";
7
+ import { usePasswordGenerator } from "../hooks/usePasswordGenerator";
17
8
  /**
18
9
  * UI shell over `utils/password.generatePassword`. Owns length slider,
19
10
  * character-set toggles, copy-to-clipboard (via shared util with auto-
20
11
  * clear), and a coarse strength indicator.
21
12
  */
22
13
  export const PasswordGenerator = React.memo(function PasswordGenerator({ onSelect, hideCopy, }) {
23
- const [password, setPassword] = useState("");
24
- const [length, setLength] = useState(16);
25
- const [includeUppercase, setIncludeUppercase] = useState(true);
26
- const [includeLowercase, setIncludeLowercase] = useState(true);
27
- const [includeNumbers, setIncludeNumbers] = useState(true);
28
- const [includeSymbols, setIncludeSymbols] = useState(true);
29
- const [excludeSimilar, setExcludeSimilar] = useState(false);
30
- const [breach, setBreach] = useState("idle");
14
+ const { password, length, setLength, includeUppercase, setIncludeUppercase, includeLowercase, setIncludeLowercase, includeNumbers, setIncludeNumbers, includeSymbols, setIncludeSymbols, excludeSimilar, setExcludeSimilar, breach, strength: passwordStrength, handleGenerate, handleCopy, } = usePasswordGenerator();
31
15
  const { colorScheme } = useColorScheme();
32
16
  const isDark = colorScheme === "dark";
33
- // Latest options, read by the breach-regen path without re-subscribing the
34
- // check effect on every option change.
35
- const optsRef = useRef({
36
- length,
37
- includeUppercase,
38
- includeLowercase,
39
- includeNumbers,
40
- includeSymbols,
41
- excludeSimilar,
42
- });
43
- optsRef.current = {
44
- length,
45
- includeUppercase,
46
- includeLowercase,
47
- includeNumbers,
48
- includeSymbols,
49
- excludeSimilar,
50
- };
51
- // Ignore stale async HIBP results once a newer password supersedes them.
52
- const breachTokenRef = useRef(0);
53
- const breachAttemptRef = useRef(0);
54
- // 150 ms throttle on Generate. The CSPRNG draw itself is cheap
55
- // (synchronous bytes), but a held-down Enter key or a stuck
56
- // accessibility-keyboard repeat could fire many calls per second
57
- // and the rejection-sampling loop inside `generatePassword` does
58
- // a small amount of work per call. The throttle also gates the
59
- // haptic so the user isn't getting buzzed at keypress rate.
60
- const lastGenerateRef = useRef(0);
61
- const handleGenerate = useCallback(() => {
62
- const now = Date.now();
63
- if (now - lastGenerateRef.current < 150) {
64
- return;
65
- }
66
- lastGenerateRef.current = now;
67
- try {
68
- const next = generatePassword({
69
- length,
70
- includeUppercase,
71
- includeLowercase,
72
- includeNumbers,
73
- includeSymbols,
74
- excludeSimilar,
75
- });
76
- setPassword(next);
77
- if (!isWeb) {
78
- Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
79
- }
80
- }
81
- catch (e) {
82
- Toast.show({
83
- type: "error",
84
- text1: e instanceof Error ? e.message : "Please select at least one character type",
85
- visibilityTime: 3000,
86
- });
87
- }
88
- }, [length, includeUppercase, includeLowercase, includeNumbers, includeSymbols, excludeSimilar]);
89
- const handleCopy = useCallback(async () => {
90
- if (!password) {
91
- return;
92
- }
93
- const ok = await copyToClipboard(password);
94
- if (!ok) {
95
- return;
96
- }
97
- if (!isWeb) {
98
- Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success);
99
- }
100
- Toast.show({ type: "success", text1: "Copied to clipboard", visibilityTime: 2000 });
101
- }, [password]);
102
- // Generate on mount and when character-class options change.
103
- useEffect(() => {
104
- handleGenerate();
105
- }, [handleGenerate]);
106
- // Verify each generated password against HIBP (k-anonymity — only the SHA-1
107
- // prefix leaves the device). Debounced so spamming Generate only checks the
108
- // final password. On the (near-impossible) breach hit, silently regenerate;
109
- // a failed/offline check surfaces as "unverified" rather than blocking.
110
- useEffect(() => {
111
- if (!password) {
112
- setBreach("idle");
113
- return;
114
- }
115
- const token = ++breachTokenRef.current;
116
- setBreach("checking");
117
- const timer = setTimeout(async () => {
118
- const res = await checkPasswordPwned(password);
119
- if (token !== breachTokenRef.current) {
120
- return;
121
- } // superseded
122
- if (res.isPwned === true && breachAttemptRef.current < MAX_BREACH_REGEN) {
123
- breachAttemptRef.current += 1;
124
- try {
125
- setPassword(generatePassword(optsRef.current));
126
- }
127
- catch {
128
- setBreach("unverified");
129
- }
130
- return;
131
- }
132
- breachAttemptRef.current = 0;
133
- setBreach(res.isPwned === null ? "unverified" : "safe");
134
- }, 400);
135
- return () => clearTimeout(timer);
136
- }, [password]);
137
- const passwordStrength = useStrength(password);
138
17
  return (<>
139
18
  <View className="mb-4 rounded-lg border border-border dark:border-neutral-800 bg-card p-4 dark:bg-neutral-900/50">
140
19
  <View className="mb-3 flex-row items-center justify-between">
@@ -221,38 +100,6 @@ export const PasswordGenerator = React.memo(function PasswordGenerator({ onSelec
221
100
  </>);
222
101
  });
223
102
  export default PasswordGenerator;
224
- /** Coarse strength indicator — purely UX, server doesn't see it. */
225
- function useStrength(password) {
226
- if (!password) {
227
- return { label: "Generate a password", color: "gray" };
228
- }
229
- let strength = 0;
230
- if (password.length >= 12) {
231
- strength++;
232
- }
233
- if (password.length >= 16) {
234
- strength++;
235
- }
236
- if (/[a-z]/.test(password)) {
237
- strength++;
238
- }
239
- if (/[A-Z]/.test(password)) {
240
- strength++;
241
- }
242
- if (/[0-9]/.test(password)) {
243
- strength++;
244
- }
245
- if (/[^A-Za-z0-9]/.test(password)) {
246
- strength++;
247
- }
248
- if (strength <= 2) {
249
- return { label: "Weak", color: "#ef4444" };
250
- }
251
- if (strength <= 4) {
252
- return { label: "Medium", color: "#f59e0b" };
253
- }
254
- return { label: "Strong", color: "#22c55e" };
255
- }
256
103
  function CharsetCheckbox({ label, checked, onToggle, isDark }) {
257
104
  return (<Pressable onPress={onToggle} className="flex-row items-center gap-3" accessibilityRole="checkbox" accessibilityLabel={label} accessibilityState={{ checked }}>
258
105
  <View style={{
@@ -1 +1 @@
1
- {"version":3,"file":"password-generator.jsx","sourceRoot":"","sources":["../../src/components/password-generator.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"password-generator.jsx","sourceRoot":"","sources":["../../src/components/password-generator.tsx"],"names":[],"mappings":"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"}
@@ -3,4 +3,5 @@ export { useShakeAnimation } from "./useShakeAnimation";
3
3
  export { useDebouncedValue } from "./useDebouncedValue";
4
4
  export { useAutoLock, type UseAutoLockOptions } from "./useAutoLock";
5
5
  export { useUploadBlobUrl, type UploadBlobUrlResult } from "./useUploadBlobUrl";
6
+ export { usePasswordGenerator, getPasswordStrength, type UsePasswordGeneratorResult, type PasswordStrength, type BreachState, } from "./usePasswordGenerator";
6
7
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,OAAO,EACP,OAAO,EACP,cAAc,EACd,UAAU,EACV,KAAK,iBAAiB,EACtB,KAAK,eAAe,EACpB,KAAK,kBAAkB,EACvB,KAAK,uBAAuB,GAC7B,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAExD,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAExD,OAAO,EAAE,WAAW,EAAE,KAAK,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAErE,OAAO,EAAE,gBAAgB,EAAE,KAAK,mBAAmB,EAAE,MAAM,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,OAAO,EACP,OAAO,EACP,cAAc,EACd,UAAU,EACV,KAAK,iBAAiB,EACtB,KAAK,eAAe,EACpB,KAAK,kBAAkB,EACvB,KAAK,uBAAuB,GAC7B,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAExD,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAExD,OAAO,EAAE,WAAW,EAAE,KAAK,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAErE,OAAO,EAAE,gBAAgB,EAAE,KAAK,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAEhF,OAAO,EACL,oBAAoB,EACpB,mBAAmB,EACnB,KAAK,0BAA0B,EAC/B,KAAK,gBAAgB,EACrB,KAAK,WAAW,GACjB,MAAM,wBAAwB,CAAC"}
@@ -3,4 +3,5 @@ export { useShakeAnimation } from "./useShakeAnimation";
3
3
  export { useDebouncedValue } from "./useDebouncedValue";
4
4
  export { useAutoLock } from "./useAutoLock";
5
5
  export { useUploadBlobUrl } from "./useUploadBlobUrl";
6
+ export { usePasswordGenerator, getPasswordStrength, } from "./usePasswordGenerator";
6
7
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,OAAO,EACP,OAAO,EACP,cAAc,EACd,UAAU,GAKX,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAExD,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAExD,OAAO,EAAE,WAAW,EAA2B,MAAM,eAAe,CAAC;AAErE,OAAO,EAAE,gBAAgB,EAA4B,MAAM,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,OAAO,EACP,OAAO,EACP,cAAc,EACd,UAAU,GAKX,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAExD,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAExD,OAAO,EAAE,WAAW,EAA2B,MAAM,eAAe,CAAC;AAErE,OAAO,EAAE,gBAAgB,EAA4B,MAAM,oBAAoB,CAAC;AAEhF,OAAO,EACL,oBAAoB,EACpB,mBAAmB,GAIpB,MAAM,wBAAwB,CAAC"}
@@ -0,0 +1,29 @@
1
+ import { type Dispatch, type SetStateAction } from "react";
2
+ export type BreachState = "idle" | "checking" | "safe" | "unverified";
3
+ export interface PasswordStrength {
4
+ label: string;
5
+ color: string;
6
+ }
7
+ export interface UsePasswordGeneratorResult {
8
+ password: string;
9
+ length: number;
10
+ setLength: Dispatch<SetStateAction<number>>;
11
+ includeUppercase: boolean;
12
+ setIncludeUppercase: Dispatch<SetStateAction<boolean>>;
13
+ includeLowercase: boolean;
14
+ setIncludeLowercase: Dispatch<SetStateAction<boolean>>;
15
+ includeNumbers: boolean;
16
+ setIncludeNumbers: Dispatch<SetStateAction<boolean>>;
17
+ includeSymbols: boolean;
18
+ setIncludeSymbols: Dispatch<SetStateAction<boolean>>;
19
+ excludeSimilar: boolean;
20
+ setExcludeSimilar: Dispatch<SetStateAction<boolean>>;
21
+ breach: BreachState;
22
+ strength: PasswordStrength;
23
+ handleGenerate: () => void;
24
+ handleCopy: () => Promise<void>;
25
+ }
26
+ export declare function usePasswordGenerator(): UsePasswordGeneratorResult;
27
+ /** Coarse strength indicator — purely UX, server doesn't see it. */
28
+ export declare function getPasswordStrength(password: string): PasswordStrength;
29
+ //# sourceMappingURL=usePasswordGenerator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usePasswordGenerator.d.ts","sourceRoot":"","sources":["../../src/hooks/usePasswordGenerator.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,KAAK,QAAQ,EAAE,KAAK,cAAc,EAA4C,MAAM,OAAO,CAAC;AAYrG,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,UAAU,GAAG,MAAM,GAAG,YAAY,CAAC;AAItE,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,0BAA0B;IACzC,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,QAAQ,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC;IAC5C,gBAAgB,EAAE,OAAO,CAAC;IAC1B,mBAAmB,EAAE,QAAQ,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;IACvD,gBAAgB,EAAE,OAAO,CAAC;IAC1B,mBAAmB,EAAE,QAAQ,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;IACvD,cAAc,EAAE,OAAO,CAAC;IACxB,iBAAiB,EAAE,QAAQ,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;IACrD,cAAc,EAAE,OAAO,CAAC;IACxB,iBAAiB,EAAE,QAAQ,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;IACrD,cAAc,EAAE,OAAO,CAAC;IACxB,iBAAiB,EAAE,QAAQ,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;IACrD,MAAM,EAAE,WAAW,CAAC;IACpB,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,cAAc,EAAE,MAAM,IAAI,CAAC;IAC3B,UAAU,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACjC;AAED,wBAAgB,oBAAoB,IAAI,0BAA0B,CA0IjE;AAED,oEAAoE;AACpE,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,gBAAgB,CA8BtE"}
@@ -0,0 +1,187 @@
1
+ // Stateful core of PasswordGenerator (components/password-generator.tsx),
2
+ // extracted so a platform-specific presentational layer can consume the
3
+ // same state/logic without re-implementing generate/copy/breach-check.
4
+ // Native's own UI (the vault mobile app's own screen) needed a genuinely
5
+ // different, more native-feeling layout rather than the web card/checkbox
6
+ // layout — see components/password-generator.tsx's own doc comment for
7
+ // why that stayed a separate render rather than one component branching
8
+ // on Platform.OS internally. User-requested, 2026-08-17.
9
+ import { useCallback, useEffect, useRef, useState } from "react";
10
+ import { Platform } from "react-native";
11
+ import * as Haptics from "expo-haptics";
12
+ import Toast from "react-native-toast-message";
13
+ import { copyToClipboard } from "../utils/clipboard";
14
+ import { generatePassword } from "../utils/password";
15
+ import { checkPasswordPwned } from "../services/hibp";
16
+ // A randomly drawn password being in a breach corpus is astronomically
17
+ // unlikely, but if it ever hits, regenerate until clean — capped so a
18
+ // misbehaving API can't loop forever.
19
+ const MAX_BREACH_REGEN = 5;
20
+ const isWeb = Platform.OS === "web";
21
+ export function usePasswordGenerator() {
22
+ const [password, setPassword] = useState("");
23
+ const [length, setLength] = useState(16);
24
+ const [includeUppercase, setIncludeUppercase] = useState(true);
25
+ const [includeLowercase, setIncludeLowercase] = useState(true);
26
+ const [includeNumbers, setIncludeNumbers] = useState(true);
27
+ const [includeSymbols, setIncludeSymbols] = useState(true);
28
+ const [excludeSimilar, setExcludeSimilar] = useState(false);
29
+ const [breach, setBreach] = useState("idle");
30
+ // Latest options, read by the breach-regen path without re-subscribing
31
+ // the check effect on every option change.
32
+ const optsRef = useRef({
33
+ length,
34
+ includeUppercase,
35
+ includeLowercase,
36
+ includeNumbers,
37
+ includeSymbols,
38
+ excludeSimilar,
39
+ });
40
+ optsRef.current = {
41
+ length,
42
+ includeUppercase,
43
+ includeLowercase,
44
+ includeNumbers,
45
+ includeSymbols,
46
+ excludeSimilar,
47
+ };
48
+ // Ignore stale async HIBP results once a newer password supersedes them.
49
+ const breachTokenRef = useRef(0);
50
+ const breachAttemptRef = useRef(0);
51
+ // 150 ms throttle on Generate. The CSPRNG draw itself is cheap
52
+ // (synchronous bytes), but a held-down Enter key or a stuck
53
+ // accessibility-keyboard repeat could fire many calls per second
54
+ // and the rejection-sampling loop inside `generatePassword` does
55
+ // a small amount of work per call. The throttle also gates the
56
+ // haptic so the user isn't getting buzzed at keypress rate.
57
+ const lastGenerateRef = useRef(0);
58
+ const handleGenerate = useCallback(() => {
59
+ const now = Date.now();
60
+ if (now - lastGenerateRef.current < 150) {
61
+ return;
62
+ }
63
+ lastGenerateRef.current = now;
64
+ try {
65
+ const next = generatePassword({
66
+ length,
67
+ includeUppercase,
68
+ includeLowercase,
69
+ includeNumbers,
70
+ includeSymbols,
71
+ excludeSimilar,
72
+ });
73
+ setPassword(next);
74
+ if (!isWeb) {
75
+ Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
76
+ }
77
+ }
78
+ catch (e) {
79
+ Toast.show({
80
+ type: "error",
81
+ text1: e instanceof Error ? e.message : "Please select at least one character type",
82
+ visibilityTime: 3000,
83
+ });
84
+ }
85
+ }, [length, includeUppercase, includeLowercase, includeNumbers, includeSymbols, excludeSimilar]);
86
+ const handleCopy = useCallback(async () => {
87
+ if (!password) {
88
+ return;
89
+ }
90
+ const ok = await copyToClipboard(password);
91
+ if (!ok) {
92
+ return;
93
+ }
94
+ if (!isWeb) {
95
+ Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success);
96
+ }
97
+ Toast.show({ type: "success", text1: "Copied to clipboard", visibilityTime: 2000 });
98
+ }, [password]);
99
+ // Generate on mount and when character-class options change.
100
+ useEffect(() => {
101
+ handleGenerate();
102
+ }, [handleGenerate]);
103
+ // Verify each generated password against HIBP (k-anonymity — only the SHA-1
104
+ // prefix leaves the device). Debounced so spamming Generate only checks the
105
+ // final password. On the (near-impossible) breach hit, silently regenerate;
106
+ // a failed/offline check surfaces as "unverified" rather than blocking.
107
+ useEffect(() => {
108
+ if (!password) {
109
+ setBreach("idle");
110
+ return;
111
+ }
112
+ const token = ++breachTokenRef.current;
113
+ setBreach("checking");
114
+ const timer = setTimeout(async () => {
115
+ const res = await checkPasswordPwned(password);
116
+ if (token !== breachTokenRef.current) {
117
+ return;
118
+ } // superseded
119
+ if (res.isPwned === true && breachAttemptRef.current < MAX_BREACH_REGEN) {
120
+ breachAttemptRef.current += 1;
121
+ try {
122
+ setPassword(generatePassword(optsRef.current));
123
+ }
124
+ catch {
125
+ setBreach("unverified");
126
+ }
127
+ return;
128
+ }
129
+ breachAttemptRef.current = 0;
130
+ setBreach(res.isPwned === null ? "unverified" : "safe");
131
+ }, 400);
132
+ return () => clearTimeout(timer);
133
+ }, [password]);
134
+ const strength = getPasswordStrength(password);
135
+ return {
136
+ password,
137
+ length,
138
+ setLength,
139
+ includeUppercase,
140
+ setIncludeUppercase,
141
+ includeLowercase,
142
+ setIncludeLowercase,
143
+ includeNumbers,
144
+ setIncludeNumbers,
145
+ includeSymbols,
146
+ setIncludeSymbols,
147
+ excludeSimilar,
148
+ setExcludeSimilar,
149
+ breach,
150
+ strength,
151
+ handleGenerate,
152
+ handleCopy,
153
+ };
154
+ }
155
+ /** Coarse strength indicator — purely UX, server doesn't see it. */
156
+ export function getPasswordStrength(password) {
157
+ if (!password) {
158
+ return { label: "Generate a password", color: "gray" };
159
+ }
160
+ let strength = 0;
161
+ if (password.length >= 12) {
162
+ strength++;
163
+ }
164
+ if (password.length >= 16) {
165
+ strength++;
166
+ }
167
+ if (/[a-z]/.test(password)) {
168
+ strength++;
169
+ }
170
+ if (/[A-Z]/.test(password)) {
171
+ strength++;
172
+ }
173
+ if (/[0-9]/.test(password)) {
174
+ strength++;
175
+ }
176
+ if (/[^A-Za-z0-9]/.test(password)) {
177
+ strength++;
178
+ }
179
+ if (strength <= 2) {
180
+ return { label: "Weak", color: "#ef4444" };
181
+ }
182
+ if (strength <= 4) {
183
+ return { label: "Medium", color: "#f59e0b" };
184
+ }
185
+ return { label: "Strong", color: "#22c55e" };
186
+ }
187
+ //# sourceMappingURL=usePasswordGenerator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usePasswordGenerator.js","sourceRoot":"","sources":["../../src/hooks/usePasswordGenerator.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"api-fetch.d.ts","sourceRoot":"","sources":["../../src/utils/api-fetch.ts"],"names":[],"mappings":"AAuBA;;;;;;;;GAQG;AACH,wBAAsB,QAAQ,CAC5B,IAAI,EAAE,MAAM,EACZ,IAAI,GAAE,WAAgB,EACtB,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,QAAQ,CAAC,CAwBnB"}
1
+ {"version":3,"file":"api-fetch.d.ts","sourceRoot":"","sources":["../../src/utils/api-fetch.ts"],"names":[],"mappings":"AAuBA;;;;;;;;GAQG;AACH,wBAAsB,QAAQ,CAC5B,IAAI,EAAE,MAAM,EACZ,IAAI,GAAE,WAAgB,EACtB,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,QAAQ,CAAC,CA0CnB"}
@@ -47,6 +47,24 @@ export async function apiFetch(path, init = {}, timeoutMs) {
47
47
  if (token) {
48
48
  headers.set("Authorization", `Bearer ${token}`);
49
49
  }
50
- return fetchWithTimeout(url, { ...init, headers }, timeoutMs);
50
+ // Explicit `credentials: "omit"` native auth is Bearer-only, and the
51
+ // server's CSRF middleware branches purely on whether a session cookie
52
+ // is PRESENT on the request (see server/src/auth/middleware.rs's
53
+ // csrf_protect: any request carrying the session cookie is treated as
54
+ // cookie-auth and required to also carry a matching X-CSRF-Token +
55
+ // Origin, neither of which this native branch ever sends). React
56
+ // Native's Android fetch persists Set-Cookie responses in a shared
57
+ // CookieManager and reattaches them to matching-origin requests
58
+ // regardless of the web `credentials` semantics being followed
59
+ // elsewhere — so if the server ever sets a session cookie as a side
60
+ // effect for a native client (e.g. during passkey/WebAuthn login,
61
+ // which can involve a system browser or Credential Manager sheet),
62
+ // every subsequent native mutation silently starts requiring CSRF
63
+ // headers it can never provide, failing with "Missing CSRF header".
64
+ // Reported as folder-creation failing this way, 2026-08-17 — but the
65
+ // same stray cookie would affect ANY mutating request once present,
66
+ // not just this one. Omitting credentials here is the actual fix:
67
+ // native should never send or rely on cookies at all.
68
+ return fetchWithTimeout(url, { ...init, headers, credentials: "omit" }, timeoutMs);
51
69
  }
52
70
  //# sourceMappingURL=api-fetch.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"api-fetch.js","sourceRoot":"","sources":["../../src/utils/api-fetch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AACxC,OAAO,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAErE,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC;AAEzD,MAAM,gBAAgB,GAAG,YAAY,CAAC;AACtC,MAAM,gBAAgB,GAAG,cAAc,CAAC;AAExC,SAAS,UAAU,CAAC,IAAY;IAC9B,IAAI,OAAO,QAAQ,KAAK,WAAW,EAAE,CAAC;QACpC,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC;IAC1B,KAAK,MAAM,IAAI,IAAI,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;QAC9C,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAC5B,IAAI,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;YAC/B,OAAO,kBAAkB,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;QAC1D,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,IAAY,EACZ,OAAoB,EAAE,EACtB,SAAkB;IAElB,MAAM,GAAG,GAAG,GAAG,UAAU,EAAE,GAAG,IAAI,EAAE,CAAC;IACrC,MAAM,MAAM,GAAG,CAAC,IAAI,CAAC,MAAM,IAAI,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC;IACpD,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAE1C,IAAI,QAAQ,CAAC,EAAE,KAAK,KAAK,EAAE,CAAC;QAC1B,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;YAC9B,MAAM,IAAI,GAAG,UAAU,CAAC,gBAAgB,CAAC,CAAC;YAC1C,IAAI,IAAI,EAAE,CAAC;gBACT,OAAO,CAAC,GAAG,CAAC,gBAAgB,EAAE,IAAI,CAAC,CAAC;YACtC,CAAC;QACH,CAAC;QACD,MAAM,QAAQ,GAAG,MAAM,eAAe,EAAE,CAAC;QACzC,IAAI,QAAQ,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,EAAE,CAAC;YAC9C,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,UAAU,QAAQ,EAAE,CAAC,CAAC;QACrD,CAAC;QACD,OAAO,gBAAgB,CAAC,GAAG,EAAE,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,EAAE,SAAS,CAAC,CAAC;IACxF,CAAC;IAED,MAAM,KAAK,GAAG,MAAM,eAAe,EAAE,CAAC;IACtC,IAAI,KAAK,EAAE,CAAC;QACV,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,UAAU,KAAK,EAAE,CAAC,CAAC;IAClD,CAAC;IACD,OAAO,gBAAgB,CAAC,GAAG,EAAE,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,EAAE,SAAS,CAAC,CAAC;AAChE,CAAC"}
1
+ {"version":3,"file":"api-fetch.js","sourceRoot":"","sources":["../../src/utils/api-fetch.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@entropy-softworks/ui",
3
- "version": "2026.8.30",
3
+ "version": "2026.8.32",
4
4
  "private": false,
5
5
  "description": "Shared React Native UI primitives, auth screens, hooks, and services for Entropy Softworks Expo apps",
6
6
  "license": "MIT",
@@ -1,22 +1,10 @@
1
- import React, { useCallback, useEffect, useRef, useState } from "react";
2
- import { ActivityIndicator, Platform, Pressable, View } from "react-native";
1
+ import React from "react";
2
+ import { ActivityIndicator, Pressable, View } from "react-native";
3
3
  import { Ionicons } from "@expo/vector-icons";
4
4
  import { useColorScheme } from "nativewind";
5
- import * as Haptics from "expo-haptics";
6
5
  import Slider from "@react-native-community/slider";
7
- import Toast from "react-native-toast-message";
8
6
  import { Text } from "./text";
9
- import { copyToClipboard } from "../utils/clipboard";
10
- import { generatePassword } from "../utils/password";
11
- import { checkPasswordPwned } from "../services/hibp";
12
-
13
- // A randomly drawn password being in a breach corpus is astronomically
14
- // unlikely, but if it ever hits, regenerate until clean — capped so a
15
- // misbehaving API can't loop forever.
16
- const MAX_BREACH_REGEN = 5;
17
- type BreachState = "idle" | "checking" | "safe" | "unverified";
18
-
19
- const isWeb = Platform.OS === "web";
7
+ import { usePasswordGenerator } from "../hooks/usePasswordGenerator";
20
8
 
21
9
  export interface PasswordGeneratorProps {
22
10
  /** Called when the user taps "Use Password" (only shown when `onSelect` is provided). */
@@ -34,126 +22,28 @@ export const PasswordGenerator = React.memo(function PasswordGenerator({
34
22
  onSelect,
35
23
  hideCopy,
36
24
  }: PasswordGeneratorProps) {
37
- const [password, setPassword] = useState("");
38
- const [length, setLength] = useState(16);
39
- const [includeUppercase, setIncludeUppercase] = useState(true);
40
- const [includeLowercase, setIncludeLowercase] = useState(true);
41
- const [includeNumbers, setIncludeNumbers] = useState(true);
42
- const [includeSymbols, setIncludeSymbols] = useState(true);
43
- const [excludeSimilar, setExcludeSimilar] = useState(false);
44
- const [breach, setBreach] = useState<BreachState>("idle");
45
-
46
- const { colorScheme } = useColorScheme();
47
- const isDark = colorScheme === "dark";
48
-
49
- // Latest options, read by the breach-regen path without re-subscribing the
50
- // check effect on every option change.
51
- const optsRef = useRef({
52
- length,
53
- includeUppercase,
54
- includeLowercase,
55
- includeNumbers,
56
- includeSymbols,
57
- excludeSimilar,
58
- });
59
- optsRef.current = {
25
+ const {
26
+ password,
60
27
  length,
28
+ setLength,
61
29
  includeUppercase,
30
+ setIncludeUppercase,
62
31
  includeLowercase,
32
+ setIncludeLowercase,
63
33
  includeNumbers,
34
+ setIncludeNumbers,
64
35
  includeSymbols,
36
+ setIncludeSymbols,
65
37
  excludeSimilar,
66
- };
67
- // Ignore stale async HIBP results once a newer password supersedes them.
68
- const breachTokenRef = useRef(0);
69
- const breachAttemptRef = useRef(0);
70
-
71
- // 150 ms throttle on Generate. The CSPRNG draw itself is cheap
72
- // (synchronous bytes), but a held-down Enter key or a stuck
73
- // accessibility-keyboard repeat could fire many calls per second
74
- // and the rejection-sampling loop inside `generatePassword` does
75
- // a small amount of work per call. The throttle also gates the
76
- // haptic so the user isn't getting buzzed at keypress rate.
77
- const lastGenerateRef = useRef(0);
78
- const handleGenerate = useCallback(() => {
79
- const now = Date.now();
80
- if (now - lastGenerateRef.current < 150) {
81
- return;
82
- }
83
- lastGenerateRef.current = now;
84
- try {
85
- const next = generatePassword({
86
- length,
87
- includeUppercase,
88
- includeLowercase,
89
- includeNumbers,
90
- includeSymbols,
91
- excludeSimilar,
92
- });
93
- setPassword(next);
94
- if (!isWeb) {
95
- Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
96
- }
97
- } catch (e) {
98
- Toast.show({
99
- type: "error",
100
- text1: e instanceof Error ? e.message : "Please select at least one character type",
101
- visibilityTime: 3000,
102
- });
103
- }
104
- }, [length, includeUppercase, includeLowercase, includeNumbers, includeSymbols, excludeSimilar]);
105
-
106
- const handleCopy = useCallback(async () => {
107
- if (!password) {
108
- return;
109
- }
110
- const ok = await copyToClipboard(password);
111
- if (!ok) {
112
- return;
113
- }
114
- if (!isWeb) {
115
- Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success);
116
- }
117
- Toast.show({ type: "success", text1: "Copied to clipboard", visibilityTime: 2000 });
118
- }, [password]);
119
-
120
- // Generate on mount and when character-class options change.
121
- useEffect(() => {
122
- handleGenerate();
123
- }, [handleGenerate]);
38
+ setExcludeSimilar,
39
+ breach,
40
+ strength: passwordStrength,
41
+ handleGenerate,
42
+ handleCopy,
43
+ } = usePasswordGenerator();
124
44
 
125
- // Verify each generated password against HIBP (k-anonymity — only the SHA-1
126
- // prefix leaves the device). Debounced so spamming Generate only checks the
127
- // final password. On the (near-impossible) breach hit, silently regenerate;
128
- // a failed/offline check surfaces as "unverified" rather than blocking.
129
- useEffect(() => {
130
- if (!password) {
131
- setBreach("idle");
132
- return;
133
- }
134
- const token = ++breachTokenRef.current;
135
- setBreach("checking");
136
- const timer = setTimeout(async () => {
137
- const res = await checkPasswordPwned(password);
138
- if (token !== breachTokenRef.current) {
139
- return;
140
- } // superseded
141
- if (res.isPwned === true && breachAttemptRef.current < MAX_BREACH_REGEN) {
142
- breachAttemptRef.current += 1;
143
- try {
144
- setPassword(generatePassword(optsRef.current));
145
- } catch {
146
- setBreach("unverified");
147
- }
148
- return;
149
- }
150
- breachAttemptRef.current = 0;
151
- setBreach(res.isPwned === null ? "unverified" : "safe");
152
- }, 400);
153
- return () => clearTimeout(timer);
154
- }, [password]);
155
-
156
- const passwordStrength = useStrength(password);
45
+ const { colorScheme } = useColorScheme();
46
+ const isDark = colorScheme === "dark";
157
47
 
158
48
  return (
159
49
  <>
@@ -324,39 +214,6 @@ export const PasswordGenerator = React.memo(function PasswordGenerator({
324
214
 
325
215
  export default PasswordGenerator;
326
216
 
327
- /** Coarse strength indicator — purely UX, server doesn't see it. */
328
- function useStrength(password: string): { label: string; color: string } {
329
- if (!password) {
330
- return { label: "Generate a password", color: "gray" };
331
- }
332
- let strength = 0;
333
- if (password.length >= 12) {
334
- strength++;
335
- }
336
- if (password.length >= 16) {
337
- strength++;
338
- }
339
- if (/[a-z]/.test(password)) {
340
- strength++;
341
- }
342
- if (/[A-Z]/.test(password)) {
343
- strength++;
344
- }
345
- if (/[0-9]/.test(password)) {
346
- strength++;
347
- }
348
- if (/[^A-Za-z0-9]/.test(password)) {
349
- strength++;
350
- }
351
- if (strength <= 2) {
352
- return { label: "Weak", color: "#ef4444" };
353
- }
354
- if (strength <= 4) {
355
- return { label: "Medium", color: "#f59e0b" };
356
- }
357
- return { label: "Strong", color: "#22c55e" };
358
- }
359
-
360
217
  interface CharsetCheckboxProps {
361
218
  label: string;
362
219
  checked: boolean;
@@ -17,3 +17,11 @@ export { useDebouncedValue } from "./useDebouncedValue";
17
17
  export { useAutoLock, type UseAutoLockOptions } from "./useAutoLock";
18
18
 
19
19
  export { useUploadBlobUrl, type UploadBlobUrlResult } from "./useUploadBlobUrl";
20
+
21
+ export {
22
+ usePasswordGenerator,
23
+ getPasswordStrength,
24
+ type UsePasswordGeneratorResult,
25
+ type PasswordStrength,
26
+ type BreachState,
27
+ } from "./usePasswordGenerator";
@@ -0,0 +1,221 @@
1
+ // Stateful core of PasswordGenerator (components/password-generator.tsx),
2
+ // extracted so a platform-specific presentational layer can consume the
3
+ // same state/logic without re-implementing generate/copy/breach-check.
4
+ // Native's own UI (the vault mobile app's own screen) needed a genuinely
5
+ // different, more native-feeling layout rather than the web card/checkbox
6
+ // layout — see components/password-generator.tsx's own doc comment for
7
+ // why that stayed a separate render rather than one component branching
8
+ // on Platform.OS internally. User-requested, 2026-08-17.
9
+ import { type Dispatch, type SetStateAction, useCallback, useEffect, useRef, useState } from "react";
10
+ import { Platform } from "react-native";
11
+ import * as Haptics from "expo-haptics";
12
+ import Toast from "react-native-toast-message";
13
+ import { copyToClipboard } from "../utils/clipboard";
14
+ import { generatePassword } from "../utils/password";
15
+ import { checkPasswordPwned } from "../services/hibp";
16
+
17
+ // A randomly drawn password being in a breach corpus is astronomically
18
+ // unlikely, but if it ever hits, regenerate until clean — capped so a
19
+ // misbehaving API can't loop forever.
20
+ const MAX_BREACH_REGEN = 5;
21
+ export type BreachState = "idle" | "checking" | "safe" | "unverified";
22
+
23
+ const isWeb = Platform.OS === "web";
24
+
25
+ export interface PasswordStrength {
26
+ label: string;
27
+ color: string;
28
+ }
29
+
30
+ export interface UsePasswordGeneratorResult {
31
+ password: string;
32
+ length: number;
33
+ setLength: Dispatch<SetStateAction<number>>;
34
+ includeUppercase: boolean;
35
+ setIncludeUppercase: Dispatch<SetStateAction<boolean>>;
36
+ includeLowercase: boolean;
37
+ setIncludeLowercase: Dispatch<SetStateAction<boolean>>;
38
+ includeNumbers: boolean;
39
+ setIncludeNumbers: Dispatch<SetStateAction<boolean>>;
40
+ includeSymbols: boolean;
41
+ setIncludeSymbols: Dispatch<SetStateAction<boolean>>;
42
+ excludeSimilar: boolean;
43
+ setExcludeSimilar: Dispatch<SetStateAction<boolean>>;
44
+ breach: BreachState;
45
+ strength: PasswordStrength;
46
+ handleGenerate: () => void;
47
+ handleCopy: () => Promise<void>;
48
+ }
49
+
50
+ export function usePasswordGenerator(): UsePasswordGeneratorResult {
51
+ const [password, setPassword] = useState("");
52
+ const [length, setLength] = useState(16);
53
+ const [includeUppercase, setIncludeUppercase] = useState(true);
54
+ const [includeLowercase, setIncludeLowercase] = useState(true);
55
+ const [includeNumbers, setIncludeNumbers] = useState(true);
56
+ const [includeSymbols, setIncludeSymbols] = useState(true);
57
+ const [excludeSimilar, setExcludeSimilar] = useState(false);
58
+ const [breach, setBreach] = useState<BreachState>("idle");
59
+
60
+ // Latest options, read by the breach-regen path without re-subscribing
61
+ // the check effect on every option change.
62
+ const optsRef = useRef({
63
+ length,
64
+ includeUppercase,
65
+ includeLowercase,
66
+ includeNumbers,
67
+ includeSymbols,
68
+ excludeSimilar,
69
+ });
70
+ optsRef.current = {
71
+ length,
72
+ includeUppercase,
73
+ includeLowercase,
74
+ includeNumbers,
75
+ includeSymbols,
76
+ excludeSimilar,
77
+ };
78
+ // Ignore stale async HIBP results once a newer password supersedes them.
79
+ const breachTokenRef = useRef(0);
80
+ const breachAttemptRef = useRef(0);
81
+
82
+ // 150 ms throttle on Generate. The CSPRNG draw itself is cheap
83
+ // (synchronous bytes), but a held-down Enter key or a stuck
84
+ // accessibility-keyboard repeat could fire many calls per second
85
+ // and the rejection-sampling loop inside `generatePassword` does
86
+ // a small amount of work per call. The throttle also gates the
87
+ // haptic so the user isn't getting buzzed at keypress rate.
88
+ const lastGenerateRef = useRef(0);
89
+ const handleGenerate = useCallback(() => {
90
+ const now = Date.now();
91
+ if (now - lastGenerateRef.current < 150) {
92
+ return;
93
+ }
94
+ lastGenerateRef.current = now;
95
+ try {
96
+ const next = generatePassword({
97
+ length,
98
+ includeUppercase,
99
+ includeLowercase,
100
+ includeNumbers,
101
+ includeSymbols,
102
+ excludeSimilar,
103
+ });
104
+ setPassword(next);
105
+ if (!isWeb) {
106
+ Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
107
+ }
108
+ } catch (e) {
109
+ Toast.show({
110
+ type: "error",
111
+ text1: e instanceof Error ? e.message : "Please select at least one character type",
112
+ visibilityTime: 3000,
113
+ });
114
+ }
115
+ }, [length, includeUppercase, includeLowercase, includeNumbers, includeSymbols, excludeSimilar]);
116
+
117
+ const handleCopy = useCallback(async () => {
118
+ if (!password) {
119
+ return;
120
+ }
121
+ const ok = await copyToClipboard(password);
122
+ if (!ok) {
123
+ return;
124
+ }
125
+ if (!isWeb) {
126
+ Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success);
127
+ }
128
+ Toast.show({ type: "success", text1: "Copied to clipboard", visibilityTime: 2000 });
129
+ }, [password]);
130
+
131
+ // Generate on mount and when character-class options change.
132
+ useEffect(() => {
133
+ handleGenerate();
134
+ }, [handleGenerate]);
135
+
136
+ // Verify each generated password against HIBP (k-anonymity — only the SHA-1
137
+ // prefix leaves the device). Debounced so spamming Generate only checks the
138
+ // final password. On the (near-impossible) breach hit, silently regenerate;
139
+ // a failed/offline check surfaces as "unverified" rather than blocking.
140
+ useEffect(() => {
141
+ if (!password) {
142
+ setBreach("idle");
143
+ return;
144
+ }
145
+ const token = ++breachTokenRef.current;
146
+ setBreach("checking");
147
+ const timer = setTimeout(async () => {
148
+ const res = await checkPasswordPwned(password);
149
+ if (token !== breachTokenRef.current) {
150
+ return;
151
+ } // superseded
152
+ if (res.isPwned === true && breachAttemptRef.current < MAX_BREACH_REGEN) {
153
+ breachAttemptRef.current += 1;
154
+ try {
155
+ setPassword(generatePassword(optsRef.current));
156
+ } catch {
157
+ setBreach("unverified");
158
+ }
159
+ return;
160
+ }
161
+ breachAttemptRef.current = 0;
162
+ setBreach(res.isPwned === null ? "unverified" : "safe");
163
+ }, 400);
164
+ return () => clearTimeout(timer);
165
+ }, [password]);
166
+
167
+ const strength = getPasswordStrength(password);
168
+
169
+ return {
170
+ password,
171
+ length,
172
+ setLength,
173
+ includeUppercase,
174
+ setIncludeUppercase,
175
+ includeLowercase,
176
+ setIncludeLowercase,
177
+ includeNumbers,
178
+ setIncludeNumbers,
179
+ includeSymbols,
180
+ setIncludeSymbols,
181
+ excludeSimilar,
182
+ setExcludeSimilar,
183
+ breach,
184
+ strength,
185
+ handleGenerate,
186
+ handleCopy,
187
+ };
188
+ }
189
+
190
+ /** Coarse strength indicator — purely UX, server doesn't see it. */
191
+ export function getPasswordStrength(password: string): PasswordStrength {
192
+ if (!password) {
193
+ return { label: "Generate a password", color: "gray" };
194
+ }
195
+ let strength = 0;
196
+ if (password.length >= 12) {
197
+ strength++;
198
+ }
199
+ if (password.length >= 16) {
200
+ strength++;
201
+ }
202
+ if (/[a-z]/.test(password)) {
203
+ strength++;
204
+ }
205
+ if (/[A-Z]/.test(password)) {
206
+ strength++;
207
+ }
208
+ if (/[0-9]/.test(password)) {
209
+ strength++;
210
+ }
211
+ if (/[^A-Za-z0-9]/.test(password)) {
212
+ strength++;
213
+ }
214
+ if (strength <= 2) {
215
+ return { label: "Weak", color: "#ef4444" };
216
+ }
217
+ if (strength <= 4) {
218
+ return { label: "Medium", color: "#f59e0b" };
219
+ }
220
+ return { label: "Strong", color: "#22c55e" };
221
+ }
@@ -57,5 +57,23 @@ export async function apiFetch(
57
57
  if (token) {
58
58
  headers.set("Authorization", `Bearer ${token}`);
59
59
  }
60
- return fetchWithTimeout(url, { ...init, headers }, timeoutMs);
60
+ // Explicit `credentials: "omit"` native auth is Bearer-only, and the
61
+ // server's CSRF middleware branches purely on whether a session cookie
62
+ // is PRESENT on the request (see server/src/auth/middleware.rs's
63
+ // csrf_protect: any request carrying the session cookie is treated as
64
+ // cookie-auth and required to also carry a matching X-CSRF-Token +
65
+ // Origin, neither of which this native branch ever sends). React
66
+ // Native's Android fetch persists Set-Cookie responses in a shared
67
+ // CookieManager and reattaches them to matching-origin requests
68
+ // regardless of the web `credentials` semantics being followed
69
+ // elsewhere — so if the server ever sets a session cookie as a side
70
+ // effect for a native client (e.g. during passkey/WebAuthn login,
71
+ // which can involve a system browser or Credential Manager sheet),
72
+ // every subsequent native mutation silently starts requiring CSRF
73
+ // headers it can never provide, failing with "Missing CSRF header".
74
+ // Reported as folder-creation failing this way, 2026-08-17 — but the
75
+ // same stray cookie would affect ANY mutating request once present,
76
+ // not just this one. Omitting credentials here is the actual fix:
77
+ // native should never send or rely on cookies at all.
78
+ return fetchWithTimeout(url, { ...init, headers, credentials: "omit" }, timeoutMs);
61
79
  }