dpv007 commited on
Commit
964d38b
·
1 Parent(s): 758cb51
keystone-app/app.json CHANGED
@@ -3,23 +3,15 @@
3
  "name": "keystone-app",
4
  "slug": "keystone-app",
5
  "version": "1.0.0",
6
- "orientation": "portrait",
7
- "icon": "./assets/icon.png",
8
- "userInterfaceStyle": "light",
9
- "ios": {
10
- "supportsTablet": true
11
- },
12
- "android": {
13
- "adaptiveIcon": {
14
- "backgroundColor": "#E6F4FE",
15
- "foregroundImage": "./assets/android-icon-foreground.png",
16
- "backgroundImage": "./assets/android-icon-background.png",
17
- "monochromeImage": "./assets/android-icon-monochrome.png"
18
- },
19
- "predictiveBackGestureEnabled": false
20
- },
21
- "web": {
22
- "favicon": "./assets/favicon.png"
23
- }
24
  }
25
- }
 
3
  "name": "keystone-app",
4
  "slug": "keystone-app",
5
  "version": "1.0.0",
6
+ "plugins": [
7
+ [
8
+ "expo-media-library",
9
+ {
10
+ "photosPermission": "Allow KeyStone to access your photos to back them up to your private cloud.",
11
+ "savePhotosPermission": "Allow KeyStone to save photos.",
12
+ "isAccessMediaLocationEnabled": true
13
+ }
14
+ ]
15
+ ]
 
 
 
 
 
 
 
 
16
  }
17
+ }
keystone-app/app/(tabs)/_layout.tsx ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Tabs } from 'expo-router';
2
+ import { MaterialIcons } from '@expo/vector-icons';
3
+
4
+ export default function TabLayout() {
5
+ return (
6
+ <Tabs screenOptions={{
7
+ tabBarActiveTintColor: '#1a73e8', // Google Blue
8
+ tabBarInactiveTintColor: '#5f6368',
9
+ tabBarStyle: {
10
+ borderTopWidth: 0,
11
+ elevation: 10,
12
+ shadowColor: '#000',
13
+ shadowOpacity: 0.1,
14
+ shadowRadius: 4,
15
+ shadowOffset: { width: 0, height: -2 },
16
+ },
17
+ headerShown: false,
18
+ }}>
19
+ <Tabs.Screen
20
+ name="index"
21
+ options={{
22
+ title: 'Photos',
23
+ tabBarIcon: ({ color, size }) => (
24
+ <MaterialIcons name="photo" size={size} color={color} />
25
+ ),
26
+ }}
27
+ />
28
+ <Tabs.Screen
29
+ name="memories"
30
+ options={{
31
+ title: 'Memories',
32
+ tabBarIcon: ({ color, size }) => (
33
+ <MaterialIcons name="auto-awesome" size={size} color={color} />
34
+ ),
35
+ }}
36
+ />
37
+ <Tabs.Screen
38
+ name="library"
39
+ options={{
40
+ title: 'Library',
41
+ tabBarIcon: ({ color, size }) => (
42
+ <MaterialIcons name="photo-library" size={size} color={color} />
43
+ ),
44
+ }}
45
+ />
46
+ <Tabs.Screen
47
+ name="search"
48
+ options={{
49
+ title: 'Search',
50
+ tabBarIcon: ({ color, size }) => (
51
+ <MaterialIcons name="search" size={size} color={color} />
52
+ ),
53
+ }}
54
+ />
55
+ </Tabs>
56
+ );
57
+ }
keystone-app/app/(tabs)/index.tsx ADDED
@@ -0,0 +1,343 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React, { useEffect, useState } from 'react';
2
+ import {
3
+ View,
4
+ SectionList,
5
+ StyleSheet,
6
+ Dimensions,
7
+ TouchableOpacity,
8
+ Text,
9
+ SafeAreaView,
10
+ Alert,
11
+ Platform,
12
+ ActivityIndicator
13
+ } from 'react-native';
14
+ import * as MediaLibrary from 'expo-media-library';
15
+ import { Image } from 'expo-image';
16
+ import * as FileSystem from 'expo-file-system';
17
+ import { StatusBar } from 'expo-status-bar';
18
+ import { MaterialIcons } from '@expo/vector-icons';
19
+ import { format, isToday, isYesterday } from 'date-fns';
20
+ import AsyncStorage from '@react-native-async-storage/async-storage';
21
+
22
+ const BACKEND_URL = 'https://dpv007-keystone.hf.space';
23
+ const MOCK_TOKEN = 'mock-jwt-token-xyz123';
24
+ const SYNCED_PHOTOS_KEY = '@synced_photos';
25
+
26
+ const { width } = Dimensions.get('window');
27
+ const NUM_COLUMNS = 4;
28
+ const SPACING = 2;
29
+ // Adjust tile size to account for spacing
30
+ const TILE_SIZE = (width - (SPACING * (NUM_COLUMNS + 1))) / NUM_COLUMNS;
31
+
32
+ type SectionData = {
33
+ title: string;
34
+ data: MediaLibrary.Asset[][];
35
+ };
36
+
37
+ export default function PhotosScreen() {
38
+ const [permissionResponse, requestPermission] = MediaLibrary.usePermissions();
39
+ const [sections, setSections] = useState<SectionData[]>([]);
40
+ const [allPhotos, setAllPhotos] = useState<MediaLibrary.Asset[]>([]);
41
+
42
+ // Sync States
43
+ const [isSyncing, setIsSyncing] = useState(false);
44
+ const [syncProgress, setSyncProgress] = useState(0);
45
+ const [syncTotal, setSyncTotal] = useState(0);
46
+
47
+ useEffect(() => {
48
+ if (Platform.OS !== 'web') {
49
+ if (!permissionResponse) {
50
+ requestPermission();
51
+ } else if (permissionResponse.status !== 'granted') {
52
+ // Optionally prompt user if they denied it previously
53
+ if (permissionResponse.canAskAgain) {
54
+ requestPermission();
55
+ }
56
+ } else {
57
+ loadPhotos();
58
+ }
59
+ }
60
+ }, [permissionResponse]);
61
+
62
+ const groupPhotosByDate = (photos: MediaLibrary.Asset[]): SectionData[] => {
63
+ const groups: { [key: string]: MediaLibrary.Asset[] } = {};
64
+
65
+ photos.forEach(photo => {
66
+ const date = new Date(photo.creationTime);
67
+ let dateString = '';
68
+ if (isToday(date)) {
69
+ dateString = 'Today';
70
+ } else if (isYesterday(date)) {
71
+ dateString = 'Yesterday';
72
+ } else {
73
+ dateString = format(date, 'EEE, MMM d');
74
+ }
75
+
76
+ if (!groups[dateString]) {
77
+ groups[dateString] = [];
78
+ }
79
+ groups[dateString].push(photo);
80
+ });
81
+
82
+ return Object.keys(groups).map(key => {
83
+ const items = groups[key];
84
+ const rows = [];
85
+ for (let i = 0; i < items.length; i += NUM_COLUMNS) {
86
+ rows.push(items.slice(i, i + NUM_COLUMNS));
87
+ }
88
+ return { title: key, data: rows };
89
+ });
90
+ };
91
+
92
+ const loadPhotos = async () => {
93
+ // We fetch a larger batch for robust syncing demonstration
94
+ const media = await MediaLibrary.getAssetsAsync({
95
+ first: 500,
96
+ sortBy: [MediaLibrary.SortBy.creationTime],
97
+ mediaType: MediaLibrary.MediaType.photo,
98
+ });
99
+ setAllPhotos(media.assets);
100
+ setSections(groupPhotosByDate(media.assets));
101
+ };
102
+
103
+ const getSyncedPhotosList = async (): Promise<string[]> => {
104
+ try {
105
+ const saved = await AsyncStorage.getItem(SYNCED_PHOTOS_KEY);
106
+ return saved ? JSON.parse(saved) : [];
107
+ } catch (e) {
108
+ return [];
109
+ }
110
+ };
111
+
112
+ const saveSyncedPhotoId = async (id: string, currentSyncedList: string[]) => {
113
+ try {
114
+ const newList = [...currentSyncedList, id];
115
+ await AsyncStorage.setItem(SYNCED_PHOTOS_KEY, JSON.stringify(newList));
116
+ return newList;
117
+ } catch (e) {
118
+ return currentSyncedList;
119
+ }
120
+ };
121
+
122
+ const syncPhotos = async () => {
123
+ if (allPhotos.length === 0) return;
124
+
125
+ setIsSyncing(true);
126
+ let syncedList = await getSyncedPhotosList();
127
+
128
+ // Filter out photos that have already been synced
129
+ const photosToSync = allPhotos.filter(p => !syncedList.includes(p.id));
130
+
131
+ if (photosToSync.length === 0) {
132
+ setIsSyncing(false);
133
+ Alert.alert('All Caught Up!', 'There are no new photos to backup.');
134
+ return;
135
+ }
136
+
137
+ setSyncTotal(photosToSync.length);
138
+ setSyncProgress(0);
139
+
140
+ let successCount = 0;
141
+
142
+ for (let i = 0; i < photosToSync.length; i++) {
143
+ const photo = photosToSync[i];
144
+ setSyncProgress(i + 1);
145
+
146
+ try {
147
+ const assetInfo = await MediaLibrary.getAssetInfoAsync(photo);
148
+ const localUri = assetInfo.localUri || photo.uri;
149
+
150
+ const response = await FileSystem.uploadAsync(
151
+ `${BACKEND_URL}/upload`,
152
+ localUri,
153
+ {
154
+ fieldName: 'file',
155
+ httpMethod: 'POST',
156
+ uploadType: FileSystem.FileSystemUploadType.MULTIPART,
157
+ parameters: {
158
+ // Send the creationTime in milliseconds so the backend can sort into year/month
159
+ creation_time: photo.creationTime.toString()
160
+ },
161
+ headers: {
162
+ Authorization: `Bearer ${MOCK_TOKEN}`,
163
+ },
164
+ }
165
+ );
166
+
167
+ if (response.status === 200) {
168
+ syncedList = await saveSyncedPhotoId(photo.id, syncedList);
169
+ successCount++;
170
+ } else {
171
+ console.log(`Failed to upload ${photo.id}, status: ${response.status}`);
172
+ // If it's a 500 error, we might want to stop the batch, but we'll just skip for now to be robust
173
+ }
174
+ } catch (error) {
175
+ console.error('Error uploading photo:', error);
176
+ }
177
+ }
178
+
179
+ setIsSyncing(false);
180
+ if (successCount > 0) {
181
+ Alert.alert('Backup Complete', `Successfully backed up ${successCount} photos to KeyStone.`);
182
+ } else {
183
+ Alert.alert('Backup Failed', 'Could not backup photos. Ensure backend URL is correct.');
184
+ }
185
+ };
186
+
187
+ const renderRow = ({ item: row }: { item: MediaLibrary.Asset[] }) => (
188
+ <View style={styles.row}>
189
+ {row.map((photo, index) => (
190
+ <TouchableOpacity key={photo.id} style={styles.tileContainer}>
191
+ <Image
192
+ source={{ uri: photo.uri }}
193
+ style={styles.image}
194
+ contentFit="cover"
195
+ cachePolicy="memory-disk"
196
+ />
197
+ </TouchableOpacity>
198
+ ))}
199
+ {row.length < NUM_COLUMNS &&
200
+ Array.from({ length: NUM_COLUMNS - row.length }).map((_, i) => (
201
+ <View key={`empty-${i}`} style={styles.tileContainer} />
202
+ ))
203
+ }
204
+ </View>
205
+ );
206
+
207
+ const renderSectionHeader = ({ section }: { section: SectionData }) => (
208
+ <View style={styles.sectionHeader}>
209
+ <Text style={styles.sectionTitle}>{section.title}</Text>
210
+ </View>
211
+ );
212
+
213
+ if (Platform.OS === 'web') {
214
+ return (
215
+ <View style={styles.center}>
216
+ <MaterialIcons name="devices" size={64} color="#ccc" />
217
+ <Text style={styles.webMessage}>Optimized for Mobile</Text>
218
+ <Text style={styles.webSubMessage}>Please open in Expo Go on a mobile device.</Text>
219
+ </View>
220
+ );
221
+ }
222
+
223
+ if (!permissionResponse?.granted) {
224
+ return (
225
+ <SafeAreaView style={styles.center}>
226
+ <MaterialIcons name="photo-library" size={64} color="#ccc" />
227
+ <Text style={styles.permissionText}>Allow KeyStone to access your photos</Text>
228
+ <Text style={styles.webSubMessage}>We need this permission to build your gallery and securely back up your photos.</Text>
229
+ <TouchableOpacity style={styles.button} onPress={requestPermission}>
230
+ <Text style={styles.buttonText}>Allow Access</Text>
231
+ </TouchableOpacity>
232
+ </SafeAreaView>
233
+ );
234
+ }
235
+
236
+ return (
237
+ <SafeAreaView style={styles.container}>
238
+ <StatusBar style="dark" />
239
+
240
+ <View style={styles.header}>
241
+ <View style={styles.headerLeft}>
242
+ <Text style={styles.headerLogo}>KeyStone</Text>
243
+ {isSyncing && (
244
+ <Text style={styles.syncStatusText}>
245
+ Syncing {syncProgress} of {syncTotal}...
246
+ </Text>
247
+ )}
248
+ </View>
249
+ <TouchableOpacity
250
+ style={styles.syncIconContainer}
251
+ onPress={syncPhotos}
252
+ disabled={isSyncing}
253
+ >
254
+ {isSyncing ? (
255
+ <ActivityIndicator size="small" color="#1a73e8" />
256
+ ) : (
257
+ <MaterialIcons name="cloud-upload" size={28} color="#5f6368" />
258
+ )}
259
+ </TouchableOpacity>
260
+ </View>
261
+
262
+ <SectionList
263
+ sections={sections}
264
+ keyExtractor={(item, index) => `row-${index}`}
265
+ renderItem={renderRow}
266
+ renderSectionHeader={renderSectionHeader}
267
+ showsVerticalScrollIndicator={false}
268
+ contentContainerStyle={styles.listContent}
269
+ stickySectionHeadersEnabled={false}
270
+ />
271
+ </SafeAreaView>
272
+ );
273
+ }
274
+
275
+ const styles = StyleSheet.create({
276
+ container: { flex: 1, backgroundColor: '#fff' },
277
+ center: { flex: 1, justifyContent: 'center', alignItems: 'center', padding: 20, backgroundColor: '#fff' },
278
+ webMessage: { fontSize: 20, fontWeight: 'bold', marginTop: 16, marginBottom: 8, color: '#202124' },
279
+ webSubMessage: { fontSize: 16, color: '#5f6368', textAlign: 'center', marginBottom: 20 },
280
+ permissionText: { textAlign: 'center', paddingHorizontal: 20, fontSize: 18, color: '#202124', marginVertical: 16 },
281
+ button: { backgroundColor: '#1a73e8', paddingHorizontal: 24, paddingVertical: 12, borderRadius: 24 },
282
+ buttonText: { color: '#fff', fontWeight: '600', fontSize: 16 },
283
+
284
+ header: {
285
+ flexDirection: 'row',
286
+ justifyContent: 'space-between',
287
+ alignItems: 'center',
288
+ paddingHorizontal: 16,
289
+ paddingVertical: 12,
290
+ },
291
+ headerLeft: {
292
+ flexDirection: 'column',
293
+ justifyContent: 'center',
294
+ },
295
+ headerLogo: {
296
+ fontSize: 22,
297
+ fontWeight: '600',
298
+ color: '#202124',
299
+ letterSpacing: -0.5,
300
+ },
301
+ syncStatusText: {
302
+ fontSize: 12,
303
+ color: '#1a73e8',
304
+ fontWeight: '500',
305
+ marginTop: 2
306
+ },
307
+ syncIconContainer: {
308
+ padding: 8,
309
+ borderRadius: 20,
310
+ backgroundColor: '#f1f3f4'
311
+ },
312
+
313
+ listContent: {
314
+ paddingHorizontal: SPACING,
315
+ },
316
+ sectionHeader: {
317
+ paddingVertical: 16,
318
+ paddingHorizontal: 4,
319
+ backgroundColor: '#fff',
320
+ },
321
+ sectionTitle: {
322
+ fontSize: 16,
323
+ fontWeight: '500',
324
+ color: '#3c4043',
325
+ },
326
+ row: {
327
+ flexDirection: 'row',
328
+ marginBottom: SPACING,
329
+ },
330
+ tileContainer: {
331
+ width: TILE_SIZE,
332
+ height: TILE_SIZE,
333
+ marginRight: SPACING,
334
+ borderRadius: 8,
335
+ overflow: 'hidden',
336
+ backgroundColor: '#f1f3f4'
337
+ },
338
+ image: {
339
+ flex: 1,
340
+ width: '100%',
341
+ height: '100%'
342
+ },
343
+ });
keystone-app/app/(tabs)/library.tsx ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { View, Text, StyleSheet } from 'react-native';
2
+
3
+ export default function LibraryScreen() {
4
+ return (
5
+ <View style={styles.container}>
6
+ <Text style={styles.title}>Library</Text>
7
+ <Text style={styles.subtitle}>Folders, albums, and trash.</Text>
8
+ </View>
9
+ );
10
+ }
11
+
12
+ const styles = StyleSheet.create({
13
+ container: {
14
+ flex: 1,
15
+ justifyContent: 'center',
16
+ alignItems: 'center',
17
+ backgroundColor: '#fff',
18
+ },
19
+ title: {
20
+ fontSize: 24,
21
+ fontWeight: 'bold',
22
+ marginBottom: 8,
23
+ },
24
+ subtitle: {
25
+ fontSize: 16,
26
+ color: '#666',
27
+ }
28
+ });
keystone-app/app/(tabs)/memories.tsx ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { View, Text, StyleSheet } from 'react-native';
2
+
3
+ export default function MemoriesScreen() {
4
+ return (
5
+ <View style={styles.container}>
6
+ <Text style={styles.title}>Memories</Text>
7
+ <Text style={styles.subtitle}>Relive your best moments.</Text>
8
+ </View>
9
+ );
10
+ }
11
+
12
+ const styles = StyleSheet.create({
13
+ container: {
14
+ flex: 1,
15
+ justifyContent: 'center',
16
+ alignItems: 'center',
17
+ backgroundColor: '#fff',
18
+ },
19
+ title: {
20
+ fontSize: 24,
21
+ fontWeight: 'bold',
22
+ marginBottom: 8,
23
+ },
24
+ subtitle: {
25
+ fontSize: 16,
26
+ color: '#666',
27
+ }
28
+ });
keystone-app/app/(tabs)/search.tsx ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { View, Text, StyleSheet } from 'react-native';
2
+
3
+ export default function SearchScreen() {
4
+ return (
5
+ <View style={styles.container}>
6
+ <Text style={styles.title}>Search</Text>
7
+ <Text style={styles.subtitle}>Find your photos.</Text>
8
+ </View>
9
+ );
10
+ }
11
+
12
+ const styles = StyleSheet.create({
13
+ container: {
14
+ flex: 1,
15
+ justifyContent: 'center',
16
+ alignItems: 'center',
17
+ backgroundColor: '#fff',
18
+ },
19
+ title: {
20
+ fontSize: 24,
21
+ fontWeight: 'bold',
22
+ marginBottom: 8,
23
+ },
24
+ subtitle: {
25
+ fontSize: 16,
26
+ color: '#666',
27
+ }
28
+ });
keystone-app/app/_layout.tsx ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ import { Stack } from 'expo-router';
2
+
3
+ export default function Layout() {
4
+ return (
5
+ <Stack>
6
+ <Stack.Screen name="(tabs)" options={{ headerShown: false }} />
7
+ </Stack>
8
+ );
9
+ }
keystone-app/package-lock.json CHANGED
@@ -8,10 +8,19 @@
8
  "name": "keystone-app",
