- 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)
45 lines
1.3 KiB
JSON
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
|
|
}
|