mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2026-10-28 03:53:20 +00:00
Refactor optional file synchronisation ownership
This commit is contained in:
1 parent
3b2d5aa5af
commit
826413bf84
67 files changed
+6294
-1856
No files matched your search
@@ -1,15 +1,15 @@
|
||||
import type { FilePath } from "@vrtmrz/livesync-commonlib/compat/common/types";
|
||||
import type ObsidianLiveSyncPlugin from "@/main";
|
||||
import type { LiveSyncCore } from "@/main";
|
||||
import { StorageEventManagerBase, type StorageEventManagerBaseDependencies } from "@vrtmrz/livesync-commonlib/compat/managers/StorageEventManager";
|
||||
import {
|
||||
StorageEventManagerBase,
|
||||
type StorageEventManagerBaseDependencies,
|
||||
} from "@vrtmrz/livesync-commonlib/compat/managers/StorageEventManager";
|
||||
import { ObsidianStorageEventManagerAdapter } from "./ObsidianStorageEventManagerAdapter";
|
||||
|
||||
export class StorageEventManagerObsidian extends StorageEventManagerBase<ObsidianStorageEventManagerAdapter> {
|
||||
core: LiveSyncCore;
|
||||
|
||||
// Necessary evil.
|
||||
// cmdHiddenFileSync: HiddenFileSync;
|
||||
|
||||
constructor(plugin: ObsidianLiveSyncPlugin, core: LiveSyncCore, dependencies: StorageEventManagerBaseDependencies) {
|
||||
const adapter = new ObsidianStorageEventManagerAdapter(plugin, core, dependencies.fileProcessing);
|
||||
super(adapter, dependencies);
|
||||
@@ -22,6 +22,8 @@ export class StorageEventManagerObsidian extends StorageEventManagerBase<Obsidia
|
||||
protected override async _watchVaultRawEvents(path: FilePath) {
|
||||
if (!this.settings.syncInternalFiles && !this.settings.usePluginSync) return;
|
||||
if (!this.settings.watchInternalFileChanges) return;
|
||||
if (!this.settings.isConfigured || this.settings.suspendFileWatching) return;
|
||||
if (this.settings.maxMTimeForReflectEvents > 0) return;
|
||||
if (!path.startsWith(this.core.services.API.getSystemConfigDir())) return;
|
||||
if (path.endsWith("/")) {
|
||||
// Folder
|
||||
@@ -30,6 +32,15 @@ export class StorageEventManagerObsidian extends StorageEventManagerBase<Obsidia
|
||||
const isTargetFile = await this.vaultService.isTargetFileInExtra(path);
|
||||
if (!isTargetFile) return;
|
||||
|
||||
// Commonlib's ordinary queue excludes every dot path when Hidden File
|
||||
// Sync is disabled. A Customisation-only event has already passed the
|
||||
// optional-file policy, so dispatch it without that unrelated gate.
|
||||
if (!this.settings.syncInternalFiles) {
|
||||
this.fileProcessing.onStorageFileEvent();
|
||||
await this.fileProcessing.processOptionalFileEvent(path);
|
||||
return;
|
||||
}
|
||||
|
||||
void this.appendQueue(
|
||||
[
|
||||
{
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import type { FilePath } from "@vrtmrz/livesync-commonlib/compat/common/types";
|
||||
|
||||
vi.mock("@vrtmrz/livesync-commonlib/compat/managers/StorageEventManager", () => ({
|
||||
StorageEventManagerBase: class StorageEventManagerBase {
|
||||
settings: unknown;
|
||||
vaultService: unknown;
|
||||
fileProcessing: unknown;
|
||||
adapter: unknown;
|
||||
appendQueue = vi.fn(async () => undefined);
|
||||
|
||||
constructor(adapter: unknown, dependencies: Record<string, unknown>) {
|
||||
this.adapter = adapter;
|
||||
this.settings = dependencies.settings;
|
||||
this.vaultService = dependencies.vaultService;
|
||||
this.fileProcessing = dependencies.fileProcessing;
|
||||
}
|
||||
},
|
||||
}));
|
||||
vi.mock("./ObsidianStorageEventManagerAdapter", () => ({
|
||||
ObsidianStorageEventManagerAdapter: class ObsidianStorageEventManagerAdapter {
|
||||
converter = {
|
||||
toInternalFileInfo: vi.fn((path: FilePath) => ({ path, isInternal: true, isFolder: false })),
|
||||
};
|
||||
},
|
||||
}));
|
||||
|
||||
import { StorageEventManagerObsidian } from "./StorageEventManagerObsidian.ts";
|
||||
|
||||
function createManager(
|
||||
options: {
|
||||
customisationEnabled?: boolean;
|
||||
hiddenFileEnabled?: boolean;
|
||||
target?: boolean;
|
||||
configured?: boolean;
|
||||
suspended?: boolean;
|
||||
maxMTime?: number;
|
||||
} = {}
|
||||
) {
|
||||
const settings = {
|
||||
usePluginSync: options.customisationEnabled ?? true,
|
||||
syncInternalFiles: options.hiddenFileEnabled ?? true,
|
||||
watchInternalFileChanges: true,
|
||||
isConfigured: options.configured ?? true,
|
||||
suspendFileWatching: options.suspended ?? false,
|
||||
maxMTimeForReflectEvents: options.maxMTime ?? 0,
|
||||
};
|
||||
const onStorageFileEvent = vi.fn();
|
||||
const processOptionalFileEvent = vi.fn(async () => true);
|
||||
const isTargetFileInExtra = vi.fn(async () => options.target ?? true);
|
||||
const core = {
|
||||
services: {
|
||||
API: { getSystemConfigDir: vi.fn(() => ".obsidian") },
|
||||
},
|
||||
};
|
||||
const manager = new StorageEventManagerObsidian(
|
||||
{} as never,
|
||||
core as never,
|
||||
{
|
||||
settings,
|
||||
vaultService: { isTargetFileInExtra },
|
||||
fileProcessing: { onStorageFileEvent, processOptionalFileEvent },
|
||||
} as never
|
||||
);
|
||||
const runRawEvent = (path: string) =>
|
||||
(manager as unknown as { _watchVaultRawEvents(path: FilePath): Promise<void> })._watchVaultRawEvents(
|
||||
path as FilePath
|
||||
);
|
||||
return {
|
||||
appendQueue: (manager as unknown as { appendQueue: ReturnType<typeof vi.fn> }).appendQueue,
|
||||
isTargetFileInExtra,
|
||||
onStorageFileEvent,
|
||||
processOptionalFileEvent,
|
||||
runRawEvent,
|
||||
};
|
||||
}
|
||||
|
||||
describe("StorageEventManagerObsidian optional-file raw events", () => {
|
||||
it("dispatches a Customisation-only event without the base Hidden File Sync target gate", async () => {
|
||||
const fixture = createManager({ hiddenFileEnabled: false });
|
||||
|
||||
await fixture.runRawEvent(".obsidian/app.json");
|
||||
|
||||
expect(fixture.processOptionalFileEvent).toHaveBeenCalledWith(".obsidian/app.json");
|
||||
expect(fixture.onStorageFileEvent).toHaveBeenCalledOnce();
|
||||
expect(fixture.appendQueue).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("retains the base queue for events while Hidden File Sync is enabled", async () => {
|
||||
const fixture = createManager();
|
||||
|
||||
await fixture.runRawEvent(".obsidian/workspace");
|
||||
|
||||
expect(fixture.appendQueue).toHaveBeenCalledOnce();
|
||||
expect(fixture.processOptionalFileEvent).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("does not dispatch disabled, filtered, folder, or out-of-directory paths", async () => {
|
||||
const disabled = createManager({ customisationEnabled: false, hiddenFileEnabled: false });
|
||||
await disabled.runRawEvent(".obsidian/app.json");
|
||||
expect(disabled.appendQueue).not.toHaveBeenCalled();
|
||||
|
||||
const filtered = createManager({ target: false });
|
||||
await filtered.runRawEvent(".obsidian/workspace");
|
||||
await filtered.runRawEvent(".obsidian/folder/");
|
||||
await filtered.runRawEvent("notes/example.md");
|
||||
expect(filtered.appendQueue).not.toHaveBeenCalled();
|
||||
expect(filtered.processOptionalFileEvent).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it.each([
|
||||
["unconfigured", { configured: false }],
|
||||
["suspended", { suspended: true }],
|
||||
["bounded by reflection time", { maxMTime: 1 }],
|
||||
] as const)("preserves the base queue gate while %s", async (_label, options) => {
|
||||
const fixture = createManager({ hiddenFileEnabled: false, ...options });
|
||||
|
||||
await fixture.runRawEvent(".obsidian/app.json");
|
||||
|
||||
expect(fixture.isTargetFileInExtra).not.toHaveBeenCalled();
|
||||
expect(fixture.processOptionalFileEvent).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user