9
  "version": "1.0.0",
10
  "dependencies": {
 
 
 
 
11
  "expo": "~57.0.6",
 
 
 
12
  "expo-status-bar": "~57.0.1",
13
  "react": "19.2.3",
14
- "react-native": "0.86.0"
 
 
15
  },
16
  "devDependencies": {
17
  "@types/react": "~19.2.2",
@@ -1157,6 +1166,17 @@
1157
  }
1158
  }
1159
  },
 
 
 
 
 
 
 
 
 
 
 
1160
  "node_modules/@expo/env": {
1161
  "version": "2.4.2",
1162
  "resolved": "https://registry.npmjs.org/@expo/env/-/env-2.4.2.tgz",
@@ -1243,6 +1263,23 @@
1243
  "chalk": "^4.1.2"
1244
  }
1245
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1246
  "node_modules/@expo/metro": {
1247
  "version": "56.0.0",
1248
  "resolved": "https://registry.npmjs.org/@expo/metro/-/metro-56.0.0.tgz",
@@ -1279,6 +1316,218 @@
1279
  "walker": "^1.0.8"
1280
  }
1281
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1282
  "node_modules/@expo/osascript": {
1283
  "version": "2.7.1",
1284
  "resolved": "https://registry.npmjs.org/@expo/osascript/-/osascript-2.7.1.tgz",
@@ -1490,6 +1739,18 @@
1490
  "@jridgewell/sourcemap-codec": "^1.4.14"
1491
  }
1492
  },
 
 
 
 
 
 
 
 
 
 
 
 
1493
  "node_modules/@react-native/assets-registry": {
1494
  "version": "0.86.0",
1495
  "resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.86.0.tgz",
@@ -1677,6 +1938,48 @@
1677
  "integrity": "sha512-hhyNJ+nbR6ZR7pToHvllEFun9TL0sbL+tk/ON75lo+Xas054uez98qRbsuNt7MBCyZKK4+8Yli/OAGZhmfBZ/g==",
1678
  "license": "MIT"
1679
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1680
  "node_modules/@types/istanbul-lib-coverage": {
1681
  "version": "2.0.6",
1682
  "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz",
@@ -1701,6 +2004,15 @@
1701
  "@types/istanbul-lib-report": "*"
1702
  }
1703
  },
 
 
 
 
 
 
 
 
 
1704
  "node_modules/@types/node": {
1705
  "version": "26.1.1",
1706
  "resolved": "https://registry.npmjs.org/@types/node/-/node-26.1.1.tgz",
@@ -1720,6 +2032,15 @@
1720
  "csstype": "^3.2.2"
1721
  }
1722
  },
 
 
 
 
 
 
 
 
 
1723
  "node_modules/@types/yargs": {
1724
  "version": "17.0.35",
1725
  "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz",
@@ -2131,6 +2452,33 @@
2131
  "node": ">= 0.8"
2132
  }
2133
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2134
  "node_modules/camelcase": {
2135
  "version": "6.3.0",
2136
  "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
@@ -2263,6 +2611,18 @@
2263
  "node": ">=0.8"
2264
  }
2265
  },
 
 
 
 
 
 
 
 
 
 
 
 
2266
  "node_modules/color-convert": {
2267
  "version": "2.0.1",
2268
  "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
@@ -2393,6 +2753,15 @@
2393
  "url": "https://opencollective.com/core-js"
2394
  }
2395
  },
 
 
 
 
 
 
 
 
 
2396
  "node_modules/cross-spawn": {
2397
  "version": "7.0.6",
2398
  "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
@@ -2407,6 +2776,15 @@
2407
  "node": ">= 8"
2408
  }
