Files
Kazier/mobile/package.json
T
m 197c7db8a9 feat(mobile): boucle sync client↔serveur — outbox push + snapshot permissions
- api/types.ts : SyncOperation, SyncResult (sémantique `applied` = INDEX documentée),
  ResourcePermission (miroir docs/api-v1.md §6, consommé tel quel par saveResourcePermission)
- api/client.ts : syncOps (POST /sync/ops), getSyncPermissions (GET /sync/permissions?after=),
  hasAuthToken() ; INVALID_RESPONSE documenté dans docs/api-v1.md §1/§4
- services/db/repositories/pendingOps.ts : listQueuedOperations(limit) (batch FIFO due)
  + scheduleRetries(ids, retryAt) qui ne modifie JAMAIS attempts (erreur transitoire,
  contrairement à markPendingOperation('failed') qui incrémente + backoff)
- features/syncOutbox.ts (nouveau) :
  * pushPendingOps : batch ≤ SYNC_BATCH_SIZE (50) → ops [0, applied) passent 'completed' ;
    op à l'index applied refusée → 'failed' (backoff, dead-letter MAX_PENDING_ATTEMPTS) ;
    ops suivantes intactes (re-soumission) ; réseau/5xx → scheduleRetries (retried) sans
    dead-letter prématurée ; applied==0 && failed → rien commité, aucun compteur touché
  * refreshPermissions : delta monotone en mémoire lastPermissionCachedAt → `after`,
    upsert saveResourcePermission ; no-op sans token
- features/syncDevice.ts : le tick useSyncDevice() enchaîne SAF walk → pushPendingOps()
  → refreshPermissions(), tous deux gardés par hasAuthToken()
- tests/syncOutbox.test.ts : 12 cas (FIFO/limit, succès complet/partiel, applied=0,
  transitoire réseau + 5xx sans incrément, dead-letter après MAX_PENDING_ATTEMPTS,
  delta snapshot croisé) — npm run test:sync
- tests/e2e.live.test.ts : smoke real backend (register → push → relecture /files/folders →
  snapshot), skip si serveur down, SORTI de npm test via npm run test:e2e
- gates : npx tsc --noEmit + npm run test → 56/56 verts (24 db + 20 api + 12 sync)
2026-09-10 20:23:55 +02:00

45 lines
1.3 KiB
JSON

{
"name": "webui",
"version": "2.0.0",
"main": "expo-router/entry",
"dependencies": {
"@expo/vector-icons": "^15.1.1",
"@tanstack/react-query": "^5.102.8",
"expo": "~57.0.8",
"expo-constants": "~57.0.17",
"expo-file-system": "~57.0.6",
"expo-linking": "~57.0.9",
"expo-localization": "~57.0.1",
"expo-router": "~57.0.20",
"expo-sqlite": "~57.0.2",
"expo-status-bar": "~57.0.1",
"i18n-js": "^4.5.3",
"react": "19.2.3",
"react-dom": "19.2.3",
"react-native": "0.86.0",
"react-native-safe-area-context": "~5.7.0",
"react-native-screens": "~4.26.0"
},
"devDependencies": {
"@types/better-sqlite3": "^9.6.0",
"@types/react": "~19.2.2",
"babel-preset-expo": "^57.0.4",
"better-sqlite3": "^13.0.3",
"tsx": "^4.23.13",
"typescript": "~6.0.3"
},
"scripts": {
"start": "expo start",
"android": "expo run:android",
"ios": "expo run:ios",
"web": "expo start --web",
"test:db": "tsx --test tests/migrations.test.ts tests/repositories.test.ts",
"test:migrations": "tsx --test tests/migrations.test.ts",
"test:api": "tsx --test tests/apiClient.test.ts",
"test:sync": "tsx --test tests/syncOutbox.test.ts",
"test:e2e": "tsx --test tests/e2e.live.test.ts",
"test": "npm run test:db && npm run test:api && npm run test:sync"
},
"private": true
}