Adopt Commonlib storage path contracts

Use the shared Node adapter and retain one comparison policy throughout
each CLI scan. Propagate permission and I/O failures instead of reporting
absence or a successful partial scan.

Use shared namespace and comparison helpers in Obsidian while preserving
its Vault APIs and alias policy. Cover native spelling and host filename
compatibility, and document the dependency and integration boundaries.
This commit is contained in:
vorotamoroz committed 2026-11-03 08:44:31 +00:00
1 parent 2716d68b3d
commit d557298ceb
13 files changed
+301 -272

No files matched your search

@@ -15,7 +15,7 @@ import type { App, TFile } from "obsidian";
import { afterAll, describe, expect, it, vi } from "vitest";
import { ObsidianStorageAdapter } from "./ObsidianStorageAdapter";
import { ObsidianVaultAdapter } from "./ObsidianVaultAdapter";
import { createPathAliasGuard } from "./pathAliasGuard";
import { createPathAliasGuard, PathAliasGuard } from "./pathAliasGuard";
const REFUSAL = "Refusing a path which reaches an existing entry by a name its folder does not list.";
const CONFIG_DIRECTORY = ".obsidian";
@@ -141,6 +141,30 @@ describe("paths which resolve to an existing entry under an unlisted name", () =
expect(calls.trashFile).not.toHaveBeenCalled();
});
it("allows NFC and case equivalents of names exposed by the host listing", async () => {
const folder = "Café".normalize("NFD");
const guard = new PathAliasGuard({
isIndexed: () => false,
exists: async () => true,
list: async (path) => (path === "" ? [folder] : [`${folder}/Note.md`]),
});
await expect(guard.assertNoAlias("CAFÉ/note.md")).resolves.toBeUndefined();
});
it.each(["x:cache", "host\\name"])(
"allows a listed target beside the unrelated host-listed name %s",
async (unrelatedName) => {
const guard = new PathAliasGuard({
isIndexed: () => false,
exists: async () => true,
list: async () => [unrelatedName, "note.md"],
});
await expect(guard.assertNoAlias("note.md")).resolves.toBeUndefined();
}
);
it("allows listed names, case-only differences, indexed paths, and new files", async () => {
const { app, calls } = fakeApp();
const adapter = new ObsidianStorageAdapter(app);