2409
  },
 
 
 
 
 
 
 
 
 
2410
  "node_modules/csstype": {
2411
  "version": "3.2.3",
2412
  "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
@@ -2414,6 +2792,16 @@
2414
  "devOptional": true,
2415
  "license": "MIT"
2416
  },
 
 
 
 
 
 
 
 
 
 
2417
  "node_modules/debug": {
2418
  "version": "4.4.3",
2419
  "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
@@ -2431,6 +2819,33 @@
2431
  }
2432
  }
2433
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2434
  "node_modules/deepmerge": {
2435
  "version": "4.3.1",
2436
  "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
@@ -2452,6 +2867,15 @@
2452
  "url": "https://github.com/sponsors/sindresorhus"
2453
  }
2454
  },
 
 
 
 
 
 
 
 
 
2455
  "node_modules/depd": {
2456
  "version": "2.0.0",
2457
  "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
@@ -2513,6 +2937,15 @@
2513
  "node": ">= 0.8"
2514
  }
2515
  },
 
 
 
 
 
 
 
 
 
2516
  "node_modules/error-stack-parser": {
2517
  "version": "2.1.4",
2518
  "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz",
@@ -2632,12 +3065,52 @@
2632
  },
2633
  "react-native-web": {
2634
  "optional": true
2635
- },
2636
- "react-native-webview": {
2637
- "optional": true
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2638
  }
2639
  }
2640
  },
 
 
 
 
 
 
 
 
 
 
2641
  "node_modules/expo-modules-autolinking": {
2642
  "version": "57.0.7",
2643
  "resolved": "https://registry.npmjs.org/expo-modules-autolinking/-/expo-modules-autolinking-57.0.7.tgz",
@@ -2819,34 +3292,6 @@
2819
  }
2820
  }
2821
  },
2822
- "node_modules/expo/node_modules/@expo/dom-webview": {
2823
- "version": "57.0.1",
2824
- "resolved": "https://registry.npmjs.org/@expo/dom-webview/-/dom-webview-57.0.1.tgz",
2825
- "integrity": "sha512-lAKsME4SAq+8sf56oN0DX5TBYyruupoRxbWbD2xf9RnKY8y6x8eb9LCE5pxSN0qyWdqnp+0wmyWzDkKboThKAw==",
2826
- "license": "MIT",
2827
- "peerDependencies": {
2828
- "expo": "*",
2829
- "react": "*",
2830
- "react-native": "*"
2831
- }
2832
- },
2833
- "node_modules/expo/node_modules/@expo/log-box": {
2834
- "version": "57.0.1",
2835
- "resolved": "https://registry.npmjs.org/@expo/log-box/-/log-box-57.0.1.tgz",
2836
- "integrity": "sha512-fuVNHhOerdRWtpq27gD6JTSVYESsfRu+SMdrNCWxW+gFnusS6dGKfx3lKGBZ4ZkMNiLWn8maBHo39YKzJNXFYQ==",
2837
- "license": "MIT",
2838
- "dependencies": {
2839
- "@expo/dom-webview": "^57.0.1",
2840
- "anser": "^1.4.9",
2841
- "stacktrace-parser": "^0.1.10"
2842
- },
2843
- "peerDependencies": {
2844
- "@expo/dom-webview": "^57.0.1",
2845
- "expo": "*",
2846
- "react": "*",
2847
- "react-native": "*"
2848
- }
2849
- },
2850
  "node_modules/expo/node_modules/@expo/metro-config": {
2851
  "version": "57.0.5",
2852
  "resolved": "https://registry.npmjs.org/@expo/metro-config/-/metro-config-57.0.5.tgz",
@@ -3018,16 +3463,6 @@
3018
  "react-native": "*"
3019
  }
3020
  },
3021
- "node_modules/expo/node_modules/expo-file-system": {
3022
- "version": "57.0.1",
3023
- "resolved": "https://registry.npmjs.org/expo-file-system/-/expo-file-system-57.0.1.tgz",
3024
- "integrity": "sha512-w7/ERvQFrGP2apTO9lDtZ+O6JQIhfakL7+Xqzh+rfMO9B4LB4qwrz+YvLgir8KFRVX64JHBnRuYBVLY1oQZcqw==",
3025
- "license": "MIT",
3026
- "peerDependencies": {
3027
- "expo": "*",
3028
- "react-native": "*"
3029
- }
3030
- },
3031
  "node_modules/expo/node_modules/expo-font": {
3032
  "version": "57.0.1",
3033
  "resolved": "https://registry.npmjs.org/expo-font/-/expo-font-57.0.1.tgz",
@@ -3157,6 +3592,36 @@
3157
  "bser": "2.1.1"
3158
  }
3159
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3160
  "node_modules/fetch-nodeshim": {
3161
  "version": "0.4.10",
3162
  "resolved": "https://registry.npmjs.org/fetch-nodeshim/-/fetch-nodeshim-0.4.10.tgz",
@@ -3256,6 +3721,21 @@
3256
  "node": "6.* || 8.* || >= 10.*"
3257
  }
3258
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3259
  "node_modules/getenv": {
3260
  "version": "2.0.0",
3261
  "resolved": "https://registry.npmjs.org/getenv/-/getenv-2.0.0.tgz",
@@ -3282,6 +3762,31 @@
3282
  "url": "https://github.com/sponsors/isaacs"
3283
  }
3284
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3285
  "node_modules/graceful-fs": {
3286
  "version": "4.2.11",
3287
  "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
@@ -3348,6 +3853,12 @@
3348
  "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
3349
  "license": "ISC"
3350
  },
 
 
 
 
 
 
3351
  "node_modules/http-errors": {
3352
  "version": "2.0.1",
3353
  "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
@@ -3377,6 +3888,19 @@
3377
  "node": ">= 0.8"
3378
  }
3379
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
3380
  "node_modules/https-proxy-agent": {
3381
  "version": "7.0.6",
3382
  "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
@@ -3390,6 +3914,12 @@
3390
  "node": ">= 14"
3391
  }
3392
  },
 
 
 
 
 
 
3393
  "node_modules/ignore": {
3394
  "version": "5.3.2",
3395
  "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
@@ -3420,6 +3950,15 @@
3420
  "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
3421
  "license": "ISC"
3422
  },
 
 
 
 
 
 
 
 
 
3423
  "node_modules/invariant": {
3424
  "version": "2.2.4",
3425
  "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
@@ -3477,6 +4016,15 @@
3477
  "node": ">=0.12.0"
3478
  }
3479
  },
 
 
 
 
 
 
 
 
 
3480
  "node_modules/is-wsl": {
3481
  "version": "2.2.0",
3482
  "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",
@@ -3635,6 +4183,12 @@
3635
  "node": ">=6"
3636
  }
3637
  },
 
 
 
 
 
 
3638
  "node_modules/json5": {
3639
  "version": "2.2.3",
3640
  "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
@@ -3647,6 +4201,15 @@
3647
  "node": ">=6"
3648
  }
3649
  },
 
 
 
 
 
 
 
 
 
3650
  "node_modules/kleur": {
3651
  "version": "3.0.3",
3652
  "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz",
@@ -4055,6 +4618,15 @@
4055
  "loose-envify": "cli.js"
4056
  }
4057
  },
 
 
 
 
 
 
 
 
 
4058
  "node_modules/lru-cache": {
4059
  "version": "5.1.1",
4060
  "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
@@ -4085,6 +4657,18 @@
4085
  "integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==",
4086
  "license": "MIT"
4087
  },
 
 
 
 
 
 
 
 
 
 
 
 
4088
  "node_modules/merge-stream": {
4089
  "version": "2.0.0",
4090
  "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
@@ -4418,6 +5002,15 @@
4418
  "node": ">=4"
4419
  }
4420
  },
 
 
 
 
 
 
 
 
 
4421
  "node_modules/minimatch": {
4422
  "version": "10.2.5",
4423
  "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
@@ -4493,6 +5086,26 @@
4493
  "node": ">= 0.6"
4494
  }
4495
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4496
  "node_modules/node-forge": {
4497
  "version": "1.4.0",
4498
  "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.4.0.tgz",
@@ -4517,6 +5130,18 @@
4517
  "node": ">=18"
4518
  }
4519
  },
 
 
 
 
 
 
 
 
 
 
 
 
4520
  "node_modules/npm-package-arg": {
4521
  "version": "11.0.3",
4522
  "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-11.0.3.tgz",
@@ -4550,6 +5175,15 @@
4550
  "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0"
4551
  }
4552
  },
 
 
 
 
 
 
 
 
 
4553
  "node_modules/on-finished": {
4554
  "version": "2.3.0",
4555
  "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
@@ -4571,6 +5205,15 @@
4571
  "node": ">= 0.8"
4572
  }
4573
  },
 
 
 
 
 
 
 
 
 
4574
  "node_modules/onetime": {
4575
  "version": "2.0.1",
4576
  "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz",
@@ -4708,6 +5351,15 @@
4708
  "node": ">=4"
4709
  }
4710
  },
 
 
 
 
 
 
 
 
 
4711
  "node_modules/parse-png": {
4712
  "version": "2.1.0",
4713
  "resolved": "https://registry.npmjs.org/parse-png/-/parse-png-2.1.0.tgz",
@@ -4847,6 +5499,12 @@
4847
  "node": "^10 || ^12 || >=14"
4848
  }
4849
  },
 
 
 
 
 
 
4850
  "node_modules/pretty-format": {
4851
  "version": "29.7.0",
4852
  "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz",
@@ -4913,6 +5571,16 @@
4913
  "node": ">= 6"
4914
  }
4915
  },
 
 
 
 
 
 
 
 
 
 
4916
  "node_modules/queue": {
4917
  "version": "6.0.2",
4918
  "resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz",
@@ -4922,6 +5590,18 @@
4922
  "inherits": "~2.0.3"
4923
  }
4924
  },
 
 
 
 
 
 
 
 
 
 
 
 
4925
  "node_modules/range-parser": {
4926
  "version": "1.2.1",
4927
  "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
@@ -4950,6 +5630,18 @@
4950
  "ws": "^7"
4951
  }
4952
  },
 
 
 
 
 
 
 
 
 
 
 
 
4953
  "node_modules/react-is": {
4954
  "version": "18.3.1",
4955
  "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
@@ -5015,6 +5707,38 @@
5015
  }
5016
  }
5017
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5018
  "node_modules/react-native/node_modules/commander": {
5019
  "version": "12.1.0",
5020
  "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz",
@@ -5122,6 +5846,12 @@
5122
  "url": "https://github.com/sponsors/ljharb"
5123
  }
5124
  },
 
 
 
 
 
 
5125
  "node_modules/resolve-from": {
5126
  "version": "5.0.0",
5127
  "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
@@ -5137,6 +5867,18 @@
5137
  "integrity": "sha512-nR23LHAvaI6aHtMg6RWoaHpdR4D881Nydkzi2CixINyg9T00KgaJdJI6Vwty+Ps8WLxZHuxsS0BseWjxSA4C+w==",
5138
  "license": "MIT"
5139
  },
 
 
 
 
 
 
 
 
 
 
 
 
5140
  "node_modules/restore-cursor": {
5141
  "version": "2.0.0",
5142
  "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz",
@@ -5299,12 +6041,27 @@
5299
  "node": ">= 0.8"
5300
  }
5301
  },
 
 
 
 
 
 
5302
  "node_modules/setprototypeof": {
5303
  "version": "1.2.0",
5304
  "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
5305
  "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
5306
  "license": "ISC"
5307
  },
 
 
 
 
 
 
 
 
 
