diffwiki-core 0.6.1-rc.202607242058.7b3501c → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -577,7 +577,6 @@ __export(index_exports, {
577
577
  addTags: () => addTags,
578
578
  buildArticleTree: () => buildArticleTree,
579
579
  buildCollections: () => buildCollections,
580
- buildLinkGraph: () => buildLinkGraph,
581
580
  buildNavTree: () => buildNavTree,
582
581
  buildSearchIndex: () => buildSearchIndex,
583
582
  collectionDir: () => collectionDir,
@@ -602,7 +601,6 @@ __export(index_exports, {
602
601
  gitToplevel: () => gitToplevel,
603
602
  initRepoWiki: () => initRepoWiki,
604
603
  installPlugin: () => installPlugin,
605
- invalidateLinkGraph: () => invalidateLinkGraph,
606
604
  isLinkedWorktree: () => isLinkedWorktree,
607
605
  listArticleTree: () => listArticleTree,
608
606
  listAvailablePlugins: () => listAvailablePlugins,
@@ -611,7 +609,6 @@ __export(index_exports, {
611
609
  listPlugins: () => listPlugins,
612
610
  listSearchModes: () => listSearchModes,
613
611
  loadSearchProvider: () => loadSearchProvider,
614
- localGraph: () => localGraph,
615
612
  nativeSearch: () => nativeSearch,
616
613
  parseAddTarget: () => parseAddTarget,
617
614
  parseArticle: () => parseArticle,
@@ -946,8 +943,8 @@ function isEpochish(d) {
946
943
  }
947
944
  var timestampCache = /* @__PURE__ */ new Map();
948
945
  async function fileTimestamps(absPath) {
949
- const cached2 = timestampCache.get(absPath);
950
- if (cached2) return cached2;
946
+ const cached = timestampCache.get(absPath);
947
+ if (cached) return cached;
951
948
  const dir = import_node_path4.default.dirname(absPath);
952
949
  const base = import_node_path4.default.basename(absPath);
953
950
  const [addLog, lastLog] = await Promise.all([
@@ -1307,56 +1304,6 @@ function rehypeCollectToc(toc) {
1307
1304
  });
1308
1305
  };
1309
1306
  }
1310
- function isExternalHref(href) {
1311
- if (!href) return true;
1312
- if (href.startsWith("#")) return true;
1313
- if (href.startsWith("//")) return true;
1314
- return /^[a-z][a-z0-9+.-]*:/i.test(href);
1315
- }
1316
- function resolveHref(href, ctx) {
1317
- if (isExternalHref(href)) return null;
1318
- const clean = (p) => p.replace(/[?#].*$/, "").replace(/\.(mdx?|html)$/i, "").replace(/\/+$/, "");
1319
- const target = clean(href);
1320
- if (!target) return null;
1321
- if (target.startsWith("/")) {
1322
- const base = ctx.base ?? process.env.DIFFWIKI_BASE ?? "";
1323
- const baseSegs2 = base.split("/").filter(Boolean);
1324
- let segs = target.split("/").filter(Boolean);
1325
- if (baseSegs2.length > 0 && baseSegs2.every((b, i) => segs[i] === b)) {
1326
- segs = segs.slice(baseSegs2.length);
1327
- }
1328
- if (segs.length < 2) return null;
1329
- return segs.join("/");
1330
- }
1331
- const dir = ctx.isIndex ? ctx.slug : ctx.slug.includes("/") ? ctx.slug.slice(0, ctx.slug.lastIndexOf("/")) : "";
1332
- const baseSegs = dir ? dir.split("/") : [];
1333
- const out = [...baseSegs];
1334
- for (const seg of target.split("/")) {
1335
- if (seg === "" || seg === ".") continue;
1336
- if (seg === "..") {
1337
- if (out.length === 0) return null;
1338
- out.pop();
1339
- continue;
1340
- }
1341
- out.push(seg);
1342
- }
1343
- if (out.length === 0) return null;
1344
- return `${ctx.coll}/${out.join("/")}`;
1345
- }
1346
- function rehypeCollectLinks(links, ctx) {
1347
- const seen = /* @__PURE__ */ new Set();
1348
- return (tree) => {
1349
- (0, import_unist_util_visit.visit)(tree, "element", (node) => {
1350
- if (node.tagName !== "a") return;
1351
- const href = node.properties?.href;
1352
- if (typeof href !== "string") return;
1353
- const id = resolveHref(href, ctx);
1354
- if (!id || seen.has(id)) return;
1355
- seen.add(id);
1356
- links.push(id);
1357
- });
1358
- };
1359
- }
1360
1307
  function rehypeStripLeadingH1() {
1361
1308
  return (tree) => {
1362
1309
  let removed = false;
@@ -1384,112 +1331,13 @@ function rehypeMarkMermaid() {
1384
1331
  }
1385
1332
  async function renderArticle(body, ctx) {
1386
1333
  const toc = [];
1387
- const links = [];
1388
- const pipeline = (0, import_unified.unified)().use(import_remark_parse.default).use(import_remark_gfm.default).use(import_remark_rehype.default).use(import_rehype_slug.default).use(rehypeCollectToc, toc).use(rehypeCollectLinks, links, ctx);
1389
- const file = await pipeline.use(rehypeStripLeadingH1).use(rehypeMarkMermaid).use(rehypeRewriteLinks, ctx).use(import_rehype.default, {
1334
+ const file = await (0, import_unified.unified)().use(import_remark_parse.default).use(import_remark_gfm.default).use(import_remark_rehype.default).use(import_rehype_slug.default).use(rehypeCollectToc, toc).use(rehypeStripLeadingH1).use(rehypeMarkMermaid).use(rehypeRewriteLinks, ctx).use(import_rehype.default, {
1390
1335
  themes: { light: "github-light", dark: "github-dark" },
1391
1336
  defaultColor: false,
1392
1337
  langAlias: LANG_ALIASES,
1393
1338
  fallbackLanguage: "plaintext"
1394
1339
  }).use(import_rehype_stringify.default).process(body);
1395
- return { html: String(file), toc, links };
1396
- }
1397
-
1398
- // src/graph.ts
1399
- init_collections();
1400
- init_errors();
1401
- function tagId(tag) {
1402
- return `tag:${tag}`;
1403
- }
1404
- async function collectArticle(coll, slug, fallbackTitle, outNodes, outEdges, outTags) {
1405
- let art;
1406
- try {
1407
- art = await readArticle(coll, slug);
1408
- } catch (err) {
1409
- if (err instanceof ArticleNotFoundError) return;
1410
- throw err;
1411
- }
1412
- const id = `${coll}/${slug}`;
1413
- outNodes.push({ id, kind: "article", coll, slug, title: art.title ?? fallbackTitle ?? slug });
1414
- const { links } = await renderArticle(art.body, { coll, slug, isIndex: art.isIndex });
1415
- for (const target of links) outEdges.push({ source: id, target, kind: "link" });
1416
- for (const tag of art.tags) outTags.push({ source: id, tag });
1417
- }
1418
- async function collectCollection(coll, nodes, outNodes, outEdges, outTags) {
1419
- for (const n of nodes) {
1420
- if (n.slug) await collectArticle(coll, n.slug, n.title ?? n.name, outNodes, outEdges, outTags);
1421
- if (n.children) await collectCollection(coll, n.children, outNodes, outEdges, outTags);
1422
- }
1423
- }
1424
- var cached;
1425
- function invalidateLinkGraph() {
1426
- cached = void 0;
1427
- }
1428
- async function buildLinkGraph() {
1429
- if (cached) return cached;
1430
- const nodes = [];
1431
- const rawEdges = [];
1432
- const rawTags = [];
1433
- for (const e of await listCollections()) {
1434
- try {
1435
- await collectArticle(e.name, "", e.name, nodes, rawEdges, rawTags);
1436
- await collectCollection(e.name, await listArticleTree(e.name), nodes, rawEdges, rawTags);
1437
- } catch {
1438
- }
1439
- }
1440
- const nodeIds = new Set(nodes.map((n) => n.id));
1441
- const seen = /* @__PURE__ */ new Set();
1442
- const edges = [];
1443
- for (const edge of rawEdges) {
1444
- if (edge.source === edge.target) continue;
1445
- if (!nodeIds.has(edge.target)) continue;
1446
- const key = `${edge.source} ${edge.target}`;
1447
- if (seen.has(key)) continue;
1448
- seen.add(key);
1449
- edges.push(edge);
1450
- }
1451
- const tagSeen = /* @__PURE__ */ new Set();
1452
- for (const { source, tag } of rawTags) {
1453
- if (!nodeIds.has(source)) continue;
1454
- const id = tagId(tag);
1455
- if (!tagSeen.has(id)) {
1456
- tagSeen.add(id);
1457
- nodes.push({ id, kind: "tag", coll: "", slug: "", title: `#${tag}` });
1458
- }
1459
- const key = `${source} ${id}`;
1460
- if (seen.has(key)) continue;
1461
- seen.add(key);
1462
- edges.push({ source, target: id, kind: "tag" });
1463
- }
1464
- cached = { nodes, edges };
1465
- return cached;
1466
- }
1467
- function localGraph(graph, focusId, include, siblingCap = 12) {
1468
- const scoped = include ? {
1469
- // Tag hubs survive the filter — never a page, so they can't 404.
1470
- nodes: graph.nodes.filter((n) => n.kind === "tag" || include.has(n.coll)),
1471
- edges: graph.edges
1472
- } : graph;
1473
- const byId = new Map(scoped.nodes.map((n) => [n.id, n]));
1474
- const focus = byId.get(focusId);
1475
- if (!focus) return { focus: focusId, nodes: [], edges: [] };
1476
- const keep = /* @__PURE__ */ new Set([focusId]);
1477
- for (const e of scoped.edges) {
1478
- if (e.source === focusId && byId.has(e.target)) keep.add(e.target);
1479
- if (e.target === focusId && byId.has(e.source)) keep.add(e.source);
1480
- }
1481
- const focusTagHubs = new Set([...keep].filter((id) => byId.get(id)?.kind === "tag"));
1482
- let siblings = 0;
1483
- for (const e of scoped.edges) {
1484
- if (siblings >= siblingCap) break;
1485
- if (e.kind === "tag" && focusTagHubs.has(e.target) && byId.has(e.source) && !keep.has(e.source)) {
1486
- keep.add(e.source);
1487
- siblings++;
1488
- }
1489
- }
1490
- const nodes = scoped.nodes.filter((n) => keep.has(n.id));
1491
- const edges = scoped.edges.filter((e) => keep.has(e.source) && keep.has(e.target));
1492
- return { focus: focusId, nodes, edges };
1340
+ return { html: String(file), toc };
1493
1341
  }
1494
1342
 
1495
1343
  // src/site.ts
@@ -1543,7 +1391,7 @@ function findNode(nodes, slug) {
1543
1391
  }
1544
1392
  return void 0;
1545
1393
  }
1546
- async function resolvePage(coll, slug, opts) {
1394
+ async function resolvePage(coll, slug) {
1547
1395
  try {
1548
1396
  const art = await readArticle(coll, slug);
1549
1397
  const { html, toc } = await renderArticle(art.body, {
@@ -1552,7 +1400,6 @@ async function resolvePage(coll, slug, opts) {
1552
1400
  isIndex: art.isIndex
1553
1401
  });
1554
1402
  const { created, updated } = await fileTimestamps(art.path);
1555
- const graph = localGraph(await buildLinkGraph(), `${art.coll}/${art.slug}`, opts?.include);
1556
1403
  return {
1557
1404
  kind: "doc",
1558
1405
  coll: art.coll,
@@ -1563,8 +1410,7 @@ async function resolvePage(coll, slug, opts) {
1563
1410
  toc,
1564
1411
  path: `/${art.coll}/${art.slug}`,
1565
1412
  created,
1566
- updated,
1567
- graph
1413
+ updated
1568
1414
  };
1569
1415
  } catch (err) {
1570
1416
  if (err instanceof CollectionNotFoundError) return null;
@@ -2251,7 +2097,6 @@ init_registry2();
2251
2097
  addTags,
2252
2098
  buildArticleTree,
2253
2099
  buildCollections,
2254
- buildLinkGraph,
2255
2100
  buildNavTree,
2256
2101
  buildSearchIndex,
2257
2102
  collectionDir,
@@ -2276,7 +2121,6 @@ init_registry2();
2276
2121
  gitToplevel,
2277
2122
  initRepoWiki,
2278
2123
  installPlugin,
2279
- invalidateLinkGraph,
2280
2124
  isLinkedWorktree,
2281
2125
  listArticleTree,
2282
2126
  listAvailablePlugins,
@@ -2285,7 +2129,6 @@ init_registry2();
2285
2129
  listPlugins,
2286
2130
  listSearchModes,
2287
2131
  loadSearchProvider,
2288
- localGraph,
2289
2132
  nativeSearch,
2290
2133
  parseAddTarget,
2291
2134
  parseArticle,
package/dist/index.d.cts CHANGED
@@ -192,8 +192,6 @@ interface DocData {
192
192
  created?: string;
193
193
  /** ISO-8601 last-update date, sourced from git history / filesystem (not frontmatter). */
194
194
  updated?: string;
195
- /** Local knowledge-graph neighbourhood for this page (this node + its direct links). */
196
- graph: LocalGraph;
197
195
  }
198
196
  interface ListingEntry {
199
197
  name: string;
@@ -210,44 +208,6 @@ type PageData = ({
210
208
  title: string;
211
209
  entries: ListingEntry[];
212
210
  };
213
- /**
214
- * One node in the knowledge graph. Two kinds:
215
- * - `article` — a wiki page. `id` is `"<coll>/<slug>"`, with `coll`/`slug` set.
216
- * - `tag` — an Obsidian-style tag hub. `id` is `"tag:<name>"`; `coll`/`slug` are
217
- * empty (a tag is not a page, so clicking it never navigates), `title` is `#name`.
218
- */
219
- interface GraphNode {
220
- id: string;
221
- kind: 'article' | 'tag';
222
- coll: string;
223
- slug: string;
224
- title: string;
225
- }
226
- /**
227
- * A graph edge. `kind` distinguishes an explicit hyperlink between two articles
228
- * (`link`) from an article↔tag-hub membership edge (`tag`). For `link` edges the
229
- * direction (source→target) is the link direction; `tag` edges go article→tag.
230
- */
231
- interface GraphEdge {
232
- source: string;
233
- target: string;
234
- kind: 'link' | 'tag';
235
- }
236
- /** The whole-wiki knowledge graph. */
237
- interface LinkGraph {
238
- nodes: GraphNode[];
239
- edges: GraphEdge[];
240
- }
241
- /**
242
- * The local neighbourhood of one node: the node itself plus its directly linked
243
- * (outgoing) and linking (incoming) neighbours, and the edges among them. `focus`
244
- * is the id of the page this neighbourhood is centred on.
245
- */
246
- interface LocalGraph {
247
- focus: string;
248
- nodes: GraphNode[];
249
- edges: GraphEdge[];
250
- }
251
211
 
252
212
  /** Base class for all expected, user-facing diffwiki failures. */
253
213
  declare class DiffwikiError extends Error {
@@ -440,18 +400,8 @@ declare function fileTimestamps(absPath: string): Promise<FileTimestamps>;
440
400
  declare function toTreeNodes(coll: string, nodes: ArticleNode[]): TreeNode[];
441
401
  declare function buildCollections(): Promise<CollectionInfo[]>;
442
402
  declare function buildNavTree(): Promise<CollectionTree[]>;
443
- /**
444
- * Resolve a route to a rendered doc or a generated directory listing (or null).
445
- *
446
- * `opts.include` restricts the doc's sidebar `graph` preview to the given set of
447
- * collection names — supplied by the static exporter for subset exports so the
448
- * per-page local graph matches the filtered overlay `graph.json` and never links
449
- * to a page that was excluded from the build. Omitting it (the dynamic server
450
- * path, which serves every collection) keeps the whole-wiki neighbourhood.
451
- */
452
- declare function resolvePage(coll: string, slug: string, opts?: {
453
- include?: ReadonlySet<string>;
454
- }): Promise<PageData | null>;
403
+ /** Resolve a route to a rendered doc or a generated directory listing (or null). */
404
+ declare function resolvePage(coll: string, slug: string): Promise<PageData | null>;
455
405
 
456
406
  /**
457
407
  * Collection selection for `diffwiki export` — resolve which collection names to
@@ -653,26 +603,18 @@ declare function listArticleTree(coll: string): Promise<ArticleNode[]>;
653
603
  */
654
604
  declare function readArticle(coll: string, slug: string): Promise<ArticleContent>;
655
605
 
656
- /**
657
- * Route context for an article being rendered — used both to rewrite relative
658
- * inter-page links into in-app routes and to resolve links to `coll/slug` graph
659
- * node ids.
660
- */
606
+ /** Route context for an article being rendered — used to resolve relative links. */
661
607
  interface RenderContext {
662
608
  /** Collection name (first route segment). */
663
609
  coll: string;
664
610
  /** Article route slug (path within the collection, no extension), e.g. "a/b/deep". */
665
611
  slug: string;
666
612
  /**
667
- * True for a directory index page: relative links resolve against the folder
668
- * (`slug`) itself, not its parent so `./sub` from `guide/index.md` → `guide/sub`.
613
+ * True when the source file is a folder-index page (`<slug>/index.md`). Such a
614
+ * page shares its route slug with the folder, so relative links must resolve
615
+ * against the folder itself, not its parent. Defaults to false (leaf page).
669
616
  */
670
617
  isIndex?: boolean;
671
- /**
672
- * Deployment base path (e.g. `/my-repo/`) stripped from absolute route links so
673
- * the collection segment resolves cleanly. Defaults to `DIFFWIKI_BASE`.
674
- */
675
- base?: string;
676
618
  }
677
619
  /** The result of rendering an article body to HTML. */
678
620
  interface RenderedArticle {
@@ -680,47 +622,17 @@ interface RenderedArticle {
680
622
  html: string;
681
623
  /** h2/h3 headings collected during render, in document order. */
682
624
  toc: TocItem[];
683
- /**
684
- * Internal outgoing links as `coll/slug` node ids, de-duplicated, in document
685
- * order. Only populated when a {@link RenderContext} is supplied; external
686
- * links, in-page anchors, and unresolvable targets are dropped.
687
- */
688
- links: string[];
689
625
  }
690
626
  /**
691
627
  * Render an article body (markdown) to Shiki-highlighted HTML plus its table of
692
- * contents. Rewrites relative inter-page links to in-app routes and collects the
693
- * article's internal outgoing links (`coll/slug` node ids) for the knowledge graph.
694
- * Runs entirely server-side; the client mounts the HTML directly with no
695
- * `eval`/`new Function`, so it works under a strict CSP. Code is highlighted here
696
- * (dual github-light/github-dark via CSS variables); mermaid fences are marked for
697
- * client rendering.
628
+ * contents, rewriting relative links to in-app routes via `ctx`.
629
+ * Runs entirely server-side; the client mounts the HTML directly with
630
+ * no `eval`/`new Function`, so it works under a strict CSP. Code is highlighted
631
+ * here (dual github-light/github-dark via CSS variables); mermaid fences are
632
+ * marked for client rendering.
698
633
  */
699
634
  declare function renderArticle(body: string, ctx: RenderContext): Promise<RenderedArticle>;
700
635
 
701
- /** Clear the memoised whole-wiki graph (tests; future live-reload invalidation). */
702
- declare function invalidateLinkGraph(): void;
703
- /**
704
- * Build the whole-wiki graph: one node per article, one directed edge per resolved
705
- * internal link (self/dead/external dropped, duplicates collapsed), plus tag hubs.
706
- * Memoised per process; {@link invalidateLinkGraph} clears it.
707
- *
708
- * TODO(live-reload): the memo is process-keyed and the dynamic server fn never
709
- * invalidates, so a long-lived server freezes the graph until restart.
710
- */
711
- declare function buildLinkGraph(): Promise<LinkGraph>;
712
- /**
713
- * Slice the local neighbourhood of `focusId`: the focus, its direct link neighbours,
714
- * its tag hubs, and its co-tagged sibling articles (2 hops through a shared hub, so
715
- * the tag-proximity view has neighbours rather than a lone dot). All edges among the
716
- * kept nodes are retained; an unknown focus yields an empty-but-focused graph.
717
- * `siblingCap` bounds the co-tagged siblings pulled in.
718
- *
719
- * `include` prunes to the given collection set before slicing (tag hubs kept), so a
720
- * subset export's preview never links to an excluded page.
721
- */
722
- declare function localGraph(graph: LinkGraph, focusId: string, include?: ReadonlySet<string>, siblingCap?: number): LocalGraph;
723
-
724
636
  /**
725
637
  * Host-side plugin invocation and search-provider loading.
726
638
  *
@@ -890,4 +802,4 @@ declare function findEnabledPlugin(kind: PluginKind, name?: string): Promise<Plu
890
802
  /** Find a plugin record by name (regardless of enabled state). */
891
803
  declare function findPluginRecord(name: string): Promise<PluginRecord | undefined>;
892
804
 
893
- export { type AddExternalOptions, type Article, type ArticleContent, type ArticleNode, ArticleNotFoundError, CollectionAlreadyAddedError, type CollectionEntry, CollectionExistsError, type CollectionGitStatus, type CollectionInfo, CollectionNotFoundError, type CollectionSelection, type CollectionSelectionResult, type CollectionTree, type CollectionType, type Config, type Diagnostic, type DiagnosticLevel, DiffwikiError, type DiffwikiLogLevel, type DocData, type FileTimestamps, type GraphEdge, type GraphNode, type InitOptions, type InstallOutcome, InvalidExportSelectionError, InvalidTargetError, type KnownPlugin, type LinkGraph, type ListingEntry, type LocalGraph, NATIVE_ENGINE, NoDefaultCollectionError, type NodeMeta, type PageData, type ParsedArticle, type PluginCapabilities, PluginError, type PluginEvent, type PluginHealth, type PluginKind, type PluginReadiness, type PluginRecord, type PluginRegistryFile, type QueryHit, type QueryOptions, REPO_CONFIG_FILE, type Registry, type RenderContext, type RenderedArticle, type RepoConfig, SearchDoc, type SearchMode, type SearchOutcome, type SearchProvider, type SerializableArticle, type SetupOutcome, type SetupStep, type TocItem, type TreeNode, type UpstreamStatus, WorktreeNotAllowedError, addExternalCollection, addTags, buildArticleTree, buildCollections, buildLinkGraph, buildNavTree, buildSearchIndex, collectionDir, collectionGitStatus, collectionsDir, configPath, createArticle, createCollection, currentBranch, deregisterPlugin, doctor, emitPluginEvent, ensureGitRepo, ensurePluginRoot, fileTimestamps, findCollection, findCollectionById, findEnabledPlugin, findPluginRecord, getConfigValue, getLogger, gitToplevel, initRepoWiki, installPlugin, invalidateLinkGraph, isLinkedWorktree, listArticleTree, listAvailablePlugins, listCollections, listEnabledPlugins, listPlugins, listSearchModes, loadSearchProvider, localGraph, nativeSearch, parseAddTarget, parseArticle, parsePathTarget, pluginsDir, pluginsRegistryPath, query, readArticle, readConfig, readPluginRegistry, readRegistry, readRepoConfig, registerCollection, registerPlugin, registryPath, removeArticle, removeCollection, removePlugin, removeTags, renderArticle, repoId, repoRemoteName, resolveCollectionSelection, resolveCwdCollection, resolveDefaultSearchMode, resolveHome, resolveLogLevel, resolvePage, search, serializeArticle, setConfigValue, setTags, slugify, toTreeNodes, unregisterCollection, updateArticleBody, upsertPlugin, upstreamStatus, writeConfig, writePluginRegistry, writeRegistry };
805
+ export { type AddExternalOptions, type Article, type ArticleContent, type ArticleNode, ArticleNotFoundError, CollectionAlreadyAddedError, type CollectionEntry, CollectionExistsError, type CollectionGitStatus, type CollectionInfo, CollectionNotFoundError, type CollectionSelection, type CollectionSelectionResult, type CollectionTree, type CollectionType, type Config, type Diagnostic, type DiagnosticLevel, DiffwikiError, type DiffwikiLogLevel, type DocData, type FileTimestamps, type InitOptions, type InstallOutcome, InvalidExportSelectionError, InvalidTargetError, type KnownPlugin, type ListingEntry, NATIVE_ENGINE, NoDefaultCollectionError, type NodeMeta, type PageData, type ParsedArticle, type PluginCapabilities, PluginError, type PluginEvent, type PluginHealth, type PluginKind, type PluginReadiness, type PluginRecord, type PluginRegistryFile, type QueryHit, type QueryOptions, REPO_CONFIG_FILE, type Registry, type RenderContext, type RenderedArticle, type RepoConfig, SearchDoc, type SearchMode, type SearchOutcome, type SearchProvider, type SerializableArticle, type SetupOutcome, type SetupStep, type TocItem, type TreeNode, type UpstreamStatus, WorktreeNotAllowedError, addExternalCollection, addTags, buildArticleTree, buildCollections, buildNavTree, buildSearchIndex, collectionDir, collectionGitStatus, collectionsDir, configPath, createArticle, createCollection, currentBranch, deregisterPlugin, doctor, emitPluginEvent, ensureGitRepo, ensurePluginRoot, fileTimestamps, findCollection, findCollectionById, findEnabledPlugin, findPluginRecord, getConfigValue, getLogger, gitToplevel, initRepoWiki, installPlugin, isLinkedWorktree, listArticleTree, listAvailablePlugins, listCollections, listEnabledPlugins, listPlugins, listSearchModes, loadSearchProvider, nativeSearch, parseAddTarget, parseArticle, parsePathTarget, pluginsDir, pluginsRegistryPath, query, readArticle, readConfig, readPluginRegistry, readRegistry, readRepoConfig, registerCollection, registerPlugin, registryPath, removeArticle, removeCollection, removePlugin, removeTags, renderArticle, repoId, repoRemoteName, resolveCollectionSelection, resolveCwdCollection, resolveDefaultSearchMode, resolveHome, resolveLogLevel, resolvePage, search, serializeArticle, setConfigValue, setTags, slugify, toTreeNodes, unregisterCollection, updateArticleBody, upsertPlugin, upstreamStatus, writeConfig, writePluginRegistry, writeRegistry };
package/dist/index.d.ts CHANGED
@@ -192,8 +192,6 @@ interface DocData {
192
192
  created?: string;
193
193
  /** ISO-8601 last-update date, sourced from git history / filesystem (not frontmatter). */
194
194
  updated?: string;
195
- /** Local knowledge-graph neighbourhood for this page (this node + its direct links). */
196
- graph: LocalGraph;
197
195
  }
198
196
  interface ListingEntry {
199
197
  name: string;
@@ -210,44 +208,6 @@ type PageData = ({
210
208
  title: string;
211
209
  entries: ListingEntry[];
212
210
  };
213
- /**
214
- * One node in the knowledge graph. Two kinds:
215
- * - `article` — a wiki page. `id` is `"<coll>/<slug>"`, with `coll`/`slug` set.
216
- * - `tag` — an Obsidian-style tag hub. `id` is `"tag:<name>"`; `coll`/`slug` are
217
- * empty (a tag is not a page, so clicking it never navigates), `title` is `#name`.
218
- */
219
- interface GraphNode {
220
- id: string;
221
- kind: 'article' | 'tag';
222
- coll: string;
223
- slug: string;
224
- title: string;
225
- }
226
- /**
227
- * A graph edge. `kind` distinguishes an explicit hyperlink between two articles
228
- * (`link`) from an article↔tag-hub membership edge (`tag`). For `link` edges the
229
- * direction (source→target) is the link direction; `tag` edges go article→tag.
230
- */
231
- interface GraphEdge {
232
- source: string;
233
- target: string;
234
- kind: 'link' | 'tag';
235
- }
236
- /** The whole-wiki knowledge graph. */
237
- interface LinkGraph {
238
- nodes: GraphNode[];
239
- edges: GraphEdge[];
240
- }
241
- /**
242
- * The local neighbourhood of one node: the node itself plus its directly linked
243
- * (outgoing) and linking (incoming) neighbours, and the edges among them. `focus`
244
- * is the id of the page this neighbourhood is centred on.
245
- */
246
- interface LocalGraph {
247
- focus: string;
248
- nodes: GraphNode[];
249
- edges: GraphEdge[];
250
- }
251
211
 
252
212
  /** Base class for all expected, user-facing diffwiki failures. */
253
213
  declare class DiffwikiError extends Error {
@@ -440,18 +400,8 @@ declare function fileTimestamps(absPath: string): Promise<FileTimestamps>;
440
400
  declare function toTreeNodes(coll: string, nodes: ArticleNode[]): TreeNode[];
441
401
  declare function buildCollections(): Promise<CollectionInfo[]>;
442
402
  declare function buildNavTree(): Promise<CollectionTree[]>;
443
- /**
444
- * Resolve a route to a rendered doc or a generated directory listing (or null).
445
- *
446
- * `opts.include` restricts the doc's sidebar `graph` preview to the given set of
447
- * collection names — supplied by the static exporter for subset exports so the
448
- * per-page local graph matches the filtered overlay `graph.json` and never links
449
- * to a page that was excluded from the build. Omitting it (the dynamic server
450
- * path, which serves every collection) keeps the whole-wiki neighbourhood.
451
- */
452
- declare function resolvePage(coll: string, slug: string, opts?: {
453
- include?: ReadonlySet<string>;
454
- }): Promise<PageData | null>;
403
+ /** Resolve a route to a rendered doc or a generated directory listing (or null). */
404
+ declare function resolvePage(coll: string, slug: string): Promise<PageData | null>;
455
405
 
456
406
  /**
457
407
  * Collection selection for `diffwiki export` — resolve which collection names to
@@ -653,26 +603,18 @@ declare function listArticleTree(coll: string): Promise<ArticleNode[]>;
653
603
  */
654
604
  declare function readArticle(coll: string, slug: string): Promise<ArticleContent>;
655
605
 
656
- /**
657
- * Route context for an article being rendered — used both to rewrite relative
658
- * inter-page links into in-app routes and to resolve links to `coll/slug` graph
659
- * node ids.
660
- */
606
+ /** Route context for an article being rendered — used to resolve relative links. */
661
607
  interface RenderContext {
662
608
  /** Collection name (first route segment). */
663
609
  coll: string;
664
610
  /** Article route slug (path within the collection, no extension), e.g. "a/b/deep". */
665
611
  slug: string;
666
612
  /**
667
- * True for a directory index page: relative links resolve against the folder
668
- * (`slug`) itself, not its parent so `./sub` from `guide/index.md` → `guide/sub`.
613
+ * True when the source file is a folder-index page (`<slug>/index.md`). Such a
614
+ * page shares its route slug with the folder, so relative links must resolve
615
+ * against the folder itself, not its parent. Defaults to false (leaf page).
669
616
  */
670
617
  isIndex?: boolean;
671
- /**
672
- * Deployment base path (e.g. `/my-repo/`) stripped from absolute route links so
673
- * the collection segment resolves cleanly. Defaults to `DIFFWIKI_BASE`.
674
- */
675
- base?: string;
676
618
  }
677
619
  /** The result of rendering an article body to HTML. */
678
620
  interface RenderedArticle {
@@ -680,47 +622,17 @@ interface RenderedArticle {
680
622
  html: string;
681
623
  /** h2/h3 headings collected during render, in document order. */
682
624
  toc: TocItem[];
683
- /**
684
- * Internal outgoing links as `coll/slug` node ids, de-duplicated, in document
685
- * order. Only populated when a {@link RenderContext} is supplied; external
686
- * links, in-page anchors, and unresolvable targets are dropped.
687
- */
688
- links: string[];
689
625
  }
690
626
  /**
691
627
  * Render an article body (markdown) to Shiki-highlighted HTML plus its table of
692
- * contents. Rewrites relative inter-page links to in-app routes and collects the
693
- * article's internal outgoing links (`coll/slug` node ids) for the knowledge graph.
694
- * Runs entirely server-side; the client mounts the HTML directly with no
695
- * `eval`/`new Function`, so it works under a strict CSP. Code is highlighted here
696
- * (dual github-light/github-dark via CSS variables); mermaid fences are marked for
697
- * client rendering.
628
+ * contents, rewriting relative links to in-app routes via `ctx`.
629
+ * Runs entirely server-side; the client mounts the HTML directly with
630
+ * no `eval`/`new Function`, so it works under a strict CSP. Code is highlighted
631
+ * here (dual github-light/github-dark via CSS variables); mermaid fences are
632
+ * marked for client rendering.
698
633
  */
699
634
  declare function renderArticle(body: string, ctx: RenderContext): Promise<RenderedArticle>;
700
635
 
701
- /** Clear the memoised whole-wiki graph (tests; future live-reload invalidation). */
702
- declare function invalidateLinkGraph(): void;
703
- /**
704
- * Build the whole-wiki graph: one node per article, one directed edge per resolved
705
- * internal link (self/dead/external dropped, duplicates collapsed), plus tag hubs.
706
- * Memoised per process; {@link invalidateLinkGraph} clears it.
707
- *
708
- * TODO(live-reload): the memo is process-keyed and the dynamic server fn never
709
- * invalidates, so a long-lived server freezes the graph until restart.
710
- */
711
- declare function buildLinkGraph(): Promise<LinkGraph>;
712
- /**
713
- * Slice the local neighbourhood of `focusId`: the focus, its direct link neighbours,
714
- * its tag hubs, and its co-tagged sibling articles (2 hops through a shared hub, so
715
- * the tag-proximity view has neighbours rather than a lone dot). All edges among the
716
- * kept nodes are retained; an unknown focus yields an empty-but-focused graph.
717
- * `siblingCap` bounds the co-tagged siblings pulled in.
718
- *
719
- * `include` prunes to the given collection set before slicing (tag hubs kept), so a
720
- * subset export's preview never links to an excluded page.
721
- */
722
- declare function localGraph(graph: LinkGraph, focusId: string, include?: ReadonlySet<string>, siblingCap?: number): LocalGraph;
723
-
724
636
  /**
725
637
  * Host-side plugin invocation and search-provider loading.
726
638
  *
@@ -890,4 +802,4 @@ declare function findEnabledPlugin(kind: PluginKind, name?: string): Promise<Plu
890
802
  /** Find a plugin record by name (regardless of enabled state). */
891
803
  declare function findPluginRecord(name: string): Promise<PluginRecord | undefined>;
892
804
 
893
- export { type AddExternalOptions, type Article, type ArticleContent, type ArticleNode, ArticleNotFoundError, CollectionAlreadyAddedError, type CollectionEntry, CollectionExistsError, type CollectionGitStatus, type CollectionInfo, CollectionNotFoundError, type CollectionSelection, type CollectionSelectionResult, type CollectionTree, type CollectionType, type Config, type Diagnostic, type DiagnosticLevel, DiffwikiError, type DiffwikiLogLevel, type DocData, type FileTimestamps, type GraphEdge, type GraphNode, type InitOptions, type InstallOutcome, InvalidExportSelectionError, InvalidTargetError, type KnownPlugin, type LinkGraph, type ListingEntry, type LocalGraph, NATIVE_ENGINE, NoDefaultCollectionError, type NodeMeta, type PageData, type ParsedArticle, type PluginCapabilities, PluginError, type PluginEvent, type PluginHealth, type PluginKind, type PluginReadiness, type PluginRecord, type PluginRegistryFile, type QueryHit, type QueryOptions, REPO_CONFIG_FILE, type Registry, type RenderContext, type RenderedArticle, type RepoConfig, SearchDoc, type SearchMode, type SearchOutcome, type SearchProvider, type SerializableArticle, type SetupOutcome, type SetupStep, type TocItem, type TreeNode, type UpstreamStatus, WorktreeNotAllowedError, addExternalCollection, addTags, buildArticleTree, buildCollections, buildLinkGraph, buildNavTree, buildSearchIndex, collectionDir, collectionGitStatus, collectionsDir, configPath, createArticle, createCollection, currentBranch, deregisterPlugin, doctor, emitPluginEvent, ensureGitRepo, ensurePluginRoot, fileTimestamps, findCollection, findCollectionById, findEnabledPlugin, findPluginRecord, getConfigValue, getLogger, gitToplevel, initRepoWiki, installPlugin, invalidateLinkGraph, isLinkedWorktree, listArticleTree, listAvailablePlugins, listCollections, listEnabledPlugins, listPlugins, listSearchModes, loadSearchProvider, localGraph, nativeSearch, parseAddTarget, parseArticle, parsePathTarget, pluginsDir, pluginsRegistryPath, query, readArticle, readConfig, readPluginRegistry, readRegistry, readRepoConfig, registerCollection, registerPlugin, registryPath, removeArticle, removeCollection, removePlugin, removeTags, renderArticle, repoId, repoRemoteName, resolveCollectionSelection, resolveCwdCollection, resolveDefaultSearchMode, resolveHome, resolveLogLevel, resolvePage, search, serializeArticle, setConfigValue, setTags, slugify, toTreeNodes, unregisterCollection, updateArticleBody, upsertPlugin, upstreamStatus, writeConfig, writePluginRegistry, writeRegistry };
805
+ export { type AddExternalOptions, type Article, type ArticleContent, type ArticleNode, ArticleNotFoundError, CollectionAlreadyAddedError, type CollectionEntry, CollectionExistsError, type CollectionGitStatus, type CollectionInfo, CollectionNotFoundError, type CollectionSelection, type CollectionSelectionResult, type CollectionTree, type CollectionType, type Config, type Diagnostic, type DiagnosticLevel, DiffwikiError, type DiffwikiLogLevel, type DocData, type FileTimestamps, type InitOptions, type InstallOutcome, InvalidExportSelectionError, InvalidTargetError, type KnownPlugin, type ListingEntry, NATIVE_ENGINE, NoDefaultCollectionError, type NodeMeta, type PageData, type ParsedArticle, type PluginCapabilities, PluginError, type PluginEvent, type PluginHealth, type PluginKind, type PluginReadiness, type PluginRecord, type PluginRegistryFile, type QueryHit, type QueryOptions, REPO_CONFIG_FILE, type Registry, type RenderContext, type RenderedArticle, type RepoConfig, SearchDoc, type SearchMode, type SearchOutcome, type SearchProvider, type SerializableArticle, type SetupOutcome, type SetupStep, type TocItem, type TreeNode, type UpstreamStatus, WorktreeNotAllowedError, addExternalCollection, addTags, buildArticleTree, buildCollections, buildNavTree, buildSearchIndex, collectionDir, collectionGitStatus, collectionsDir, configPath, createArticle, createCollection, currentBranch, deregisterPlugin, doctor, emitPluginEvent, ensureGitRepo, ensurePluginRoot, fileTimestamps, findCollection, findCollectionById, findEnabledPlugin, findPluginRecord, getConfigValue, getLogger, gitToplevel, initRepoWiki, installPlugin, isLinkedWorktree, listArticleTree, listAvailablePlugins, listCollections, listEnabledPlugins, listPlugins, listSearchModes, loadSearchProvider, nativeSearch, parseAddTarget, parseArticle, parsePathTarget, pluginsDir, pluginsRegistryPath, query, readArticle, readConfig, readPluginRegistry, readRegistry, readRepoConfig, registerCollection, registerPlugin, registryPath, removeArticle, removeCollection, removePlugin, removeTags, renderArticle, repoId, repoRemoteName, resolveCollectionSelection, resolveCwdCollection, resolveDefaultSearchMode, resolveHome, resolveLogLevel, resolvePage, search, serializeArticle, setConfigValue, setTags, slugify, toTreeNodes, unregisterCollection, updateArticleBody, upsertPlugin, upstreamStatus, writeConfig, writePluginRegistry, writeRegistry };
package/dist/index.js CHANGED
@@ -318,8 +318,8 @@ function isEpochish(d) {
318
318
  }
319
319
  var timestampCache = /* @__PURE__ */ new Map();
320
320
  async function fileTimestamps(absPath) {
321
- const cached2 = timestampCache.get(absPath);
322
- if (cached2) return cached2;
321
+ const cached = timestampCache.get(absPath);
322
+ if (cached) return cached;
323
323
  const dir = path2.dirname(absPath);
324
324
  const base = path2.basename(absPath);
325
325
  const [addLog, lastLog] = await Promise.all([
@@ -673,56 +673,6 @@ function rehypeCollectToc(toc) {
673
673
  });
674
674
  };
675
675
  }
676
- function isExternalHref(href) {
677
- if (!href) return true;
678
- if (href.startsWith("#")) return true;
679
- if (href.startsWith("//")) return true;
680
- return /^[a-z][a-z0-9+.-]*:/i.test(href);
681
- }
682
- function resolveHref(href, ctx) {
683
- if (isExternalHref(href)) return null;
684
- const clean = (p) => p.replace(/[?#].*$/, "").replace(/\.(mdx?|html)$/i, "").replace(/\/+$/, "");
685
- const target = clean(href);
686
- if (!target) return null;
687
- if (target.startsWith("/")) {
688
- const base = ctx.base ?? process.env.DIFFWIKI_BASE ?? "";
689
- const baseSegs2 = base.split("/").filter(Boolean);
690
- let segs = target.split("/").filter(Boolean);
691
- if (baseSegs2.length > 0 && baseSegs2.every((b, i) => segs[i] === b)) {
692
- segs = segs.slice(baseSegs2.length);
693
- }
694
- if (segs.length < 2) return null;
695
- return segs.join("/");
696
- }
697
- const dir = ctx.isIndex ? ctx.slug : ctx.slug.includes("/") ? ctx.slug.slice(0, ctx.slug.lastIndexOf("/")) : "";
698
- const baseSegs = dir ? dir.split("/") : [];
699
- const out = [...baseSegs];
700
- for (const seg of target.split("/")) {
701
- if (seg === "" || seg === ".") continue;
702
- if (seg === "..") {
703
- if (out.length === 0) return null;
704
- out.pop();
705
- continue;
706
- }
707
- out.push(seg);
708
- }
709
- if (out.length === 0) return null;
710
- return `${ctx.coll}/${out.join("/")}`;
711
- }
712
- function rehypeCollectLinks(links, ctx) {
713
- const seen = /* @__PURE__ */ new Set();
714
- return (tree) => {
715
- visit(tree, "element", (node) => {
716
- if (node.tagName !== "a") return;
717
- const href = node.properties?.href;
718
- if (typeof href !== "string") return;
719
- const id = resolveHref(href, ctx);
720
- if (!id || seen.has(id)) return;
721
- seen.add(id);
722
- links.push(id);
723
- });
724
- };
725
- }
726
676
  function rehypeStripLeadingH1() {
727
677
  return (tree) => {
728
678
  let removed = false;
@@ -750,110 +700,13 @@ function rehypeMarkMermaid() {
750
700
  }
751
701
  async function renderArticle(body, ctx) {
752
702
  const toc = [];
753
- const links = [];
754
- const pipeline = unified().use(remarkParse).use(remarkGfm).use(remarkRehype).use(rehypeSlug).use(rehypeCollectToc, toc).use(rehypeCollectLinks, links, ctx);
755
- const file = await pipeline.use(rehypeStripLeadingH1).use(rehypeMarkMermaid).use(rehypeRewriteLinks, ctx).use(rehypeShiki, {
703
+ const file = await unified().use(remarkParse).use(remarkGfm).use(remarkRehype).use(rehypeSlug).use(rehypeCollectToc, toc).use(rehypeStripLeadingH1).use(rehypeMarkMermaid).use(rehypeRewriteLinks, ctx).use(rehypeShiki, {
756
704
  themes: { light: "github-light", dark: "github-dark" },
757
705
  defaultColor: false,
758
706
  langAlias: LANG_ALIASES,
759
707
  fallbackLanguage: "plaintext"
760
708
  }).use(rehypeStringify).process(body);
761
- return { html: String(file), toc, links };
762
- }
763
-
764
- // src/graph.ts
765
- function tagId(tag) {
766
- return `tag:${tag}`;
767
- }
768
- async function collectArticle(coll, slug, fallbackTitle, outNodes, outEdges, outTags) {
769
- let art;
770
- try {
771
- art = await readArticle(coll, slug);
772
- } catch (err) {
773
- if (err instanceof ArticleNotFoundError) return;
774
- throw err;
775
- }
776
- const id = `${coll}/${slug}`;
777
- outNodes.push({ id, kind: "article", coll, slug, title: art.title ?? fallbackTitle ?? slug });
778
- const { links } = await renderArticle(art.body, { coll, slug, isIndex: art.isIndex });
779
- for (const target of links) outEdges.push({ source: id, target, kind: "link" });
780
- for (const tag of art.tags) outTags.push({ source: id, tag });
781
- }
782
- async function collectCollection(coll, nodes, outNodes, outEdges, outTags) {
783
- for (const n of nodes) {
784
- if (n.slug) await collectArticle(coll, n.slug, n.title ?? n.name, outNodes, outEdges, outTags);
785
- if (n.children) await collectCollection(coll, n.children, outNodes, outEdges, outTags);
786
- }
787
- }
788
- var cached;
789
- function invalidateLinkGraph() {
790
- cached = void 0;
791
- }
792
- async function buildLinkGraph() {
793
- if (cached) return cached;
794
- const nodes = [];
795
- const rawEdges = [];
796
- const rawTags = [];
797
- for (const e of await listCollections()) {
798
- try {
799
- await collectArticle(e.name, "", e.name, nodes, rawEdges, rawTags);
800
- await collectCollection(e.name, await listArticleTree(e.name), nodes, rawEdges, rawTags);
801
- } catch {
802
- }
803
- }
804
- const nodeIds = new Set(nodes.map((n) => n.id));
805
- const seen = /* @__PURE__ */ new Set();
806
- const edges = [];
807
- for (const edge of rawEdges) {
808
- if (edge.source === edge.target) continue;
809
- if (!nodeIds.has(edge.target)) continue;
810
- const key = `${edge.source} ${edge.target}`;
811
- if (seen.has(key)) continue;
812
- seen.add(key);
813
- edges.push(edge);
814
- }
815
- const tagSeen = /* @__PURE__ */ new Set();
816
- for (const { source, tag } of rawTags) {
817
- if (!nodeIds.has(source)) continue;
818
- const id = tagId(tag);
819
- if (!tagSeen.has(id)) {
820
- tagSeen.add(id);
821
- nodes.push({ id, kind: "tag", coll: "", slug: "", title: `#${tag}` });
822
- }
823
- const key = `${source} ${id}`;
824
- if (seen.has(key)) continue;
825
- seen.add(key);
826
- edges.push({ source, target: id, kind: "tag" });
827
- }
828
- cached = { nodes, edges };
829
- return cached;
830
- }
831
- function localGraph(graph, focusId, include, siblingCap = 12) {
832
- const scoped = include ? {
833
- // Tag hubs survive the filter — never a page, so they can't 404.
834
- nodes: graph.nodes.filter((n) => n.kind === "tag" || include.has(n.coll)),
835
- edges: graph.edges
836
- } : graph;
837
- const byId = new Map(scoped.nodes.map((n) => [n.id, n]));
838
- const focus = byId.get(focusId);
839
- if (!focus) return { focus: focusId, nodes: [], edges: [] };
840
- const keep = /* @__PURE__ */ new Set([focusId]);
841
- for (const e of scoped.edges) {
842
- if (e.source === focusId && byId.has(e.target)) keep.add(e.target);
843
- if (e.target === focusId && byId.has(e.source)) keep.add(e.source);
844
- }
845
- const focusTagHubs = new Set([...keep].filter((id) => byId.get(id)?.kind === "tag"));
846
- let siblings = 0;
847
- for (const e of scoped.edges) {
848
- if (siblings >= siblingCap) break;
849
- if (e.kind === "tag" && focusTagHubs.has(e.target) && byId.has(e.source) && !keep.has(e.source)) {
850
- keep.add(e.source);
851
- siblings++;
852
- }
853
- }
854
- const nodes = scoped.nodes.filter((n) => keep.has(n.id));
855
- const edges = scoped.edges.filter((e) => keep.has(e.source) && keep.has(e.target));
856
- return { focus: focusId, nodes, edges };
709
+ return { html: String(file), toc };
857
710
  }
858
711
 
859
712
  // src/site.ts
@@ -906,7 +759,7 @@ function findNode(nodes, slug) {
906
759
  }
907
760
  return void 0;
908
761
  }
909
- async function resolvePage(coll, slug, opts) {
762
+ async function resolvePage(coll, slug) {
910
763
  try {
911
764
  const art = await readArticle(coll, slug);
912
765
  const { html, toc } = await renderArticle(art.body, {
@@ -915,7 +768,6 @@ async function resolvePage(coll, slug, opts) {
915
768
  isIndex: art.isIndex
916
769
  });
917
770
  const { created, updated } = await fileTimestamps(art.path);
918
- const graph = localGraph(await buildLinkGraph(), `${art.coll}/${art.slug}`, opts?.include);
919
771
  return {
920
772
  kind: "doc",
921
773
  coll: art.coll,
@@ -926,8 +778,7 @@ async function resolvePage(coll, slug, opts) {
926
778
  toc,
927
779
  path: `/${art.coll}/${art.slug}`,
928
780
  created,
929
- updated,
930
- graph
781
+ updated
931
782
  };
932
783
  } catch (err) {
933
784
  if (err instanceof CollectionNotFoundError) return null;
@@ -1509,7 +1360,6 @@ export {
1509
1360
  addTags,
1510
1361
  buildArticleTree,
1511
1362
  buildCollections,
1512
- buildLinkGraph,
1513
1363
  buildNavTree,
1514
1364
  buildSearchIndex,
1515
1365
  collectionDir,
@@ -1534,7 +1384,6 @@ export {
1534
1384
  gitToplevel,
1535
1385
  initRepoWiki,
1536
1386
  installPlugin,
1537
- invalidateLinkGraph,
1538
1387
  isLinkedWorktree,
1539
1388
  listArticleTree,
1540
1389
  listAvailablePlugins,
@@ -1543,7 +1392,6 @@ export {
1543
1392
  listPlugins,
1544
1393
  listSearchModes,
1545
1394
  loadSearchProvider,
1546
- localGraph,
1547
1395
  nativeSearch,
1548
1396
  parseAddTarget,
1549
1397
  parseArticle,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "diffwiki-core",
3
- "version": "0.6.1-rc.202607242058.7b3501c",
3
+ "version": "0.6.1",
4
4
  "description": "Core configuration, registry, and article management for diffwiki",
5
5
  "license": "MIT",
6
6
  "type": "module",