diff --git a/docs/src/pages/en/usage/settings.md b/docs/src/pages/en/usage/settings.md index ea01b5f..0549ccc 100644 --- a/docs/src/pages/en/usage/settings.md +++ b/docs/src/pages/en/usage/settings.md @@ -244,10 +244,9 @@ These actions are mainly for troubleshooting. Use them carefully. Sync Engine keeps records of which local and remote files were last matched. It uses these records to tell a new change from a conflict and to plan safe sync operations. -- **Clear vault records** removes records for the current backend + vault combo. -- **Clear all records** removes records for all vaults on this machine and installed modules. +Clicking **Clear** button removes records for the current backend + vault combo. -These actions do not directly delete files, but the next sync will have less history and may plan uploads, downloads, or deletions differently. The setting warns that clearing records can cause data loss. Export or back up important files before using it. +These actions do not directly delete files, but the next sync will have less history and may plan uploads, downloads, or deletions differently. The setting warns that clearing records can cause data loss. Only use it when you manually modified remote files and the plugin already reports wrong operations. ### Export Logs to File diff --git a/packages/plugin/src/fs/wrappers/asymmetric-storage.ts b/packages/plugin/src/fs/wrappers/asymmetric-storage.ts index 2a2cc8b..c526e33 100644 --- a/packages/plugin/src/fs/wrappers/asymmetric-storage.ts +++ b/packages/plugin/src/fs/wrappers/asymmetric-storage.ts @@ -157,11 +157,15 @@ class AsymmetricStorageFs implements WrappedFs { const stats = await this.original.list(this.flattenKey(key), () => 'include'); const seen = new Set(); const result: Array = []; + let ignoredCount = 0; await Promise.all( stats.map(async (stat, index) => { const inflated = this.inflateStat(stat); + if (!inflated) { + ignoredCount++; + return; + } if ( - !inflated || !isSub(key, inflated.key) || seen.has(inflated.key) || (await reporter({ @@ -175,6 +179,10 @@ class AsymmetricStorageFs implements WrappedFs { result.push(inflated); }), ); + if (ignoredCount / (stats.length || 1) >= 0.3) + throw new Error( + "There are too many files at remote that don't adopt asymmetric storage, maybe you want to turn it off in settings.", + ); return result; } diff --git a/packages/plugin/test/fs-asymmetric-storage-wrapper.test.ts b/packages/plugin/test/fs-asymmetric-storage-wrapper.test.ts index 31874e3..ccaeeab 100644 --- a/packages/plugin/test/fs-asymmetric-storage-wrapper.test.ts +++ b/packages/plugin/test/fs-asymmetric-storage-wrapper.test.ts @@ -52,12 +52,11 @@ test('list should infer folder anchors from remoteStatContext and return hierarc expect(remote.calls.list).toStrictEqual(['/']); }); -test('list should skip malformed or orphan flattened entries without throwing', async () => { +test('list should throw when encountering too many malformed or orphan flattened entries without proceeding', () => { seedRemoteContext(file('00000abcde~folder')); const remote = fs({ control: { list: () => [ - folder('/'), file('bad-key', { size: 1, uid: 'bad' }), file('zzzzz~lost.md', { size: 2, uid: 'orphan-file' }), file('zzzzzqqqqq~ghost', { size: 0, uid: 'orphan-folder' }), @@ -68,10 +67,9 @@ test('list should skip malformed or orphan flattened entries without throwing', }); const wrapper = asymmetricStorageWrapper(remote.fs, store); - expect(await wrapper.list('/', () => 'include')).toStrictEqual([ - folder('folder/'), - file('folder/child.md', { size: 4, uid: 'child' }), - ]); + expect(() => wrapper.list('/', () => 'include')).toThrow( + "There are too many files at remote that don't adopt asymmetric storage, maybe you want to turn it off in settings.", + ); }); test('mkdir should write empty folder marker file and reuse same generated anchor later', async () => {