5308
  "node_modules/shebang-command": {
5309
  "version": "2.0.0",
5310
  "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
@@ -5475,6 +6232,12 @@
5475
  "integrity": "sha512-0MP/Cxx5SzeeZ10p/bZI0S6MpgD+yxAhi1BOQ34jgnMXsCq3j1t6tQnZu+KdlL7dvJTLT3g9xN8tl10TqgFMcg==",
5476
  "license": "MIT"
5477
  },
 
 
 
 
 
 
5478
  "node_modules/supports-color": {
5479
  "version": "7.2.0",
5480
  "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
@@ -5636,6 +6399,12 @@
5636
  "integrity": "sha512-FWAPzCIHZHnrE/5/w9MPk0kK25hSQSH2IKhYh9PyjS3SG/+IEMvlwIHbhz+oF7xl54I+ueZlVnMjyzdSwLmAwA==",
5637
  "license": "MIT"
5638
  },
 
 
 
 
 
 
5639
  "node_modules/type-fest": {
5640
  "version": "0.7.1",
5641
  "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.7.1.tgz",
@@ -5659,6 +6428,32 @@
5659
  "node": ">=14.17"
5660
  }
5661
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5662
  "node_modules/undici-types": {
5663
  "version": "8.3.0",
5664
  "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.3.0.tgz",
@@ -5805,12 +6600,28 @@
5805
  "defaults": "^1.0.3"
5806
  }
5807
  },
 
 
 
 
 
 
5808
  "node_modules/whatwg-fetch": {
5809
  "version": "3.6.20",
5810
  "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz",
5811
  "integrity": "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==",
5812
  "license": "MIT"
5813
  },
 
 
 
 
 
 
 
 
 
 
5814
  "node_modules/whatwg-url-minimum": {
5815
  "version": "0.1.2",
5816
  "resolved": "https://registry.npmjs.org/whatwg-url-minimum/-/whatwg-url-minimum-0.1.2.tgz",
@@ -5849,6 +6660,12 @@
5849
  "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
5850
  }
5851
  },
 
 
 
 
 
 
5852
  "node_modules/ws": {
5853
  "version": "7.5.12",
5854
  "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.12.tgz",
 
8
  "name": "keystone-app",
9
  "version": "1.0.0",
10
  "dependencies": {
11
+ "@expo/metro-runtime": "~57.0.5",
12
+ "@expo/ngrok": "^4.1.3",
13
+ "@react-native-async-storage/async-storage": "2.2.0",
14
+ "date-fns": "^4.4.0",
15
  "expo": "~57.0.6",
16
+ "expo-file-system": "~57.0.1",
17
+ "expo-image": "~57.0.1",
18
+ "expo-media-library": "~57.0.2",
19
  "expo-status-bar": "~57.0.1",
20
  "react": "19.2.3",
21
+ "react-dom": "19.2.3",
22
+ "react-native": "0.86.0",
23
+ "react-native-web": "^0.21.2"
24
  },
25
  "devDependencies": {
26
  "@types/react": "~19.2.2",
 
1166
  }
1167
  }
1168
  },
1169
+ "node_modules/@expo/dom-webview": {
1170
+ "version": "57.0.1",
1171
+ "resolved": "https://registry.npmjs.org/@expo/dom-webview/-/dom-webview-57.0.1.tgz",
1172
+ "integrity": "sha512-lAKsME4SAq+8sf56oN0DX5TBYyruupoRxbWbD2xf9RnKY8y6x8eb9LCE5pxSN0qyWdqnp+0wmyWzDkKboThKAw==",
1173
+ "license": "MIT",
1174
+ "peerDependencies": {
1175
+ "expo": "*",
1176
+ "react": "*",
1177
+ "react-native": "*"
1178
+ }
1179
+ },
1180
  "node_modules/@expo/env": {
1181
  "version": "2.4.2",
1182
  "resolved": "https://registry.npmjs.org/@expo/env/-/env-2.4.2.tgz",
 
1263
  "chalk": "^4.1.2"
1264
  }
1265
  },
1266
+ "node_modules/@expo/log-box": {
1267
+ "version": "57.0.1",
1268
+ "resolved": "https://registry.npmjs.org/@expo/log-box/-/log-box-57.0.1.tgz",
1269
+ "integrity": "sha512-fuVNHhOerdRWtpq27gD6JTSVYESsfRu+SMdrNCWxW+gFnusS6dGKfx3lKGBZ4ZkMNiLWn8maBHo39YKzJNXFYQ==",
1270
+ "license": "MIT",
1271
+ "dependencies": {
1272
+ "@expo/dom-webview": "^57.0.1",
1273
+ "anser": "^1.4.9",
1274
+ "stacktrace-parser": "^0.1.10"
1275
+ },
1276
+ "peerDependencies": {
1277
+ "@expo/dom-webview": "^57.0.1",
1278
+ "expo": "*",
1279
+ "react": "*",
1280
+ "react-native": "*"
1281
+ }
1282
+ },
1283
  "node_modules/@expo/metro": {
1284
  "version": "56.0.0",
1285
  "resolved": "https://registry.npmjs.org/@expo/metro/-/metro-56.0.0.tgz",
 
1316
  "walker": "^1.0.8"
1317
  }
1318
  },
1319
+ "node_modules/@expo/metro-runtime": {
1320
+ "version": "57.0.5",
1321
+ "resolved": "https://registry.npmjs.org/@expo/metro-runtime/-/metro-runtime-57.0.5.tgz",
1322
+ "integrity": "sha512-OBHbGgLeDROnoeWO2td9ZS9EFC5ADzlXaAVM4sdYDe8vrmOb4rIuSrwBSjnkmr7svwzqAhX0hg089A1bQvOu7g==",
1323
+ "license": "MIT",
1324
+ "dependencies": {
1325
+ "@expo/log-box": "^57.0.1",
1326
+ "anser": "^1.4.9",
1327
+ "pretty-format": "^29.7.0",
1328
+ "stacktrace-parser": "^0.1.10",
1329
+ "whatwg-fetch": "^3.0.0"
1330
+ },
1331
+ "peerDependencies": {
1332
+ "@expo/log-box": "^57.0.1",
1333
+ "expo": "*",
1334
+ "react": "*",
1335
+ "react-dom": "*",
1336
+ "react-native": "*"
1337
+ },
1338
+ "peerDependenciesMeta": {
1339
+ "react-dom": {
1340
+ "optional": true
1341
+ }
1342
+ }
1343
+ },
1344
+ "node_modules/@expo/ngrok": {
1345
+ "version": "4.1.3",
1346
+ "resolved": "https://registry.npmjs.org/@expo/ngrok/-/ngrok-4.1.3.tgz",
1347
+ "integrity": "sha512-AESYaROGIGKWwWmUyQoUXcbvaUZjmpecC5buArXxYou+RID813F8T0Y5jQ2HUY49mZpYfJiy9oh4VSN37GgrXA==",
1348
+ "license": "BSD-2-Clause",
1349
+ "dependencies": {
1350
+ "@expo/ngrok-bin": "2.3.42",
1351
+ "got": "^11.5.1",
1352
+ "uuid": "^3.3.2",
1353
+ "yaml": "^1.10.0"
1354
+ },
1355
+ "engines": {
1356
+ "node": ">=10.19.0"
1357
+ }
1358
+ },
1359
+ "node_modules/@expo/ngrok-bin": {
1360
+ "version": "2.3.42",
1361
+ "resolved": "https://registry.npmjs.org/@expo/ngrok-bin/-/ngrok-bin-2.3.42.tgz",
1362
+ "integrity": "sha512-kyhORGwv9XpbPeNIrX6QZ9wDVCDOScyTwxeS+ScNmUqYoZqD9LRmEqF7bpDh5VonTsrXgWrGl7wD2++oSHcaTQ==",
1363
+ "bin": {
1364
+ "ngrok": "bin/ngrok.js"
1365
+ },
1366
+ "optionalDependencies": {
1367
+ "@expo/ngrok-bin-darwin-arm64": "2.3.41",
1368
+ "@expo/ngrok-bin-darwin-x64": "2.3.41",
1369
+ "@expo/ngrok-bin-freebsd-ia32": "2.3.41",
1370
+ "@expo/ngrok-bin-freebsd-x64": "2.3.41",
1371
+ "@expo/ngrok-bin-linux-arm": "2.3.41",
1372
+ "@expo/ngrok-bin-linux-arm64": "2.3.41",
1373
+ "@expo/ngrok-bin-linux-ia32": "2.3.41",
1374
+ "@expo/ngrok-bin-linux-x64": "2.3.41",
1375
+ "@expo/ngrok-bin-sunos-x64": "2.3.41",
1376
+ "@expo/ngrok-bin-win32-ia32": "2.3.41",
1377
+ "@expo/ngrok-bin-win32-x64": "2.3.41"
1378
+ }
1379
+ },
1380
+ "node_modules/@expo/ngrok-bin-darwin-arm64": {
1381
+ "version": "2.3.41",
1382
+ "resolved": "https://registry.npmjs.org/@expo/ngrok-bin-darwin-arm64/-/ngrok-bin-darwin-arm64-2.3.41.tgz",
1383
+ "integrity": "sha512-TPf95xp6SkvbRONZjltTOFcCJbmzAH7lrQ36Dv+djrOckWGPVq4HCur48YAeiGDqspmFEmqZ7ykD5c/bDfRFOA==",
1384
+ "cpu": [
1385
+ "arm64"
1386
+ ],
1387
+ "optional": true,
1388
+ "os": [
1389
+ "darwin"
1390
+ ]
1391
+ },
1392
+ "node_modules/@expo/ngrok-bin-darwin-x64": {
1393
+ "version": "2.3.41",
1394
+ "resolved": "https://registry.npmjs.org/@expo/ngrok-bin-darwin-x64/-/ngrok-bin-darwin-x64-2.3.41.tgz",
1395
+ "integrity": "sha512-29QZHfX4Ec0p0pQF5UrqiP2/Qe7t2rI96o+5b8045VCEl9AEAKHceGuyo+jfUDR4FSQBGFLSDb06xy8ghL3ZYA==",
1396
+ "cpu": [
1397
+ "x64"
1398
+ ],
1399
+ "optional": true,
1400
+ "os": [
1401
+ "darwin"
1402
+ ]
1403
+ },
1404
+ "node_modules/@expo/ngrok-bin-freebsd-ia32": {
1405
+ "version": "2.3.41",
1406
+ "resolved": "https://registry.npmjs.org/@expo/ngrok-bin-freebsd-ia32/-/ngrok-bin-freebsd-ia32-2.3.41.tgz",
1407
+ "integrity": "sha512-YYXgwNZ+p0aIrwgb+1/RxJbsWhGEzBDBhZulKg1VB7tKDAd2C8uGnbK1rOCuZy013iOUsJDXaj9U5QKc13iIXw==",
1408
+ "cpu": [
1409
+ "ia32"
1410
+ ],
1411
+ "optional": true,
1412
+ "os": [
1413
+ "freebsd"
1414
+ ]
1415
+ },
1416
+ "node_modules/@expo/ngrok-bin-freebsd-x64": {
1417
+ "version": "2.3.41",
1418
+ "resolved": "https://registry.npmjs.org/@expo/ngrok-bin-freebsd-x64/-/ngrok-bin-freebsd-x64-2.3.41.tgz",
1419
+ "integrity": "sha512-1Ei6K8BB+3etmmBT0tXYC4dyVkJMigT4ELbRTF5jKfw1pblqeXM9Qpf3p8851PTlH142S3bockCeO39rSkOnkg==",
1420
+ "cpu": [
1421
+ "x64"
1422
+ ],
1423
+ "optional": true,
1424
+ "os": [
1425
+ "freebsd"
1426
+ ]
1427
+ },
1428
+ "node_modules/@expo/ngrok-bin-linux-arm": {
1429
+ "version": "2.3.41",
1430
+ "resolved": "https://registry.npmjs.org/@expo/ngrok-bin-linux-arm/-/ngrok-bin-linux-arm-2.3.41.tgz",
1431
+ "integrity": "sha512-B6+rW/+tEi7ZrKWQGkRzlwmKo7c1WJhNODFBSgkF/Sj9PmmNhBz67mer91S2+6nNt5pfcwLLd61CjtWfR1LUHQ==",
1432
+ "cpu": [
1433
+ "arm"
1434
+ ],
1435
+ "optional": true,
1436
+ "os": [
1437
+ "linux"
1438
+ ]
1439
+ },
1440
+ "node_modules/@expo/ngrok-bin-linux-arm64": {
1441
+ "version": "2.3.41",
1442
+ "resolved": "https://registry.npmjs.org/@expo/ngrok-bin-linux-arm64/-/ngrok-bin-linux-arm64-2.3.41.tgz",
1443
+ "integrity": "sha512-eC8GA/xPcmQJy4h+g2FlkuQB3lf5DjITy8Y6GyydmPYMByjUYAGEXe0brOcP893aalAzRqbNOAjSuAw1lcCLSQ==",
1444
+ "cpu": [
1445
+ "arm64"
1446
+ ],
1447
+ "optional": true,
1448
+ "os": [
1449
+ "linux"
1450
+ ]
1451
+ },
1452
+ "node_modules/@expo/ngrok-bin-linux-ia32": {
1453
+ "version": "2.3.41",
1454
+ "resolved": "https://registry.npmjs.org/@expo/ngrok-bin-linux-ia32/-/ngrok-bin-linux-ia32-2.3.41.tgz",
1455
+ "integrity": "sha512-w5Cy31wSz4jYnygEHS7eRizR1yt8s9TX6kHlkjzayIiRTFRb2E1qD2l0/4T2w0LJpBjM5ZFPaaKqsNWgCUIEow==",
1456
+ "cpu": [
1457
+ "ia32"
1458
+ ],
1459
+ "optional": true,
1460
+ "os": [
1461
+ "linux"
1462
+ ]
1463
+ },
1464
+ "node_modules/@expo/ngrok-bin-linux-x64": {
1465
+ "version": "2.3.41",
1466
+ "resolved": "https://registry.npmjs.org/@expo/ngrok-bin-linux-x64/-/ngrok-bin-linux-x64-2.3.41.tgz",
1467
+ "integrity": "sha512-LcU3MbYHv7Sn2eFz8Yzo2rXduufOvX1/hILSirwCkH+9G8PYzpwp2TeGqVWuO+EmvtBe6NEYwgdQjJjN6I4L1A==",
1468
+ "cpu": [
1469
+ "x64"
1470
+ ],
1471
+ "optional": true,
1472
+ "os": [
1473
+ "linux"
1474
+ ]
1475
+ },
1476
+ "node_modules/@expo/ngrok-bin-sunos-x64": {
1477
+ "version": "2.3.41",
1478
+ "resolved": "https://registry.npmjs.org/@expo/ngrok-bin-sunos-x64/-/ngrok-bin-sunos-x64-2.3.41.tgz",
1479
+ "integrity": "sha512-bcOj45BLhiV2PayNmLmEVZlFMhEiiGpOr36BXC0XSL+cHUZHd6uNaS28AaZdz95lrRzGpeb0hAF8cuJjo6nq4g==",
1480
+ "cpu": [
1481
+ "x64"
1482
+ ],
1483
+ "optional": true,
1484
+ "os": [
1485
+ "sunos"
1486
+ ]
1487
+ },
1488
+ "node_modules/@expo/ngrok-bin-win32-ia32": {
1489
+ "version": "2.3.41",
1490
+ "resolved": "https://registry.npmjs.org/@expo/ngrok-bin-win32-ia32/-/ngrok-bin-win32-ia32-2.3.41.tgz",
1491
+ "integrity": "sha512-0+vPbKvUA+a9ERgiAknmZCiWA3AnM5c6beI+51LqmjKEM4iAAlDmfXNJ89aAbvZMUtBNwEPHzJHnaM4s2SeBhA==",
1492
+ "cpu": [
1493
+ "ia32"
1494
+ ],
1495
+ "optional": true,
1496
+ "os": [
1497
+ "win32"
1498
+ ]
1499
+ },
1500
+ "node_modules/@expo/ngrok-bin-win32-x64": {
1501
+ "version": "2.3.41",
1502
+ "resolved": "https://registry.npmjs.org/@expo/ngrok-bin-win32-x64/-/ngrok-bin-win32-x64-2.3.41.tgz",
1503
+ "integrity": "sha512-mncsPRaG462LiYrM8mQT8OYe3/i44m3N/NzUeieYpGi8+pCOo8TIC23kR9P93CVkbM9mmXsy3X6hq91a8FWBdA==",
1504
+ "cpu": [
1505
+ "x64"
1506
+ ],
1507
+ "optional": true,
1508
+ "os": [
1509
+ "win32"
1510
+ ]
1511
+ },
1512
+ "node_modules/@expo/ngrok/node_modules/uuid": {
1513
+ "version": "3.4.0",
1514
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
1515
+ "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
1516
+ "deprecated": "uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028).",
1517
+ "license": "MIT",
1518
+ "bin": {
1519
+ "uuid": "bin/uuid"
1520
+ }
1521
+ },
1522
+ "node_modules/@expo/ngrok/node_modules/yaml": {
1523
+ "version": "1.10.3",
1524
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.3.tgz",
1525
+ "integrity": "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==",
1526
+ "license": "ISC",
1527
+ "engines": {
1528
+ "node": ">= 6"
1529
+ }
1530
+ },
1531
  "node_modules/@expo/osascript": {
1532
  "version": "2.7.1",
1533
  "resolved": "https://registry.npmjs.org/@expo/osascript/-/osascript-2.7.1.tgz",
 
1739
  "@jridgewell/sourcemap-codec": "^1.4.14"
1740
  }
