@@ -51,6 +51,10 @@ import { clearFlaggedAccountsEntry } from "./storage/flagged-entry.js";
5151import { loadFlaggedAccountsEntry } from "./storage/flagged-load-entry.js" ;
5252import { saveFlaggedAccountsEntry } from "./storage/flagged-save-entry.js" ;
5353import { normalizeFlaggedStorage } from "./storage/flagged-storage.js" ;
54+ import {
55+ createStorageHealthSummary ,
56+ type StorageHealthSummary ,
57+ } from "./storage/health.js" ;
5458import {
5559 clearFlaggedAccountsOnDisk ,
5660 loadFlaggedAccountsState ,
@@ -128,6 +132,7 @@ import {
128132} from "./storage/transactions.js" ;
129133
130134export type {
135+ StorageHealthSummary ,
131136 CooldownReason ,
132137 RateLimitStateV3 ,
133138 AccountMetadataV1 ,
@@ -1264,6 +1269,105 @@ export async function getRestoreAssessment(): Promise<RestoreAssessment> {
12641269 } ) ;
12651270}
12661271
1272+ export async function inspectStorageHealth ( ) : Promise < StorageHealthSummary > {
1273+ const path = getStoragePath ( ) ;
1274+ const walPath = getAccountsWalPath ( path ) ;
1275+ const resetMarkerPath = getIntentionalResetMarkerPath ( path ) ;
1276+ if ( existsSync ( resetMarkerPath ) ) {
1277+ return createStorageHealthSummary ( {
1278+ state : "intentional-reset" ,
1279+ path,
1280+ walPath,
1281+ resetMarkerPath,
1282+ details : "intentional reset marker present" ,
1283+ } ) ;
1284+ }
1285+ if ( ! existsSync ( path ) ) {
1286+ const walRecovered = await loadAccountsFromJournal ( path ) ;
1287+ if ( walRecovered && walRecovered . accounts . length > 0 ) {
1288+ return createStorageHealthSummary ( {
1289+ state : "recoverable" ,
1290+ path,
1291+ walPath,
1292+ resetMarkerPath,
1293+ details : "primary storage missing but WAL recovery is available" ,
1294+ recoverySource : "wal" ,
1295+ } ) ;
1296+ }
1297+ return createStorageHealthSummary ( {
1298+ state : "empty" ,
1299+ path,
1300+ walPath,
1301+ resetMarkerPath,
1302+ details : "storage file is missing" ,
1303+ } ) ;
1304+ }
1305+ try {
1306+ const { normalized, schemaErrors } = await loadAccountsFromPath ( path , {
1307+ normalizeAccountStorage,
1308+ isRecord,
1309+ } ) ;
1310+ if ( normalized && normalized . accounts . length > 0 ) {
1311+ return createStorageHealthSummary ( {
1312+ state : "healthy" ,
1313+ path,
1314+ walPath,
1315+ resetMarkerPath,
1316+ schemaErrors,
1317+ } ) ;
1318+ }
1319+ if ( normalized ) {
1320+ return createStorageHealthSummary ( {
1321+ state : "empty" ,
1322+ path,
1323+ walPath,
1324+ resetMarkerPath,
1325+ schemaErrors,
1326+ details : "storage parsed but contains no accounts" ,
1327+ } ) ;
1328+ }
1329+ const walRecovered = await loadAccountsFromJournal ( path ) ;
1330+ if ( walRecovered && walRecovered . accounts . length > 0 ) {
1331+ return createStorageHealthSummary ( {
1332+ state : "recoverable" ,
1333+ path,
1334+ walPath,
1335+ resetMarkerPath,
1336+ schemaErrors,
1337+ details : "primary storage is invalid but WAL recovery is available" ,
1338+ recoverySource : "wal" ,
1339+ } ) ;
1340+ }
1341+ return createStorageHealthSummary ( {
1342+ state : "corrupt" ,
1343+ path,
1344+ walPath,
1345+ resetMarkerPath,
1346+ schemaErrors,
1347+ details : "storage could not be normalized" ,
1348+ } ) ;
1349+ } catch ( error ) {
1350+ const walRecovered = await loadAccountsFromJournal ( path ) ;
1351+ if ( walRecovered && walRecovered . accounts . length > 0 ) {
1352+ return createStorageHealthSummary ( {
1353+ state : "recoverable" ,
1354+ path,
1355+ walPath,
1356+ resetMarkerPath,
1357+ details : error instanceof Error ? error . message : String ( error ) ,
1358+ recoverySource : "wal" ,
1359+ } ) ;
1360+ }
1361+ return createStorageHealthSummary ( {
1362+ state : "corrupt" ,
1363+ path,
1364+ walPath,
1365+ resetMarkerPath,
1366+ details : error instanceof Error ? error . message : String ( error ) ,
1367+ } ) ;
1368+ }
1369+ }
1370+
12671371async function loadAccountsFromJournal (
12681372 path : string ,
12691373) : Promise < AccountStorageV3 | null > {
0 commit comments