Skip to content

Commit 7a2f89d

Browse files
committed
refactor: extract account snapshot helpers
1 parent 44b4844 commit 7a2f89d

2 files changed

Lines changed: 107 additions & 78 deletions

File tree

lib/storage.ts

Lines changed: 25 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ import {
1111
} from "./named-backup-export.js";
1212
import { MODEL_FAMILIES, type ModelFamily } from "./prompts/codex.js";
1313
import { AnyAccountStorageSchema, getValidationErrors } from "./schemas.js";
14+
import {
15+
describeAccountSnapshot,
16+
statSnapshot,
17+
} from "./storage/account-snapshot.js";
1418
import {
1519
type BackupMetadataSection,
1620
type BackupSnapshotMetadata,
@@ -115,17 +119,6 @@ type AccountStorageWithMetadata = AccountStorageV3 & {
115119
restoreReason?: RestoreReason;
116120
};
117121

118-
type BackupSnapshotKind =
119-
| "accounts-primary"
120-
| "accounts-wal"
121-
| "accounts-backup"
122-
| "accounts-backup-history"
123-
| "accounts-discovered-backup"
124-
| "flagged-primary"
125-
| "flagged-backup"
126-
| "flagged-backup-history"
127-
| "flagged-discovered-backup";
128-
129122
export type BackupMetadata = {
130123
accounts: BackupMetadataSection;
131124
flaggedAccounts: BackupMetadataSection;
@@ -511,70 +504,6 @@ function isCacheLikeBackupArtifactName(entryName: string): boolean {
511504
return entryName.toLowerCase().includes(".cache");
512505
}
513506

514-
async function statSnapshot(path: string): Promise<{
515-
exists: boolean;
516-
bytes?: number;
517-
mtimeMs?: number;
518-
}> {
519-
try {
520-
const stats = await fs.stat(path);
521-
return { exists: true, bytes: stats.size, mtimeMs: stats.mtimeMs };
522-
} catch (error) {
523-
const code = (error as NodeJS.ErrnoException).code;
524-
if (code !== "ENOENT") {
525-
log.warn("Failed to stat backup candidate", {
526-
path,
527-
error: String(error),
528-
});
529-
}
530-
return { exists: false };
531-
}
532-
}
533-
534-
async function describeAccountSnapshot(
535-
path: string,
536-
kind: BackupSnapshotKind,
537-
index?: number,
538-
): Promise<BackupSnapshotMetadata> {
539-
const stats = await statSnapshot(path);
540-
if (!stats.exists) {
541-
return { kind, path, index, exists: false, valid: false };
542-
}
543-
try {
544-
const { normalized, schemaErrors, storedVersion } =
545-
await loadAccountsFromPath(path);
546-
return {
547-
kind,
548-
path,
549-
index,
550-
exists: true,
551-
valid: !!normalized,
552-
bytes: stats.bytes,
553-
mtimeMs: stats.mtimeMs,
554-
version: typeof storedVersion === "number" ? storedVersion : undefined,
555-
accountCount: normalized?.accounts.length,
556-
schemaErrors: schemaErrors.length > 0 ? schemaErrors : undefined,
557-
};
558-
} catch (error) {
559-
const code = (error as NodeJS.ErrnoException).code;
560-
if (code !== "ENOENT") {
561-
log.warn("Failed to inspect account snapshot", {
562-
path,
563-
error: String(error),
564-
});
565-
}
566-
return {
567-
kind,
568-
path,
569-
index,
570-
exists: true,
571-
valid: false,
572-
bytes: stats.bytes,
573-
mtimeMs: stats.mtimeMs,
574-
};
575-
}
576-
}
577-
578507
async function loadFlaggedAccountsFromPath(
579508
path: string,
580509
): Promise<FlaggedAccountStorageV1> {
@@ -1241,10 +1170,24 @@ export async function getBackupMetadata(): Promise<BackupMetadata> {
12411170
flaggedPath,
12421171
getAccountsWalPath,
12431172
getAccountsBackupRecoveryCandidatesWithDiscovery,
1244-
describeAccountSnapshot,
1173+
describeAccountSnapshot: (path, kind, index) =>
1174+
describeAccountSnapshot(path, kind, {
1175+
index,
1176+
statSnapshot: (targetPath) =>
1177+
statSnapshot(targetPath, {
1178+
stat: fs.stat,
1179+
logWarn: (message, meta) => log.warn(message, meta),
1180+
}),
1181+
loadAccountsFromPath,
1182+
logWarn: (message, meta) => log.warn(message, meta),
1183+
}),
12451184
describeAccountsWalSnapshot: (path) =>
12461185
describeAccountsWalSnapshot(path, {
1247-
statSnapshot,
1186+
statSnapshot: (targetPath) =>
1187+
statSnapshot(targetPath, {
1188+
stat: fs.stat,
1189+
logWarn: (message, meta) => log.warn(message, meta),
1190+
}),
12481191
readFile: fs.readFile,
12491192
isRecord,
12501193
computeSha256,
@@ -1253,7 +1196,11 @@ export async function getBackupMetadata(): Promise<BackupMetadata> {
12531196
describeFlaggedSnapshot: (path, kind, index) =>
12541197
describeFlaggedSnapshot(path, kind, {
12551198
index,
1256-
statSnapshot,
1199+
statSnapshot: (targetPath) =>
1200+
statSnapshot(targetPath, {
1201+
stat: fs.stat,
1202+
logWarn: (message, meta) => log.warn(message, meta),
1203+
}),
12571204
loadFlaggedAccountsFromPath,
12581205
logWarn: (message, meta) => log.warn(message, meta),
12591206
}),

lib/storage/account-snapshot.ts

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import type { BackupSnapshotMetadata } from "./backup-metadata.js";
2+
3+
type SnapshotStats = {
4+
exists: boolean;
5+
bytes?: number;
6+
mtimeMs?: number;
7+
};
8+
9+
export async function statSnapshot(
10+
path: string,
11+
deps: {
12+
stat: typeof import("node:fs").promises.stat;
13+
logWarn?: (message: string, meta: Record<string, unknown>) => void;
14+
},
15+
): Promise<SnapshotStats> {
16+
try {
17+
const stats = await deps.stat(path);
18+
return { exists: true, bytes: stats.size, mtimeMs: stats.mtimeMs };
19+
} catch (error) {
20+
const code = (error as NodeJS.ErrnoException).code;
21+
if (code !== "ENOENT") {
22+
deps.logWarn?.("Failed to stat backup candidate", {
23+
path,
24+
error: String(error),
25+
});
26+
}
27+
return { exists: false };
28+
}
29+
}
30+
31+
export async function describeAccountSnapshot(
32+
path: string,
33+
kind: BackupSnapshotMetadata["kind"],
34+
deps: {
35+
index?: number;
36+
statSnapshot: (path: string) => Promise<SnapshotStats>;
37+
loadAccountsFromPath: (path: string) => Promise<{
38+
normalized: { accounts: unknown[] } | null;
39+
schemaErrors: string[];
40+
storedVersion?: unknown;
41+
}>;
42+
logWarn?: (message: string, meta: Record<string, unknown>) => void;
43+
},
44+
): Promise<BackupSnapshotMetadata> {
45+
const stats = await deps.statSnapshot(path);
46+
if (!stats.exists) {
47+
return { kind, path, index: deps.index, exists: false, valid: false };
48+
}
49+
try {
50+
const { normalized, schemaErrors, storedVersion } =
51+
await deps.loadAccountsFromPath(path);
52+
return {
53+
kind,
54+
path,
55+
index: deps.index,
56+
exists: true,
57+
valid: !!normalized,
58+
bytes: stats.bytes,
59+
mtimeMs: stats.mtimeMs,
60+
version: typeof storedVersion === "number" ? storedVersion : undefined,
61+
accountCount: normalized?.accounts.length,
62+
schemaErrors: schemaErrors.length > 0 ? schemaErrors : undefined,
63+
};
64+
} catch (error) {
65+
const code = (error as NodeJS.ErrnoException).code;
66+
if (code !== "ENOENT") {
67+
deps.logWarn?.("Failed to inspect account snapshot", {
68+
path,
69+
error: String(error),
70+
});
71+
}
72+
return {
73+
kind,
74+
path,
75+
index: deps.index,
76+
exists: true,
77+
valid: false,
78+
bytes: stats.bytes,
79+
mtimeMs: stats.mtimeMs,
80+
};
81+
}
82+
}

0 commit comments

Comments
 (0)