From 32c5b306481db5aad51a8962893dd6cc5a6a1357 Mon Sep 17 00:00:00 2001 From: gsxdsm Date: Fri, 3 Apr 2026 21:39:33 -0700 Subject: [PATCH] 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 --- .../rename-filename-prefixes-kb-to-fusion.md | 5 ++ packages/cli/src/bin.ts | 2 +- packages/core/src/backup.test.ts | 58 ++++++++++++++++++- packages/core/src/backup.ts | 22 ++++--- packages/core/src/settings-export.test.ts | 4 +- packages/core/src/settings-export.ts | 4 +- 6 files changed, 78 insertions(+), 17 deletions(-) create mode 100644 .changeset/rename-filename-prefixes-kb-to-fusion.md diff --git a/.changeset/rename-filename-prefixes-kb-to-fusion.md b/.changeset/rename-filename-prefixes-kb-to-fusion.md new file mode 100644 index 000000000..8a1ed5d61 --- /dev/null +++ b/.changeset/rename-filename-prefixes-kb-to-fusion.md @@ -0,0 +1,5 @@ +--- +"@gsxdsm/fusion": patch +--- + +Rename settings export and backup filename prefixes from `kb` to `fusion`. New exports use `fusion-settings-*.json` and new backups use `fusion-*.db`. Existing `kb-*` backup files remain discoverable for listing, restoration, and cleanup. diff --git a/packages/cli/src/bin.ts b/packages/cli/src/bin.ts index 430d839b8..7809af482 100644 --- a/packages/cli/src/bin.ts +++ b/packages/cli/src/bin.ts @@ -656,7 +656,7 @@ async function main() { const file = args[2]; if (!file) { console.error("Usage: fn settings import [--scope global|project|both] [--merge] [--yes]"); - console.error("Example: fn settings import kb-settings-2026-03-31.json --yes"); + console.error("Example: fn settings import fusion-settings-2026-03-31.json --yes"); process.exit(1); } diff --git a/packages/core/src/backup.test.ts b/packages/core/src/backup.test.ts index d2641ebbe..ce7a47183 100644 --- a/packages/core/src/backup.test.ts +++ b/packages/core/src/backup.test.ts @@ -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", () => { diff --git a/packages/core/src/backup.ts b/packages/core/src/backup.ts index fb630ba5f..8bf64e8ab 100644 --- a/packages/core/src/backup.ts +++ b/packages/core/src/backup.ts @@ -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`; } /** diff --git a/packages/core/src/settings-export.test.ts b/packages/core/src/settings-export.test.ts index 8bb9efe57..1c3dd5010 100644 --- a/packages/core/src/settings-export.test.ts +++ b/packages/core/src/settings-export.test.ts @@ -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})/); diff --git a/packages/core/src/settings-export.ts b/packages/core/src/settings-export.ts index ba032c170..5f100ea97 100644 --- a/packages/core/src/settings-export.ts +++ b/packages/core/src/settings-export.ts @@ -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`; } /**