-
Notifications
You must be signed in to change notification settings - Fork 38
♻️ refactor(expo): route all platform-sensitive deps through lib/ wrappers #2564
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: development
Are you sure you want to change the base?
Changes from all commits
c96deb5
14d97a3
9fd8c23
625d67f
0a56892
c9b064b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| // @react-native-async-storage/async-storage ships a localStorage-backed web | ||
| // implementation out of the box, so a single re-export works on every | ||
| // platform. This wrapper exists so call sites never import the native module | ||
| // path directly (per the lib/ wrapper convention). | ||
| export { default } from '@react-native-async-storage/async-storage'; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| // Default export is the `Storage` singleton from expo-sqlite/kv-store, an | ||
| // AsyncStorage-compatible store with extra synchronous methods (getItemSync, | ||
| // setItemSync). The native module has no working web build, so .web.ts backs | ||
| // the same surface with localStorage. | ||
| export { default } from 'expo-sqlite/kv-store'; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,61 @@ | ||
| // expo-sqlite/kv-store relies on the native SQLite module, which has no usable | ||
| // web build. Back the same API (async aliases + sync variants + getAllKeys) | ||
| // with localStorage so callers need no Platform branches on web. | ||
|
|
||
| const getItemSync = (key: string): string | null => { | ||
| try { | ||
| return localStorage.getItem(key); | ||
| } catch { | ||
| return null; | ||
| } | ||
| }; | ||
|
|
||
| const setItemSync = (key: string, value: string): void => { | ||
| localStorage.setItem(key, value); | ||
| }; | ||
|
|
||
| const removeItemSync = (key: string): void => { | ||
| localStorage.removeItem(key); | ||
| }; | ||
|
|
||
| const getAllKeysSync = (): string[] => { | ||
| const keys: string[] = []; | ||
| for (let i = 0; i < localStorage.length; i++) { | ||
| const key = localStorage.key(i); | ||
| if (key !== null) keys.push(key); | ||
| } | ||
| return keys; | ||
| }; | ||
|
|
||
| const Storage = { | ||
| getItem: (key: string): Promise<string | null> => Promise.resolve(getItemSync(key)), | ||
| setItem: (key: string, value: string): Promise<void> => { | ||
| setItemSync(key, value); | ||
| return Promise.resolve(); | ||
| }, | ||
| removeItem: (key: string): Promise<void> => { | ||
| removeItemSync(key); | ||
| return Promise.resolve(); | ||
| }, | ||
| getAllKeys: (): Promise<string[]> => Promise.resolve(getAllKeysSync()), | ||
| clear: (): Promise<void> => { | ||
| localStorage.clear(); | ||
| return Promise.resolve(); | ||
|
Comment on lines
+41
to
+43
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Critical: clear() destroys all localStorage data, not just KV store scope. Native 🛡️ Proposed fix to scope clear() to managed keysTrack a prefix or registry of managed keys. For now, iterate all keys and remove only those set via this Storage instance: +const STORAGE_PREFIX = '__expo_kv_';
+
const setItemSync = (key: string, value: string): void => {
- localStorage.setItem(key, value);
+ localStorage.setItem(STORAGE_PREFIX + key, value);
};
const removeItemSync = (key: string): void => {
- localStorage.removeItem(key);
+ localStorage.removeItem(STORAGE_PREFIX + key);
};
const getAllKeysSync = (): string[] => {
const keys: string[] = [];
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
- if (key !== null) keys.push(key);
+ if (key !== null && key.startsWith(STORAGE_PREFIX)) {
+ keys.push(key.slice(STORAGE_PREFIX.length));
+ }
}
return keys;
};
// ... in Storage object:
clear: (): Promise<void> => {
- localStorage.clear();
+ const keys = getAllKeysSync();
+ keys.forEach(k => removeItemSync(k));
return Promise.resolve();
},Adjust 🤖 Prompt for AI Agents |
||
| }, | ||
| getItemAsync: (key: string): Promise<string | null> => Promise.resolve(getItemSync(key)), | ||
| setItemAsync: (key: string, value: string): Promise<void> => { | ||
| setItemSync(key, value); | ||
| return Promise.resolve(); | ||
| }, | ||
| removeItemAsync: (key: string): Promise<boolean> => { | ||
| removeItemSync(key); | ||
| return Promise.resolve(true); | ||
| }, | ||
| getAllKeysAsync: (): Promise<string[]> => Promise.resolve(getAllKeysSync()), | ||
| getItemSync, | ||
| setItemSync, | ||
| removeItemSync, | ||
| getAllKeysSync, | ||
| }; | ||
|
|
||
| export default Storage; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| export { | ||
| GoogleSignin, | ||
| GoogleSigninButton, | ||
| isCancelledResponse, | ||
| isErrorWithCode, | ||
| isNoSavedCredentialFoundResponse, | ||
| isSuccessResponse, | ||
| statusCodes, | ||
| } from '@react-native-google-signin/google-signin'; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| // @react-native-google-signin/google-signin is a native module with no usable | ||
| // web implementation. Native Google Sign-In is not wired up for web (web auth | ||
| // uses a different OAuth flow), so this stub keeps the bundle building and | ||
| // fails loudly only if a web caller actually tries to trigger native sign-in. | ||
|
|
||
| const notAvailable = (): never => { | ||
| throw new Error('Native Google Sign-In is not available on web.'); | ||
| }; | ||
|
|
||
| export const GoogleSignin = { | ||
| configure: () => {}, | ||
| hasPlayServices: () => Promise.resolve(false), | ||
| signIn: notAvailable, | ||
| signInSilently: notAvailable, | ||
| getTokens: notAvailable, | ||
| hasPreviousSignIn: () => false, | ||
| signOut: () => Promise.resolve(null), | ||
| revokeAccess: () => Promise.resolve(null), | ||
| getCurrentUser: () => null, | ||
| }; | ||
|
|
||
| export const statusCodes = { | ||
| SIGN_IN_CANCELLED: 'SIGN_IN_CANCELLED', | ||
| IN_PROGRESS: 'IN_PROGRESS', | ||
| PLAY_SERVICES_NOT_AVAILABLE: 'PLAY_SERVICES_NOT_AVAILABLE', | ||
| SIGN_IN_REQUIRED: 'SIGN_IN_REQUIRED', | ||
| } as const; | ||
|
|
||
| export const isErrorWithCode = (_error: unknown): boolean => false; | ||
| export const isSuccessResponse = (_response: unknown): boolean => false; | ||
| export const isCancelledResponse = (_response: unknown): boolean => false; | ||
| export const isNoSavedCredentialFoundResponse = (_response: unknown): boolean => false; | ||
|
|
||
| export const GoogleSigninButton = (): null => null; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,4 @@ | ||
| import { observablePersistSqlite } from '@legendapp/state/persist-plugins/expo-sqlite'; | ||
| import Storage from 'expo-sqlite/kv-store'; | ||
| import Storage from 'expo-app/lib/expoSqliteKvStore'; | ||
|
|
||
| export const persistPlugin = observablePersistSqlite(Storage); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
Repository: PackRat-AI/PackRat
Length of output: 90
🏁 Script executed:
Repository: PackRat-AI/PackRat
Length of output: 184
🏁 Script executed:
Repository: PackRat-AI/PackRat
Length of output: 2279
🏁 Script executed:
Repository: PackRat-AI/PackRat
Length of output: 6353
🏁 Script executed:
Repository: PackRat-AI/PackRat
Length of output: 5905
🏁 Script executed:
Repository: PackRat-AI/PackRat
Length of output: 2007
🏁 Script executed:
Repository: PackRat-AI/PackRat
Length of output: 626
🏁 Script executed:
Repository: PackRat-AI/PackRat
Length of output: 581
🏁 Script executed:
Repository: PackRat-AI/PackRat
Length of output: 428
Align web
setItemSyncwith UpdateFn-capable kv-store contractNo call sites pass a functional updater to
setItemSync/setItemAsync/setItemin this repo, so nothing breaks today. However,apps/expo/mocks/expo-sqlite-kv-store.tssupportssetItemSync(key, value: string | UpdateFn)whileapps/expo/lib/expoSqliteKvStore.web.tsonly acceptsvalue: string, creating a contract mismatch—add theUpdateFnoverload to the web shim and derive the final value fromgetItemSync(key)whenvalueis a function.🤖 Prompt for AI Agents