diff --git a/.obsidian/plugins/webinar-dash/main.js b/.obsidian/plugins/webinar-dash/main.js index 8689bde..9977e06 100644 --- a/.obsidian/plugins/webinar-dash/main.js +++ b/.obsidian/plugins/webinar-dash/main.js @@ -29,9 +29,16 @@ function extractRawPath(text) { } function derivePipeline({ rawFiles, sourcePages }) { + // First claim on a raw path wins. A later page claiming the same file is a + // duplicate claim — real catalog drift — and joins `orphaned` rather than + // being silently dropped. `orphaned` therefore means "source page not paired + // with a raw file", whatever the reason. const claimed = new Map(); + const duplicates = []; for (const page of sourcePages) { - if (page.rawPath) claimed.set(page.rawPath, page); + if (!page.rawPath) continue; + if (claimed.has(page.rawPath)) duplicates.push(page); + else claimed.set(page.rawPath, page); } const processed = []; @@ -43,7 +50,9 @@ function derivePipeline({ rawFiles, sourcePages }) { } const rawPaths = new Set(rawFiles.map((f) => f.path)); - const orphaned = sourcePages.filter((p) => !p.rawPath || !rawPaths.has(p.rawPath)); + const orphaned = sourcePages + .filter((p) => !p.rawPath || !rawPaths.has(p.rawPath)) + .concat(duplicates); unprocessed.sort((a, b) => a.name.localeCompare(b.name)); processed.sort((a, b) => b.page.name.localeCompare(a.page.name)); diff --git a/.obsidian/plugins/webinar-dash/test/pipeline.test.js b/.obsidian/plugins/webinar-dash/test/pipeline.test.js index ebd7fe9..b5e95f9 100644 --- a/.obsidian/plugins/webinar-dash/test/pipeline.test.js +++ b/.obsidian/plugins/webinar-dash/test/pipeline.test.js @@ -68,6 +68,20 @@ test("derivePipeline treats a page with no raw path as orphaned", () => { assert.equal(out.unprocessed.length, 1); }); +test("derivePipeline routes a duplicate raw-path claim to orphaned", () => { + const out = derivePipeline({ + rawFiles: [{ path: "raw/sources/a.md", name: "a.md", size: 10 }], + sourcePages: [ + { path: "wiki/sources/first.md", name: "first.md", rawPath: "raw/sources/a.md" }, + { path: "wiki/sources/second.md", name: "second.md", rawPath: "raw/sources/a.md" }, + ], + }); + assert.equal(out.processed.length, 1); + assert.equal(out.processed[0].page.name, "first.md"); + assert.equal(out.unprocessed.length, 0); + assert.deepEqual(out.orphaned.map((p) => p.name), ["second.md"]); +}); + test("derivePipeline sorts unprocessed by name and processed newest first", () => { const out = derivePipeline({ rawFiles: [