@xingwangzhe/stalux 1.25.0 → 1.25.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/package.json +1 -1
- package/src/index.ts +1 -0
- package/src/pages/about.md.ts +4 -1
- package/src/pages/archives.md.ts +4 -1
- package/src/pages/categories/[category].md.ts +4 -1
- package/src/pages/categories/index.md.ts +4 -1
- package/src/pages/index.md.ts +4 -1
- package/src/pages/links.md.ts +4 -1
- package/src/pages/llms-full.txt.ts +1 -0
- package/src/pages/llms.txt.ts +1 -0
- package/src/pages/openapi.json.ts +77 -0
- package/src/pages/posts/[post].md.ts +1 -0
- package/src/pages/tags/[tag].md.ts +4 -1
- package/src/pages/tags/index.md.ts +4 -1
- package/src/pages/words.md.ts +4 -1
- package/src/utils/ai-discovery.ts +32 -0
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -147,6 +147,7 @@ function getRoutes(baseDir: URL): RouteEntry[] {
|
|
|
147
147
|
{ pattern: "/atom.xml", entrypoint: resolvePage("./pages/atom.xml.ts") },
|
|
148
148
|
{ pattern: "/llms.txt", entrypoint: resolvePage("./pages/llms.txt.ts") },
|
|
149
149
|
{ pattern: "/llms-full.txt", entrypoint: resolvePage("./pages/llms-full.txt.ts") },
|
|
150
|
+
{ pattern: "/openapi.json", entrypoint: resolvePage("./pages/openapi.json.ts") },
|
|
150
151
|
{
|
|
151
152
|
pattern: "/api/post.abbrlink.json",
|
|
152
153
|
entrypoint: resolvePage("./pages/api/post.abbrlink.json.ts"),
|
package/src/pages/about.md.ts
CHANGED
|
@@ -10,6 +10,9 @@ export const GET: APIRoute = async (context) => {
|
|
|
10
10
|
const site = getSite(config, context.site?.toString());
|
|
11
11
|
const text = await renderAboutMd(site);
|
|
12
12
|
return new Response(text, {
|
|
13
|
-
headers: {
|
|
13
|
+
headers: {
|
|
14
|
+
"Content-Type": "text/markdown; charset=utf-8",
|
|
15
|
+
Vary: "Accept, Accept-Encoding",
|
|
16
|
+
},
|
|
14
17
|
});
|
|
15
18
|
};
|
package/src/pages/archives.md.ts
CHANGED
|
@@ -19,6 +19,9 @@ export const GET: APIRoute = async (context) => {
|
|
|
19
19
|
const { t } = createTranslator(lang);
|
|
20
20
|
const text = await renderArchivesMd(site, exportMd, t);
|
|
21
21
|
return new Response(text, {
|
|
22
|
-
headers: {
|
|
22
|
+
headers: {
|
|
23
|
+
"Content-Type": "text/markdown; charset=utf-8",
|
|
24
|
+
Vary: "Accept, Accept-Encoding",
|
|
25
|
+
},
|
|
23
26
|
});
|
|
24
27
|
};
|
|
@@ -29,6 +29,9 @@ export const GET: APIRoute = async (context) => {
|
|
|
29
29
|
const site = getSite(config, context.site?.toString());
|
|
30
30
|
const text = renderTaxonomyPageMd(category, posts, site, "categories", exportMd);
|
|
31
31
|
return new Response(text, {
|
|
32
|
-
headers: {
|
|
32
|
+
headers: {
|
|
33
|
+
"Content-Type": "text/markdown; charset=utf-8",
|
|
34
|
+
Vary: "Accept, Accept-Encoding",
|
|
35
|
+
},
|
|
33
36
|
});
|
|
34
37
|
};
|
|
@@ -22,6 +22,9 @@ export const GET: APIRoute = async (context) => {
|
|
|
22
22
|
const map = buildTaxonomyMap(posts, "categories");
|
|
23
23
|
const text = renderTaxonomyListMd(map, site, "categories", t("ai.allCategories"));
|
|
24
24
|
return new Response(text, {
|
|
25
|
-
headers: {
|
|
25
|
+
headers: {
|
|
26
|
+
"Content-Type": "text/markdown; charset=utf-8",
|
|
27
|
+
Vary: "Accept, Accept-Encoding",
|
|
28
|
+
},
|
|
26
29
|
});
|
|
27
30
|
};
|
package/src/pages/index.md.ts
CHANGED
|
@@ -13,6 +13,9 @@ export const GET: APIRoute = async (context) => {
|
|
|
13
13
|
const { t } = createTranslator(lang);
|
|
14
14
|
const text = await renderIndexMd(config, site, t);
|
|
15
15
|
return new Response(text, {
|
|
16
|
-
headers: {
|
|
16
|
+
headers: {
|
|
17
|
+
"Content-Type": "text/markdown; charset=utf-8",
|
|
18
|
+
Vary: "Accept, Accept-Encoding",
|
|
19
|
+
},
|
|
17
20
|
});
|
|
18
21
|
};
|
package/src/pages/links.md.ts
CHANGED
|
@@ -13,6 +13,9 @@ export const GET: APIRoute = async (context) => {
|
|
|
13
13
|
const { t } = createTranslator(lang);
|
|
14
14
|
const text = renderLinksMd(config, site, t);
|
|
15
15
|
return new Response(text, {
|
|
16
|
-
headers: {
|
|
16
|
+
headers: {
|
|
17
|
+
"Content-Type": "text/markdown; charset=utf-8",
|
|
18
|
+
Vary: "Accept, Accept-Encoding",
|
|
19
|
+
},
|
|
17
20
|
});
|
|
18
21
|
};
|
package/src/pages/llms.txt.ts
CHANGED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type { APIRoute } from "astro";
|
|
2
|
+
|
|
3
|
+
export const prerender = true;
|
|
4
|
+
|
|
5
|
+
const responseSchema = {
|
|
6
|
+
type: "array",
|
|
7
|
+
items: { type: "object", additionalProperties: true },
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export const GET: APIRoute = async ({ site }) => {
|
|
11
|
+
const server = site?.toString().replace(/\/$/, "") || "";
|
|
12
|
+
const document = {
|
|
13
|
+
openapi: "3.1.0",
|
|
14
|
+
info: {
|
|
15
|
+
title: "Stalux Blog Read API",
|
|
16
|
+
version: "1.0.0",
|
|
17
|
+
description:
|
|
18
|
+
"Read-only machine-readable endpoints for discovering and retrieving published Stalux blog content.",
|
|
19
|
+
},
|
|
20
|
+
servers: server ? [{ url: server }] : undefined,
|
|
21
|
+
paths: {
|
|
22
|
+
"/api/posts.json": {
|
|
23
|
+
get: {
|
|
24
|
+
operationId: "listPublishedPosts",
|
|
25
|
+
summary: "List published posts",
|
|
26
|
+
description: "Returns metadata for every published post, newest first.",
|
|
27
|
+
responses: {
|
|
28
|
+
"200": {
|
|
29
|
+
description: "Published post metadata",
|
|
30
|
+
content: { "application/json": { schema: responseSchema } },
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
"/api/post.abbrlink.json": {
|
|
36
|
+
get: {
|
|
37
|
+
operationId: "listPostIdentifiers",
|
|
38
|
+
summary: "List post identifiers",
|
|
39
|
+
description: "Returns the title and stable abbrlink for every published post.",
|
|
40
|
+
responses: {
|
|
41
|
+
"200": {
|
|
42
|
+
description: "Published post identifiers",
|
|
43
|
+
content: { "application/json": { schema: responseSchema } },
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
"/posts/{abbrlink}.md": {
|
|
49
|
+
get: {
|
|
50
|
+
operationId: "readPostMarkdown",
|
|
51
|
+
summary: "Read one post as Markdown",
|
|
52
|
+
description: "Returns the normalized Markdown source for a published post.",
|
|
53
|
+
parameters: [
|
|
54
|
+
{
|
|
55
|
+
name: "abbrlink",
|
|
56
|
+
in: "path",
|
|
57
|
+
required: true,
|
|
58
|
+
description: "Stable post identifier returned by the post index.",
|
|
59
|
+
schema: { type: "string" },
|
|
60
|
+
},
|
|
61
|
+
],
|
|
62
|
+
responses: {
|
|
63
|
+
"200": {
|
|
64
|
+
description: "Markdown post content",
|
|
65
|
+
content: { "text/markdown": { schema: { type: "string" } } },
|
|
66
|
+
},
|
|
67
|
+
"404": { description: "Post not found" },
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
return new Response(JSON.stringify(document, null, 2), {
|
|
75
|
+
headers: { "Content-Type": "application/json; charset=utf-8" },
|
|
76
|
+
});
|
|
77
|
+
};
|
|
@@ -29,6 +29,9 @@ export const GET: APIRoute = async (context) => {
|
|
|
29
29
|
const site = getSite(config, context.site?.toString());
|
|
30
30
|
const text = renderTaxonomyPageMd(tag, posts, site, "tags", exportMd);
|
|
31
31
|
return new Response(text, {
|
|
32
|
-
headers: {
|
|
32
|
+
headers: {
|
|
33
|
+
"Content-Type": "text/markdown; charset=utf-8",
|
|
34
|
+
Vary: "Accept, Accept-Encoding",
|
|
35
|
+
},
|
|
33
36
|
});
|
|
34
37
|
};
|
|
@@ -22,6 +22,9 @@ export const GET: APIRoute = async (context) => {
|
|
|
22
22
|
const map = buildTaxonomyMap(posts, "tags");
|
|
23
23
|
const text = renderTaxonomyListMd(map, site, "tags", t("ai.allTags"));
|
|
24
24
|
return new Response(text, {
|
|
25
|
-
headers: {
|
|
25
|
+
headers: {
|
|
26
|
+
"Content-Type": "text/markdown; charset=utf-8",
|
|
27
|
+
Vary: "Accept, Accept-Encoding",
|
|
28
|
+
},
|
|
26
29
|
});
|
|
27
30
|
};
|
package/src/pages/words.md.ts
CHANGED
|
@@ -9,6 +9,9 @@ export const GET: APIRoute = async () => {
|
|
|
9
9
|
|
|
10
10
|
const text = await renderWordsMd();
|
|
11
11
|
return new Response(text, {
|
|
12
|
-
headers: {
|
|
12
|
+
headers: {
|
|
13
|
+
"Content-Type": "text/markdown; charset=utf-8",
|
|
14
|
+
Vary: "Accept, Accept-Encoding",
|
|
15
|
+
},
|
|
13
16
|
});
|
|
14
17
|
};
|
|
@@ -468,6 +468,38 @@ export async function renderLlmsTxt(config: ConfigMap, site: string): Promise<st
|
|
|
468
468
|
lines.push(buildMdLink("llms-full.txt", `${site}/llms-full.txt`, t("ai.llmsFullTxtDesc")));
|
|
469
469
|
lines.push("");
|
|
470
470
|
|
|
471
|
+
// Developer resources and agent guidance. Keep these links predictable so an
|
|
472
|
+
// agent can discover the read API without scraping the HTML application shell.
|
|
473
|
+
lines.push("## Developer resources");
|
|
474
|
+
lines.push(
|
|
475
|
+
buildMdLink(
|
|
476
|
+
"OpenAPI specification",
|
|
477
|
+
`${site}/openapi.json`,
|
|
478
|
+
"Machine-readable description of the read-only blog API.",
|
|
479
|
+
),
|
|
480
|
+
);
|
|
481
|
+
lines.push(
|
|
482
|
+
buildMdLink(
|
|
483
|
+
"Published post index",
|
|
484
|
+
`${site}/api/posts.json`,
|
|
485
|
+
"JSON metadata for published posts, including stable abbrlink identifiers.",
|
|
486
|
+
),
|
|
487
|
+
);
|
|
488
|
+
lines.push(
|
|
489
|
+
buildMdLink(
|
|
490
|
+
"Post identifier index",
|
|
491
|
+
`${site}/api/post.abbrlink.json`,
|
|
492
|
+
"JSON mapping of published post titles to stable identifiers.",
|
|
493
|
+
),
|
|
494
|
+
);
|
|
495
|
+
lines.push("");
|
|
496
|
+
|
|
497
|
+
lines.push("## When to use this site");
|
|
498
|
+
lines.push(
|
|
499
|
+
"Use the read API or Markdown endpoints when you need to search, cite, summarize, or retrieve the author's published technical articles. Start with `/openapi.json` or `/api/posts.json`, then fetch a specific `/posts/{abbrlink}.md` resource. The endpoints are read-only and do not require authentication.",
|
|
500
|
+
);
|
|
501
|
+
lines.push("");
|
|
502
|
+
|
|
471
503
|
// Optional
|
|
472
504
|
lines.push(`## ${t("ai.optional")}`);
|
|
473
505
|
lines.push(buildMdLink(t("ai.rss"), `${site}/rss.xml`));
|