@croacroa/react-native-template 1.0.0 → 2.0.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 (69) hide show
  1. package/.github/workflows/ci.yml +187 -184
  2. package/.github/workflows/eas-build.yml +55 -55
  3. package/.github/workflows/eas-update.yml +50 -50
  4. package/CHANGELOG.md +106 -106
  5. package/CONTRIBUTING.md +377 -377
  6. package/README.md +399 -399
  7. package/__tests__/components/snapshots.test.tsx +131 -0
  8. package/__tests__/integration/auth-api.test.tsx +227 -0
  9. package/__tests__/performance/VirtualizedList.perf.test.tsx +362 -0
  10. package/app/(public)/onboarding.tsx +5 -5
  11. package/app.config.ts +45 -2
  12. package/assets/images/.gitkeep +7 -7
  13. package/components/onboarding/OnboardingScreen.tsx +370 -370
  14. package/components/onboarding/index.ts +2 -2
  15. package/components/providers/SuspenseBoundary.tsx +357 -0
  16. package/components/providers/index.ts +13 -0
  17. package/components/ui/Avatar.tsx +316 -316
  18. package/components/ui/Badge.tsx +416 -416
  19. package/components/ui/BottomSheet.tsx +307 -307
  20. package/components/ui/Checkbox.tsx +261 -261
  21. package/components/ui/OptimizedImage.tsx +369 -369
  22. package/components/ui/Select.tsx +240 -240
  23. package/components/ui/VirtualizedList.tsx +285 -0
  24. package/components/ui/index.ts +23 -18
  25. package/constants/config.ts +97 -54
  26. package/docs/adr/001-state-management.md +79 -79
  27. package/docs/adr/002-styling-approach.md +130 -130
  28. package/docs/adr/003-data-fetching.md +155 -155
  29. package/docs/adr/004-auth-adapter-pattern.md +144 -144
  30. package/docs/adr/README.md +78 -78
  31. package/hooks/index.ts +27 -25
  32. package/hooks/useApi.ts +102 -5
  33. package/hooks/useAuth.tsx +82 -0
  34. package/hooks/useBiometrics.ts +295 -295
  35. package/hooks/useDeepLinking.ts +256 -256
  36. package/hooks/useMFA.ts +499 -0
  37. package/hooks/useNotifications.ts +39 -0
  38. package/hooks/useOffline.ts +32 -2
  39. package/hooks/usePerformance.ts +434 -434
  40. package/hooks/useTheme.tsx +76 -0
  41. package/hooks/useUpdates.ts +358 -358
  42. package/i18n/index.ts +194 -77
  43. package/i18n/locales/ar.json +101 -0
  44. package/i18n/locales/de.json +101 -0
  45. package/i18n/locales/en.json +101 -101
  46. package/i18n/locales/es.json +101 -0
  47. package/i18n/locales/fr.json +101 -101
  48. package/jest.config.js +4 -4
  49. package/maestro/README.md +113 -113
  50. package/maestro/config.yaml +35 -35
  51. package/maestro/flows/login.yaml +62 -62
  52. package/maestro/flows/mfa-login.yaml +92 -0
  53. package/maestro/flows/mfa-setup.yaml +86 -0
  54. package/maestro/flows/navigation.yaml +68 -68
  55. package/maestro/flows/offline-conflict.yaml +101 -0
  56. package/maestro/flows/offline-sync.yaml +128 -0
  57. package/maestro/flows/offline.yaml +60 -60
  58. package/maestro/flows/register.yaml +94 -94
  59. package/package.json +175 -170
  60. package/services/analytics.ts +428 -428
  61. package/services/api.ts +340 -340
  62. package/services/authAdapter.ts +333 -333
  63. package/services/backgroundSync.ts +626 -0
  64. package/services/index.ts +54 -22
  65. package/services/security.ts +229 -0
  66. package/tailwind.config.js +47 -47
  67. package/utils/accessibility.ts +446 -446
  68. package/utils/index.ts +52 -43
  69. package/utils/withAccessibility.tsx +272 -0
