( journalPath: string, journal: MigrationJournal, )
| 1440 | }; |
| 1441 | |
| 1442 | const completeJournaledFlip = async ( |
| 1443 | journalPath: string, |
| 1444 | journal: MigrationJournal, |
| 1445 | ): Promise<void> => { |
| 1446 | // The staging base file is the monotonic recovery marker. While it exists, |
| 1447 | // the staged v2 DB has not been installed yet, so it is safe to keep moving |
| 1448 | // any remaining canonical v1 file-set members to the backup and then consume |
| 1449 | // staging. Once the staging base is gone, the install rename already happened |
| 1450 | // and `source` may hold the v2 DB; recovery must never move/delete it again. |
| 1451 | if (fs.existsSync(journal.staging)) { |
| 1452 | await checkpointSqliteFileSetIfBaseExists(journal.source); |
| 1453 | await writeMigrationJournal(journalPath, { ...journal, phase: "canonical-moved" }); |
| 1454 | await moveExistingSqliteFileSet(journal.source, journal.backup); |
| 1455 | await pauseMigrationForTest("canonical-moved"); |
| 1456 | await moveSqliteFileSet(journal.staging, journal.source); |
| 1457 | await pauseMigrationForTest("staging-consumed"); |
| 1458 | } |
| 1459 | await writeMigrationJournal(journalPath, { ...journal, phase: "committed" }); |
| 1460 | await removeSqliteFileSetWithRetry(journal.normalizedSource); |
| 1461 | await removeSqliteFileSetWithRetry(journal.staging); |
| 1462 | await cleanupSecretBackups(journal); |
| 1463 | removeMigrationJournal(journalPath); |
| 1464 | }; |
| 1465 | |
| 1466 | type MigrationRecoveryResult = "missing" | "recovered" | "unreadable"; |
| 1467 |
no test coverage detected