1741
  },
1742
+ "node_modules/@react-native-async-storage/async-storage": {
1743
+ "version": "2.2.0",
1744
+ "resolved": "https://registry.npmjs.org/@react-native-async-storage/async-storage/-/async-storage-2.2.0.tgz",
1745
+ "integrity": "sha512-gvRvjR5JAaUZF8tv2Kcq/Gbt3JHwbKFYfmb445rhOj6NUMx3qPLixmDx5pZAyb9at1bYvJ4/eTUipU5aki45xw==",
1746
+ "license": "MIT",
1747
+ "dependencies": {
1748
+ "merge-options": "^3.0.4"
1749
+ },
1750
+ "peerDependencies": {
1751
+ "react-native": "^0.0.0-0 || >=0.65 <1.0"
1752
+ }
1753
+ },
1754
  "node_modules/@react-native/assets-registry": {
1755
  "version": "0.86.0",
1756
  "resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.86.0.tgz",
 
1938
  "integrity": "sha512-hhyNJ+nbR6ZR7pToHvllEFun9TL0sbL+tk/ON75lo+Xas054uez98qRbsuNt7MBCyZKK4+8Yli/OAGZhmfBZ/g==",
1939
  "license": "MIT"
1940
  },
1941
+ "node_modules/@sindresorhus/is": {
1942
+ "version": "4.6.0",
1943
+ "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz",
1944
+ "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==",
1945
+ "license": "MIT",
1946
+ "engines": {
1947
+ "node": ">=10"
1948
+ },
1949
+ "funding": {
1950
+ "url": "https://github.com/sindresorhus/is?sponsor=1"
1951
+ }
1952
+ },
1953
+ "node_modules/@szmarczak/http-timer": {
1954
+ "version": "4.0.6",
1955
+ "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz",
1956
+ "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==",
1957
+ "license": "MIT",
1958
+ "dependencies": {
1959
+ "defer-to-connect": "^2.0.0"
1960
+ },
1961
+ "engines": {
1962
+ "node": ">=10"
1963
+ }
1964
+ },
1965
+ "node_modules/@types/cacheable-request": {
1966
+ "version": "6.0.3",
1967
+ "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz",
1968
+ "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==",
1969
+ "license": "MIT",
1970
+ "dependencies": {
1971
+ "@types/http-cache-semantics": "*",
1972
+ "@types/keyv": "^3.1.4",
1973
+ "@types/node": "*",
1974
+ "@types/responselike": "^1.0.0"
1975
+ }
1976
+ },
1977
+ "node_modules/@types/http-cache-semantics": {
1978
+ "version": "4.2.0",
1979
+ "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz",
1980
+ "integrity": "sha512-L3LgimLHXtGkWikKnsPg0/VFx9OGZaC+eN1u4r+OB1XRqH3meBIAVC2zr1WdMH+RHmnRkqliQAOHNJ/E0j/e0Q==",
1981
+ "license": "MIT"
1982
+ },
1983
  "node_modules/@types/istanbul-lib-coverage": {
1984
  "version": "2.0.6",
1985
  "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz",
 
2004
  "@types/istanbul-lib-report": "*"
2005
  }
2006
  },
2007
+ "node_modules/@types/keyv": {
2008
+ "version": "3.1.4",
2009
+ "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz",
2010
+ "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==",
2011
+ "license": "MIT",
2012
+ "dependencies": {
2013
+ "@types/node": "*"
2014
+ }
2015
+ },
2016
  "node_modules/@types/node": {
2017
  "version": "26.1.1",
2018
  "resolved": "https://registry.npmjs.org/@types/node/-/node-26.1.1.tgz",
 
2032
  "csstype": "^3.2.2"
2033
  }
2034
  },
2035
+ "node_modules/@types/responselike": {
2036
+ "version": "1.0.3",
2037
+ "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz",
2038
+ "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==",
2039
+ "license": "MIT",
2040
+ "dependencies": {
2041
+ "@types/node": "*"
2042
+ }
2043
+ },
2044
  "node_modules/@types/yargs": {
2045
  "version": "17.0.35",
2046
  "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz",
 
2452
  "node": ">= 0.8"
2453
  }
2454
  },
2455
+ "node_modules/cacheable-lookup": {
2456
+ "version": "5.0.4",
2457
+ "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz",
2458
+ "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==",
2459
+ "license": "MIT",
2460
+ "engines": {
2461
+ "node": ">=10.6.0"
2462
+ }
2463
+ },
2464
+ "node_modules/cacheable-request": {
2465
+ "version": "7.0.4",
2466
+ "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz",
2467
+ "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==",
2468
+ "license": "MIT",
2469
+ "dependencies": {
2470
+ "clone-response": "^1.0.2",
2471
+ "get-stream": "^5.1.0",
2472
+ "http-cache-semantics": "^4.0.0",
2473
+ "keyv": "^4.0.0",
2474
+ "lowercase-keys": "^2.0.0",
2475
+ "normalize-url": "^6.0.1",
2476
+ "responselike": "^2.0.0"
2477
+ },
2478
+ "engines": {
2479
+ "node": ">=8"
2480
+ }
2481
+ },
2482
  "node_modules/camelcase": {
2483
  "version": "6.3.0",
2484
  "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
 
2611
  "node": ">=0.8"
2612
  }
2613
  },
2614
+ "node_modules/clone-response": {
2615
+ "version": "1.0.3",
2616
+ "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz",
2617
+ "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==",
2618
+ "license": "MIT",
2619
+ "dependencies": {
2620
+ "mimic-response": "^1.0.0"
2621
+ },
2622
+ "funding": {
2623
+ "url": "https://github.com/sponsors/sindresorhus"
2624
+ }
2625
+ },
2626
  "node_modules/color-convert": {
2627
  "version": "2.0.1",
2628
  "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
 
2753
  "url": "https://opencollective.com/core-js"
2754
  }
2755
  },
