@brightsideoy/kama 0.1.0-alpha.121 → 0.1.0-alpha.122
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/bin/cli.js +94 -14
- package/content/definitions/node-layouts/single-column.yml +2 -5
- package/content/definitions/node-layouts/two-column.yml +2 -5
- package/content/definitions/nodes/page.yaml +16 -3
- package/dist/assets/{index-Drmp3h6P.css → index-CEmkJap6.css} +1 -1
- package/dist/assets/index-DsSUfReu.js +162 -0
- package/dist/index.html +2 -2
- package/dist/index.js +1063 -1034
- package/package.json +1 -1
- package/templates/astro-starter/src/components/global/Breadcrumb.astro +5 -2
- package/templates/astro-starter/src/components/global/Header.astro +11 -2
- package/templates/astro-starter/src/components/global/NavigationMenu.astro +18 -2
- package/templates/astro-starter/src/pages/[...slug].astro +40 -17
- package/templates/astro-starter/src/pages/llms-full.txt.ts +22 -12
- package/templates/astro-starter/src/pages/llms.txt.ts +18 -12
- package/dist/assets/index-C6GoXL-R.js +0 -162
- package/test-local.sh +0 -76
package/package.json
CHANGED
|
@@ -2,10 +2,13 @@
|
|
|
2
2
|
interface Props {
|
|
3
3
|
title: string;
|
|
4
4
|
slug?: string;
|
|
5
|
+
isHomePage?: boolean;
|
|
5
6
|
}
|
|
6
7
|
|
|
7
|
-
const { title, slug } = Astro.props;
|
|
8
|
-
|
|
8
|
+
const { title, slug, isHomePage } = Astro.props;
|
|
9
|
+
|
|
10
|
+
// 🎯 Detect home page via URL pathname or explicit flag
|
|
11
|
+
const isHome = isHomePage || Astro.url.pathname === "/" || !slug || slug === "";
|
|
9
12
|
---
|
|
10
13
|
|
|
11
14
|
<nav class="breadcrumb-nav" aria-label="Breadcrumb">
|
|
@@ -30,8 +30,17 @@ if (!rawTree || rawTree.length === 0) {
|
|
|
30
30
|
rawTree = await getTree(getTreeId(), getChannelId(), customFetch);
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
//
|
|
34
|
-
const
|
|
33
|
+
// 🎯 Find the home node in the tree just in case `logoText` isn't set to inherit to subpages
|
|
34
|
+
const homeSlug = import.meta.env.PUBLIC_HOME_SLUG || "home";
|
|
35
|
+
const homeNodeInTree =
|
|
36
|
+
rawTree?.find((n: any) => n.slug === homeSlug) || rawTree?.[0];
|
|
37
|
+
|
|
38
|
+
// Automatically use logoText from the current page, fallback to the Home node in the tree, then fallback to ENV
|
|
39
|
+
const siteName =
|
|
40
|
+
node?.logoText ||
|
|
41
|
+
homeNodeInTree?.logoText ||
|
|
42
|
+
import.meta.env.PUBLIC_SITE_NAME ||
|
|
43
|
+
"KAMA";
|
|
35
44
|
---
|
|
36
45
|
|
|
37
46
|
<header class="site-header">
|
|
@@ -11,14 +11,30 @@ const { tree = [] } = Astro.props;
|
|
|
11
11
|
// 1. Get current path from Astro context
|
|
12
12
|
const currentPath = Astro.url.pathname.replace(/\/$/, "") || "/";
|
|
13
13
|
|
|
14
|
+
// 🎯 Extract configurable fallback home slug
|
|
15
|
+
const homeSlug = String(import.meta.env.PUBLIC_HOME_SLUG || "home").trim();
|
|
16
|
+
|
|
17
|
+
// 2. Locate Home Node (either root item, slug matching homeSlug, or path === "/")
|
|
14
18
|
const homeNode = tree.find(
|
|
15
|
-
(item: TreeNode) =>
|
|
19
|
+
(item: TreeNode) =>
|
|
20
|
+
item.slug === homeSlug ||
|
|
21
|
+
item.slug === "" ||
|
|
22
|
+
item.path === "/" ||
|
|
23
|
+
item.path === "",
|
|
16
24
|
);
|
|
17
25
|
|
|
26
|
+
// 3. Determine top-level navigation items
|
|
27
|
+
// If home node has children (e.g. nested navigation), render its children; otherwise filter out the home node.
|
|
18
28
|
const topLevelNavItems =
|
|
19
29
|
homeNode?.children && homeNode.children.length > 0
|
|
20
30
|
? homeNode.children
|
|
21
|
-
: tree.filter(
|
|
31
|
+
: tree.filter(
|
|
32
|
+
(item: TreeNode) =>
|
|
33
|
+
item.slug !== homeSlug &&
|
|
34
|
+
item.slug !== "" &&
|
|
35
|
+
item.path !== "/" &&
|
|
36
|
+
item.path !== "",
|
|
37
|
+
);
|
|
22
38
|
|
|
23
39
|
// Normalize path comparison helper
|
|
24
40
|
const normalizePath = (path?: string) => {
|
|
@@ -42,18 +42,16 @@ if (isStaticAsset) {
|
|
|
42
42
|
return new Response(null, { status: 404 });
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
// 2.
|
|
46
|
-
const rawSlug = Array.isArray(slug) ? slug.join("/") : slugStr;
|
|
47
|
-
const pathParts = rawSlug.split("/").filter(part => part.trim() !== "");
|
|
48
|
-
const pathToFetch = pathParts.length > 0 ? pathParts.join("/") : "home";
|
|
49
|
-
|
|
50
|
-
// 3. ENV & WORKER RESOLUTION
|
|
45
|
+
// 2. ENV & WORKER RESOLUTION
|
|
51
46
|
const locals = (Astro.locals as any) || {};
|
|
52
|
-
// SAFELY EXTRACT WORKER ENV USING THE CLOUDFLARE MODULE
|
|
53
47
|
const workerEnv = env || process.env || {};
|
|
54
48
|
const cmsWorker = locals.cfContext?.env?.CMS_WORKER || workerEnv.CMS_WORKER;
|
|
55
49
|
|
|
56
|
-
//
|
|
50
|
+
// Extract environment configuration & fallback slug
|
|
51
|
+
const envHomeSlug = import.meta.env.PUBLIC_HOME_SLUG;
|
|
52
|
+
const workerHomeSlug = (workerEnv as any).PUBLIC_HOME_SLUG;
|
|
53
|
+
const homeSlug = String(envHomeSlug || workerHomeSlug || "home").trim();
|
|
54
|
+
|
|
57
55
|
const envApiUrl = import.meta.env.PUBLIC_API_URL;
|
|
58
56
|
const workerApiUrl = workerEnv.PUBLIC_API_URL;
|
|
59
57
|
const baseApiUrl = String(envApiUrl || workerApiUrl || "http://localhost:8787");
|
|
@@ -94,19 +92,36 @@ const customFetch = async (input: RequestInfo | URL, init?: RequestInit) => {
|
|
|
94
92
|
const channelId = getChannelId();
|
|
95
93
|
const treeId = getTreeId();
|
|
96
94
|
|
|
95
|
+
// 3. PATH RESOLUTION
|
|
96
|
+
const rawSlug = Array.isArray(slug) ? slug.join("/") : slugStr;
|
|
97
|
+
const pathParts = rawSlug.split("/").filter(part => part.trim() !== "");
|
|
98
|
+
|
|
97
99
|
let pageData: NodeResponse | null = null;
|
|
98
100
|
let navTree: TreeNode[] = [];
|
|
101
|
+
let targetSlug = pathParts.join("/");
|
|
99
102
|
|
|
103
|
+
// 🎯 FETCH TREE FIRST (to resolve root page if visiting '/')
|
|
100
104
|
try {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
105
|
+
navTree = await getTree(treeId, channelId, customFetch);
|
|
106
|
+
} catch (err) {
|
|
107
|
+
console.error(`❌ [SSR Error] Failed to fetch tree "${treeId}":`, err);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// 🎯 DYNAMIC HOME SLUG RESOLUTION
|
|
111
|
+
if (!targetSlug) {
|
|
112
|
+
const rootTreeSlug =
|
|
113
|
+
navTree && navTree.length > 0
|
|
114
|
+
? navTree[0].slug || (navTree[0] as any).key
|
|
115
|
+
: "";
|
|
116
|
+
targetSlug = rootTreeSlug || homeSlug;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// 🎯 FETCH NODE DATA
|
|
120
|
+
try {
|
|
121
|
+
pageData = await getNodeByPath(targetSlug, channelId, isPreview, customFetch);
|
|
107
122
|
} catch (err) {
|
|
108
123
|
console.error(
|
|
109
|
-
`❌ [SSR Error] Failed to fetch data for path "${
|
|
124
|
+
`❌ [SSR Error] Failed to fetch page data for path "${targetSlug}":`,
|
|
110
125
|
err,
|
|
111
126
|
);
|
|
112
127
|
}
|
|
@@ -118,9 +133,17 @@ if (!pageData || !pageData.node) {
|
|
|
118
133
|
const node = pageData.node;
|
|
119
134
|
const content = pageData.content || {};
|
|
120
135
|
|
|
136
|
+
const rootTreeSlug =
|
|
137
|
+
navTree && navTree.length > 0
|
|
138
|
+
? navTree[0].slug || (navTree[0] as any).key
|
|
139
|
+
: "";
|
|
121
140
|
const isHomePage =
|
|
122
|
-
|
|
123
|
-
|
|
141
|
+
targetSlug === homeSlug ||
|
|
142
|
+
(rootTreeSlug && targetSlug === rootTreeSlug) ||
|
|
143
|
+
node?.path === "/" ||
|
|
144
|
+
pathParts.length === 0;
|
|
145
|
+
|
|
146
|
+
const normalizedSlug = isHomePage ? "" : targetSlug;
|
|
124
147
|
|
|
125
148
|
const headerBlocks = content?.header || [];
|
|
126
149
|
const mainBlocks = content?.content || [];
|
|
@@ -26,24 +26,32 @@ export const GET: APIRoute = async (context) => {
|
|
|
26
26
|
return fetch(req);
|
|
27
27
|
};
|
|
28
28
|
|
|
29
|
-
const channelId = getChannelId() || import.meta.env.PUBLIC_CHANNEL_ID || "
|
|
30
|
-
const treeId = getTreeId() || import.meta.env.PUBLIC_TREE_ID || "main-
|
|
29
|
+
const channelId = getChannelId() || import.meta.env.PUBLIC_CHANNEL_ID || "main-channel";
|
|
30
|
+
const treeId = getTreeId() || import.meta.env.PUBLIC_TREE_ID || "main-tree";
|
|
31
|
+
|
|
32
|
+
// 🎯 Extract configurable fallback home slug
|
|
33
|
+
const envHomeSlug = import.meta.env.PUBLIC_HOME_SLUG;
|
|
34
|
+
const workerHomeSlug = cfEnv.PUBLIC_HOME_SLUG || (typeof process !== "undefined" ? process.env?.PUBLIC_HOME_SLUG : undefined);
|
|
35
|
+
const homeSlug = String(envHomeSlug || workerHomeSlug || "home").trim();
|
|
31
36
|
|
|
32
37
|
const rawOrigin = import.meta.env.PUBLIC_SITE_URL || context.url.origin;
|
|
33
38
|
const siteOrigin = String(rawOrigin).replace(/\/$/, "");
|
|
34
39
|
const brandName = import.meta.env.PUBLIC_SITE_NAME || "KAMA Starter";
|
|
35
40
|
|
|
36
41
|
try {
|
|
37
|
-
// 2. Fetch
|
|
38
|
-
const
|
|
39
|
-
getNodeByPath("home", channelId, false, customFetch).catch(() => null),
|
|
40
|
-
getTree(treeId, channelId, customFetch).catch(() => []),
|
|
41
|
-
]);
|
|
42
|
-
|
|
42
|
+
// 2. Fetch Navigation Tree first to determine root nodes
|
|
43
|
+
const rawNavTree = await getTree(treeId, channelId, customFetch).catch(() => []);
|
|
43
44
|
const navTree = Array.isArray(rawNavTree)
|
|
44
45
|
? rawNavTree
|
|
45
46
|
: (rawNavTree?.tree || rawNavTree?.items || rawNavTree?.data || []);
|
|
46
47
|
|
|
48
|
+
// Determine home page slug (first root node in tree, or fallback home slug)
|
|
49
|
+
const rootTreeSlug = navTree.length > 0 ? (navTree[0].slug || navTree[0].key || "") : "";
|
|
50
|
+
const resolvedHomeSlug = rootTreeSlug || homeSlug;
|
|
51
|
+
|
|
52
|
+
// Fetch Home Node (for global context)
|
|
53
|
+
const homeData = await getNodeByPath(resolvedHomeSlug, channelId, false, customFetch).catch(() => null);
|
|
54
|
+
|
|
47
55
|
const siteSummary = homeData?.node?.abstract || homeData?.node?.summary || "Full Application Documentation Corpus";
|
|
48
56
|
|
|
49
57
|
// 3. Header section matching llmstxt.org spec
|
|
@@ -82,14 +90,16 @@ export const GET: APIRoute = async (context) => {
|
|
|
82
90
|
const nodeData = item.node || item;
|
|
83
91
|
if (!nodeData || nodeData.isHidden) continue;
|
|
84
92
|
|
|
85
|
-
const
|
|
86
|
-
const
|
|
87
|
-
const itemUrl =
|
|
93
|
+
const itemSlug = nodeData.slug || nodeData.path || "";
|
|
94
|
+
const isRootItem = itemSlug === resolvedHomeSlug || itemSlug === "" || itemSlug === "/";
|
|
95
|
+
const itemUrl = isRootItem ? siteOrigin : `${siteOrigin}/${itemSlug.replace(/^\//, "")}`;
|
|
88
96
|
const itemTitle = nodeData.title || nodeData.label || "Untitled Page";
|
|
89
97
|
|
|
98
|
+
const fetchSlug = isRootItem ? resolvedHomeSlug : itemSlug.replace(/^\//, "");
|
|
99
|
+
|
|
90
100
|
// Fetch node's block contents
|
|
91
101
|
const pageData = await getNodeByPath(
|
|
92
|
-
|
|
102
|
+
fetchSlug,
|
|
93
103
|
channelId,
|
|
94
104
|
false,
|
|
95
105
|
customFetch
|
|
@@ -27,25 +27,31 @@ export const GET: APIRoute = async (context) => {
|
|
|
27
27
|
};
|
|
28
28
|
|
|
29
29
|
// 2. Load keys from environment or fallback defaults
|
|
30
|
-
const channelId = getChannelId() || import.meta.env.PUBLIC_CHANNEL_ID || "
|
|
31
|
-
const treeId = getTreeId() || import.meta.env.PUBLIC_TREE_ID || "main-
|
|
30
|
+
const channelId = getChannelId() || import.meta.env.PUBLIC_CHANNEL_ID || "main-channel";
|
|
31
|
+
const treeId = getTreeId() || import.meta.env.PUBLIC_TREE_ID || "main-tree";
|
|
32
|
+
|
|
33
|
+
// 🎯 Extract configurable fallback home slug
|
|
34
|
+
const envHomeSlug = import.meta.env.PUBLIC_HOME_SLUG;
|
|
35
|
+
const workerHomeSlug = cfEnv.PUBLIC_HOME_SLUG || (typeof process !== "undefined" ? process.env?.PUBLIC_HOME_SLUG : undefined);
|
|
36
|
+
const homeSlug = String(envHomeSlug || workerHomeSlug || "home").trim();
|
|
32
37
|
|
|
33
38
|
const rawOrigin = import.meta.env.PUBLIC_SITE_URL || context.url.origin;
|
|
34
39
|
const siteOrigin = String(rawOrigin).replace(/\/$/, "");
|
|
35
40
|
const brandName = import.meta.env.PUBLIC_SITE_NAME || "KAMA Starter";
|
|
36
41
|
|
|
37
42
|
try {
|
|
38
|
-
// 3. Parallel fetch for
|
|
39
|
-
const
|
|
40
|
-
getNodeByPath("home", channelId, false, customFetch).catch(() => null),
|
|
41
|
-
getTree(treeId, channelId, customFetch).catch(() => []),
|
|
42
|
-
]);
|
|
43
|
-
|
|
44
|
-
// Normalize tree data payload across direct array or wrapped responses
|
|
43
|
+
// 3. Parallel fetch for Navigation Tree first to resolve root node slug
|
|
44
|
+
const rawNavTree = await getTree(treeId, channelId, customFetch).catch(() => []);
|
|
45
45
|
const navTree = Array.isArray(rawNavTree)
|
|
46
46
|
? rawNavTree
|
|
47
47
|
: (rawNavTree?.tree || rawNavTree?.items || rawNavTree?.data || []);
|
|
48
48
|
|
|
49
|
+
const rootTreeSlug = navTree.length > 0 ? (navTree[0].slug || navTree[0].key || "") : "";
|
|
50
|
+
const resolvedHomeSlug = rootTreeSlug || homeSlug;
|
|
51
|
+
|
|
52
|
+
// Fetch Home Node summary
|
|
53
|
+
const homeData = await getNodeByPath(resolvedHomeSlug, channelId, false, customFetch).catch(() => null);
|
|
54
|
+
|
|
49
55
|
const siteSummary = homeData?.node?.abstract || homeData?.node?.summary || "Global Content Delivery at the Edge";
|
|
50
56
|
|
|
51
57
|
// 4. Build standard-compliant llms.txt Markdown
|
|
@@ -63,9 +69,9 @@ export const GET: APIRoute = async (context) => {
|
|
|
63
69
|
const nodeData = item.node || item;
|
|
64
70
|
if (!nodeData || nodeData.isHidden) continue;
|
|
65
71
|
|
|
66
|
-
const
|
|
67
|
-
const
|
|
68
|
-
const itemUrl =
|
|
72
|
+
const itemSlug = nodeData.slug || nodeData.path || "";
|
|
73
|
+
const isRootItem = itemSlug === resolvedHomeSlug || itemSlug === "" || itemSlug === "/";
|
|
74
|
+
const itemUrl = isRootItem ? siteOrigin : `${siteOrigin}/${itemSlug.replace(/^\//, "")}`;
|
|
69
75
|
|
|
70
76
|
const itemTitle = nodeData.title || nodeData.label || nodeData.menuTitle || "Untitled";
|
|
71
77
|
const abstract = nodeData.abstract || nodeData.summary || nodeData.description;
|