From 66fbda30a8811bfd2d2c8eab74c59ff68d1b0f44 Mon Sep 17 00:00:00 2001 From: Anton Reshetov Date: Thu, 9 Apr 2026 07:26:30 +0300 Subject: [PATCH] fix: prevent sqlite reimport over legacy vault --- src/main/index.ts | 28 ++----------- src/main/storage/providers/markdown/index.ts | 6 ++- .../markdown/runtime/__tests__/paths.test.ts | 41 ++++++++++++++++++- .../providers/markdown/runtime/index.ts | 1 + .../providers/markdown/runtime/paths.ts | 25 +++++++++++ 5 files changed, 75 insertions(+), 26 deletions(-) diff --git a/src/main/index.ts b/src/main/index.ts index 03781c56..42979f11 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -1,5 +1,4 @@ /* eslint-disable node/prefer-global/process */ -import { readFileSync } from 'node:fs' import { readFile } from 'node:fs/promises' import { createRequire } from 'node:module' import path from 'node:path' @@ -145,29 +144,10 @@ else { = (store.preferences.get('storage.vaultPath') as string | null) || path.join(storagePath, 'markdown-vault') ensureFlatSpacesLayout(vaultPath) - const statePath = path.join( - vaultPath, - 'code', - '.masscode', - 'state.json', - ) - let vaultHasData = false - - try { - const stateContent = readFileSync(statePath, 'utf8') - const state = JSON.parse(stateContent) as { - folders?: unknown[] - snippets?: unknown[] - tags?: unknown[] - } - - vaultHasData = [state.folders, state.snippets, state.tags].some( - collection => Array.isArray(collection) && collection.length > 0, - ) - } - catch { - // state.json doesn't exist or is invalid; treat the vault as empty - } + const { hasMarkdownVaultData } = lazyRequire( + './storage/providers/markdown', + ) as typeof import('./storage/providers/markdown') + const vaultHasData = hasMarkdownVaultData(vaultPath) if (!vaultHasData) { const { closeDB } = lazyRequire('./db') as typeof import('./db') diff --git a/src/main/storage/providers/markdown/index.ts b/src/main/storage/providers/markdown/index.ts index 1f76db35..5540c606 100644 --- a/src/main/storage/providers/markdown/index.ts +++ b/src/main/storage/providers/markdown/index.ts @@ -1,4 +1,8 @@ export { migrateSqliteToMarkdownStorage } from './migrations' -export { getMarkdownStorageErrorMessage, resetRuntimeCache } from './runtime' +export { + getMarkdownStorageErrorMessage, + hasMarkdownVaultData, + resetRuntimeCache, +} from './runtime' export { createMarkdownStorageProvider } from './storages' export { startMarkdownWatcher, stopMarkdownWatcher } from './watcher' diff --git a/src/main/storage/providers/markdown/runtime/__tests__/paths.test.ts b/src/main/storage/providers/markdown/runtime/__tests__/paths.test.ts index 22d40020..2b2d3aea 100644 --- a/src/main/storage/providers/markdown/runtime/__tests__/paths.test.ts +++ b/src/main/storage/providers/markdown/runtime/__tests__/paths.test.ts @@ -2,7 +2,7 @@ import os from 'node:os' import path from 'node:path' import fs from 'fs-extra' import { afterEach, describe, expect, it, vi } from 'vitest' -import { getPaths } from '../paths' +import { getPaths, hasMarkdownVaultData } from '../paths' vi.mock('electron-store', () => { class MockStore { @@ -80,6 +80,45 @@ afterEach(() => { }) describe('getPaths', () => { + it('detects existing data in legacy root vault layout', () => { + const vaultPath = createTempDir() + + fs.ensureDirSync(path.join(vaultPath, '.masscode')) + fs.writeJSONSync(path.join(vaultPath, '.masscode', 'state.json'), { + counters: { + contentId: 0, + folderId: 1, + snippetId: 1, + tagId: 0, + }, + folders: [ + { + id: 1, + name: 'Legacy', + orderIndex: 0, + parentId: null, + }, + ], + snippets: [ + { + filePath: 'Legacy/file.md', + id: 1, + }, + ], + tags: [], + version: 2, + }) + fs.ensureDirSync(path.join(vaultPath, 'Legacy')) + fs.writeFileSync(path.join(vaultPath, 'Legacy', 'file.md'), '# Legacy') + + expect(hasMarkdownVaultData(vaultPath)).toBe(true) + expect( + fs.pathExistsSync( + path.join(vaultPath, 'code', '.masscode', 'state.json'), + ), + ).toBe(true) + }) + it('migrates legacy code vault root into code', () => { const vaultPath = createTempDir() diff --git a/src/main/storage/providers/markdown/runtime/index.ts b/src/main/storage/providers/markdown/runtime/index.ts index 7ad5b226..88d5459d 100644 --- a/src/main/storage/providers/markdown/runtime/index.ts +++ b/src/main/storage/providers/markdown/runtime/index.ts @@ -33,6 +33,7 @@ export { getNextFolderOrder, getPaths, getVaultPath, + hasMarkdownVaultData, normalizeDirectoryPath, } from './paths' diff --git a/src/main/storage/providers/markdown/runtime/paths.ts b/src/main/storage/providers/markdown/runtime/paths.ts index 0317187a..2a5cd96b 100644 --- a/src/main/storage/providers/markdown/runtime/paths.ts +++ b/src/main/storage/providers/markdown/runtime/paths.ts @@ -41,6 +41,12 @@ interface LegacyStateFolderLike { parentId: number | null } +interface MarkdownStateCollectionsLike { + folders?: unknown[] + snippets?: unknown[] + tags?: unknown[] +} + function toTopLevelEntry(relativePath: string): string | null { const normalized = relativePath .replaceAll('\\', '/') @@ -233,6 +239,25 @@ export function getPaths(vaultPath: string): Paths { } } +export function hasMarkdownVaultData(vaultPath: string): boolean { + const { statePath } = getPaths(vaultPath) + + if (!fs.pathExistsSync(statePath)) { + return false + } + + try { + const state = fs.readJSONSync(statePath) as MarkdownStateCollectionsLike + + return [state.folders, state.snippets, state.tags].some( + collection => Array.isArray(collection) && collection.length > 0, + ) + } + catch { + return false + } +} + export { depthOfRelativePath, normalizeDirectoryPath,