2756
+ "node_modules/cross-fetch": {
2757
+ "version": "3.2.0",
2758
+ "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.2.0.tgz",
2759
+ "integrity": "sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q==",
2760
+ "license": "MIT",
2761
+ "dependencies": {
2762
+ "node-fetch": "^2.7.0"
2763
+ }
2764
+ },
2765
  "node_modules/cross-spawn": {
2766
  "version": "7.0.6",
2767
  "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
 
2776
  "node": ">= 8"
2777
  }
2778
  },
2779
+ "node_modules/css-in-js-utils": {
2780
+ "version": "3.1.0",
2781
+ "resolved": "https://registry.npmjs.org/css-in-js-utils/-/css-in-js-utils-3.1.0.tgz",
2782
+ "integrity": "sha512-fJAcud6B3rRu+KHYk+Bwf+WFL2MDCJJ1XG9x137tJQ0xYxor7XziQtuGFbWNdqrvF4Tk26O3H73nfVqXt/fW1A==",
2783
+ "license": "MIT",
2784
+ "dependencies": {
2785
+ "hyphenate-style-name": "^1.0.3"
2786
+ }
2787
+ },
2788
  "node_modules/csstype": {
2789
  "version": "3.2.3",
2790
  "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
 
2792
  "devOptional": true,
2793
  "license": "MIT"
2794
  },
2795
+ "node_modules/date-fns": {
2796
+ "version": "4.4.0",
2797
+ "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.4.0.tgz",
2798
+ "integrity": "sha512-+1UMbeh68lH1SegH83CGWwpb6OHHbpSgr3+s5Eww5M4CAgswBpoWS0AjTOfEJ33HiYKz1hdj/KTFprzXHmq/6w==",
2799
+ "license": "MIT",
2800
+ "funding": {
2801
+ "type": "github",
2802
+ "url": "https://github.com/sponsors/kossnocorp"
2803
+ }
2804
+ },
2805
  "node_modules/debug": {
2806
  "version": "4.4.3",
2807
  "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
 
2819
  }
2820
  }
2821
  },
2822
+ "node_modules/decompress-response": {
2823
+ "version": "6.0.0",
2824
+ "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
2825
+ "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
2826
+ "license": "MIT",
2827
+ "dependencies": {
2828
+ "mimic-response": "^3.1.0"
2829
+ },
2830
+ "engines": {
2831
+ "node": ">=10"
2832
+ },
2833
+ "funding": {
2834
+ "url": "https://github.com/sponsors/sindresorhus"
2835
+ }
2836
+ },
2837
+ "node_modules/decompress-response/node_modules/mimic-response": {
2838
+ "version": "3.1.0",
2839
+ "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
2840
+ "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==",
2841
+ "license": "MIT",
2842
+ "engines": {
2843
+ "node": ">=10"
2844
+ },
2845
+ "funding": {
2846
+ "url": "https://github.com/sponsors/sindresorhus"
2847
+ }
2848
+ },
2849
  "node_modules/deepmerge": {
2850
  "version": "4.3.1",
2851
  "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
 
2867
  "url": "https://github.com/sponsors/sindresorhus"
2868
  }
2869
  },
2870
+ "node_modules/defer-to-connect": {
2871
+ "version": "2.0.1",
2872
+ "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz",
2873
+ "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==",
2874
+ "license": "MIT",
2875
+ "engines": {
2876
+ "node": ">=10"
2877
+ }
2878
+ },
2879
  "node_modules/depd": {
2880
  "version": "2.0.0",
2881
  "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
 
2937
  "node": ">= 0.8"
2938
  }
2939
  },
2940
+ "node_modules/end-of-stream": {
2941
+ "version": "1.4.5",
2942
+ "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz",
2943
+ "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==",
2944
+ "license": "MIT",
2945
+ "dependencies": {
2946
+ "once": "^1.4.0"
2947
+ }
2948
+ },
2949
  "node_modules/error-stack-parser": {
2950
  "version": "2.1.4",
2951
  "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz",
 
3065
  },
3066
  "react-native-web": {
3067
  "optional": true
3068
+ },
3069
+ "react-native-webview": {
3070
+ "optional": true
3071
+ }
3072
+ }
3073
+ },
3074
+ "node_modules/expo-file-system": {
3075
+ "version": "57.0.1",
3076
+ "resolved": "https://registry.npmjs.org/expo-file-system/-/expo-file-system-57.0.1.tgz",
3077
+ "integrity": "sha512-w7/ERvQFrGP2apTO9lDtZ+O6JQIhfakL7+Xqzh+rfMO9B4LB4qwrz+YvLgir8KFRVX64JHBnRuYBVLY1oQZcqw==",
3078
+ "license": "MIT",
3079
+ "peerDependencies": {
3080
+ "expo": "*",
3081
+ "react-native": "*"
3082
+ }
3083
+ },
3084
+ "node_modules/expo-image": {
3085
+ "version": "57.0.1",
3086
+ "resolved": "https://registry.npmjs.org/expo-image/-/expo-image-57.0.1.tgz",
3087
+ "integrity": "sha512-EP0lisd2bUqtErry4weRcMW9bLMxtKsht/MLLK3/3do5u4ZMiJbWkY5zfYV+WYmeGab7x9G0sjbeFeEagYGjMw==",
3088
+ "license": "MIT",
3089
+ "dependencies": {
3090
+ "sf-symbols-typescript": "^2.2.0"
3091
+ },
3092
+ "peerDependencies": {
3093
+ "expo": "*",
3094
+ "react": "*",
3095
+ "react-native": "*",
3096
+ "react-native-web": "*"
3097
+ },
3098
+ "peerDependenciesMeta": {
3099
+ "react-native-web": {
3100
+ "optional": true
3101
  }
3102
  }
3103
  },
3104
+ "node_modules/expo-media-library": {
3105
+ "version": "57.0.2",
3106
+ "resolved": "https://registry.npmjs.org/expo-media-library/-/expo-media-library-57.0.2.tgz",
3107
+ "integrity": "sha512-rgcOHdshQ9sXPeNchUbs7k/tm4wy0qGJawttmoqLqDteFLlLpGuWkcix7Ype7t5fMdgtquNRckM4vlCMVKgeqg==",
3108
+ "license": "MIT",
3109
+ "peerDependencies": {
3110
+ "expo": "*",
3111
+ "react-native": "*"
3112
+ }
3113
+ },
3114
  "node_modules/expo-modules-autolinking": {
3115
  "version": "57.0.7",
3116
  "resolved": "https://registry.npmjs.org/expo-modules-autolinking/-/expo-modules-autolinking-57.0.7.tgz",
 
3292
  }
3293
  }
3294
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3295
  "node_modules/expo/node_modules/@expo/metro-config": {
3296
  "version": "57.0.5",
3297
  "resolved": "https://registry.npmjs.org/@expo/metro-config/-/metro-config-57.0.5.tgz",
 
3463
  "react-native": "*"
3464
  }
3465
  },
 
 
 
 
 
 
 
 
 
 
3466
  "node_modules/expo/node_modules/expo-font": {
3467
  "version": "57.0.1",
3468
  "resolved": "https://registry.npmjs.org/expo-font/-/expo-font-57.0.1.tgz",
 
3592
  "bser": "2.1.1"
3593
  }
3594
  },
3595
+ "node_modules/fbjs": {
3596
+ "version": "3.0.5",
3597
+ "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-3.0.5.tgz",
3598
+ "integrity": "sha512-ztsSx77JBtkuMrEypfhgc3cI0+0h+svqeie7xHbh1k/IKdcydnvadp/mUaGgjAOXQmQSxsqgaRhS3q9fy+1kxg==",
3599
+ "license": "MIT",
3600
+ "dependencies": {
3601
+ "cross-fetch": "^3.1.5",
3602
+ "fbjs-css-vars": "^1.0.0",
3603
+ "loose-envify": "^1.0.0",
3604
+ "object-assign": "^4.1.0",
3605
+ "promise": "^7.1.1",
3606
+ "setimmediate": "^1.0.5",
3607
+ "ua-parser-js": "^1.0.35"
3608
+ }
3609
+ },
3610
+ "node_modules/fbjs-css-vars": {
3611
+ "version": "1.0.2",
3612
+ "resolved": "https://registry.npmjs.org/fbjs-css-vars/-/fbjs-css-vars-1.0.2.tgz",
3613
+ "integrity": "sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ==",
3614
+ "license": "MIT"
3615
+ },
3616
+ "node_modules/fbjs/node_modules/promise": {
3617
+ "version": "7.3.1",
3618
+ "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz",
3619
+ "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==",
3620
+ "license": "MIT",
3621
+ "dependencies": {
3622
+ "asap": "~2.0.3"
3623
+ }
3624
+ },
3625
  "node_modules/fetch-nodeshim": {
3626
  "version": "0.4.10",
3627
  "resolved": "https://registry.npmjs.org/fetch-nodeshim/-/fetch-nodeshim-0.4.10.tgz",
 
3721
  "node": "6.* || 8.* || >= 10.*"
3722
  }
3723
  },
3724
+ "node_modules/get-stream": {
3725
+ "version": "5.2.0",
3726
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
3727
+ "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
3728
+ "license": "MIT",
3729
+ "dependencies": {
3730
+ "pump": "^3.0.0"
3731
+ },
3732
+ "engines": {
3733
+ "node": ">=8"
3734
+ },
3735
+ "funding": {
3736
+ "url": "https://github.com/sponsors/sindresorhus"
3737
+ }
3738
+ },
3739
  "node_modules/getenv": {
3740
  "version": "2.0.0",
3741
  "resolved": "https://registry.npmjs.org/getenv/-/getenv-2.0.0.tgz",
 
3762
  "url": "https://github.com/sponsors/isaacs"
3763
  }
3764
  },
3765
+ "node_modules/got": {
3766
+ "version": "11.8.6",
3767
+ "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz",
3768
+ "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==",
3769
+ "license": "MIT",
3770
+ "dependencies": {
3771
+ "@sindresorhus/is": "^4.0.0",
3772
+ "@szmarczak/http-timer": "^4.0.5",
3773
+ "@types/cacheable-request": "^6.0.1",
3774
+ "@types/responselike": "^1.0.0",
3775
+ "cacheable-lookup": "^5.0.3",
3776
+ "cacheable-request": "^7.0.2",
3777
+ "decompress-response": "^6.0.0",
3778
+ "http2-wrapper": "^1.0.0-beta.5.2",
3779
+ "lowercase-keys": "^2.0.0",
3780
+ "p-cancelable": "^2.0.0",
3781
+ "responselike": "^2.0.0"
3782
+ },
3783
+ "engines": {
3784
+ "node": ">=10.19.0"
3785
+ },
3786
+ "funding": {
3787
+ "url": "https://github.com/sindresorhus/got?sponsor=1"
3788
+ }
3789
+ },
3790
  "node_modules/graceful-fs": {
3791
  "version": "4.2.11",
3792
  "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
 
3853
  "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
3854
  "license": "ISC"
3855
  },
3856
+ "node_modules/http-cache-semantics": {
3857
+ "version": "4.2.0",
3858
+ "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz",
3859
+ "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==",
3860
+ "license": "BSD-2-Clause"
3861
+ },
3862
  "node_modules/http-errors": {
3863
  "version": "2.0.1",
3864
  "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
 
3888
  "node": ">= 0.8"
3889
  }
3890
  },
3891
+ "node_modules/http2-wrapper": {
3892
+ "version": "1.0.3",
3893
+ "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz",
3894
+ "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==",
3895
+ "license": "MIT",
3896
+ "dependencies": {
3897
+ "quick-lru": "^5.1.1",
3898
+ "resolve-alpn": "^1.0.0"
3899
+ },
3900
+ "engines": {
3901
+ "node": ">=10.19.0"
3902
+ }
3903
+ },
3904
  "node_modules/https-proxy-agent": {
3905
  "version": "7.0.6",
3906
  "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
 
3914
  "node": ">= 14"
3915
  }
3916
  },