@@ -0,0 +1,229 @@
1
+ /**
2
+ * @fileoverview Security utilities for enhanced app protection
3
+ * Provides SSL pinning validation, request signing, and security checks.
4
+ * @module services/security
5
+ */
6
+
7
+ import { SECURITY, IS_DEV } from "@/constants/config";
8
+
9
+ /**
10
+ * Validate SSL certificate pins for a given hostname.
11
+ * Note: In React Native, actual certificate pinning requires native module configuration.
12
+ * This service provides the configuration and validation helpers.
13
+ *
14
+ * For full SSL pinning, configure your native modules:
15
+ * - iOS: Use TrustKit or configure ATS in Info.plist
16
+ * - Android: Configure network_security_config.xml
17
+ *
18
+ * @param hostname - The hostname to validate pins for
19
+ * @returns Array of certificate pins for the hostname, or empty array if not configured
20
+ *
21
+ * @example
22
+ * ```ts
23
+ * const pins = getCertificatePins('api.yourapp.com');
24
+ * if (pins.length > 0) {
25
+ * // Use pins for validation
26
+ * }
27
+ * ```
28
+ */
29
+ export function getCertificatePins(hostname: string): string[] {
30
+ if (!SECURITY.ENABLE_SSL_PINNING) {
31
+ return [];
32
+ }
33
+
34
+ return SECURITY.SSL_PINS[hostname] || [];
35
+ }
36
+
37
+ /**
38
+ * Check if SSL pinning is enabled for a hostname
39
+ */
40
+ export function isSslPinningEnabled(hostname: string): boolean {
41
+ if (!SECURITY.ENABLE_SSL_PINNING) {
42
+ return false;
43
+ }
44
+
45
+ const pins = SECURITY.SSL_PINS[hostname];
46
+ return Array.isArray(pins) && pins.length > 0;
47
+ }
48
+
49
+ /**
50
+ * Generate a request signature for API requests.
51
+ * Useful for preventing request tampering and replay attacks.
52
+ *
53
+ * @param method - HTTP method
54
+ * @param url - Request URL
55
+ * @param body - Request body (optional)
56
+ * @param timestamp - Request timestamp
57
+ * @returns Signature string or null if signing is disabled
58
+ *
59
+ * @example
60
+ * ```ts
61
+ * const signature = generateRequestSignature('POST', '/api/users', { name: 'John' }, Date.now());
62
+ * if (signature) {
63
+ * headers['X-Request-Signature'] = signature;
64
+ * }
65
+ * ```
66
+ */
67
+ export function generateRequestSignature(
68
+ method: string,
69
+ url: string,
70
+ body: unknown,
71
+ timestamp: number
72
+ ): string | null {
73
+ if (!SECURITY.REQUEST_SIGNING.ENABLED) {
74
+ return null;
75
+ }
76
+
77
+ // Create payload to sign
78
+ const payload = [
79
+ method.toUpperCase(),
80
+ url,
81
+ body ? JSON.stringify(body) : "",
82
+ timestamp.toString(),
83
+ ].join(":");
84
+
85
+ // In production, use a proper HMAC implementation with a secret key
86
+ // This is a placeholder - implement actual signing based on your backend requirements
87
+ if (IS_DEV) {
88
+ console.log("[Security] Would sign payload:", payload);
89
+ }
90
+
91
+ // TODO: Implement actual HMAC signing
92
+ // const hmac = crypto.createHmac(SECURITY.REQUEST_SIGNING.ALGORITHM, SECRET_KEY);
93
+ // return hmac.update(payload).digest('base64');
94
+
95
+ return null;
96
+ }
97
+
98
+ /**
99
+ * Security headers to add to API requests
100
+ */
101
+ export function getSecurityHeaders(): Record<string, string> {
102
+ const headers: Record<string, string> = {};
103
+
104
+ // Add timestamp for request signing/replay protection
105
+ if (SECURITY.REQUEST_SIGNING.ENABLED) {
106
+ headers["X-Request-Timestamp"] = Date.now().toString();
107
+ }
108
+
109
+ return headers;
110
+ }
111
+
112
+ /**
113
+ * Validate that a URL is allowed (not blocked by security policy)
114
+ * Useful for preventing open redirects and SSRF
115
+ *
116
+ * @param url - URL to validate
117
+ * @param allowedHosts - List of allowed hostnames
118
+ * @returns True if URL is allowed
119
+ */
120
+ export function isUrlAllowed(url: string, allowedHosts: string[]): boolean {
121
+ try {
122
+ const parsed = new URL(url);
123
+ return allowedHosts.some((host) => {
124
+ // Support wildcards (e.g., *.yourapp.com)
125
+ if (host.startsWith("*.")) {
126
+ const domain = host.slice(2);
127
+ return (
128
+ parsed.hostname === domain || parsed.hostname.endsWith(`.${domain}`)
129
+ );
130
+ }
131
+ return parsed.hostname === host;
132
+ });
133
+ } catch {
134
+ // Invalid URL
135
+ return false;
136
+ }
137
+ }
138
+
139
+ /**
140
+ * Sanitize user input to prevent XSS
141
+ * Basic sanitization - use a proper library for comprehensive XSS protection
142
+ */
143
+ export function sanitizeInput(input: string): string {
144
+ return input
145
+ .replace(/&/g, "&amp;")
146
+ .replace(/</g, "&lt;")
147
+ .replace(/>/g, "&gt;")
148
+ .replace(/"/g, "&quot;")
149
+ .replace(/'/g, "&#x27;");
150
+ }
151
+
152
+ /**
153
+ * Check if the app is running in a potentially compromised environment
154
+ * (rooted/jailbroken device, debugger attached, etc.)
155
+ *
156
+ * Note: This requires additional native modules for full detection:
157
+ * - iOS: Consider using a jailbreak detection library
158
+ * - Android: Consider using SafetyNet Attestation or Play Integrity API
159
+ */
160
+ export async function checkSecurityEnvironment(): Promise<{
161
+ isSecure: boolean;
162
+ warnings: string[];
163
+ }> {
164
+ const warnings: string[] = [];
165
+
166
+ // Check for development mode
167
+ if (IS_DEV) {
168
+ warnings.push("Running in development mode");
169
+ }
170
+
171
+ // Check for debugger
172
+ // Note: __DEV__ is automatically true when debugger is attached
173
+ if (__DEV__) {
174
+ warnings.push("Debugger may be attached");
175
+ }
176
+
177
+ // Additional checks would require native modules:
178
+ // - Jailbreak/root detection
179
+ // - Frida/debugging tools detection
180
+ // - Emulator detection
181
+ // - Hook detection
182
+
183
+ return {
184
+ isSecure: warnings.length === 0,
185
+ warnings,
186
+ };
187
+ }
188
+
189
+ /**
190
+ * SSL Pinning configuration for native modules.
191
+ * Export this configuration to use with native SSL pinning libraries.
192
+ *
193
+ * For iOS (TrustKit), add to Info.plist or configure programmatically.
194
+ * For Android, use network_security_config.xml.
195
+ */
196
+ export const SSL_PINNING_CONFIG = {
197
+ enabled: SECURITY.ENABLE_SSL_PINNING,
198
+ pins: SECURITY.SSL_PINS,
199
+
200
+ /**
201
+ * Generate Android network_security_config.xml content
202
+ */
203
+ getAndroidConfig(): string {
204
+ const pinEntries = Object.entries(SECURITY.SSL_PINS)
205
+ .map(([domain, pins]) => {
206
+ const pinElements = pins
207
+ .map((pin) => ` <pin digest="SHA-256">${pin.replace("sha256/", "")}</pin>`)
208
+ .join("\n");
209
+
210
+ return ` <domain-config cleartextTrafficPermitted="false">
211
+ <domain includeSubdomains="true">${domain}</domain>
212
+ <pin-set expiration="2025-12-31">
213
+ ${pinElements}
214
+ </pin-set>
215
+ </domain-config>`;
216
+ })
217
+ .join("\n");
218
+
219
+ return `<?xml version="1.0" encoding="utf-8"?>
220
+ <network-security-config>
221
+ <base-config cleartextTrafficPermitted="false">
222
+ <trust-anchors>
223
+ <certificates src="system" />
224
+ </trust-anchors>
225
+ </base-config>
226
+ ${pinEntries}
227
+ </network-security-config>`;
228
+ },
229
+ };
@@ -1,47 +1,47 @@
1
- /** @type {import('tailwindcss').Config} */
2
- module.exports = {
3
- content: [
4
- "./app/**/*.{js,jsx,ts,tsx}",
5
- "./components/**/*.{js,jsx,ts,tsx}",
6
- ],
7
- presets: [require("nativewind/preset")],
8
- theme: {
9
- extend: {
10
- colors: {
11
- // Croacroa emerald green
12
- primary: {
13
- 50: "#ecfdf5",
14
- 100: "#d1fae5",
15
- 200: "#a7f3d0",
16
- 300: "#6ee7b7",
17
- 400: "#34d399",
18
- 500: "#10b981",
19
- 600: "#059669",
20
- 700: "#047857",
21
- 800: "#065f46",
22
- 900: "#064e3b",
23
- },
24
- background: {
25
- light: "#ffffff",
26
- dark: "#0f172a",
27
- },
28
- surface: {
29
- light: "#f8fafc",
30
- dark: "#1e293b",
31
- },
32
- text: {
33
- light: "#0f172a",
34
- dark: "#f8fafc",
35
- },
36
- muted: {
37
- light: "#64748b",
38
- dark: "#94a3b8",
39
- },
40
- },
41
- fontFamily: {
42
- sans: ["Inter", "system-ui", "sans-serif"],
43
- },
44
- },
45
- },
46
- plugins: [],
47
- };
1
+ /** @type {import('tailwindcss').Config} */
2
+ module.exports = {
3
+ content: [
4
+ "./app/**/*.{js,jsx,ts,tsx}",
5
+ "./components/**/*.{js,jsx,ts,tsx}",
6
+ ],
7
+ presets: [require("nativewind/preset")],
8
+ theme: {
9
+ extend: {
10
+ colors: {
11
+ // Croacroa emerald green
12
+ primary: {
13
+ 50: "#ecfdf5",
14
+ 100: "#d1fae5",
15
+ 200: "#a7f3d0",
16
+ 300: "#6ee7b7",
17
+ 400: "#34d399",
18
+ 500: "#10b981",
19
+ 600: "#059669",
20
+ 700: "#047857",
21
+ 800: "#065f46",
22
+ 900: "#064e3b",
23
+ },
24
+ background: {
25
+ light: "#ffffff",
26
+ dark: "#0f172a",
27
+ },
28
+ surface: {
29
+ light: "#f8fafc",
30
+ dark: "#1e293b",
31
+ },
32
+ text: {
33
+ light: "#0f172a",
34
+ dark: "#f8fafc",
35
+ },
36
+ muted: {
37
+ light: "#64748b",
38
+ dark: "#94a3b8",
39
+ },
40
+ },
41
+ fontFamily: {
42
+ sans: ["Inter", "system-ui", "sans-serif"],
43
+ },
44
+ },
45
+ },
46
+ plugins: [],
47
+ };