@blogmcp/nav-core 0.1.0
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/README.md +41 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/mutate/index.d.ts +41 -0
- package/dist/mutate/index.js +123 -0
- package/dist/mutate/index.js.map +1 -0
- package/dist/resolve/index.d.ts +105 -0
- package/dist/resolve/index.js +217 -0
- package/dist/resolve/index.js.map +1 -0
- package/dist/traverse/index.d.ts +70 -0
- package/dist/traverse/index.js +213 -0
- package/dist/traverse/index.js.map +1 -0
- package/dist/types/index.d.ts +165 -0
- package/dist/types/index.js +29 -0
- package/dist/types/index.js.map +1 -0
- package/dist/validate/index.d.ts +44 -0
- package/dist/validate/index.js +175 -0
- package/dist/validate/index.js.map +1 -0
- package/package.json +62 -0
- package/src/index.ts +18 -0
- package/src/mutate/index.ts +162 -0
- package/src/resolve/index.ts +303 -0
- package/src/traverse/index.ts +274 -0
- package/src/types/index.ts +206 -0
- package/src/validate/index.ts +233 -0
package/README.md
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# @blogmcp/nav-core
|
|
2
|
+
|
|
3
|
+
Framework-agnostic core for the KB navigation (menu) tree. Pure TypeScript — no
|
|
4
|
+
I/O, no framework. The design lives in
|
|
5
|
+
[`competitor-monitor/docs/internal/navigation-model.md`](../../docs/internal/navigation-model.md).
|
|
6
|
+
|
|
7
|
+
This package is the shared logic surfaced two ways: the SDK runs it in-memory
|
|
8
|
+
over a cached tree, and the server exposes it as MCP/API tools. Mirrors the
|
|
9
|
+
`@blogmcp/mdx-schema` → renderer split.
|
|
10
|
+
|
|
11
|
+
## Modules
|
|
12
|
+
|
|
13
|
+
| Import | Contents |
|
|
14
|
+
|---|---|
|
|
15
|
+
| `@blogmcp/nav-core` | everything (barrel) |
|
|
16
|
+
| `@blogmcp/nav-core/types` | node model: `NavNode` union, `NavTree`, `FlatNode`, `LocalizedNode` |
|
|
17
|
+
| `@blogmcp/nav-core/validate` | `validate(tree, opts)` / `assertValid` — cycles, depth, dangling refs, single-owner, `data`-bag caps |
|
|
18
|
+
| `@blogmcp/nav-core/traverse` | `findNode`, `breadcrumbs`, `getSiblings`, `getNextPrev`, `getSubtree`, `flatten` (FLAT), `localize`, `prune`, `sortTree` |
|
|
19
|
+
| `@blogmcp/nav-core/mutate` | `insertNode`, `removeNode`, `moveNode`, `reorderChildren` — immutable; throw `NavMutationError` |
|
|
20
|
+
|
|
21
|
+
## Invariants
|
|
22
|
+
|
|
23
|
+
- **Immutable**: traversal and mutation never edit inputs; mutations return new trees.
|
|
24
|
+
- **Server-authoritative**: the API re-runs `validate()` after every mutation.
|
|
25
|
+
- **Opaque `data`**: the `data` bag is never interpreted, only size/depth-capped.
|
|
26
|
+
- **Single multilingual tree**: `labels` is a per-locale map; `localize()` resolves
|
|
27
|
+
to one locale with fallback + an optional `labelResolver` (article titles come
|
|
28
|
+
from the consumer — nav-core does not know article content).
|
|
29
|
+
|
|
30
|
+
## Not here yet
|
|
31
|
+
|
|
32
|
+
The content→default-tree auto-builder and the auto-build ⊕ overrides merge ship
|
|
33
|
+
with the API service slice (they need the article content index and the
|
|
34
|
+
auto-append policy — see navigation-model.md §9).
|
|
35
|
+
|
|
36
|
+
## Scripts
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
npm test --workspace=@blogmcp/nav-core # vitest
|
|
40
|
+
npm run build --workspace=@blogmcp/nav-core # tsc → dist
|
|
41
|
+
```
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @blogmcp/nav-core
|
|
3
|
+
*
|
|
4
|
+
* Framework-agnostic core for the KB navigation (menu) tree: the node-model
|
|
5
|
+
* contract, structural validation, pure traversal, and immutable mutations.
|
|
6
|
+
* Pure TypeScript — no I/O, no framework. Consumed by the KB API service, the
|
|
7
|
+
* MCP nav tools, and the blog SDK.
|
|
8
|
+
*
|
|
9
|
+
* What lives here: types, validate, traverse, mutate, and resolve (the
|
|
10
|
+
* content→default-tree auto-builder + the auto-build⊕overrides merge). See
|
|
11
|
+
* competitor-monitor/docs/internal/navigation-model.md §2, §4, §9.
|
|
12
|
+
*/
|
|
13
|
+
export * from './types/index.js';
|
|
14
|
+
export * from './validate/index.js';
|
|
15
|
+
export * from './traverse/index.js';
|
|
16
|
+
export * from './mutate/index.js';
|
|
17
|
+
export * from './resolve/index.js';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @blogmcp/nav-core
|
|
3
|
+
*
|
|
4
|
+
* Framework-agnostic core for the KB navigation (menu) tree: the node-model
|
|
5
|
+
* contract, structural validation, pure traversal, and immutable mutations.
|
|
6
|
+
* Pure TypeScript — no I/O, no framework. Consumed by the KB API service, the
|
|
7
|
+
* MCP nav tools, and the blog SDK.
|
|
8
|
+
*
|
|
9
|
+
* What lives here: types, validate, traverse, mutate, and resolve (the
|
|
10
|
+
* content→default-tree auto-builder + the auto-build⊕overrides merge). See
|
|
11
|
+
* competitor-monitor/docs/internal/navigation-model.md §2, §4, §9.
|
|
12
|
+
*/
|
|
13
|
+
export * from './types/index.js';
|
|
14
|
+
export * from './validate/index.js';
|
|
15
|
+
export * from './traverse/index.js';
|
|
16
|
+
export * from './mutate/index.js';
|
|
17
|
+
export * from './resolve/index.js';
|
|
18
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,cAAc,kBAAkB,CAAC;AACjC,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @blogmcp/nav-core — mutate
|
|
3
|
+
*
|
|
4
|
+
* Immutable tree mutations. Every function returns a NEW tree and never edits
|
|
5
|
+
* its input. These back both the optimistic client-side apply (SDK) and the
|
|
6
|
+
* server-authoritative MCP/API tools — the server always re-runs `validate()`
|
|
7
|
+
* after a mutation (see navigation-model.md §5).
|
|
8
|
+
*
|
|
9
|
+
* Invalid operations throw {@link NavMutationError} (e.g. unknown id, or moving
|
|
10
|
+
* a node into its own descendant).
|
|
11
|
+
*/
|
|
12
|
+
import type { NavNode, NavTree } from '../types/index.js';
|
|
13
|
+
export declare class NavMutationError extends Error {
|
|
14
|
+
readonly code: NavMutationCode;
|
|
15
|
+
constructor(message: string, code: NavMutationCode);
|
|
16
|
+
}
|
|
17
|
+
export type NavMutationCode = 'NODE_NOT_FOUND' | 'PARENT_NOT_FOUND' | 'DUPLICATE_ID' | 'CYCLE' | 'NOT_A_CONTAINER';
|
|
18
|
+
export interface InsertOptions {
|
|
19
|
+
/** Parent id, or null/undefined to insert at the root level. */
|
|
20
|
+
readonly parentId?: string | null;
|
|
21
|
+
/** Position among target siblings; clamped to [0, len]. Defaults to end. */
|
|
22
|
+
readonly index?: number;
|
|
23
|
+
}
|
|
24
|
+
/** Insert a (sub)tree. Throws if the id already exists or the parent is missing. */
|
|
25
|
+
export declare function insertNode(tree: NavTree, node: NavNode, options?: InsertOptions): NavNode[];
|
|
26
|
+
/** Remove a node and its subtree. Throws if the node is not found. */
|
|
27
|
+
export declare function removeNode(tree: NavTree, id: string): NavNode[];
|
|
28
|
+
export interface MoveOptions {
|
|
29
|
+
/** New parent id, or null for the root level. */
|
|
30
|
+
readonly parentId?: string | null;
|
|
31
|
+
/** Position among the new siblings; clamped. Defaults to end. */
|
|
32
|
+
readonly index?: number;
|
|
33
|
+
}
|
|
34
|
+
/** Move a node (with its subtree) under a new parent. Throws on cycles. */
|
|
35
|
+
export declare function moveNode(tree: NavTree, id: string, options?: MoveOptions): NavNode[];
|
|
36
|
+
/**
|
|
37
|
+
* Reorder a parent's children to match `orderedIds`, reassigning each child's
|
|
38
|
+
* `order` to its new index. Ids present in the tree but absent from `orderedIds`
|
|
39
|
+
* keep their relative order at the end. Throws on unknown ids or missing parent.
|
|
40
|
+
*/
|
|
41
|
+
export declare function reorderChildren(tree: NavTree, parentId: string | null, orderedIds: readonly string[]): NavNode[];
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @blogmcp/nav-core — mutate
|
|
3
|
+
*
|
|
4
|
+
* Immutable tree mutations. Every function returns a NEW tree and never edits
|
|
5
|
+
* its input. These back both the optimistic client-side apply (SDK) and the
|
|
6
|
+
* server-authoritative MCP/API tools — the server always re-runs `validate()`
|
|
7
|
+
* after a mutation (see navigation-model.md §5).
|
|
8
|
+
*
|
|
9
|
+
* Invalid operations throw {@link NavMutationError} (e.g. unknown id, or moving
|
|
10
|
+
* a node into its own descendant).
|
|
11
|
+
*/
|
|
12
|
+
import { findNode, walk } from '../traverse/index.js';
|
|
13
|
+
export class NavMutationError extends Error {
|
|
14
|
+
code;
|
|
15
|
+
constructor(message, code) {
|
|
16
|
+
super(message);
|
|
17
|
+
this.code = code;
|
|
18
|
+
this.name = 'NavMutationError';
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
/** Insert a (sub)tree. Throws if the id already exists or the parent is missing. */
|
|
22
|
+
export function insertNode(tree, node, options = {}) {
|
|
23
|
+
if (findNode(tree, node.id)) {
|
|
24
|
+
throw new NavMutationError(`Node "${node.id}" already exists.`, 'DUPLICATE_ID');
|
|
25
|
+
}
|
|
26
|
+
const { parentId = null, index } = options;
|
|
27
|
+
if (parentId === null) {
|
|
28
|
+
return insertAt([...tree], node, index);
|
|
29
|
+
}
|
|
30
|
+
if (!findNode(tree, parentId)) {
|
|
31
|
+
throw new NavMutationError(`Parent "${parentId}" not found.`, 'PARENT_NOT_FOUND');
|
|
32
|
+
}
|
|
33
|
+
return mapInto(tree, parentId, (children) => insertAt([...children], node, index));
|
|
34
|
+
}
|
|
35
|
+
/** Remove a node and its subtree. Throws if the node is not found. */
|
|
36
|
+
export function removeNode(tree, id) {
|
|
37
|
+
if (!findNode(tree, id)) {
|
|
38
|
+
throw new NavMutationError(`Node "${id}" not found.`, 'NODE_NOT_FOUND');
|
|
39
|
+
}
|
|
40
|
+
const recur = (nodes) => nodes
|
|
41
|
+
.filter((n) => n.id !== id)
|
|
42
|
+
.map((n) => (n.children ? { ...n, children: recur(n.children) } : n));
|
|
43
|
+
return recur(tree);
|
|
44
|
+
}
|
|
45
|
+
/** Move a node (with its subtree) under a new parent. Throws on cycles. */
|
|
46
|
+
export function moveNode(tree, id, options = {}) {
|
|
47
|
+
const node = findNode(tree, id);
|
|
48
|
+
if (!node) {
|
|
49
|
+
throw new NavMutationError(`Node "${id}" not found.`, 'NODE_NOT_FOUND');
|
|
50
|
+
}
|
|
51
|
+
const { parentId = null, index } = options;
|
|
52
|
+
if (parentId !== null) {
|
|
53
|
+
if (parentId === id) {
|
|
54
|
+
throw new NavMutationError(`Cannot move node "${id}" into itself.`, 'CYCLE');
|
|
55
|
+
}
|
|
56
|
+
if (descendantIds(node).has(parentId)) {
|
|
57
|
+
throw new NavMutationError(`Cannot move node "${id}" into its own descendant "${parentId}".`, 'CYCLE');
|
|
58
|
+
}
|
|
59
|
+
if (!findNode(tree, parentId)) {
|
|
60
|
+
throw new NavMutationError(`Parent "${parentId}" not found.`, 'PARENT_NOT_FOUND');
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
const detached = removeNode(tree, id);
|
|
64
|
+
return insertNode(detached, node, { parentId, index });
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Reorder a parent's children to match `orderedIds`, reassigning each child's
|
|
68
|
+
* `order` to its new index. Ids present in the tree but absent from `orderedIds`
|
|
69
|
+
* keep their relative order at the end. Throws on unknown ids or missing parent.
|
|
70
|
+
*/
|
|
71
|
+
export function reorderChildren(tree, parentId, orderedIds) {
|
|
72
|
+
const apply = (children) => {
|
|
73
|
+
const byId = new Map(children.map((c) => [c.id, c]));
|
|
74
|
+
for (const oid of orderedIds) {
|
|
75
|
+
if (!byId.has(oid)) {
|
|
76
|
+
throw new NavMutationError(`Cannot reorder: "${oid}" is not a child of "${parentId ?? '<root>'}".`, 'NODE_NOT_FOUND');
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
const ordered = [
|
|
80
|
+
...orderedIds.map((oid) => byId.get(oid)),
|
|
81
|
+
...children.filter((c) => !orderedIds.includes(c.id)),
|
|
82
|
+
];
|
|
83
|
+
return ordered.map((child, i) => ({ ...child, order: i }));
|
|
84
|
+
};
|
|
85
|
+
if (parentId === null) {
|
|
86
|
+
return apply(tree);
|
|
87
|
+
}
|
|
88
|
+
if (!findNode(tree, parentId)) {
|
|
89
|
+
throw new NavMutationError(`Parent "${parentId}" not found.`, 'PARENT_NOT_FOUND');
|
|
90
|
+
}
|
|
91
|
+
return mapInto(tree, parentId, apply);
|
|
92
|
+
}
|
|
93
|
+
/* -------------------------------------------------------------------------- */
|
|
94
|
+
/* helpers */
|
|
95
|
+
/* -------------------------------------------------------------------------- */
|
|
96
|
+
function insertAt(siblings, node, index) {
|
|
97
|
+
const at = index === undefined ? siblings.length : clamp(index, 0, siblings.length);
|
|
98
|
+
siblings.splice(at, 0, node);
|
|
99
|
+
return siblings;
|
|
100
|
+
}
|
|
101
|
+
/** Rebuild the tree, replacing the children array of the node with id === targetId. */
|
|
102
|
+
function mapInto(tree, targetId, transform) {
|
|
103
|
+
return tree.map((node) => {
|
|
104
|
+
if (node.id === targetId) {
|
|
105
|
+
return { ...node, children: transform(node.children ?? []) };
|
|
106
|
+
}
|
|
107
|
+
if (node.children) {
|
|
108
|
+
return { ...node, children: mapInto(node.children, targetId, transform) };
|
|
109
|
+
}
|
|
110
|
+
return node;
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
function descendantIds(node) {
|
|
114
|
+
const ids = new Set();
|
|
115
|
+
walk(node.children ?? [], (n) => {
|
|
116
|
+
ids.add(n.id);
|
|
117
|
+
});
|
|
118
|
+
return ids;
|
|
119
|
+
}
|
|
120
|
+
function clamp(n, lo, hi) {
|
|
121
|
+
return Math.max(lo, Math.min(hi, n));
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/mutate/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @blogmcp/nav-core — resolve (auto-build + override merge)
|
|
3
|
+
*
|
|
4
|
+
* The heart of the "one resolved tree, never two" design (navigation-model.md §2/§4):
|
|
5
|
+
*
|
|
6
|
+
* resolved = applyOverrides( buildDefaultTree(currentContent), curatedOverrides )
|
|
7
|
+
*
|
|
8
|
+
* `buildDefaultTree` is recomputed from the CURRENT published content on every
|
|
9
|
+
* resolve, so it is never stale: a new article appears automatically, a deleted
|
|
10
|
+
* one disappears, and an override that targets a now-absent node is simply
|
|
11
|
+
* skipped (non-fatal). The curated overrides (`doc` in `kb_navigation`) store
|
|
12
|
+
* only deviations — never the flattened tree.
|
|
13
|
+
*
|
|
14
|
+
* Pure: no I/O. The consumer (KB API service) supplies the content index from SQL
|
|
15
|
+
* and persists the overrides; this module just transforms data → tree.
|
|
16
|
+
*
|
|
17
|
+
* v1 auto-build policy: group published articles by their primary tag (first tag
|
|
18
|
+
* by slug); untagged articles land in an "Unfiled" group. `frontmatter`-based
|
|
19
|
+
* parent/order is intentionally NOT used (that column is legacy/empty in the KB).
|
|
20
|
+
*/
|
|
21
|
+
import type { Locale, LocaleLabels, NavData, NavNode, NavTree } from '../types/index.js';
|
|
22
|
+
import type { BadgeSpec } from '../types/index.js';
|
|
23
|
+
export interface ContentLocaleMeta {
|
|
24
|
+
readonly title: string;
|
|
25
|
+
readonly description?: string;
|
|
26
|
+
}
|
|
27
|
+
/** One published article, as needed to auto-build the default tree. */
|
|
28
|
+
export interface ContentItem {
|
|
29
|
+
/** kb_articles.id */
|
|
30
|
+
readonly id: string;
|
|
31
|
+
/** Public URL handle (filename without extension). */
|
|
32
|
+
readonly slug?: string;
|
|
33
|
+
/** Tag slugs. */
|
|
34
|
+
readonly tags: readonly string[];
|
|
35
|
+
/** Per-locale title/description. */
|
|
36
|
+
readonly locales: Readonly<Record<Locale, ContentLocaleMeta>>;
|
|
37
|
+
/** Source language code (fallback locale for this item). */
|
|
38
|
+
readonly sourceLocale: Locale;
|
|
39
|
+
/** ISO timestamp, used as a secondary sort key. */
|
|
40
|
+
readonly updatedAt?: string;
|
|
41
|
+
}
|
|
42
|
+
export interface BuildOptions {
|
|
43
|
+
readonly nodeIdPrefix?: string;
|
|
44
|
+
readonly groupIdPrefix?: string;
|
|
45
|
+
readonly unfiledGroupId?: string;
|
|
46
|
+
readonly unfiledLabel?: string;
|
|
47
|
+
/** Per-tag display labels (e.g. from kb_tags). Falls back to the tag slug. */
|
|
48
|
+
readonly tagLabels?: Readonly<Record<string, LocaleLabels>>;
|
|
49
|
+
/** Choose an item's primary tag. Default: first tag by slug ascending. */
|
|
50
|
+
readonly primaryTag?: (item: ContentItem) => string | undefined;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Build the default navigation tree from the current published content.
|
|
54
|
+
* Deterministic: groups sorted by tag slug (Unfiled last), articles sorted by
|
|
55
|
+
* source-locale title then updatedAt. Labels are baked from content so the tree
|
|
56
|
+
* is self-contained; overrides may relabel.
|
|
57
|
+
*/
|
|
58
|
+
export declare function buildDefaultTree(items: readonly ContentItem[], options?: BuildOptions): NavNode[];
|
|
59
|
+
/** A per-node deviation from the auto-built tree. */
|
|
60
|
+
export interface NodePatch {
|
|
61
|
+
/** Move under a new parent (null = root). */
|
|
62
|
+
readonly parentId?: string | null;
|
|
63
|
+
readonly order?: number;
|
|
64
|
+
readonly hidden?: boolean;
|
|
65
|
+
/** Merged over the node's existing labels. */
|
|
66
|
+
readonly labels?: LocaleLabels;
|
|
67
|
+
readonly badge?: BadgeSpec;
|
|
68
|
+
readonly icon?: string;
|
|
69
|
+
/** group only. */
|
|
70
|
+
readonly collapsed?: boolean;
|
|
71
|
+
/** article-ref only. */
|
|
72
|
+
readonly owned?: boolean;
|
|
73
|
+
/** Merged over the node's existing data bag. */
|
|
74
|
+
readonly data?: NavData;
|
|
75
|
+
/** Drop this node (and its subtree) from the menu. */
|
|
76
|
+
readonly removed?: boolean;
|
|
77
|
+
}
|
|
78
|
+
/** A hand-authored node not derivable from content (external link, custom group, separator…). */
|
|
79
|
+
export interface PlacedNode {
|
|
80
|
+
readonly node: NavNode;
|
|
81
|
+
readonly parentId?: string | null;
|
|
82
|
+
readonly index?: number;
|
|
83
|
+
}
|
|
84
|
+
/** The curated overrides document persisted in `kb_navigation.doc`. */
|
|
85
|
+
export interface NavOverrides {
|
|
86
|
+
readonly patches?: Readonly<Record<string, NodePatch>>;
|
|
87
|
+
readonly extra?: readonly PlacedNode[];
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Apply curated overrides to a freshly auto-built tree. Order of operations:
|
|
91
|
+
* extra inserts → field patches + removals → moves → sort. Targets that no
|
|
92
|
+
* longer exist (e.g. an unpublished article) are skipped — the resolved tree
|
|
93
|
+
* always reflects current content.
|
|
94
|
+
*
|
|
95
|
+
* Extra (hand-authored) nodes are inserted FIRST so that field patches and
|
|
96
|
+
* removals below apply to them uniformly: an external link or custom group can
|
|
97
|
+
* be relabeled, hidden, reordered, or removed via the same `patches` mechanism
|
|
98
|
+
* as an auto-built node. (Previously inserts ran after patching, so any patch
|
|
99
|
+
* targeting a placed node — including `removed` — was silently a no-op.)
|
|
100
|
+
*/
|
|
101
|
+
export declare function applyOverrides(autoTree: NavTree, overrides?: NavOverrides): NavNode[];
|
|
102
|
+
/** Convenience: build + apply in one call. */
|
|
103
|
+
export declare function resolve(items: readonly ContentItem[], overrides?: NavOverrides, buildOptions?: BuildOptions): NavNode[];
|
|
104
|
+
/** Sorted union of locales present across the content index. */
|
|
105
|
+
export declare function collectLocales(items: readonly ContentItem[]): Locale[];
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @blogmcp/nav-core — resolve (auto-build + override merge)
|
|
3
|
+
*
|
|
4
|
+
* The heart of the "one resolved tree, never two" design (navigation-model.md §2/§4):
|
|
5
|
+
*
|
|
6
|
+
* resolved = applyOverrides( buildDefaultTree(currentContent), curatedOverrides )
|
|
7
|
+
*
|
|
8
|
+
* `buildDefaultTree` is recomputed from the CURRENT published content on every
|
|
9
|
+
* resolve, so it is never stale: a new article appears automatically, a deleted
|
|
10
|
+
* one disappears, and an override that targets a now-absent node is simply
|
|
11
|
+
* skipped (non-fatal). The curated overrides (`doc` in `kb_navigation`) store
|
|
12
|
+
* only deviations — never the flattened tree.
|
|
13
|
+
*
|
|
14
|
+
* Pure: no I/O. The consumer (KB API service) supplies the content index from SQL
|
|
15
|
+
* and persists the overrides; this module just transforms data → tree.
|
|
16
|
+
*
|
|
17
|
+
* v1 auto-build policy: group published articles by their primary tag (first tag
|
|
18
|
+
* by slug); untagged articles land in an "Unfiled" group. `frontmatter`-based
|
|
19
|
+
* parent/order is intentionally NOT used (that column is legacy/empty in the KB).
|
|
20
|
+
*/
|
|
21
|
+
import { findNode, sortTree } from '../traverse/index.js';
|
|
22
|
+
import { insertNode, moveNode } from '../mutate/index.js';
|
|
23
|
+
const DEFAULTS = {
|
|
24
|
+
nodeIdPrefix: 'auto:',
|
|
25
|
+
groupIdPrefix: 'auto:group:',
|
|
26
|
+
unfiledLabel: 'Unfiled',
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Build the default navigation tree from the current published content.
|
|
30
|
+
* Deterministic: groups sorted by tag slug (Unfiled last), articles sorted by
|
|
31
|
+
* source-locale title then updatedAt. Labels are baked from content so the tree
|
|
32
|
+
* is self-contained; overrides may relabel.
|
|
33
|
+
*/
|
|
34
|
+
export function buildDefaultTree(items, options = {}) {
|
|
35
|
+
const nodeIdPrefix = options.nodeIdPrefix ?? DEFAULTS.nodeIdPrefix;
|
|
36
|
+
const groupIdPrefix = options.groupIdPrefix ?? DEFAULTS.groupIdPrefix;
|
|
37
|
+
const unfiledGroupId = options.unfiledGroupId ?? `${groupIdPrefix}unfiled`;
|
|
38
|
+
const unfiledLabel = options.unfiledLabel ?? DEFAULTS.unfiledLabel;
|
|
39
|
+
const pickPrimary = options.primaryTag ?? defaultPrimaryTag;
|
|
40
|
+
const allLocales = collectLocales(items);
|
|
41
|
+
// bucket items by primary tag ('' = unfiled)
|
|
42
|
+
const buckets = new Map();
|
|
43
|
+
for (const item of items) {
|
|
44
|
+
const key = pickPrimary(item) ?? '';
|
|
45
|
+
const bucket = buckets.get(key);
|
|
46
|
+
if (bucket)
|
|
47
|
+
bucket.push(item);
|
|
48
|
+
else
|
|
49
|
+
buckets.set(key, [item]);
|
|
50
|
+
}
|
|
51
|
+
const tagKeys = [...buckets.keys()].filter((k) => k !== '').sort();
|
|
52
|
+
const orderedKeys = buckets.has('') ? [...tagKeys, ''] : tagKeys;
|
|
53
|
+
return orderedKeys.map((key, groupIndex) => {
|
|
54
|
+
const isUnfiled = key === '';
|
|
55
|
+
const groupId = isUnfiled ? unfiledGroupId : `${groupIdPrefix}${key}`;
|
|
56
|
+
const labels = isUnfiled
|
|
57
|
+
? labelsForAll(allLocales, unfiledLabel)
|
|
58
|
+
: (options.tagLabels?.[key] ?? labelsForAll(allLocales, key));
|
|
59
|
+
const children = sortItems(buckets.get(key)).map((item, i) => ({
|
|
60
|
+
id: `${nodeIdPrefix}${item.id}`,
|
|
61
|
+
type: 'article-ref',
|
|
62
|
+
ref: item.id,
|
|
63
|
+
slug: item.slug,
|
|
64
|
+
owned: true,
|
|
65
|
+
order: i,
|
|
66
|
+
labels: articleLabels(item),
|
|
67
|
+
}));
|
|
68
|
+
return {
|
|
69
|
+
id: groupId,
|
|
70
|
+
type: 'group',
|
|
71
|
+
order: groupIndex,
|
|
72
|
+
labels,
|
|
73
|
+
collapsible: true,
|
|
74
|
+
data: { auto: true, tag: isUnfiled ? null : key },
|
|
75
|
+
children,
|
|
76
|
+
};
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Apply curated overrides to a freshly auto-built tree. Order of operations:
|
|
81
|
+
* extra inserts → field patches + removals → moves → sort. Targets that no
|
|
82
|
+
* longer exist (e.g. an unpublished article) are skipped — the resolved tree
|
|
83
|
+
* always reflects current content.
|
|
84
|
+
*
|
|
85
|
+
* Extra (hand-authored) nodes are inserted FIRST so that field patches and
|
|
86
|
+
* removals below apply to them uniformly: an external link or custom group can
|
|
87
|
+
* be relabeled, hidden, reordered, or removed via the same `patches` mechanism
|
|
88
|
+
* as an auto-built node. (Previously inserts ran after patching, so any patch
|
|
89
|
+
* targeting a placed node — including `removed` — was silently a no-op.)
|
|
90
|
+
*/
|
|
91
|
+
export function applyOverrides(autoTree, overrides = {}) {
|
|
92
|
+
const patches = overrides.patches ?? {};
|
|
93
|
+
const extra = overrides.extra ?? [];
|
|
94
|
+
// 1. extra inserts. A placed `article-ref` (a cross-listed internal link) is
|
|
95
|
+
// skipped when its target article is no longer a live auto node — deleted,
|
|
96
|
+
// unpublished, or not visible at this access tier — so cross-listed links
|
|
97
|
+
// never dangle, the same "never stale" guarantee the auto nodes get.
|
|
98
|
+
const liveRefs = collectArticleRefs(autoTree);
|
|
99
|
+
let tree = [...autoTree];
|
|
100
|
+
for (const placed of extra) {
|
|
101
|
+
if (placed.node.type === 'article-ref' && !liveRefs.has(placed.node.ref))
|
|
102
|
+
continue;
|
|
103
|
+
if (findNode(tree, placed.node.id))
|
|
104
|
+
continue;
|
|
105
|
+
if (placed.parentId != null && !findNode(tree, placed.parentId))
|
|
106
|
+
continue;
|
|
107
|
+
tree = insertNode(tree, placed.node, { parentId: placed.parentId ?? null, index: placed.index });
|
|
108
|
+
}
|
|
109
|
+
// 2. field patches + removals (recursive, immutable) over the combined tree
|
|
110
|
+
const patchNode = (node) => {
|
|
111
|
+
const patch = patches[node.id];
|
|
112
|
+
if (patch?.removed)
|
|
113
|
+
return null;
|
|
114
|
+
let next = patch ? applyFieldPatch(node, patch) : node;
|
|
115
|
+
if (next.children && next.children.length > 0) {
|
|
116
|
+
const kids = next.children
|
|
117
|
+
.map(patchNode)
|
|
118
|
+
.filter((n) => n !== null);
|
|
119
|
+
next = { ...next, children: kids };
|
|
120
|
+
}
|
|
121
|
+
return next;
|
|
122
|
+
};
|
|
123
|
+
tree = tree.map(patchNode).filter((n) => n !== null);
|
|
124
|
+
// 3. moves (parentId in a patch). Skip absent targets/parents; ignore cycles.
|
|
125
|
+
for (const [id, patch] of Object.entries(patches)) {
|
|
126
|
+
if (patch.parentId === undefined)
|
|
127
|
+
continue;
|
|
128
|
+
if (!findNode(tree, id))
|
|
129
|
+
continue;
|
|
130
|
+
if (patch.parentId !== null && !findNode(tree, patch.parentId))
|
|
131
|
+
continue;
|
|
132
|
+
try {
|
|
133
|
+
tree = moveNode(tree, id, { parentId: patch.parentId });
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
// cycle / invalid move → leave node where it is (non-fatal)
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
// 4. canonical display order
|
|
140
|
+
return sortTree(tree);
|
|
141
|
+
}
|
|
142
|
+
/** Convenience: build + apply in one call. */
|
|
143
|
+
export function resolve(items, overrides = {}, buildOptions = {}) {
|
|
144
|
+
return applyOverrides(buildDefaultTree(items, buildOptions), overrides);
|
|
145
|
+
}
|
|
146
|
+
/* -------------------------------------------------------------------------- */
|
|
147
|
+
/* helpers */
|
|
148
|
+
/* -------------------------------------------------------------------------- */
|
|
149
|
+
function defaultPrimaryTag(item) {
|
|
150
|
+
if (item.tags.length === 0)
|
|
151
|
+
return undefined;
|
|
152
|
+
return [...item.tags].sort()[0];
|
|
153
|
+
}
|
|
154
|
+
/** Article ids that have a live node in the auto-built tree (for prune checks). */
|
|
155
|
+
function collectArticleRefs(tree) {
|
|
156
|
+
const refs = new Set();
|
|
157
|
+
const walk = (nodes) => {
|
|
158
|
+
for (const n of nodes) {
|
|
159
|
+
if (n.type === 'article-ref')
|
|
160
|
+
refs.add(n.ref);
|
|
161
|
+
if (n.children)
|
|
162
|
+
walk(n.children);
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
walk(tree);
|
|
166
|
+
return refs;
|
|
167
|
+
}
|
|
168
|
+
/** Sorted union of locales present across the content index. */
|
|
169
|
+
export function collectLocales(items) {
|
|
170
|
+
const set = new Set();
|
|
171
|
+
for (const item of items) {
|
|
172
|
+
for (const locale of Object.keys(item.locales))
|
|
173
|
+
set.add(locale);
|
|
174
|
+
}
|
|
175
|
+
return [...set].sort();
|
|
176
|
+
}
|
|
177
|
+
function labelsForAll(locales, text) {
|
|
178
|
+
return Object.fromEntries(locales.map((l) => [l, text]));
|
|
179
|
+
}
|
|
180
|
+
function articleLabels(item) {
|
|
181
|
+
const out = {};
|
|
182
|
+
for (const [locale, meta] of Object.entries(item.locales)) {
|
|
183
|
+
out[locale] = meta.title;
|
|
184
|
+
}
|
|
185
|
+
return out;
|
|
186
|
+
}
|
|
187
|
+
function sortItems(items) {
|
|
188
|
+
return [...items].sort((a, b) => {
|
|
189
|
+
const ta = a.locales[a.sourceLocale]?.title ?? '';
|
|
190
|
+
const tb = b.locales[b.sourceLocale]?.title ?? '';
|
|
191
|
+
const byTitle = ta.localeCompare(tb);
|
|
192
|
+
if (byTitle !== 0)
|
|
193
|
+
return byTitle;
|
|
194
|
+
return (b.updatedAt ?? '').localeCompare(a.updatedAt ?? '');
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
function applyFieldPatch(node, patch) {
|
|
198
|
+
const next = { ...node };
|
|
199
|
+
if (patch.order !== undefined)
|
|
200
|
+
next.order = patch.order;
|
|
201
|
+
if (patch.hidden !== undefined)
|
|
202
|
+
next.hidden = patch.hidden;
|
|
203
|
+
if (patch.labels)
|
|
204
|
+
next.labels = { ...(node.labels ?? {}), ...patch.labels };
|
|
205
|
+
if (patch.badge !== undefined)
|
|
206
|
+
next.badge = patch.badge;
|
|
207
|
+
if (patch.icon !== undefined)
|
|
208
|
+
next.icon = patch.icon;
|
|
209
|
+
if (patch.data)
|
|
210
|
+
next.data = { ...(node.data ?? {}), ...patch.data };
|
|
211
|
+
if (patch.collapsed !== undefined && node.type === 'group')
|
|
212
|
+
next.collapsed = patch.collapsed;
|
|
213
|
+
if (patch.owned !== undefined && node.type === 'article-ref')
|
|
214
|
+
next.owned = patch.owned;
|
|
215
|
+
return next;
|
|
216
|
+
}
|
|
217
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/resolve/index.ts"],"names":[],"mappings":"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"}
|