3917
+ "node_modules/hyphenate-style-name": {
3918
+ "version": "1.1.0",
3919
+ "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.1.0.tgz",
3920
+ "integrity": "sha512-WDC/ui2VVRrz3jOVi+XtjqkDjiVjTtFaAGiW37k6b+ohyQ5wYDOGkvCZa8+H0nx3gyvv0+BST9xuOgIyGQ00gw==",
3921
+ "license": "BSD-3-Clause"
3922
+ },
3923
  "node_modules/ignore": {
3924
  "version": "5.3.2",
3925
  "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
 
3950
  "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
3951
  "license": "ISC"
3952
  },
3953
+ "node_modules/inline-style-prefixer": {
3954
+ "version": "7.0.1",
3955
+ "resolved": "https://registry.npmjs.org/inline-style-prefixer/-/inline-style-prefixer-7.0.1.tgz",
3956
+ "integrity": "sha512-lhYo5qNTQp3EvSSp3sRvXMbVQTLrvGV6DycRMJ5dm2BLMiJ30wpXKdDdgX+GmJZ5uQMucwRKHamXSst3Sj/Giw==",
3957
+ "license": "MIT",
3958
+ "dependencies": {
3959
+ "css-in-js-utils": "^3.1.0"
3960
+ }
3961
+ },
3962
  "node_modules/invariant": {
3963
  "version": "2.2.4",
3964
  "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
 
4016
  "node": ">=0.12.0"
4017
  }
4018
  },
4019
+ "node_modules/is-plain-obj": {
4020
+ "version": "2.1.0",
4021
+ "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz",
4022
+ "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==",
4023
+ "license": "MIT",
4024
+ "engines": {
4025
+ "node": ">=8"
4026
+ }
4027
+ },
4028
  "node_modules/is-wsl": {
4029
  "version": "2.2.0",
4030
  "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",
 
4183
  "node": ">=6"
4184
  }
4185
  },
4186
+ "node_modules/json-buffer": {
4187
+ "version": "3.0.1",
4188
+ "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
4189
+ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
4190
+ "license": "MIT"
4191
+ },
4192
  "node_modules/json5": {
4193
  "version": "2.2.3",
4194
  "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
 
4201
  "node": ">=6"
4202
  }
4203
  },
4204
+ "node_modules/keyv": {
4205
+ "version": "4.5.4",
4206
+ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
4207
+ "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
4208
+ "license": "MIT",
4209
+ "dependencies": {
4210
+ "json-buffer": "3.0.1"
4211
+ }
4212
+ },
4213
  "node_modules/kleur": {
4214
  "version": "3.0.3",
4215
  "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz",
 
4618
  "loose-envify": "cli.js"
4619
  }
4620
  },
4621
+ "node_modules/lowercase-keys": {
4622
+ "version": "2.0.0",
4623
+ "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz",
4624
+ "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==",
4625
+ "license": "MIT",
4626
+ "engines": {
4627
+ "node": ">=8"
4628
+ }
4629
+ },
4630
  "node_modules/lru-cache": {
4631
  "version": "5.1.1",
4632
  "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
 
4657
  "integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==",
4658
  "license": "MIT"
4659
  },
4660
+ "node_modules/merge-options": {
4661
+ "version": "3.0.4",
4662
+ "resolved": "https://registry.npmjs.org/merge-options/-/merge-options-3.0.4.tgz",
4663
+ "integrity": "sha512-2Sug1+knBjkaMsMgf1ctR1Ujx+Ayku4EdJN4Z+C2+JzoeF7A3OZ9KM2GY0CpQS51NR61LTurMJrRKPhSs3ZRTQ==",
4664
+ "license": "MIT",
4665
+ "dependencies": {
4666
+ "is-plain-obj": "^2.1.0"
4667
+ },
4668
+ "engines": {
4669
+ "node": ">=10"
4670
+ }
4671
+ },
4672
  "node_modules/merge-stream": {
4673
  "version": "2.0.0",
4674
  "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
 
5002
  "node": ">=4"
5003
  }
5004
  },
5005
+ "node_modules/mimic-response": {
5006
+ "version": "1.0.1",
5007
+ "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz",
5008
+ "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==",
5009
+ "license": "MIT",
5010
+ "engines": {
5011
+ "node": ">=4"
5012
+ }
5013
+ },
5014
  "node_modules/minimatch": {
5015
  "version": "10.2.5",
5016
  "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
 
5086
  "node": ">= 0.6"
5087
  }
5088
  },
5089
+ "node_modules/node-fetch": {
5090
+ "version": "2.7.0",
5091
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
5092
+ "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
5093
+ "license": "MIT",
5094
+ "dependencies": {
5095
+ "whatwg-url": "^5.0.0"
5096
+ },
5097
+ "engines": {
5098
+ "node": "4.x || >=6.0.0"
5099
+ },
5100
+ "peerDependencies": {
5101
+ "encoding": "^0.1.0"
5102
+ },
5103
+ "peerDependenciesMeta": {
5104
+ "encoding": {
5105
+ "optional": true
5106
+ }
5107
+ }
5108
+ },
5109
  "node_modules/node-forge": {
5110
  "version": "1.4.0",
5111
  "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.4.0.tgz",
 
5130
  "node": ">=18"
5131
  }
5132
  },
5133
+ "node_modules/normalize-url": {
5134
+ "version": "6.1.0",
5135
+ "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz",
5136
+ "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==",
5137
+ "license": "MIT",
5138
+ "engines": {
5139
+ "node": ">=10"
5140
+ },
5141
+ "funding": {
5142
+ "url": "https://github.com/sponsors/sindresorhus"
5143
+ }
5144
+ },
5145
  "node_modules/npm-package-arg": {
5146
  "version": "11.0.3",
5147
  "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-11.0.3.tgz",
 
5175
  "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0"
5176
  }
5177
  },
5178
+ "node_modules/object-assign": {
5179
+ "version": "4.1.1",
5180
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
5181
+ "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
5182
+ "license": "MIT",
5183
+ "engines": {
5184
+ "node": ">=0.10.0"
5185
+ }
5186
+ },
5187
  "node_modules/on-finished": {
5188
  "version": "2.3.0",
5189
  "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
 
5205
  "node": ">= 0.8"
5206
  }
5207
  },
5208
+ "node_modules/once": {
5209
+ "version": "1.4.0",
5210
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
5211
+ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
5212
+ "license": "ISC",
5213
+ "dependencies": {
5214
+ "wrappy": "1"
5215
+ }
5216
+ },
5217
  "node_modules/onetime": {
5218
  "version": "2.0.1",
5219
  "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz",
 
5351
  "node": ">=4"
5352
  }
5353
  },
5354
+ "node_modules/p-cancelable": {
5355
+ "version": "2.1.1",
5356
+ "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz",
5357
+ "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==",
5358
+ "license": "MIT",
5359
+ "engines": {
5360
+ "node": ">=8"
5361
+ }
5362
+ },
5363
  "node_modules/parse-png": {
5364
  "version": "2.1.0",
5365
  "resolved": "https://registry.npmjs.org/parse-png/-/parse-png-2.1.0.tgz",
 
5499
  "node": "^10 || ^12 || >=14"
5500
  }
5501
  },
5502
+ "node_modules/postcss-value-parser": {
5503
+ "version": "4.2.0",
5504
+ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
5505
+ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
5506
+ "license": "MIT"
5507
+ },
5508
  "node_modules/pretty-format": {
5509
  "version": "29.7.0",
5510
  "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz",
 
5571
  "node": ">= 6"
5572
  }
5573
  },
5574
+ "node_modules/pump": {
5575
+ "version": "3.0.4",
5576
+ "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz",
5577
+ "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==",
5578
+ "license": "MIT",
5579
+ "dependencies": {
5580
+ "end-of-stream": "^1.1.0",
5581
+ "once": "^1.3.1"
5582
+ }
5583
+ },
5584
  "node_modules/queue": {
5585
  "version": "6.0.2",
5586
  "resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz",
 
5590
  "inherits": "~2.0.3"
5591
  }
5592
  },
5593
+ "node_modules/quick-lru": {
5594
+ "version": "5.1.1",
5595
+ "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz",
5596
+ "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==",
5597
+ "license": "MIT",
5598
+ "engines": {
5599
+ "node": ">=10"
5600
+ },
5601
+ "funding": {
5602
+ "url": "https://github.com/sponsors/sindresorhus"
5603
+ }
5604
+ },
5605
  "node_modules/range-parser": {
5606
  "version": "1.2.1",
5607
  "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
 
5630
  "ws": "^7"
5631
  }
5632
  },
5633
+ "node_modules/react-dom": {
5634
+ "version": "19.2.3",
5635
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz",
5636
+ "integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==",
5637
+ "license": "MIT",
5638
+ "dependencies": {
5639
+ "scheduler": "^0.27.0"
5640
+ },
5641
+ "peerDependencies": {
5642
+ "react": "^19.2.3"
5643
+ }
5644
+ },
5645
  "node_modules/react-is": {
5646
  "version": "18.3.1",
5647
  "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
 
5707
  }
5708
  }
5709
  },
5710
+ "node_modules/react-native-web": {
5711
+ "version": "0.21.2",
5712
+ "resolved": "https://registry.npmjs.org/react-native-web/-/react-native-web-0.21.2.tgz",
5713
+ "integrity": "sha512-SO2t9/17zM4iEnFvlu2DA9jqNbzNhoUP+AItkoCOyFmDMOhUnBBznBDCYN92fGdfAkfQlWzPoez6+zLxFNsZEg==",
5714
+ "license": "MIT",
5715
+ "dependencies": {
5716
+ "@babel/runtime": "^7.18.6",
5717
+ "@react-native/normalize-colors": "^0.74.1",
5718
+ "fbjs": "^3.0.4",
5719
+ "inline-style-prefixer": "^7.0.1",
5720
+ "memoize-one": "^6.0.0",
5721
+ "nullthrows": "^1.1.1",
5722
+ "postcss-value-parser": "^4.2.0",
5723
+ "styleq": "^0.1.3"
5724
+ },
5725
+ "peerDependencies": {
5726
+ "react": "^18.0.0 || ^19.0.0",
5727
+ "react-dom": "^18.0.0 || ^19.0.0"
5728
+ }
5729
+ },
5730
+ "node_modules/react-native-web/node_modules/@react-native/normalize-colors": {
5731
+ "version": "0.74.89",
5732
+ "resolved": "https://registry.npmjs.org/@react-native/normalize-colors/-/normalize-colors-0.74.89.tgz",
5733
+ "integrity": "sha512-qoMMXddVKVhZ8PA1AbUCk83trpd6N+1nF2A6k1i6LsQObyS92fELuk8kU/lQs6M7BsMHwqyLCpQJ1uFgNvIQXg==",
5734
+ "license": "MIT"
5735
+ },
5736
+ "node_modules/react-native-web/node_modules/memoize-one": {
5737
+ "version": "6.0.0",
5738
+ "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz",
5739
+ "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==",
5740
+ "license": "MIT"
5741
+ },
5742
  "node_modules/react-native/node_modules/commander": {
5743
  "version": "12.1.0",
5744
  "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz",
 
5846
  "url": "https://github.com/sponsors/ljharb"
5847
  }
5848
  },
5849
+ "node_modules/resolve-alpn": {
5850
+ "version": "1.2.1",
5851
+ "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz",
5852
+ "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==",
5853
+ "license": "MIT"
5854
+ },
5855
  "node_modules/resolve-from": {
5856
  "version": "5.0.0",
5857
  "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
 
5867
  "integrity": "sha512-nR23LHAvaI6aHtMg6RWoaHpdR4D881Nydkzi2CixINyg9T00KgaJdJI6Vwty+Ps8WLxZHuxsS0BseWjxSA4C+w==",
5868
  "license": "MIT"
5869
  },
