@lupinum/ginko-content 0.1.2 → 0.1.3
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 +18 -4
- package/compatibility.json +1 -1
- package/dist/cli.mjs +317 -294
- package/dist/cms-import/index.js +5 -1
- package/dist/config.d.mts +1 -1
- package/dist/config.mjs +1 -1
- package/dist/core/cache-hints.d.ts +9 -1
- package/dist/core/content/collection.js +1 -1
- package/dist/core/content/path.js +1 -1
- package/dist/core/errors.js +5 -1
- package/dist/core/provider-errors +1 -0
- package/dist/core/provider-errors.d.ts +10 -0
- package/dist/core/provider-errors.js +27 -0
- package/dist/core/query/execute.js +12 -3
- package/dist/core/query/filter.js +2 -2
- package/dist/core/query/lower.js +5 -4
- package/dist/features/navigation/build.js +14 -6
- package/dist/features/navigation/canonical.js +1 -1
- package/dist/features/sitemap/query.js +2 -2
- package/dist/integrations/nitro/ingest.d.ts +3 -3
- package/dist/integrations/nitro/ingest.js +3 -3
- package/dist/integrations/nitro/storage.js +3 -1
- package/dist/module.d.mts +151 -526
- package/dist/module.json +1 -1
- package/dist/module.mjs +1091 -722
- package/dist/parsers/csv.js +4 -3
- package/dist/parsers/index.d.ts +2 -5
- package/dist/parsers/index.js +2 -2
- package/dist/parsers/markdown-plugins.d.ts +1 -0
- package/dist/parsers/markdown-plugins.js +83 -1
- package/dist/parsers/markdown.js +1 -1
- package/dist/public/client.d.ts +3 -2
- package/dist/public/client.js +2 -2
- package/dist/public/provider-errors.d.ts +2 -1
- package/dist/public/provider-errors.js +1 -18
- package/dist/public/provider.d.ts +3 -10
- package/dist/public/server.d.ts +6 -1
- package/dist/public/server.js +33 -0
- package/dist/runtime/agent-paths +1 -0
- package/dist/runtime/agent-paths.d.ts +6 -0
- package/dist/runtime/agent-paths.js +41 -0
- package/dist/runtime/app/components/internal/MarkdownRenderer.d.ts +1 -1
- package/dist/runtime/app/composables/head.d.ts +3 -0
- package/dist/runtime/app/composables/head.js +2 -1
- package/dist/runtime/app/composables/hot-reload +1 -0
- package/dist/runtime/app/composables/hot-reload.d.ts +5 -0
- package/dist/runtime/app/composables/hot-reload.js +15 -0
- package/dist/runtime/app/composables/locale-context.d.ts +1 -1
- package/dist/runtime/app/composables/locale-context.js +2 -1
- package/dist/runtime/app/composables/locale.js +2 -2
- package/dist/runtime/app/composables/search.js +10 -6
- package/dist/runtime/app/composables/use-content-document +1 -0
- package/dist/runtime/app/composables/use-content-document.d.ts +29 -0
- package/dist/runtime/app/composables/use-content-document.js +44 -0
- package/dist/runtime/app/composables/use-content-list +1 -0
- package/dist/runtime/app/composables/use-content-list.d.ts +43 -0
- package/dist/runtime/app/composables/use-content-list.js +98 -0
- package/dist/runtime/app/composables/use-content-navigation +1 -0
- package/dist/runtime/app/composables/use-content-navigation.d.ts +40 -0
- package/dist/runtime/app/composables/use-content-navigation.js +102 -0
- package/dist/runtime/app/composables/use-content-page +1 -0
- package/dist/runtime/app/composables/use-content-page.d.ts +47 -0
- package/dist/runtime/app/composables/use-content-page.js +142 -0
- package/dist/runtime/app/composables/use-content-shared +1 -0
- package/dist/runtime/app/composables/use-content-shared.d.ts +13 -0
- package/dist/runtime/app/composables/use-content-shared.js +29 -0
- package/dist/runtime/app/composables/use-content.d.ts +11 -206
- package/dist/runtime/app/composables/use-content.js +4 -377
- package/dist/runtime/app/plugins/hot-reload +1 -0
- package/dist/runtime/app/plugins/hot-reload.js +6 -0
- package/dist/runtime/query/backlinks +1 -0
- package/dist/runtime/query/backlinks.d.ts +8 -0
- package/dist/runtime/query/backlinks.js +90 -0
- package/dist/runtime/query/context +1 -0
- package/dist/runtime/query/context.d.ts +20 -0
- package/dist/runtime/query/context.js +0 -0
- package/dist/runtime/query/documents +1 -0
- package/dist/runtime/query/documents.d.ts +6 -0
- package/dist/runtime/query/documents.js +98 -0
- package/dist/runtime/query/errors +1 -0
- package/dist/runtime/query/errors.d.ts +1 -0
- package/dist/runtime/query/errors.js +4 -0
- package/dist/runtime/query/handles +1 -0
- package/dist/runtime/query/handles.d.ts +2 -0
- package/dist/runtime/query/handles.js +7 -0
- package/dist/runtime/query/locale-options +1 -0
- package/dist/runtime/query/locale-options.d.ts +5 -0
- package/dist/runtime/query/locale-options.js +14 -0
- package/dist/runtime/query/localized-docs +1 -0
- package/dist/runtime/query/localized-docs.d.ts +17 -0
- package/dist/runtime/query/localized-docs.js +44 -0
- package/dist/runtime/query/navigation +1 -0
- package/dist/runtime/query/navigation.d.ts +22 -0
- package/dist/runtime/query/navigation.js +73 -0
- package/dist/runtime/query/pagination +1 -0
- package/dist/runtime/query/pagination.d.ts +7 -0
- package/dist/runtime/query/pagination.js +78 -0
- package/dist/runtime/query/populate +1 -0
- package/dist/runtime/query/populate.d.ts +9 -0
- package/dist/runtime/query/populate.js +106 -0
- package/dist/runtime/query/responses +1 -0
- package/dist/runtime/query/responses.d.ts +10 -0
- package/dist/runtime/query/responses.js +47 -0
- package/dist/runtime/query/unified.d.ts +4 -20
- package/dist/runtime/query/unified.js +14 -544
- package/dist/runtime/query/variants +1 -0
- package/dist/runtime/query/variants.d.ts +11 -0
- package/dist/runtime/query/variants.js +36 -0
- package/dist/runtime/server/agent-http +1 -0
- package/dist/runtime/server/agent-http.d.ts +7 -0
- package/dist/runtime/server/agent-http.js +36 -0
- package/dist/runtime/server/agent-markdown +1 -0
- package/dist/runtime/server/agent-markdown.d.ts +83 -0
- package/dist/runtime/server/agent-markdown.js +449 -0
- package/dist/runtime/server/agent-site +1 -0
- package/dist/runtime/server/agent-site.d.ts +38 -0
- package/dist/runtime/server/agent-site.js +273 -0
- package/dist/runtime/server/api/agent-llms +1 -0
- package/dist/runtime/server/api/agent-llms-full +1 -0
- package/dist/runtime/server/api/agent-llms-full.d.ts +2 -0
- package/dist/runtime/server/api/agent-llms-full.js +13 -0
- package/dist/runtime/server/api/agent-llms.d.ts +2 -0
- package/dist/runtime/server/api/agent-llms.js +14 -0
- package/dist/runtime/server/api/agent-raw +1 -0
- package/dist/runtime/server/api/agent-raw.d.ts +2 -0
- package/dist/runtime/server/api/agent-raw.js +17 -0
- package/dist/runtime/server/api/locales.d.ts +2 -2
- package/dist/runtime/server/api/locales.js +4 -2
- package/dist/runtime/server/api/navigation.d.ts +1 -1
- package/dist/runtime/server/api/query.d.ts +1 -1
- package/dist/runtime/server/api/query.js +4 -1
- package/dist/runtime/server/api/revalidate.js +2 -2
- package/dist/runtime/server/api/site-data.d.ts +1 -1
- package/dist/runtime/server/collection-helpers.js +2 -1
- package/dist/runtime/server/middleware/agent-link-headers +1 -0
- package/dist/runtime/server/middleware/agent-link-headers.d.ts +2 -0
- package/dist/runtime/server/middleware/agent-link-headers.js +42 -0
- package/dist/runtime/server/middleware/agent-markdown +1 -0
- package/dist/runtime/server/middleware/agent-markdown.d.ts +2 -0
- package/dist/runtime/server/middleware/agent-markdown.js +43 -0
- package/dist/runtime/server/plugins/sitemap.js +1 -0
- package/dist/runtime/server/provider-query.d.ts +8 -5
- package/dist/runtime/server/provider-query.js +50 -8
- package/dist/runtime/server/provider-result.d.ts +19 -2
- package/dist/runtime/server/providers/filesystem.js +4 -1
- package/dist/runtime/server/providers/index.d.ts +3 -1
- package/dist/runtime/server/providers/index.js +2 -2
- package/dist/runtime/server/query-api.js +2 -1
- package/dist/runtime/server/query-executor.d.ts +3 -2
- package/dist/runtime/server/query-executor.js +13 -5
- package/dist/runtime/server/search.js +1 -1
- package/dist/runtime/server/sitemap-provider.js +40 -1
- package/dist/runtime/server/sitemap.js +3 -2
- package/dist/runtime/transformers/component-resolver.js +2 -1
- package/dist/runtime/utils/content-components.d.ts +0 -5
- package/dist/storage/content.d.ts +2 -9
- package/dist/storage/references.d.ts +1 -8
- package/dist/storage/references.js +9 -4
- package/dist/testing/provider-contract.js +3 -4
- package/dist/testing/provider-fixture.d.ts +3 -1
- package/dist/testing/provider-fixture.js +27 -14
- package/dist/types/config.d.ts +77 -0
- package/dist/types/config.js +23 -0
- package/dist/types/module.d.ts +15 -5
- package/dist/types/query-parts/collections +1 -0
- package/dist/types/query-parts/collections.d.ts +15 -0
- package/dist/types/query-parts/collections.js +0 -0
- package/dist/types/query-parts/public +1 -0
- package/dist/types/query-parts/public.d.ts +202 -0
- package/dist/types/query-parts/public.js +0 -0
- package/dist/types/query-parts/results +1 -0
- package/dist/types/query-parts/results.d.ts +95 -0
- package/dist/types/query-parts/results.js +0 -0
- package/dist/types/query-parts/transport +1 -0
- package/dist/types/query-parts/transport.d.ts +103 -0
- package/dist/types/query-parts/transport.js +0 -0
- package/dist/types/query.d.ts +4 -870
- package/dist/types/query.js +4 -0
- package/dist/types/runtime.d.ts +2 -2
- package/dist/types/virtual.d.ts +54 -0
- package/dist/types.d.mts +2 -2
- package/dist/web-types.json +1 -1
- package/package.json +5 -5
- package/dist/runtime/app/composables/web-socket +0 -1
- package/dist/runtime/app/composables/web-socket.d.ts +0 -3
- package/dist/runtime/app/composables/web-socket.js +0 -65
- package/dist/runtime/app/plugins/ws +0 -1
- package/dist/runtime/app/plugins/ws.js +0 -7
- /package/dist/runtime/app/plugins/{ws.d.ts → hot-reload.d.ts} +0 -0
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
import { getRequestURL } from "h3";
|
|
2
|
+
import { linkMarkdown, queryMarkdownEnabledContent, resolveContentMarkdownByRoute } from "./agent-markdown.js";
|
|
3
|
+
import { agentMarkdownPathForRoute, agentRawPathForRoute, agentRoutePathFromIndexSlug, agentRoutePathFromRawSlug, normalizeAgentRoutePath } from "../agent-paths.js";
|
|
4
|
+
import { contentConfig } from "./storage-access.js";
|
|
5
|
+
const localizedValue = (value, locale, fallback = "") => {
|
|
6
|
+
if (typeof value === "string") return value;
|
|
7
|
+
if (!value) return fallback;
|
|
8
|
+
return value[locale] || value[defaultLocale()] || Object.values(value)[0] || fallback;
|
|
9
|
+
};
|
|
10
|
+
const defaultLocale = () => contentConfig().agent?.site?.defaultLocale || contentConfig().defaultLocale || contentConfig().locales?.[0] || "en";
|
|
11
|
+
export const getAgentLocales = () => {
|
|
12
|
+
const site = contentConfig().agent?.site;
|
|
13
|
+
const locales = site?.locales?.length ? site.locales : contentConfig().locales;
|
|
14
|
+
return locales?.length ? locales : [defaultLocale()];
|
|
15
|
+
};
|
|
16
|
+
export const isSupportedAgentLocale = (locale) => Boolean(locale && getAgentLocales().includes(locale));
|
|
17
|
+
const resolveSiteUrl = (event) => {
|
|
18
|
+
const configured = contentConfig().agent?.site?.url;
|
|
19
|
+
if (configured) return configured;
|
|
20
|
+
if (event && !import.meta.prerender) {
|
|
21
|
+
const url = getRequestURL(event);
|
|
22
|
+
return `${url.protocol}//${url.host}`;
|
|
23
|
+
}
|
|
24
|
+
return "http://localhost:3000";
|
|
25
|
+
};
|
|
26
|
+
const joinUrl = (base, path) => new URL(normalizeAgentRoutePath(path), base).toString();
|
|
27
|
+
const prefixLocale = (path, locale) => {
|
|
28
|
+
const normalized = normalizeAgentRoutePath(path);
|
|
29
|
+
if (locale === defaultLocale()) return normalized;
|
|
30
|
+
if (normalized === `/${locale}` || normalized.startsWith(`/${locale}/`)) return normalized;
|
|
31
|
+
if (normalized === "/") return `/${locale}`;
|
|
32
|
+
return `/${locale}${normalized}`;
|
|
33
|
+
};
|
|
34
|
+
const sectionConfig = (id) => {
|
|
35
|
+
const sections = contentConfig().agent?.sections || [];
|
|
36
|
+
return sections.find((section) => section.id === id);
|
|
37
|
+
};
|
|
38
|
+
const resolveSection = (id, locale) => {
|
|
39
|
+
const resolvedId = id || "content";
|
|
40
|
+
const section = sectionConfig(resolvedId);
|
|
41
|
+
return {
|
|
42
|
+
id: resolvedId,
|
|
43
|
+
title: localizedValue(section?.title, locale, resolvedId === "content" ? "Content" : resolvedId),
|
|
44
|
+
order: section?.order ?? 1e3
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
const collectionSectionId = (collection) => contentConfig().collections?.[collection]?.agent?.section || "content";
|
|
48
|
+
const resolveLocalizedMaybeFunction = async (value, ctx) => {
|
|
49
|
+
if (typeof value === "function") return await value(ctx);
|
|
50
|
+
return localizedValue(value, ctx.locale);
|
|
51
|
+
};
|
|
52
|
+
const resolveAppPageRoute = (page, locale) => normalizeAgentRoutePath(localizedValue(page.route, locale, "/"));
|
|
53
|
+
const createAppPageContext = (locale, siteUrl) => ({
|
|
54
|
+
locale,
|
|
55
|
+
defaultLocale: defaultLocale(),
|
|
56
|
+
siteUrl
|
|
57
|
+
});
|
|
58
|
+
const metadataPolicy = () => {
|
|
59
|
+
const metadata = contentConfig().agent?.markdown?.metadata;
|
|
60
|
+
if (metadata === false) return { enabled: false, defaultFields: [] };
|
|
61
|
+
if (Array.isArray(metadata)) return { enabled: true, defaultFields: metadata };
|
|
62
|
+
return {
|
|
63
|
+
enabled: metadata?.enabled !== false,
|
|
64
|
+
defaultFields: Array.isArray(metadata?.defaultFields) ? metadata.defaultFields : []
|
|
65
|
+
};
|
|
66
|
+
};
|
|
67
|
+
const createGinkoAgentPage = (meta, locale, siteUrl) => {
|
|
68
|
+
const path = prefixLocale(meta.path, locale);
|
|
69
|
+
const rawPath = agentRawPathForRoute(path);
|
|
70
|
+
const section = resolveSection(collectionSectionId(meta.collection), locale);
|
|
71
|
+
return {
|
|
72
|
+
title: meta.title,
|
|
73
|
+
description: meta.description,
|
|
74
|
+
path,
|
|
75
|
+
markdownPath: agentMarkdownPathForRoute(path),
|
|
76
|
+
rawPath,
|
|
77
|
+
url: joinUrl(siteUrl, path),
|
|
78
|
+
markdownUrl: joinUrl(siteUrl, rawPath),
|
|
79
|
+
section: section.id,
|
|
80
|
+
sectionTitle: section.title,
|
|
81
|
+
sectionOrder: section.order,
|
|
82
|
+
locale,
|
|
83
|
+
source: "ginko",
|
|
84
|
+
collection: meta.collection,
|
|
85
|
+
updated: meta.lastModified,
|
|
86
|
+
metadataFields: meta.metadataFields,
|
|
87
|
+
includeInIndex: meta.includeInIndex,
|
|
88
|
+
includeInFull: meta.includeInFull
|
|
89
|
+
};
|
|
90
|
+
};
|
|
91
|
+
const createAppOwnedAgentPages = async (locale, siteUrl) => {
|
|
92
|
+
const pages = contentConfig().agent?.pages || [];
|
|
93
|
+
const result = [];
|
|
94
|
+
for (const page of pages) {
|
|
95
|
+
const ctx = createAppPageContext(locale, siteUrl);
|
|
96
|
+
const path = resolveAppPageRoute(page, locale);
|
|
97
|
+
const rawPath = agentRawPathForRoute(path);
|
|
98
|
+
const section = resolveSection(page.section, locale);
|
|
99
|
+
result.push({
|
|
100
|
+
title: await resolveLocalizedMaybeFunction(page.title, ctx),
|
|
101
|
+
description: await resolveLocalizedMaybeFunction(page.description, ctx),
|
|
102
|
+
path,
|
|
103
|
+
markdownPath: agentMarkdownPathForRoute(path),
|
|
104
|
+
rawPath,
|
|
105
|
+
url: joinUrl(siteUrl, path),
|
|
106
|
+
markdownUrl: joinUrl(siteUrl, rawPath),
|
|
107
|
+
section: section.id,
|
|
108
|
+
sectionTitle: section.title,
|
|
109
|
+
sectionOrder: section.order,
|
|
110
|
+
locale,
|
|
111
|
+
source: "app-owned",
|
|
112
|
+
updated: page.updated,
|
|
113
|
+
metadataFields: page.metadata,
|
|
114
|
+
includeInIndex: page.includeInIndex !== false,
|
|
115
|
+
includeInFull: page.includeInFull !== false,
|
|
116
|
+
markdown: await page.render(ctx)
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
return result;
|
|
120
|
+
};
|
|
121
|
+
const sortPages = (pages) => pages.sort((a, b) => {
|
|
122
|
+
const sectionDelta = a.sectionOrder - b.sectionOrder;
|
|
123
|
+
if (sectionDelta) return sectionDelta;
|
|
124
|
+
return a.path.localeCompare(b.path);
|
|
125
|
+
});
|
|
126
|
+
const assertUniqueAgentPages = (pages) => {
|
|
127
|
+
const seen = /* @__PURE__ */ new Map();
|
|
128
|
+
for (const page of pages) {
|
|
129
|
+
for (const [kind, value] of [
|
|
130
|
+
["route", page.path],
|
|
131
|
+
["raw route", page.rawPath],
|
|
132
|
+
["markdown route", page.markdownPath]
|
|
133
|
+
]) {
|
|
134
|
+
const key = `${kind}:${value}`;
|
|
135
|
+
const existing = seen.get(key);
|
|
136
|
+
if (existing) {
|
|
137
|
+
throw new Error(
|
|
138
|
+
`Duplicate agent ${kind} "${value}" for "${existing.title}" (${existing.source}) and "${page.title}" (${page.source}). Every agent-facing page must have one canonical route.`
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
seen.set(key, page);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
export const buildAgentPageIndex = async (event, locale = defaultLocale()) => {
|
|
146
|
+
const siteUrl = resolveSiteUrl(event);
|
|
147
|
+
const content = await queryMarkdownEnabledContent(event, { locale });
|
|
148
|
+
const pages = [
|
|
149
|
+
...await createAppOwnedAgentPages(locale, siteUrl),
|
|
150
|
+
...content.map((meta) => createGinkoAgentPage(meta, locale, siteUrl))
|
|
151
|
+
];
|
|
152
|
+
assertUniqueAgentPages(pages);
|
|
153
|
+
return sortPages(pages);
|
|
154
|
+
};
|
|
155
|
+
const sourceLabel = (source) => source === "ginko" ? "ginko-content" : "app-owned";
|
|
156
|
+
const metadataForPage = (page) => ({
|
|
157
|
+
title: page.title,
|
|
158
|
+
description: page.description,
|
|
159
|
+
url: page.url,
|
|
160
|
+
route: page.path,
|
|
161
|
+
locale: page.locale,
|
|
162
|
+
section: page.sectionTitle,
|
|
163
|
+
collection: page.collection,
|
|
164
|
+
source: sourceLabel(page.source),
|
|
165
|
+
updated: page.updated
|
|
166
|
+
});
|
|
167
|
+
const quoteYamlString = (value) => JSON.stringify(value);
|
|
168
|
+
const renderYamlValue = (value) => {
|
|
169
|
+
if (Array.isArray(value)) {
|
|
170
|
+
if (!value.length) return "[]";
|
|
171
|
+
return value.map((entry) => `
|
|
172
|
+
- ${quoteYamlString(entry)}`).join("");
|
|
173
|
+
}
|
|
174
|
+
return quoteYamlString(value);
|
|
175
|
+
};
|
|
176
|
+
export const renderAgentMarkdownFrontmatter = (page) => {
|
|
177
|
+
const policy = metadataPolicy();
|
|
178
|
+
if (!policy.enabled) return "";
|
|
179
|
+
const fields = page.metadataFields?.length ? page.metadataFields : policy.defaultFields;
|
|
180
|
+
const metadata = metadataForPage(page);
|
|
181
|
+
const lines = fields.flatMap((field) => {
|
|
182
|
+
const value = metadata[field];
|
|
183
|
+
return value ? [`${field}: ${renderYamlValue(value)}`] : [];
|
|
184
|
+
});
|
|
185
|
+
return lines.length ? `---
|
|
186
|
+
${lines.join("\n")}
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
` : "";
|
|
190
|
+
};
|
|
191
|
+
export const renderAgentMarkdownPage = (page, markdown) => `${renderAgentMarkdownFrontmatter(page)}${markdown.trim()}
|
|
192
|
+
`;
|
|
193
|
+
export const renderLlmsTxt = (pages, locale = defaultLocale()) => {
|
|
194
|
+
const site = contentConfig().agent?.site;
|
|
195
|
+
const visible = pages.filter((page) => page.includeInIndex);
|
|
196
|
+
const sections = /* @__PURE__ */ new Map();
|
|
197
|
+
for (const page of visible) {
|
|
198
|
+
const entry = sections.get(page.section) || { title: page.sectionTitle, order: page.sectionOrder, pages: [] };
|
|
199
|
+
entry.pages.push(page);
|
|
200
|
+
sections.set(page.section, entry);
|
|
201
|
+
}
|
|
202
|
+
const lines = [
|
|
203
|
+
`# ${localizedValue(site?.title, locale, "Site")}`,
|
|
204
|
+
"",
|
|
205
|
+
`> ${localizedValue(site?.description, locale, "")}`
|
|
206
|
+
];
|
|
207
|
+
if (site?.profile) lines.push("", `Profile: ${site.profile}`);
|
|
208
|
+
if (site?.contentSignals) {
|
|
209
|
+
lines.push(
|
|
210
|
+
`AI input: ${site.contentSignals.aiInput ? "yes" : "no"}`,
|
|
211
|
+
`AI training: ${site.contentSignals.aiTrain ? "yes" : "no"}`
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
for (const section of Array.from(sections.values()).sort((a, b) => a.order - b.order || a.title.localeCompare(b.title))) {
|
|
215
|
+
lines.push("", `## ${section.title}`, "");
|
|
216
|
+
for (const page of section.pages) {
|
|
217
|
+
lines.push(`- ${linkMarkdown(page.title, page.markdownUrl)}: ${page.description.replace(/\n+/g, " ")}`);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return `${lines.join("\n").trim()}
|
|
221
|
+
`;
|
|
222
|
+
};
|
|
223
|
+
export const resolveMarkdownForPublicRoute = async (event, routePath, locale = localeFromAgentPath(routePath)) => {
|
|
224
|
+
const siteUrl = resolveSiteUrl(event);
|
|
225
|
+
const normalized = normalizeAgentRoutePath(routePath);
|
|
226
|
+
const appPage = (await createAppOwnedAgentPages(locale, siteUrl)).find((page) => page.path === normalized);
|
|
227
|
+
if (appPage?.markdown) {
|
|
228
|
+
return { markdown: renderAgentMarkdownPage(appPage, appPage.markdown) };
|
|
229
|
+
}
|
|
230
|
+
const ginkoPage = await resolveContentMarkdownByRoute(event, normalized, { locale });
|
|
231
|
+
if (!ginkoPage) return null;
|
|
232
|
+
return {
|
|
233
|
+
...ginkoPage,
|
|
234
|
+
markdown: renderAgentMarkdownPage(createGinkoAgentPage(ginkoPage, locale, siteUrl), ginkoPage.markdown)
|
|
235
|
+
};
|
|
236
|
+
};
|
|
237
|
+
export const renderLlmsFullTxt = async (event, locale = defaultLocale()) => {
|
|
238
|
+
const pages = await buildAgentPageIndex(event, locale);
|
|
239
|
+
const lines = [renderLlmsTxt(pages, locale).trim()];
|
|
240
|
+
for (const page of pages.filter((page2) => page2.includeInFull)) {
|
|
241
|
+
const markdown = page.markdown ? renderAgentMarkdownPage(page, page.markdown) : (await resolveMarkdownForPublicRoute(event, page.path, locale))?.markdown;
|
|
242
|
+
if (!markdown) continue;
|
|
243
|
+
lines.push("", "---", "", `Source: ${page.url}`, "", markdown.trim());
|
|
244
|
+
}
|
|
245
|
+
return `${lines.join("\n").trim()}
|
|
246
|
+
`;
|
|
247
|
+
};
|
|
248
|
+
export const localeFromAgentPath = (path) => {
|
|
249
|
+
const normalized = normalizeAgentRoutePath(path);
|
|
250
|
+
const fallback = defaultLocale();
|
|
251
|
+
for (const locale of getAgentLocales()) {
|
|
252
|
+
if (locale !== fallback && (normalized === `/${locale}` || normalized.startsWith(`/${locale}/`))) {
|
|
253
|
+
return locale;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
return fallback;
|
|
257
|
+
};
|
|
258
|
+
export const routePathFromRawSlug = agentRoutePathFromRawSlug;
|
|
259
|
+
export const routePathFromIndexSlug = agentRoutePathFromIndexSlug;
|
|
260
|
+
export const collectAgentMarkdownPrerenderRoutes = async (event) => {
|
|
261
|
+
const routes = /* @__PURE__ */ new Set();
|
|
262
|
+
for (const locale of getAgentLocales()) {
|
|
263
|
+
const pages = await buildAgentPageIndex(event, locale);
|
|
264
|
+
for (const page of pages) {
|
|
265
|
+
routes.add(page.rawPath);
|
|
266
|
+
routes.add(page.markdownPath);
|
|
267
|
+
}
|
|
268
|
+
const prefix = locale === defaultLocale() ? "" : `/${locale}`;
|
|
269
|
+
routes.add(prefix ? `${prefix}/llms.txt` : "/llms.txt");
|
|
270
|
+
routes.add(prefix ? `${prefix}/llms-full.txt` : "/llms-full.txt");
|
|
271
|
+
}
|
|
272
|
+
return Array.from(routes);
|
|
273
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './agent-llms.js'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './agent-llms-full.js'
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { createError, defineEventHandler } from "h3";
|
|
2
|
+
import { isSupportedAgentLocale, localeFromAgentPath, renderLlmsFullTxt } from "../agent-site.js";
|
|
3
|
+
import { setAgentMarkdownHeaders } from "../agent-http.js";
|
|
4
|
+
export default defineEventHandler(async (event) => {
|
|
5
|
+
const path = event.node.req.url?.replace(/\?.*$/, "") || "/";
|
|
6
|
+
const explicitLocale = /^\/([^/]+)\/llms-full\.txt$/i.exec(path)?.[1];
|
|
7
|
+
if (explicitLocale && !isSupportedAgentLocale(explicitLocale)) {
|
|
8
|
+
throw createError({ statusCode: 404, statusMessage: "Agent locale not found" });
|
|
9
|
+
}
|
|
10
|
+
const locale = localeFromAgentPath(path);
|
|
11
|
+
setAgentMarkdownHeaders(event);
|
|
12
|
+
return renderLlmsFullTxt(event, locale);
|
|
13
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { createError, defineEventHandler } from "h3";
|
|
2
|
+
import { localeFromAgentPath, renderLlmsTxt, buildAgentPageIndex, isSupportedAgentLocale } from "../agent-site.js";
|
|
3
|
+
import { appendResponseHeader, setAgentMarkdownHeaders } from "../agent-http.js";
|
|
4
|
+
export default defineEventHandler(async (event) => {
|
|
5
|
+
const path = event.node.req.url?.replace(/\?.*$/, "") || "/";
|
|
6
|
+
const explicitLocale = /^\/([^/]+)\/llms\.txt$/i.exec(path)?.[1];
|
|
7
|
+
if (explicitLocale && !isSupportedAgentLocale(explicitLocale)) {
|
|
8
|
+
throw createError({ statusCode: 404, statusMessage: "Agent locale not found" });
|
|
9
|
+
}
|
|
10
|
+
const locale = localeFromAgentPath(path);
|
|
11
|
+
setAgentMarkdownHeaders(event);
|
|
12
|
+
appendResponseHeader(event, "link", `<${path.replace(/llms\.txt$/i, "llms-full.txt")}>; rel="alternate"; type="text/markdown"`);
|
|
13
|
+
return renderLlmsTxt(await buildAgentPageIndex(event, locale), locale);
|
|
14
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './agent-raw.js'
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { createError, defineEventHandler } from "h3";
|
|
2
|
+
import { localeFromAgentPath, resolveMarkdownForPublicRoute, routePathFromRawSlug } from "../agent-site.js";
|
|
3
|
+
import { setAgentMarkdownHeaders } from "../agent-http.js";
|
|
4
|
+
import { isUnsafeAgentRoutePath } from "../../agent-paths.js";
|
|
5
|
+
export default defineEventHandler(async (event) => {
|
|
6
|
+
const routePath = routePathFromRawSlug(event.context.params?.slug);
|
|
7
|
+
if (isUnsafeAgentRoutePath(routePath)) {
|
|
8
|
+
throw createError({ statusCode: 400, statusMessage: "Invalid markdown route path" });
|
|
9
|
+
}
|
|
10
|
+
const locale = localeFromAgentPath(routePath);
|
|
11
|
+
const page = await resolveMarkdownForPublicRoute(event, routePath, locale);
|
|
12
|
+
if (!page) {
|
|
13
|
+
throw createError({ statusCode: 404, statusMessage: "Markdown page not found" });
|
|
14
|
+
}
|
|
15
|
+
setAgentMarkdownHeaders(event, { noindex: true });
|
|
16
|
+
return page.markdown;
|
|
17
|
+
});
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { createError, defineEventHandler, getQuery } from "h3";
|
|
2
2
|
import { resolveProviderContentVariants } from "../provider-query.js";
|
|
3
3
|
export default defineEventHandler(async (event) => {
|
|
4
|
-
const
|
|
5
|
-
const
|
|
4
|
+
const query = getQuery(event);
|
|
5
|
+
const identity = typeof query.identity === "string" ? query.identity : "";
|
|
6
|
+
const params = event.context.params;
|
|
7
|
+
const collection = typeof params?.collection === "string" ? params.collection : "";
|
|
6
8
|
if (!collection || !identity) {
|
|
7
9
|
throw createError({
|
|
8
10
|
statusCode: 400,
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<import("
|
|
1
|
+
declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<import("../../../types").NavItem[]>>;
|
|
2
2
|
export default _default;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<import("
|
|
1
|
+
declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<import("../../../types/api").ContentQueryResponse<unknown>>>;
|
|
2
2
|
export default _default;
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { defineEventHandler } from "h3";
|
|
2
2
|
import { getContentQuery } from "../../utils/query.js";
|
|
3
3
|
import { getContentProvider } from "../providers/index.js";
|
|
4
|
+
import { normalizeProviderQueryResponse } from "../provider-query.js";
|
|
4
5
|
export default defineEventHandler(async (event) => {
|
|
5
|
-
|
|
6
|
+
const query = getContentQuery(event);
|
|
7
|
+
const provider = await getContentProvider(event);
|
|
8
|
+
return normalizeProviderQueryResponse(query, await provider.query(event, query), provider.name);
|
|
6
9
|
});
|
|
@@ -7,7 +7,7 @@ const normalizePath = (path) => {
|
|
|
7
7
|
const normalized = path.startsWith("/") ? path : `/${path}`;
|
|
8
8
|
return normalized.replace(/\/{2,}/g, "/");
|
|
9
9
|
};
|
|
10
|
-
const normalizeStringList = (value, normalize = (value2) => value2) => Array.isArray(value) ? Array.from(new Set(value.filter((entry) => typeof entry === "string" && entry.trim()).map((entry) => normalize(entry.trim())))) : void 0;
|
|
10
|
+
const normalizeStringList = (value, normalize = (value2) => value2) => Array.isArray(value) ? Array.from(new Set(value.filter((entry) => typeof entry === "string" && entry.trim().length > 0).map((entry) => normalize(entry.trim())))) : void 0;
|
|
11
11
|
const SIGNATURE_TOLERANCE_MS = 5 * 60 * 1e3;
|
|
12
12
|
async function hmacSha256Hex(secret, value) {
|
|
13
13
|
const encoder = new TextEncoder();
|
|
@@ -98,7 +98,7 @@ export default defineEventHandler(async (event) => {
|
|
|
98
98
|
});
|
|
99
99
|
}
|
|
100
100
|
const rawBody = await readRawBody(event).catch(() => void 0);
|
|
101
|
-
const bodyText = typeof rawBody === "string" ? rawBody : rawBody
|
|
101
|
+
const bodyText = typeof rawBody === "string" ? rawBody : rawBody === void 0 ? void 0 : String(rawBody);
|
|
102
102
|
if (!bodyText) {
|
|
103
103
|
throw createError({
|
|
104
104
|
statusCode: 400,
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<import("
|
|
1
|
+
declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<import("../../../public/provider").ContentProviderSiteDataResponse<unknown>>>;
|
|
2
2
|
export default _default;
|
|
@@ -57,7 +57,7 @@ export async function queryFilesystemCollectionPage(event, collection, routeOrPa
|
|
|
57
57
|
...options,
|
|
58
58
|
loadVariantPage: async (input) => {
|
|
59
59
|
try {
|
|
60
|
-
|
|
60
|
+
const response = await executeFilesystemContentQuery(event, {
|
|
61
61
|
collection,
|
|
62
62
|
first: true,
|
|
63
63
|
resolveVariant: {
|
|
@@ -66,6 +66,7 @@ export async function queryFilesystemCollectionPage(event, collection, routeOrPa
|
|
|
66
66
|
fallback: input.fallback
|
|
67
67
|
}
|
|
68
68
|
});
|
|
69
|
+
return response.result || null;
|
|
69
70
|
} catch (error) {
|
|
70
71
|
if (isNotFoundError(error)) {
|
|
71
72
|
return null;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './agent-link-headers.js'
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { defineEventHandler, getRequestURL, setHeader } from "h3";
|
|
2
|
+
import { getAgentLocales, localeFromAgentPath, resolveMarkdownForPublicRoute } from "../agent-site.js";
|
|
3
|
+
import { agentMarkdownPathForRoute, agentRawPathForRoute, normalizeAgentRoutePath } from "../../agent-paths.js";
|
|
4
|
+
import { appendResponseHeader } from "../agent-http.js";
|
|
5
|
+
import { contentConfig } from "../storage-access.js";
|
|
6
|
+
const shouldAdvertise = (pathname) => !pathname.startsWith("/_") && !pathname.startsWith("/api/") && !pathname.endsWith(".css") && !pathname.endsWith(".js") && !pathname.endsWith(".png") && !pathname.endsWith(".jpg") && !pathname.endsWith(".jpeg") && !pathname.endsWith(".webp") && !pathname.endsWith(".svg") && !pathname.endsWith(".ico");
|
|
7
|
+
const canAdvertisePageAlternate = (pathname) => !pathname.endsWith(".md") && !pathname.endsWith(".txt") && !pathname.endsWith(".xml") && !pathname.endsWith(".json");
|
|
8
|
+
export default defineEventHandler(async (event) => {
|
|
9
|
+
const { pathname } = getRequestURL(event);
|
|
10
|
+
if (!shouldAdvertise(pathname)) return;
|
|
11
|
+
const signals = contentConfig().agent?.site?.contentSignals;
|
|
12
|
+
if (signals) {
|
|
13
|
+
setHeader(
|
|
14
|
+
event,
|
|
15
|
+
"content-signal",
|
|
16
|
+
[
|
|
17
|
+
`search=${signals.search ? "yes" : "no"}`,
|
|
18
|
+
`ai-input=${signals.aiInput ? "yes" : "no"}`,
|
|
19
|
+
`ai-train=${signals.aiTrain ? "yes" : "no"}`
|
|
20
|
+
].join(", ")
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
const locale = localeFromAgentPath(pathname);
|
|
24
|
+
const defaultLocale = contentConfig().agent?.site?.defaultLocale || contentConfig().defaultLocale || getAgentLocales()[0];
|
|
25
|
+
const prefix = locale && locale !== defaultLocale ? `/${locale}` : "";
|
|
26
|
+
const page = canAdvertisePageAlternate(pathname) ? await resolveMarkdownForPublicRoute(event, pathname, locale) : null;
|
|
27
|
+
const pagePath = normalizeAgentRoutePath(pathname);
|
|
28
|
+
const pageLinks = page ? [
|
|
29
|
+
`<${agentRawPathForRoute(pagePath)}>; rel="alternate"; type="text/markdown"`,
|
|
30
|
+
`<${agentMarkdownPathForRoute(pagePath)}>; rel="alternate"; type="text/markdown"`
|
|
31
|
+
] : [];
|
|
32
|
+
appendResponseHeader(
|
|
33
|
+
event,
|
|
34
|
+
"link",
|
|
35
|
+
[
|
|
36
|
+
...pageLinks,
|
|
37
|
+
`<${prefix}/llms.txt>; rel="llms"; type="text/markdown"`,
|
|
38
|
+
`<${prefix}/llms-full.txt>; rel="alternate"; type="text/markdown"`,
|
|
39
|
+
'</sitemap.xml>; rel="sitemap"; type="application/xml"'
|
|
40
|
+
].join(", ")
|
|
41
|
+
);
|
|
42
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './agent-markdown.js'
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { createError, defineEventHandler, getRequestURL, send } from "h3";
|
|
2
|
+
import {
|
|
3
|
+
buildAgentPageIndex,
|
|
4
|
+
getAgentLocales,
|
|
5
|
+
localeFromAgentPath,
|
|
6
|
+
renderLlmsTxt,
|
|
7
|
+
resolveMarkdownForPublicRoute
|
|
8
|
+
} from "../agent-site.js";
|
|
9
|
+
import { acceptsMarkdown, addVaryHeader, setAgentMarkdownHeaders } from "../agent-http.js";
|
|
10
|
+
import { isUnsafeAgentRoutePath, normalizeAgentRoutePath } from "../../agent-paths.js";
|
|
11
|
+
const shouldSkip = (pathname) => pathname.startsWith("/_") || pathname.startsWith("/api/") || pathname.startsWith("/raw/") || pathname === "/robots.txt" || pathname === "/sitemap.xml" || pathname.endsWith(".txt") || pathname.endsWith(".xml") || pathname.endsWith(".json") || pathname.endsWith(".ico") || pathname.endsWith(".png") || pathname.endsWith(".jpg") || pathname.endsWith(".jpeg") || pathname.endsWith(".webp") || pathname.endsWith(".svg") || pathname.endsWith(".css") || pathname.endsWith(".js");
|
|
12
|
+
export default defineEventHandler(async (event) => {
|
|
13
|
+
const { pathname } = getRequestURL(event);
|
|
14
|
+
const rawPathname = event.node.req.url?.split("?")[0] || pathname;
|
|
15
|
+
if (isUnsafeAgentRoutePath(rawPathname)) {
|
|
16
|
+
throw createError({ statusCode: 400, statusMessage: "Invalid markdown route path" });
|
|
17
|
+
}
|
|
18
|
+
if (shouldSkip(pathname)) return;
|
|
19
|
+
addVaryHeader(event, "accept");
|
|
20
|
+
if (pathname.endsWith("/index.md")) {
|
|
21
|
+
const routePath = normalizeAgentRoutePath(pathname.replace(/\/index\.md$/i, "") || "/");
|
|
22
|
+
if (isUnsafeAgentRoutePath(routePath)) {
|
|
23
|
+
throw createError({ statusCode: 400, statusMessage: "Invalid markdown route path" });
|
|
24
|
+
}
|
|
25
|
+
const locale2 = localeFromAgentPath(routePath);
|
|
26
|
+
const page2 = await resolveMarkdownForPublicRoute(event, routePath, locale2);
|
|
27
|
+
if (!page2) return;
|
|
28
|
+
setAgentMarkdownHeaders(event, { noindex: true });
|
|
29
|
+
return send(event, page2.markdown, "text/markdown; charset=utf-8");
|
|
30
|
+
}
|
|
31
|
+
if (!acceptsMarkdown(event)) return;
|
|
32
|
+
if (pathname === "/" || getAgentLocales().some((locale2) => pathname === `/${locale2}`)) {
|
|
33
|
+
const locale2 = localeFromAgentPath(pathname);
|
|
34
|
+
setAgentMarkdownHeaders(event);
|
|
35
|
+
return send(event, renderLlmsTxt(await buildAgentPageIndex(event, locale2), locale2), "text/markdown; charset=utf-8");
|
|
36
|
+
}
|
|
37
|
+
const locale = localeFromAgentPath(pathname);
|
|
38
|
+
const page = await resolveMarkdownForPublicRoute(event, pathname, locale);
|
|
39
|
+
if (page) {
|
|
40
|
+
setAgentMarkdownHeaders(event);
|
|
41
|
+
return send(event, page.markdown, "text/markdown; charset=utf-8");
|
|
42
|
+
}
|
|
43
|
+
});
|
|
@@ -3,6 +3,7 @@ import { getContentRuntimeConfig } from "../runtime-config.js";
|
|
|
3
3
|
import { GINKO_SITEMAP_SOURCE_NAME, resolveContentSitemapSource } from "../../utils/sitemap-source.js";
|
|
4
4
|
const LEGACY_NUXT_CONTENT_V2_SOURCE = "@nuxt/content@v2:urls";
|
|
5
5
|
export default defineNitroPlugin((nitro) => {
|
|
6
|
+
;
|
|
6
7
|
nitro.hooks.hook("sitemap:sources", (ctx) => {
|
|
7
8
|
const runtimeConfig = getContentRuntimeConfig();
|
|
8
9
|
const sitemap = runtimeConfig.public.content?.sitemap;
|
|
@@ -3,18 +3,21 @@ import type { ContentCollectionMap } from '@lupinum/ginko-content';
|
|
|
3
3
|
import type { ParsedContent } from '../../types/content';
|
|
4
4
|
import type { ContentQueryResponse } from '../../types/api';
|
|
5
5
|
import type { CollectionQueryBuilder, ContentQueryBuilderParams, ContentQueryRequest, ResolveContentReferenceOptions } from '../../types/query';
|
|
6
|
-
export declare const normalizeProviderQueryResult: <T>(response: ContentQueryResponse<T>
|
|
6
|
+
export declare const normalizeProviderQueryResult: <T>(response: ContentQueryResponse<T>) => T[];
|
|
7
|
+
export declare const normalizeProviderQueryResponse: <T>(params: ContentQueryBuilderParams, response: unknown, providerName?: string) => ContentQueryResponse<T>;
|
|
7
8
|
export declare const resolveProviderContentVariants: (event: H3Event, identity: string, options: ResolveContentReferenceOptions) => Promise<{
|
|
8
|
-
canonicalKey:
|
|
9
|
+
canonicalKey: string;
|
|
9
10
|
selected: any;
|
|
10
|
-
variants:
|
|
11
|
+
variants: ParsedContent[];
|
|
11
12
|
availableLocales: string[];
|
|
12
|
-
variantPaths:
|
|
13
|
+
variantPaths: {
|
|
14
|
+
[k: string]: string;
|
|
15
|
+
};
|
|
13
16
|
requestedLocale: string | undefined;
|
|
14
17
|
resolvedLocale: any;
|
|
15
18
|
fallback: boolean;
|
|
16
19
|
} | null>;
|
|
17
|
-
export declare const createServerProviderQueryFetch: <T = ParsedContent>(event: H3Event) => (query: ContentQueryRequest) => Promise<
|
|
20
|
+
export declare const createServerProviderQueryFetch: <T = ParsedContent>(event: H3Event) => (query: ContentQueryRequest) => Promise<ContentQueryResponse<T>>;
|
|
18
21
|
export declare const createServerContentQuery: <T = ParsedContent>(event: H3Event, query?: string | ContentQueryBuilderParams, ...pathParts: string[]) => import("@lupinum/ginko-content").ContentQueryBuilder<T>;
|
|
19
22
|
export declare function serverQueryCollection<K extends keyof ContentCollectionMap & string>(event: H3Event, collection: K): CollectionQueryBuilder<ContentCollectionMap[K]>;
|
|
20
23
|
export declare function serverQueryCollection<T = ParsedContent>(event: H3Event, collection: string): CollectionQueryBuilder<T>;
|