4.4 KiB
Expo HAS CHANGED
Read the exact versioned docs at https://docs.expo.dev/versions/v57.0.0/ before writing any code.
Local storage
Persistence is SQLite-backed via services/db/ (expo-sqlite, database dot.db, user_version = 4).
services/db/client.ts— connection lifecycle:getDatabase(),closeDatabase(),withTransaction().services/db/migrations.ts— versioned, chained migrations viaPRAGMA user_version(list of{ version, up }), single-flightWeakMaplock,migrateDatabase(db, targetVersion?). v4 is a transactional rebuild (folders/files dropurikeys, gainresource_id+ partial unique index on non-nulluri). Migration tests:npm run test:migrations(better-sqlite3 harness intests/migrations.test.ts); repository tests:npm run test:db(both suites, better-sqlite3 via theDbSessionseam).services/db/session.ts—DbSession(injectablerunAsync/getFirstAsync/getAllAsync) ; repos get it throughgetSession(), tests override it with__setDbForTests().client.tsloadsexpo-sqlitelazily so the test suites run under plain Node.services/db/schema.ts—DATABASE_NAME,DATABASE_VERSION, column-list constants,DEVICE_USER_ID_KEY,PERMISSION_TTL_MS(24h offline stale-cache).services/db/id.ts—newResourceId()opaque 32-hexlower(hex(randomblob(16))), generated per row.services/db/transitions.ts—transitionSyncStatus(from, event): per-row sync status transitions.services/db/repositories/— one module per table:folders,files,user_preferences(+getDeviceUserId),resource_permissions(permissions.tswithcanAccess/canWrite/isOwner, hierarchical viaWITH RECURSIVE,inherit/expires_athonored, 24h stale-cache read-only downgrade),shares,share_links,recipients,pending_operations(pendingOps.ts, outbox: FIFO on(created_at, id), failure schedules apendingretry with backoff, dead-letterfailedafterMAX_PENDING_ATTEMPTS).services/localStorage.tsis a thin re-export (services/db) kept for legacy imports.- Tables:
folders,files,user_preferences,resource_permissions,shares,share_links,recipients,pending_operations. - Canonical identity:
resource_id(opaque, unique) on folders/files/shares/share_links;uri(physical SAF path) is nullable, NULL = cloud-only;owner_idNOT NULL seeded fromdevice_user_id. - Folder and file per-row sync status:
local|cloud|local-cloud(placement state, transitions viatransitionSyncStatus). shares/share_linkshave NOsync_status: theirpushStatus(pending/synced/failed) is derived frompending_operations(ref_type=share|share_link,ref_id).- Query usage:
getFiles(folderResourceId?),getFolders(),getFolderFolders(parentResourceId),getFolder/getFile(resourceId),saveFolder/saveDirectory,saveFile(file, folderResourceId),removeFolder/removeFile(resourceId),saveUserPreferences/getUserPreferences/getDeviceUserId,saveResourcePermission/getResourcePermission,canAccess(resourceId, type, level),saveShare/getShares/removeShare,createShareLink/getShareLinks/incrementLinkDownloads/revokeShareLink,saveRecipient/getRecipients,enqueuePendingOperation/getNextQueuedOperation/markPendingOperation. - SAF walk (
features/syncDevice.ts,syncDevice()/syncRoot()): two passes (all folders sorted by uri depth, then all files) inside a single transaction (withTransaction), receives sync: an interruption rolls back entirely. Reconciles by physicaluri; rows under the root with aurino longer seen are markedexists = 0(never deleted). Root folders are the rows withparent_resource_id IS NULL+ non-nulluri. - Heavy processing stays server-side; SQLite only persists local metadata/state.
REST API client
api/client.ts + api/types.ts = the client-side API contract (server must implement it; backend Go is the source of truth once built). Base URL = EXPO_PUBLIC_API_BASE_URL (défaut http://localhost:8080/api/v1). Envelope: success { data, meta?: { page, pageSize, total } }, errors normalized to ApiError (code from { error: { code, message } }, or NETWORK_ERROR / HTTP_<status>). Multipart upload needs the platform FormData (uri/name/type) — never set Content-Type manually. TanStack Query v5 providers live in app/_layout.tsx; hooks in hooks/ (useFiles, useSearch, useUpload, OCR jobs).