5870
+ "node_modules/responselike": {
5871
+ "version": "2.0.1",
5872
+ "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz",
5873
+ "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==",
5874
+ "license": "MIT",
5875
+ "dependencies": {
5876
+ "lowercase-keys": "^2.0.0"
5877
+ },
5878
+ "funding": {
5879
+ "url": "https://github.com/sponsors/sindresorhus"
5880
+ }
5881
+ },
5882
  "node_modules/restore-cursor": {
5883
  "version": "2.0.0",
5884
  "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz",
 
6041
  "node": ">= 0.8"
6042
  }
6043
  },
6044
+ "node_modules/setimmediate": {
6045
+ "version": "1.0.5",
6046
+ "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
6047
+ "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==",
6048
+ "license": "MIT"
6049
+ },
6050
  "node_modules/setprototypeof": {
6051
  "version": "1.2.0",
6052
  "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
6053
  "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
6054
  "license": "ISC"
6055
  },
6056
+ "node_modules/sf-symbols-typescript": {
6057
+ "version": "2.2.0",
6058
+ "resolved": "https://registry.npmjs.org/sf-symbols-typescript/-/sf-symbols-typescript-2.2.0.tgz",
6059
+ "integrity": "sha512-TPbeg0b7ylrswdGCji8FRGFAKuqbpQlLbL8SOle3j1iHSs5Ob5mhvMAxWN2UItOjgALAB5Zp3fmMfj8mbWvXKw==",
6060
+ "license": "MIT",
6061
+ "engines": {
6062
+ "node": ">=10"
6063
+ }
6064
+ },
6065
  "node_modules/shebang-command": {
6066
  "version": "2.0.0",
6067
  "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
 
6232
  "integrity": "sha512-0MP/Cxx5SzeeZ10p/bZI0S6MpgD+yxAhi1BOQ34jgnMXsCq3j1t6tQnZu+KdlL7dvJTLT3g9xN8tl10TqgFMcg==",
6233
  "license": "MIT"
6234
  },
6235
+ "node_modules/styleq": {
6236
+ "version": "0.1.3",
6237
+ "resolved": "https://registry.npmjs.org/styleq/-/styleq-0.1.3.tgz",
6238
+ "integrity": "sha512-3ZUifmCDCQanjeej1f6kyl/BeP/Vae5EYkQ9iJfUm/QwZvlgnZzyflqAsAWYURdtea8Vkvswu2GrC57h3qffcA==",
6239
+ "license": "MIT"
6240
+ },
6241
  "node_modules/supports-color": {
6242
  "version": "7.2.0",
6243
  "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
 
6399
  "integrity": "sha512-FWAPzCIHZHnrE/5/w9MPk0kK25hSQSH2IKhYh9PyjS3SG/+IEMvlwIHbhz+oF7xl54I+ueZlVnMjyzdSwLmAwA==",
6400
  "license": "MIT"
6401
  },
6402
+ "node_modules/tr46": {
6403
+ "version": "0.0.3",
6404
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
6405
+ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
6406
+ "license": "MIT"
6407
+ },
6408
  "node_modules/type-fest": {
6409
  "version": "0.7.1",
6410
  "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.7.1.tgz",
 
6428
  "node": ">=14.17"
6429
  }
6430
  },
6431
+ "node_modules/ua-parser-js": {
6432
+ "version": "1.0.41",
6433
+ "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.41.tgz",
6434
+ "integrity": "sha512-LbBDqdIC5s8iROCUjMbW1f5dJQTEFB1+KO9ogbvlb3nm9n4YHa5p4KTvFPWvh2Hs8gZMBuiB1/8+pdfe/tDPug==",
6435
+ "funding": [
6436
+ {
6437
+ "type": "opencollective",
6438
+ "url": "https://opencollective.com/ua-parser-js"
6439
+ },
6440
+ {
6441
+ "type": "paypal",
6442
+ "url": "https://paypal.me/faisalman"
6443
+ },
6444
+ {
6445
+ "type": "github",
6446
+ "url": "https://github.com/sponsors/faisalman"
6447
+ }
6448
+ ],
6449
+ "license": "MIT",
6450
+ "bin": {
6451
+ "ua-parser-js": "script/cli.js"
6452
+ },
6453
+ "engines": {
6454
+ "node": "*"
6455
+ }
6456
+ },
6457
  "node_modules/undici-types": {
6458
  "version": "8.3.0",
6459
  "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.3.0.tgz",
 
6600
  "defaults": "^1.0.3"
6601
  }
6602
  },
6603
+ "node_modules/webidl-conversions": {
6604
+ "version": "3.0.1",
6605
+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
6606
+ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
6607
+ "license": "BSD-2-Clause"
6608
+ },
6609
  "node_modules/whatwg-fetch": {
6610
  "version": "3.6.20",
6611
  "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz",
6612
  "integrity": "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==",
6613
  "license": "MIT"
6614
  },
6615
+ "node_modules/whatwg-url": {
6616
+ "version": "5.0.0",
6617
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
6618
+ "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
6619
+ "license": "MIT",
6620
+ "dependencies": {
6621
+ "tr46": "~0.0.3",
6622
+ "webidl-conversions": "^3.0.0"
6623
+ }
6624
+ },
6625
  "node_modules/whatwg-url-minimum": {
6626
  "version": "0.1.2",
6627
  "resolved": "https://registry.npmjs.org/whatwg-url-minimum/-/whatwg-url-minimum-0.1.2.tgz",
 
6660
  "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
6661
  }
6662
  },
6663
+ "node_modules/wrappy": {
6664
+ "version": "1.0.2",
6665
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
6666
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
6667
+ "license": "ISC"
6668
+ },
6669
  "node_modules/ws": {
6670
  "version": "7.5.12",
6671
  "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.12.tgz",
keystone-app/package.json CHANGED
@@ -3,10 +3,19 @@
3
  "version": "1.0.0",
4
  "main": "index.ts",
5
  "dependencies": {
 
 
 
 
6
  "expo": "~57.0.6",
 
 
 
7
  "expo-status-bar": "~57.0.1",
8
  "react": "19.2.3",
9
- "react-native": "0.86.0"
 
 
10
  },
11
  "devDependencies": {
12
  "@types/react": "~19.2.2",
 
3
  "version": "1.0.0",
4
  "main": "index.ts",
5
  "dependencies": {
6
+ "@expo/metro-runtime": "~57.0.5",
7
+ "@expo/ngrok": "^4.1.3",
8
+ "@react-native-async-storage/async-storage": "2.2.0",
9
+ "date-fns": "^4.4.0",
10
  "expo": "~57.0.6",
11
+ "expo-file-system": "~57.0.1",
12
+ "expo-image": "~57.0.1",
13
+ "expo-media-library": "~57.0.2",
14
  "expo-status-bar": "~57.0.1",
15
  "react": "19.2.3",
16
+ "react-dom": "19.2.3",
17
+ "react-native": "0.86.0",
18
+ "react-native-web": "^0.21.2"
19
  },
20
  "devDependencies": {
21
  "@types/react": "~19.2.2",
keystone-backend/main.py CHANGED
@@ -1,5 +1,6 @@
1
  import os
2
- from fastapi import FastAPI, HTTPException, Header, Depends, UploadFile, File
 
3
  from fastapi.middleware.cors import CORSMiddleware
4
  from pydantic import BaseModel
5
  from dotenv import load_dotenv
@@ -17,18 +18,13 @@ MOCK_TOKEN = "mock-jwt-token-xyz123"
17
  # --- SMART BUCKET STORAGE SETUP ---
18
  # If the root "/data" folder exists, Hugging Face mounted the bucket.
19
  if os.path.exists("/data"):
20
- BASE_STORAGE_DIR = "/data"
21
  else:
22
  # Otherwise, fall back to a local folder for Windows testing.
23
- BASE_STORAGE_DIR = os.getenv("STORAGE_DIR", "./data")
24
 
25
- # Define your specific bucket paths
26
- ORIGINALS_DIR = os.path.join(BASE_STORAGE_DIR, "originals")
27
- THUMBNAILS_DIR = os.path.join(BASE_STORAGE_DIR, "thumbnails")
28
-
29
- # Ensure these directories exist when the server starts
30
- os.makedirs(ORIGINALS_DIR, exist_ok=True)
31
- os.makedirs(THUMBNAILS_DIR, exist_ok=True)
32
  # ----------------------------------
33
 
34
  app.add_middleware(
@@ -71,9 +67,40 @@ async def mock_login(payload: LoginPayload):
71
  raise HTTPException(status_code=400, detail="Incorrect username or password")
72
 
73
  @app.post("/upload")
74
- async def upload_photo(file: UploadFile = File(...), token: str = Depends(verify_mock_token)):
75
- # Save directly to the originals folder
76
- file_path = os.path.join(ORIGINALS_DIR, file.filename)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
77
 
78
  try:
79
  # Save in 1MB chunks to prevent memory spikes
@@ -86,5 +113,6 @@ async def upload_photo(file: UploadFile = File(...), token: str = Depends(verify
86
  return {
87
  "message": "Photo securely uploaded!",
88
  "filename": file.filename,
89
- "path": file_path
 
90
  }
 
1
  import os
2
+ from datetime import datetime
3
+ from fastapi import FastAPI, HTTPException, Header, Depends, UploadFile, File, Form
4
  from fastapi.middleware.cors import CORSMiddleware
5
  from pydantic import BaseModel
6
  from dotenv import load_dotenv
 
18
  # --- SMART BUCKET STORAGE SETUP ---
19
  # If the root "/data" folder exists, Hugging Face mounted the bucket.
20
  if os.path.exists("/data"):
21
+ BASE_STORAGE_DIR = "/data/photos"
22
  else:
23
  # Otherwise, fall back to a local folder for Windows testing.
24
+ BASE_STORAGE_DIR = os.getenv("STORAGE_DIR", "./data/photos")
25
 
26
+ # Ensure the base directory exists
27
+ os.makedirs(BASE_STORAGE_DIR, exist_ok=True)
 
 
 
 
 
28
  # ----------------------------------
29
 
30
  app.add_middleware(
 
67
  raise HTTPException(status_code=400, detail="Incorrect username or password")
68
 
69
  @app.post("/upload")
70
+ async def upload_photo(
71
+ file: UploadFile = File(...),
72
+ creation_time: str = Form(None),
73
+ token: str = Depends(verify_mock_token)
74
+ ):
75
+ # Determine the date for folder structure
76
+ dt = datetime.now()
77
+ if creation_time:
78
+ try:
79
+ # creation_time might be epoch milliseconds or an ISO string
80
+ if creation_time.isdigit():
81
+ dt = datetime.fromtimestamp(int(creation_time) / 1000.0)
82
+ else:
83
+ dt = datetime.fromisoformat(creation_time.replace('Z', '+00:00'))
84
+ except Exception:
85
+ pass # Fallback to now() if parsing fails
86
+
87
+ year_folder = dt.strftime("%Y")
88
+ month_folder = dt.strftime("%m")
89
+
90
+ # Create target directory: e.g., /data/photos/2024/07
91
+ target_dir = os.path.join(BASE_STORAGE_DIR, year_folder, month_folder)
92
+ os.makedirs(target_dir, exist_ok=True)
93
+
94
+ file_path = os.path.join(target_dir, file.filename)
95
+
96
+ # If file already exists, we consider it a successful sync skip
97
+ if os.path.exists(file_path):
98
+ return {
99
+ "message": "Photo already exists, skipped.",
100
+ "filename": file.filename,
101
+ "path": file_path,
102
+ "status": "skipped"
103
+ }
104
 
105
  try:
106
  # Save in 1MB chunks to prevent memory spikes
 
113
  return {
114
  "message": "Photo securely uploaded!",
115
  "filename": file.filename,
116
+ "path": file_path,
117
+ "status": "uploaded"
118
  }