refactor(FN-846): rename filename prefixes from kb to fusion
- Switch settings export filename prefix from 'kb' to 'fusion' (e.g. fusion-settings-*.json) - Switch backup filename prefix from 'kb' to 'fusion' with legacy-safe parsing for existing kb-* backups - Remove unused ntfy deep-link code and dashboard routing for /project/:id - Remove dead notifier tests and App test assertions for removed features - Clean up dashboard command and bin.ts unused variables - Add changeset for the published package rename
This commit is contained in:
@@ -41,7 +41,7 @@ describe("BackupManager", () => {
|
||||
it("should create a backup file with correct name pattern", async () => {
|
||||
const backup = await backupManager.createBackup();
|
||||
|
||||
expect(backup.filename).toMatch(/^kb-\d{4}-\d{2}-\d{2}-\d{6}\.db$/);
|
||||
expect(backup.filename).toMatch(/^fusion-\d{4}-\d{2}-\d{2}-\d{6}\.db$/);
|
||||
expect(existsSync(backup.path)).toBe(true);
|
||||
});
|
||||
|
||||
@@ -115,7 +115,7 @@ describe("BackupManager", () => {
|
||||
const backups = await backupManager.listBackups();
|
||||
|
||||
expect(backups).toHaveLength(1);
|
||||
expect(backups[0].filename).toMatch(/^kb-\d{4}-\d{2}-\d{2}-\d{6}\.db$/);
|
||||
expect(backups[0].filename).toMatch(/^fusion-\d{4}-\d{2}-\d{2}-\d{6}\.db$/);
|
||||
});
|
||||
|
||||
it("should return correct file sizes", async () => {
|
||||
@@ -124,6 +124,57 @@ describe("BackupManager", () => {
|
||||
|
||||
expect(backups[0].size).toBe(backup.size);
|
||||
});
|
||||
|
||||
it("should list legacy kb-* backups alongside new fusion-* backups", async () => {
|
||||
// Create a new-style backup
|
||||
await backupManager.createBackup();
|
||||
|
||||
// Create a legacy-style backup file manually
|
||||
const backupDir = join(tempDir, ".fusion/backups");
|
||||
await writeFile(join(backupDir, "kb-2025-12-31-120000.db"), "legacy backup content");
|
||||
|
||||
const backups = await backupManager.listBackups();
|
||||
|
||||
expect(backups).toHaveLength(2);
|
||||
const filenames = backups.map((b) => b.filename);
|
||||
expect(filenames).toContain("kb-2025-12-31-120000.db");
|
||||
expect(filenames.some((f) => f.startsWith("fusion-"))).toBe(true);
|
||||
});
|
||||
|
||||
it("should parse timestamps from legacy kb-* filenames", async () => {
|
||||
const backupDir = join(tempDir, ".fusion/backups");
|
||||
await mkdir(backupDir, { recursive: true });
|
||||
await writeFile(join(backupDir, "kb-2025-06-15-083000.db"), "legacy");
|
||||
|
||||
const backups = await backupManager.listBackups();
|
||||
|
||||
expect(backups).toHaveLength(1);
|
||||
expect(backups[0].createdAt).toBe("2025-06-15T08:30:00Z");
|
||||
});
|
||||
|
||||
it("should parse timestamps from legacy kb-pre-restore filenames", async () => {
|
||||
const backupDir = join(tempDir, ".fusion/backups");
|
||||
await mkdir(backupDir, { recursive: true });
|
||||
await writeFile(join(backupDir, "kb-pre-restore-2025-06-15-083000.db"), "legacy pre-restore");
|
||||
|
||||
const backups = await backupManager.listBackups();
|
||||
|
||||
expect(backups).toHaveLength(1);
|
||||
expect(backups[0].filename).toBe("kb-pre-restore-2025-06-15-083000.db");
|
||||
expect(backups[0].createdAt).toBe("2025-06-15T08:30:00Z");
|
||||
});
|
||||
|
||||
it("should list only legacy kb-* backups when no fusion-* exist", async () => {
|
||||
const backupDir = join(tempDir, ".fusion/backups");
|
||||
await mkdir(backupDir, { recursive: true });
|
||||
await writeFile(join(backupDir, "kb-2025-01-01-000000.db"), "legacy1");
|
||||
await writeFile(join(backupDir, "kb-2025-01-02-000000.db"), "legacy2");
|
||||
|
||||
const backups = await backupManager.listBackups();
|
||||
|
||||
expect(backups).toHaveLength(2);
|
||||
expect(backups.every((b) => b.filename.startsWith("kb-"))).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("cleanupOldBackups", () => {
|
||||
@@ -216,6 +267,7 @@ describe("BackupManager", () => {
|
||||
const preRestoreBackup = backups.find((b) => b.filename.includes("pre-restore"));
|
||||
|
||||
expect(preRestoreBackup).toBeDefined();
|
||||
expect(preRestoreBackup!.filename).toMatch(/^fusion-pre-restore-/);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -223,7 +275,7 @@ describe("BackupManager", () => {
|
||||
describe("generateBackupFilename", () => {
|
||||
it("should generate filename with correct pattern", () => {
|
||||
const filename = generateBackupFilename();
|
||||
expect(filename).toMatch(/^kb-\d{4}-\d{2}-\d{2}-\d{6}\.db$/);
|
||||
expect(filename).toMatch(/^fusion-\d{4}-\d{2}-\d{2}-\d{6}\.db$/);
|
||||
});
|
||||
|
||||
it("should generate unique filenames for different timestamps", () => {
|
||||
|
||||
@@ -8,7 +8,7 @@ import type { ProjectSettings } from "./types.js";
|
||||
* Metadata for a database backup file.
|
||||
*/
|
||||
export interface BackupInfo {
|
||||
/** Filename of the backup (e.g., "kb-2026-03-31-020000.db") */
|
||||
/** Filename of the backup (e.g., "fusion-2026-03-31-020000.db") */
|
||||
filename: string;
|
||||
/** ISO-8601 timestamp when the backup was created */
|
||||
createdAt: string;
|
||||
@@ -105,17 +105,21 @@ export class BackupManager {
|
||||
const backups: BackupInfo[] = [];
|
||||
|
||||
for (const filename of files) {
|
||||
// Match both regular backups (kb-YYYY-MM-DD-HHmmss.db or kb-YYYY-MM-DD-HHmmss-N.db) and pre-restore backups
|
||||
if (!filename.match(/^kb(-pre-restore)?-\d{4}-\d{2}-\d{2}-\d{6}(-\d+)?\.db$/)) {
|
||||
// Match both new fusion-* and legacy kb-* backup patterns:
|
||||
// fusion-YYYY-MM-DD-HHmmss.db, fusion-YYYY-MM-DD-HHmmss-N.db,
|
||||
// fusion-pre-restore-YYYY-MM-DD-HHmmss.db,
|
||||
// kb-YYYY-MM-DD-HHmmss.db, kb-YYYY-MM-DD-HHmmss-N.db,
|
||||
// kb-pre-restore-YYYY-MM-DD-HHmmss.db
|
||||
if (!filename.match(/^(fusion|kb)(-pre-restore)?-\d{4}-\d{2}-\d{2}-\d{6}(-\d+)?\.db$/)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const filePath = join(backupDirPath, filename);
|
||||
const stats = await stat(filePath);
|
||||
|
||||
// Parse timestamp from filename: kb-YYYY-MM-DD-HHmmss.db or kb-pre-restore-YYYY-MM-DD-HHmmss.db
|
||||
// Also handles counter suffix: kb-YYYY-MM-DD-HHmmss-N.db
|
||||
const match = filename.match(/^(kb(?:-pre-restore)?)-(\d{4})-(\d{2})-(\d{2})-(\d{2})(\d{2})(\d{2})(?:-\d+)?\.db$/);
|
||||
// Parse timestamp from filename, supporting both fusion-* and kb-* prefixes
|
||||
// Also handles counter suffix: fusion-YYYY-MM-DD-HHmmss-N.db
|
||||
const match = filename.match(/^((?:fusion|kb)(?:-pre-restore)?)-(\d{4})-(\d{2})-(\d{2})-(\d{2})(\d{2})(\d{2})(?:-\d+)?\.db$/);
|
||||
const createdAt = match
|
||||
? `${match[2]}-${match[3]}-${match[4]}T${match[5]}:${match[6]}:${match[7]}Z`
|
||||
: stats.mtime.toISOString();
|
||||
@@ -199,7 +203,7 @@ export class BackupManager {
|
||||
|
||||
// Optionally create pre-restore backup
|
||||
if (options?.createPreRestoreBackup ?? true) {
|
||||
const preRestoreFilename = `kb-pre-restore-${formatTimestamp(new Date())}.db`;
|
||||
const preRestoreFilename = `fusion-pre-restore-${formatTimestamp(new Date())}.db`;
|
||||
const preRestorePath = join(backupDirPath, preRestoreFilename);
|
||||
await mkdir(backupDirPath, { recursive: true });
|
||||
await cp(targetPath, preRestorePath, { preserveTimestamps: true });
|
||||
@@ -212,10 +216,10 @@ export class BackupManager {
|
||||
|
||||
/**
|
||||
* Generates a backup filename with timestamp.
|
||||
* Format: kb-YYYY-MM-DD-HHmmss.db
|
||||
* Format: fusion-YYYY-MM-DD-HHmmss.db
|
||||
*/
|
||||
export function generateBackupFilename(): string {
|
||||
return `kb-${formatTimestamp(new Date())}.db`;
|
||||
return `fusion-${formatTimestamp(new Date())}.db`;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -70,7 +70,7 @@ describe("settings-export", () => {
|
||||
it("should generate filename with correct format", () => {
|
||||
const date = new Date("2026-03-31T12:34:56Z");
|
||||
const filename = generateExportFilename(date);
|
||||
expect(filename).toBe("kb-settings-2026-03-31-123456.json");
|
||||
expect(filename).toBe("fusion-settings-2026-03-31-123456.json");
|
||||
});
|
||||
|
||||
it("should use current date by default", () => {
|
||||
@@ -78,7 +78,7 @@ describe("settings-export", () => {
|
||||
const filename = generateExportFilename();
|
||||
const after = new Date();
|
||||
|
||||
expect(filename).toMatch(/^kb-settings-\d{4}-\d{2}-\d{2}-\d{6}\.json$/);
|
||||
expect(filename).toMatch(/^fusion-settings-\d{4}-\d{2}-\d{2}-\d{6}\.json$/);
|
||||
|
||||
// Parse the timestamp from filename
|
||||
const match = filename.match(/(\d{4})-(\d{2})-(\d{2})-(\d{2})(\d{2})(\d{2})/);
|
||||
|
||||
@@ -112,7 +112,7 @@ export function validateImportData(data: unknown): string[] {
|
||||
|
||||
/**
|
||||
* Generate a timestamped filename for settings export.
|
||||
* Format: kb-settings-YYYY-MM-DD-HHmmss.json
|
||||
* Format: fusion-settings-YYYY-MM-DD-HHmmss.json
|
||||
*/
|
||||
export function generateExportFilename(date: Date = new Date()): string {
|
||||
const year = date.getUTCFullYear();
|
||||
@@ -121,7 +121,7 @@ export function generateExportFilename(date: Date = new Date()): string {
|
||||
const hours = String(date.getUTCHours()).padStart(2, "0");
|
||||
const minutes = String(date.getUTCMinutes()).padStart(2, "0");
|
||||
const seconds = String(date.getUTCSeconds()).padStart(2, "0");
|
||||
return `kb-settings-${year}-${month}-${day}-${hours}${minutes}${seconds}.json`;
|
||||
return `fusion-settings-${year}-${month}-${day}-${hours}${minutes}${seconds}.json`;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user