@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.
Files changed (189) hide show
  1. package/README.md +18 -4
  2. package/compatibility.json +1 -1
  3. package/dist/cli.mjs +317 -294
  4. package/dist/cms-import/index.js +5 -1
  5. package/dist/config.d.mts +1 -1
  6. package/dist/config.mjs +1 -1
  7. package/dist/core/cache-hints.d.ts +9 -1
  8. package/dist/core/content/collection.js +1 -1
  9. package/dist/core/content/path.js +1 -1
  10. package/dist/core/errors.js +5 -1
  11. package/dist/core/provider-errors +1 -0
  12. package/dist/core/provider-errors.d.ts +10 -0
  13. package/dist/core/provider-errors.js +27 -0
  14. package/dist/core/query/execute.js +12 -3
  15. package/dist/core/query/filter.js +2 -2
  16. package/dist/core/query/lower.js +5 -4
  17. package/dist/features/navigation/build.js +14 -6
  18. package/dist/features/navigation/canonical.js +1 -1
  19. package/dist/features/sitemap/query.js +2 -2
  20. package/dist/integrations/nitro/ingest.d.ts +3 -3
  21. package/dist/integrations/nitro/ingest.js +3 -3
  22. package/dist/integrations/nitro/storage.js +3 -1
  23. package/dist/module.d.mts +151 -526
  24. package/dist/module.json +1 -1
  25. package/dist/module.mjs +1091 -722
  26. package/dist/parsers/csv.js +4 -3
  27. package/dist/parsers/index.d.ts +2 -5
  28. package/dist/parsers/index.js +2 -2
  29. package/dist/parsers/markdown-plugins.d.ts +1 -0
  30. package/dist/parsers/markdown-plugins.js +83 -1
  31. package/dist/parsers/markdown.js +1 -1
  32. package/dist/public/client.d.ts +3 -2
  33. package/dist/public/client.js +2 -2
  34. package/dist/public/provider-errors.d.ts +2 -1
  35. package/dist/public/provider-errors.js +1 -18
  36. package/dist/public/provider.d.ts +3 -10
  37. package/dist/public/server.d.ts +6 -1
  38. package/dist/public/server.js +33 -0
  39. package/dist/runtime/agent-paths +1 -0
  40. package/dist/runtime/agent-paths.d.ts +6 -0
  41. package/dist/runtime/agent-paths.js +41 -0
  42. package/dist/runtime/app/components/internal/MarkdownRenderer.d.ts +1 -1
  43. package/dist/runtime/app/composables/head.d.ts +3 -0
  44. package/dist/runtime/app/composables/head.js +2 -1
  45. package/dist/runtime/app/composables/hot-reload +1 -0
  46. package/dist/runtime/app/composables/hot-reload.d.ts +5 -0
  47. package/dist/runtime/app/composables/hot-reload.js +15 -0
  48. package/dist/runtime/app/composables/locale-context.d.ts +1 -1
  49. package/dist/runtime/app/composables/locale-context.js +2 -1
  50. package/dist/runtime/app/composables/locale.js +2 -2
  51. package/dist/runtime/app/composables/search.js +10 -6
  52. package/dist/runtime/app/composables/use-content-document +1 -0
  53. package/dist/runtime/app/composables/use-content-document.d.ts +29 -0
  54. package/dist/runtime/app/composables/use-content-document.js +44 -0
  55. package/dist/runtime/app/composables/use-content-list +1 -0
  56. package/dist/runtime/app/composables/use-content-list.d.ts +43 -0
  57. package/dist/runtime/app/composables/use-content-list.js +98 -0
  58. package/dist/runtime/app/composables/use-content-navigation +1 -0
  59. package/dist/runtime/app/composables/use-content-navigation.d.ts +40 -0
  60. package/dist/runtime/app/composables/use-content-navigation.js +102 -0
  61. package/dist/runtime/app/composables/use-content-page +1 -0
  62. package/dist/runtime/app/composables/use-content-page.d.ts +47 -0
  63. package/dist/runtime/app/composables/use-content-page.js +142 -0
  64. package/dist/runtime/app/composables/use-content-shared +1 -0
  65. package/dist/runtime/app/composables/use-content-shared.d.ts +13 -0
  66. package/dist/runtime/app/composables/use-content-shared.js +29 -0
  67. package/dist/runtime/app/composables/use-content.d.ts +11 -206
  68. package/dist/runtime/app/composables/use-content.js +4 -377
  69. package/dist/runtime/app/plugins/hot-reload +1 -0
  70. package/dist/runtime/app/plugins/hot-reload.js +6 -0
  71. package/dist/runtime/query/backlinks +1 -0
  72. package/dist/runtime/query/backlinks.d.ts +8 -0
  73. package/dist/runtime/query/backlinks.js +90 -0
  74. package/dist/runtime/query/context +1 -0
  75. package/dist/runtime/query/context.d.ts +20 -0
  76. package/dist/runtime/query/context.js +0 -0
  77. package/dist/runtime/query/documents +1 -0
  78. package/dist/runtime/query/documents.d.ts +6 -0
  79. package/dist/runtime/query/documents.js +98 -0
  80. package/dist/runtime/query/errors +1 -0
  81. package/dist/runtime/query/errors.d.ts +1 -0
  82. package/dist/runtime/query/errors.js +4 -0
  83. package/dist/runtime/query/handles +1 -0
  84. package/dist/runtime/query/handles.d.ts +2 -0
  85. package/dist/runtime/query/handles.js +7 -0
  86. package/dist/runtime/query/locale-options +1 -0
  87. package/dist/runtime/query/locale-options.d.ts +5 -0
  88. package/dist/runtime/query/locale-options.js +14 -0
  89. package/dist/runtime/query/localized-docs +1 -0
  90. package/dist/runtime/query/localized-docs.d.ts +17 -0
  91. package/dist/runtime/query/localized-docs.js +44 -0
  92. package/dist/runtime/query/navigation +1 -0
  93. package/dist/runtime/query/navigation.d.ts +22 -0
  94. package/dist/runtime/query/navigation.js +73 -0
  95. package/dist/runtime/query/pagination +1 -0
  96. package/dist/runtime/query/pagination.d.ts +7 -0
  97. package/dist/runtime/query/pagination.js +78 -0
  98. package/dist/runtime/query/populate +1 -0
  99. package/dist/runtime/query/populate.d.ts +9 -0
  100. package/dist/runtime/query/populate.js +106 -0
  101. package/dist/runtime/query/responses +1 -0
  102. package/dist/runtime/query/responses.d.ts +10 -0
  103. package/dist/runtime/query/responses.js +47 -0
  104. package/dist/runtime/query/unified.d.ts +4 -20
  105. package/dist/runtime/query/unified.js +14 -544
  106. package/dist/runtime/query/variants +1 -0
  107. package/dist/runtime/query/variants.d.ts +11 -0
  108. package/dist/runtime/query/variants.js +36 -0
  109. package/dist/runtime/server/agent-http +1 -0
  110. package/dist/runtime/server/agent-http.d.ts +7 -0
  111. package/dist/runtime/server/agent-http.js +36 -0
  112. package/dist/runtime/server/agent-markdown +1 -0
  113. package/dist/runtime/server/agent-markdown.d.ts +83 -0
  114. package/dist/runtime/server/agent-markdown.js +449 -0
  115. package/dist/runtime/server/agent-site +1 -0
  116. package/dist/runtime/server/agent-site.d.ts +38 -0
  117. package/dist/runtime/server/agent-site.js +273 -0
  118. package/dist/runtime/server/api/agent-llms +1 -0
  119. package/dist/runtime/server/api/agent-llms-full +1 -0
  120. package/dist/runtime/server/api/agent-llms-full.d.ts +2 -0
  121. package/dist/runtime/server/api/agent-llms-full.js +13 -0
  122. package/dist/runtime/server/api/agent-llms.d.ts +2 -0
  123. package/dist/runtime/server/api/agent-llms.js +14 -0
  124. package/dist/runtime/server/api/agent-raw +1 -0
  125. package/dist/runtime/server/api/agent-raw.d.ts +2 -0
  126. package/dist/runtime/server/api/agent-raw.js +17 -0
  127. package/dist/runtime/server/api/locales.d.ts +2 -2
  128. package/dist/runtime/server/api/locales.js +4 -2
  129. package/dist/runtime/server/api/navigation.d.ts +1 -1
  130. package/dist/runtime/server/api/query.d.ts +1 -1
  131. package/dist/runtime/server/api/query.js +4 -1
  132. package/dist/runtime/server/api/revalidate.js +2 -2
  133. package/dist/runtime/server/api/site-data.d.ts +1 -1
  134. package/dist/runtime/server/collection-helpers.js +2 -1
  135. package/dist/runtime/server/middleware/agent-link-headers +1 -0
  136. package/dist/runtime/server/middleware/agent-link-headers.d.ts +2 -0
  137. package/dist/runtime/server/middleware/agent-link-headers.js +42 -0
  138. package/dist/runtime/server/middleware/agent-markdown +1 -0
  139. package/dist/runtime/server/middleware/agent-markdown.d.ts +2 -0
  140. package/dist/runtime/server/middleware/agent-markdown.js +43 -0
  141. package/dist/runtime/server/plugins/sitemap.js +1 -0
  142. package/dist/runtime/server/provider-query.d.ts +8 -5
  143. package/dist/runtime/server/provider-query.js +50 -8
  144. package/dist/runtime/server/provider-result.d.ts +19 -2
  145. package/dist/runtime/server/providers/filesystem.js +4 -1
  146. package/dist/runtime/server/providers/index.d.ts +3 -1
  147. package/dist/runtime/server/providers/index.js +2 -2
  148. package/dist/runtime/server/query-api.js +2 -1
  149. package/dist/runtime/server/query-executor.d.ts +3 -2
  150. package/dist/runtime/server/query-executor.js +13 -5
  151. package/dist/runtime/server/search.js +1 -1
  152. package/dist/runtime/server/sitemap-provider.js +40 -1
  153. package/dist/runtime/server/sitemap.js +3 -2
  154. package/dist/runtime/transformers/component-resolver.js +2 -1
  155. package/dist/runtime/utils/content-components.d.ts +0 -5
  156. package/dist/storage/content.d.ts +2 -9
  157. package/dist/storage/references.d.ts +1 -8
  158. package/dist/storage/references.js +9 -4
  159. package/dist/testing/provider-contract.js +3 -4
  160. package/dist/testing/provider-fixture.d.ts +3 -1
  161. package/dist/testing/provider-fixture.js +27 -14
  162. package/dist/types/config.d.ts +77 -0
  163. package/dist/types/config.js +23 -0
  164. package/dist/types/module.d.ts +15 -5
  165. package/dist/types/query-parts/collections +1 -0
  166. package/dist/types/query-parts/collections.d.ts +15 -0
  167. package/dist/types/query-parts/collections.js +0 -0
  168. package/dist/types/query-parts/public +1 -0
  169. package/dist/types/query-parts/public.d.ts +202 -0
  170. package/dist/types/query-parts/public.js +0 -0
  171. package/dist/types/query-parts/results +1 -0
  172. package/dist/types/query-parts/results.d.ts +95 -0
  173. package/dist/types/query-parts/results.js +0 -0
  174. package/dist/types/query-parts/transport +1 -0
  175. package/dist/types/query-parts/transport.d.ts +103 -0
  176. package/dist/types/query-parts/transport.js +0 -0
  177. package/dist/types/query.d.ts +4 -870
  178. package/dist/types/query.js +4 -0
  179. package/dist/types/runtime.d.ts +2 -2
  180. package/dist/types/virtual.d.ts +54 -0
  181. package/dist/types.d.mts +2 -2
  182. package/dist/web-types.json +1 -1
  183. package/package.json +5 -5
  184. package/dist/runtime/app/composables/web-socket +0 -1
  185. package/dist/runtime/app/composables/web-socket.d.ts +0 -3
  186. package/dist/runtime/app/composables/web-socket.js +0 -65
  187. package/dist/runtime/app/plugins/ws +0 -1
  188. package/dist/runtime/app/plugins/ws.js +0 -7
  189. /package/dist/runtime/app/plugins/{ws.d.ts → hot-reload.d.ts} +0 -0
@@ -0,0 +1,83 @@
1
+ import type { H3Event } from 'h3';
2
+ import type { MarkdownNode, ParsedContent } from '../../types/content';
3
+ import type { AgentMetadataField, ContentCollectionConfig } from '../../types/config';
4
+ export interface AgentMarkdownPublicSignals {
5
+ search?: 'yes' | 'no';
6
+ aiInput?: 'yes' | 'no';
7
+ aiTrain?: 'yes' | 'no';
8
+ }
9
+ export interface ResolvedAgentMarkdownOptions {
10
+ includeInIndex: boolean;
11
+ includeInFull: boolean;
12
+ metadata: AgentMetadataField[];
13
+ }
14
+ export interface AgentMarkdown {
15
+ path: string;
16
+ markdownPath: string;
17
+ rawPath: string;
18
+ locale?: string;
19
+ collection: string;
20
+ title: string;
21
+ description: string;
22
+ markdown: string;
23
+ sourceFile?: string;
24
+ canonicalUrl: string;
25
+ lastModified?: string;
26
+ publicSignals?: AgentMarkdownPublicSignals;
27
+ metadataFields: string[];
28
+ includeInIndex: boolean;
29
+ includeInFull: boolean;
30
+ }
31
+ export type AgentMarkdownMeta = Omit<AgentMarkdown, 'markdown'>;
32
+ export interface AgentMarkdownContext {
33
+ collection: string;
34
+ page: ParsedContent;
35
+ path: string;
36
+ locale?: string;
37
+ prop: (name: string) => string;
38
+ props: (node?: MarkdownNode) => Record<string, unknown>;
39
+ cleanProps: (node?: MarkdownNode) => Record<string, unknown>;
40
+ children: (node?: MarkdownNode) => string;
41
+ renderChildren: (node: MarkdownNode) => string;
42
+ renderNode: (node: MarkdownNode) => string;
43
+ blockquote: (value: string) => string;
44
+ jsonFence: (value: unknown) => string;
45
+ link: (label: string, href: string) => string;
46
+ omitted: (name: string, reason?: string) => string;
47
+ xmlComponent: (name: string, props?: Record<string, unknown>, children?: string) => string;
48
+ }
49
+ export type AgentMarkdownSerializer = (node: MarkdownNode, ctx: AgentMarkdownContext) => string | null | undefined;
50
+ export type AgentMarkdownSerializerMap = Record<string, AgentMarkdownSerializer>;
51
+ export interface AgentMarkdownSerializerRegistrationOptions {
52
+ override?: boolean;
53
+ }
54
+ export interface AgentMarkdownComponent {
55
+ render: AgentMarkdownSerializer;
56
+ }
57
+ export type AgentMarkdownComponentMap = Record<string, AgentMarkdownComponent>;
58
+ export declare const getMarkdownProp: (node: MarkdownNode, name: string) => string;
59
+ export declare const renderMarkdownChildren: (node: MarkdownNode, ctx: AgentMarkdownContext) => string;
60
+ export declare const blockquoteMarkdown: (value: string) => string;
61
+ export declare const linkMarkdown: (label: string, href: string) => string;
62
+ export declare const imageMarkdown: (alt: string, src: string) => string;
63
+ export declare const jsonFenceMarkdown: (value: unknown) => string;
64
+ export declare const xmlComponentMarkdown: (name: string, props?: Record<string, unknown>, children?: string) => string;
65
+ export declare const registerAgentMarkdownSerializer: (name: string, serializer: AgentMarkdownSerializer, options?: AgentMarkdownSerializerRegistrationOptions) => void;
66
+ export declare const registerAgentMarkdownSerializers: (entries: AgentMarkdownSerializerMap, options?: AgentMarkdownSerializerRegistrationOptions) => void;
67
+ export declare const defineAgentMarkdownComponent: (component: AgentMarkdownComponent) => AgentMarkdownComponent;
68
+ export declare const registerAgentMarkdownComponent: (name: string, component: AgentMarkdownComponent, options?: AgentMarkdownSerializerRegistrationOptions) => void;
69
+ export declare const registerAgentMarkdownComponents: (entries: AgentMarkdownComponentMap, options?: AgentMarkdownSerializerRegistrationOptions) => void;
70
+ export declare const clearAgentMarkdownSerializers: () => void;
71
+ export declare const resolveAgentMarkdownOptions: (collection: ContentCollectionConfig | undefined) => ResolvedAgentMarkdownOptions | null;
72
+ export declare function resolveContentMarkdown(event: H3Event, collection: string, routeOrPath?: string, options?: {
73
+ locale?: string;
74
+ }): Promise<AgentMarkdown | null>;
75
+ export declare function resolveContentMarkdownByRoute(event: H3Event, routePath: string, options?: {
76
+ locale?: string;
77
+ collections?: string[];
78
+ }): Promise<AgentMarkdown | null>;
79
+ export declare function queryMarkdownEnabledContent(event: H3Event, options?: {
80
+ locale?: string;
81
+ collections?: string[];
82
+ limit?: number;
83
+ }): Promise<AgentMarkdownMeta[]>;
@@ -0,0 +1,449 @@
1
+ import { kebabCase, pascalCase } from "scule";
2
+ import { agentMarkdownPathForRoute, agentRawPathForRoute, normalizeAgentRoutePath } from "../agent-paths.js";
3
+ import { getCollectionPath } from "../query/routes.js";
4
+ import { getContentProvider } from "./providers/index.js";
5
+ import { contentConfig } from "./storage-access.js";
6
+ const serializers = /* @__PURE__ */ new Map();
7
+ const isRecord = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
8
+ const defaultLocale = () => contentConfig().defaultLocale || contentConfig().locales?.[0] || contentConfig().agent?.site?.defaultLocale || "en";
9
+ const configuredLocales = () => {
10
+ const locales = contentConfig().agent?.site?.locales?.length ? contentConfig().agent?.site?.locales : contentConfig().locales;
11
+ return locales?.length ? locales : [defaultLocale()];
12
+ };
13
+ const prefixLocalizedHref = (path, locale) => {
14
+ const normalized = normalizeAgentRoutePath(path);
15
+ if (!locale || locale === defaultLocale()) return normalized;
16
+ if (configuredLocales().some((candidate) => normalized === `/${candidate}` || normalized.startsWith(`/${candidate}/`))) return normalized;
17
+ return normalized === "/" ? `/${locale}` : `/${locale}${normalized}`;
18
+ };
19
+ const isExternalHref = (href) => /^[a-z][a-z0-9+.-]*:/i.test(href) || href.startsWith("//");
20
+ const routeMarkdownPathForHref = (href, locale, currentPath = "/") => {
21
+ const hash = href.match(/#.*$/)?.[0] || "";
22
+ const withoutHash = href.replace(/#.*$/, "");
23
+ if (!withoutHash) return href;
24
+ if (isExternalHref(withoutHash)) return href;
25
+ if (withoutHash.endsWith(".md")) return href;
26
+ const target = withoutHash.startsWith("/") ? prefixLocalizedHref(withoutHash, locale) : new URL(withoutHash, `https://agent.local${normalizeAgentRoutePath(currentPath)}`).pathname;
27
+ return `${agentMarkdownPathForRoute(target)}${hash}`;
28
+ };
29
+ export const getMarkdownProp = (node, name) => {
30
+ const props = isRecord(node.props) ? node.props : {};
31
+ const value = props[name];
32
+ return typeof value === "string" ? value : "";
33
+ };
34
+ export const renderMarkdownChildren = (node, ctx) => ctx.renderChildren(node);
35
+ export const blockquoteMarkdown = (value) => value.trim().split("\n").map((line) => line ? `> ${line}` : ">").join("\n");
36
+ const escapeMarkdownLinkLabel = (value) => value.replace(/\\/g, "\\\\").replace(/\[/g, "\\[").replace(/\]/g, "\\]");
37
+ const escapeMarkdownLinkHref = (value) => value.replace(/\)/g, "%29").replace(/\s/g, "%20");
38
+ const isSafeMarkdownHref = (href) => {
39
+ if (!href) return false;
40
+ if (href.startsWith("//")) return false;
41
+ if (href.startsWith("/") || href.startsWith("#") || href.startsWith("./") || href.startsWith("../")) return true;
42
+ return /^(https?:|mailto:|tel:)/i.test(href);
43
+ };
44
+ export const linkMarkdown = (label, href) => {
45
+ const resolvedLabel = label || href;
46
+ if (!href || !isSafeMarkdownHref(href)) return escapeMarkdownLinkLabel(resolvedLabel);
47
+ return `[${escapeMarkdownLinkLabel(resolvedLabel)}](${escapeMarkdownLinkHref(href)})`;
48
+ };
49
+ export const imageMarkdown = (alt, src) => {
50
+ if (!src || !isSafeMarkdownHref(src)) return escapeMarkdownLinkLabel(alt || src);
51
+ return `![${escapeMarkdownLinkLabel(alt)}](${escapeMarkdownLinkHref(src)})`;
52
+ };
53
+ export const jsonFenceMarkdown = (value) => `\`\`\`json
54
+ ${JSON.stringify(value, null, 2)}
55
+ \`\`\``;
56
+ const xmlNamePattern = /^[a-z][\w.:-]*$/i;
57
+ const safeXmlName = (name) => xmlNamePattern.test(name) ? name : "component";
58
+ const escapeXmlAttribute = (value) => value.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
59
+ const isScalarXmlAttributeValue = (value) => typeof value === "string" || typeof value === "number" || typeof value === "boolean";
60
+ const shouldDropAgentProp = (name) => {
61
+ const normalized = name.trim();
62
+ const sensitive = normalized.toLowerCase();
63
+ return !normalized || normalized === "class" || normalized === "style" || normalized === "key" || normalized === "ref" || normalized.startsWith("v-") || normalized.startsWith("@") || normalized.startsWith("on") || normalized.startsWith("data-") || normalized.startsWith("aria-") || /token|secret|password|passwd|credential|authorization|apikey|api-key|clientsecret|client-secret|privatekey|private-key|accesskey|access-key/i.test(sensitive);
64
+ };
65
+ const normalizeAgentPropName = (name) => {
66
+ const normalized = name.trim();
67
+ if (normalized.startsWith(":")) return normalized.slice(1);
68
+ if (normalized.startsWith("v-bind:")) return normalized.slice("v-bind:".length);
69
+ return normalized;
70
+ };
71
+ const cleanAgentPropValue = (value) => {
72
+ if (value === void 0 || value === null || value === "") return void 0;
73
+ if (Array.isArray(value)) {
74
+ const clean = value.map((entry) => cleanAgentPropValue(entry)).filter((entry) => entry !== void 0);
75
+ return clean.length ? clean : void 0;
76
+ }
77
+ if (isRecord(value)) {
78
+ const clean = cleanPropsObject(value);
79
+ return Object.keys(clean).length ? clean : void 0;
80
+ }
81
+ return value;
82
+ };
83
+ const cleanPropsObject = (props) => {
84
+ if (!isRecord(props)) return {};
85
+ const clean = {};
86
+ for (const [name, value] of Object.entries(props)) {
87
+ const normalizedName = normalizeAgentPropName(name);
88
+ const cleanedValue = cleanAgentPropValue(value);
89
+ if (shouldDropAgentProp(normalizedName) || cleanedValue === void 0) continue;
90
+ if (!(normalizedName in clean) || normalizedName === name.trim()) {
91
+ clean[normalizedName] = cleanedValue;
92
+ }
93
+ }
94
+ return clean;
95
+ };
96
+ export const xmlComponentMarkdown = (name, props = {}, children = "") => {
97
+ const tagName = safeXmlName(name);
98
+ const cleanProps = cleanPropsObject(props);
99
+ const attrs = [];
100
+ const complexProps = {};
101
+ for (const [key, value] of Object.entries(cleanProps)) {
102
+ if (!xmlNamePattern.test(key)) {
103
+ complexProps[key] = value;
104
+ continue;
105
+ }
106
+ if (isScalarXmlAttributeValue(value)) {
107
+ attrs.push(`${key}="${escapeXmlAttribute(String(value))}"`);
108
+ continue;
109
+ }
110
+ complexProps[key] = value;
111
+ }
112
+ const attrText = attrs.length ? ` ${attrs.join(" ")}` : "";
113
+ const bodyParts = [
114
+ Object.keys(complexProps).length ? jsonFenceMarkdown(complexProps) : "",
115
+ children.trim()
116
+ ].filter(Boolean);
117
+ if (!bodyParts.length) return `<${tagName}${attrText} />`;
118
+ return `<${tagName}${attrText}>
119
+ ${bodyParts.join("\n\n")}
120
+ </${tagName}>`;
121
+ };
122
+ export const registerAgentMarkdownSerializer = (name, serializer, options = {}) => {
123
+ const existing = serializers.get(name);
124
+ if (existing === serializer && !options.override) return;
125
+ if (existing && !options.override) {
126
+ throw new Error(
127
+ `Agent Markdown serializer "${name}" is already registered. Use { override: true } only when replacing an existing serializer intentionally.`
128
+ );
129
+ }
130
+ serializers.set(name, serializer);
131
+ };
132
+ export const registerAgentMarkdownSerializers = (entries, options = {}) => {
133
+ for (const [name, serializer] of Object.entries(entries)) {
134
+ registerAgentMarkdownSerializer(name, serializer, options);
135
+ }
136
+ };
137
+ export const defineAgentMarkdownComponent = (component) => component;
138
+ export const registerAgentMarkdownComponent = (name, component, options = {}) => {
139
+ registerAgentMarkdownSerializer(name, component.render, options);
140
+ };
141
+ export const registerAgentMarkdownComponents = (entries, options = {}) => {
142
+ for (const [name, component] of Object.entries(entries)) {
143
+ registerAgentMarkdownComponent(name, component, options);
144
+ }
145
+ };
146
+ export const clearAgentMarkdownSerializers = () => {
147
+ serializers.clear();
148
+ };
149
+ export const resolveAgentMarkdownOptions = (collection) => {
150
+ if (!collection || collection.type === "data") return null;
151
+ const value = collection.agent?.markdown;
152
+ if (value === true) {
153
+ return {
154
+ includeInIndex: true,
155
+ includeInFull: true,
156
+ metadata: []
157
+ };
158
+ }
159
+ if (!value || !isRecord(value)) return null;
160
+ return {
161
+ includeInIndex: value.includeInIndex !== false,
162
+ includeInFull: value.includeInFull !== false,
163
+ metadata: Array.isArray(value.metadata) ? value.metadata.filter((field) => typeof field === "string" && field.length > 0) : []
164
+ };
165
+ };
166
+ const collectionConfig = (collection) => contentConfig().collections?.[collection];
167
+ const markdownEnabledCollectionEntries = (collections) => Object.entries(contentConfig().collections || {}).filter(([name, config]) => (!collections?.length || collections.includes(name)) && resolveAgentMarkdownOptions(config));
168
+ const isPublicPage = (page, config) => Boolean(
169
+ page && config && config.type !== "data" && config.sitemap !== false && !page._draft && !page._partial && !page._navigation && page.navigation !== false && page.robots !== "noindex" && page.sitemap !== false
170
+ );
171
+ const escapeMarkdownText = (value) => value.replace(/\*/g, "\\*").replace(/_/g, "\\_").replace(/\[/g, "\\[").replace(/\]/g, "\\]");
172
+ const textValue = (node) => {
173
+ if (node.type === "text") return node.value || "";
174
+ return (node.children || []).map((child) => textValue(child)).join("");
175
+ };
176
+ const renderChildren = (node, ctx) => (node.children || []).map((child) => renderNode(child, ctx)).join("").trim();
177
+ const block = (value) => value.trim() ? `${value.trim()}
178
+
179
+ ` : "";
180
+ const renderList = (node, ctx, ordered = false) => block((node.children || []).map((child, index) => {
181
+ const body = renderChildren(child, ctx).replace(/\n+/g, "\n ").trim();
182
+ return `${ordered ? `${index + 1}.` : "-"} ${body}`;
183
+ }).join("\n"));
184
+ const renderTable = (node, ctx) => {
185
+ const rows = (node.children || []).flatMap(
186
+ (section) => section.tag === "thead" || section.tag === "tbody" ? section.children || [] : [section]
187
+ ).filter((row) => row.tag === "tr");
188
+ if (!rows.length) return "";
189
+ const cells = rows.map(
190
+ (row) => (row.children || []).filter((cell) => cell.tag === "th" || cell.tag === "td").map((cell) => renderChildren(cell, ctx).replace(/\|/g, "\\|").trim())
191
+ );
192
+ const width = Math.max(...cells.map((row) => row.length));
193
+ const normalized = cells.map((row) => [...row, ...Array.from({ length: width - row.length }, () => "")]);
194
+ const [head = [], ...bodyRows] = normalized;
195
+ return block([
196
+ `| ${head.join(" | ")} |`,
197
+ `| ${head.map(() => "---").join(" | ")} |`,
198
+ ...bodyRows.map((row) => `| ${row.join(" | ")} |`)
199
+ ].join("\n"));
200
+ };
201
+ const renderUnknownComponentOmission = (name, reason) => `> Component omitted: \`${name}\`.
202
+ > ${reason || "This page contains an interactive or site-specific block that has no agent markdown serializer yet."}`;
203
+ const renderUnknownComponent = (node, ctx) => {
204
+ const tag = node.tag || "component";
205
+ const props = cleanPropsObject(node.props);
206
+ const children = renderChildren(node, ctx);
207
+ if (children || Object.keys(props).length) {
208
+ return block(xmlComponentMarkdown(tag, props, children));
209
+ }
210
+ return block(renderUnknownComponentOmission(tag));
211
+ };
212
+ const serializerForTag = (tag) => {
213
+ const markdownTags = contentConfig().markdown?.tags || {};
214
+ const candidates = new Set([
215
+ tag,
216
+ pascalCase(tag),
217
+ kebabCase(tag),
218
+ markdownTags[tag],
219
+ markdownTags[pascalCase(tag)],
220
+ markdownTags[kebabCase(tag)]
221
+ ].filter((value) => Boolean(value)));
222
+ for (const candidate of candidates) {
223
+ const serializer = serializers.get(candidate);
224
+ if (serializer) return serializer;
225
+ }
226
+ };
227
+ const renderNode = (node, ctx) => {
228
+ if (node.type === "text") return node.value || "";
229
+ const tag = node.tag || "";
230
+ const serializer = tag ? serializerForTag(tag) : void 0;
231
+ if (serializer) {
232
+ const nodeCtx = {
233
+ ...ctx,
234
+ prop: (name) => getMarkdownProp(node, name),
235
+ props: (target) => isRecord((target || node).props) ? (target || node).props : {},
236
+ cleanProps: (target) => cleanPropsObject((target || node).props),
237
+ children: (target) => renderChildren(target || node, nodeCtx)
238
+ };
239
+ const rendered = serializer(node, nodeCtx);
240
+ if (typeof rendered === "string") return block(rendered);
241
+ }
242
+ if (!tag) return renderChildren(node, ctx);
243
+ if (/^h[1-6]$/.test(tag)) {
244
+ const depth = Number(tag.slice(1));
245
+ return block(`${"#".repeat(Math.max(1, Math.min(6, depth)))} ${renderChildren(node, ctx)}`);
246
+ }
247
+ switch (tag) {
248
+ case "p":
249
+ return block(renderChildren(node, ctx));
250
+ case "blockquote":
251
+ return block(renderChildren(node, ctx).split("\n").map((line) => `> ${line}`).join("\n"));
252
+ case "strong":
253
+ case "b":
254
+ return `**${renderChildren(node, ctx)}**`;
255
+ case "em":
256
+ case "i":
257
+ return `_${renderChildren(node, ctx)}_`;
258
+ case "span":
259
+ return renderChildren(node, ctx);
260
+ case "code":
261
+ return `\`${node.value || textValue(node)}\``;
262
+ case "pre":
263
+ return block(`\`\`\`
264
+ ${textValue(node)}
265
+ \`\`\``);
266
+ case "a": {
267
+ const href = typeof node.props?.href === "string" ? node.props.href : "";
268
+ const text = renderChildren(node, ctx) || href;
269
+ return href ? linkMarkdown(text, routeMarkdownPathForHref(href, ctx.locale, ctx.path)) : text;
270
+ }
271
+ case "ul":
272
+ return renderList(node, ctx);
273
+ case "ol":
274
+ return renderList(node, ctx, true);
275
+ case "li":
276
+ return renderChildren(node, ctx);
277
+ case "hr":
278
+ return "---\n\n";
279
+ case "br":
280
+ return "\n";
281
+ case "img": {
282
+ const src = typeof node.props?.src === "string" ? node.props.src : "";
283
+ const alt = typeof node.props?.alt === "string" ? node.props.alt : "";
284
+ return src ? imageMarkdown(alt, src) : escapeMarkdownLinkLabel(alt);
285
+ }
286
+ case "table":
287
+ return renderTable(node, ctx);
288
+ case "thead":
289
+ case "tbody":
290
+ case "tr":
291
+ case "th":
292
+ case "td":
293
+ return renderChildren(node, ctx);
294
+ default:
295
+ return renderUnknownComponent(node, ctx);
296
+ }
297
+ };
298
+ const renderMarkdownRoot = (body, context) => {
299
+ if (!body?.children?.length) return "";
300
+ const ctx = {
301
+ ...context,
302
+ prop: () => "",
303
+ props: (node) => node ? isRecord(node.props) ? node.props : {} : {},
304
+ cleanProps: (node) => cleanPropsObject(node?.props),
305
+ children: (node) => node ? renderChildren(node, ctx) : "",
306
+ renderChildren: (node) => renderChildren(node, ctx),
307
+ renderNode: (node) => renderNode(node, ctx),
308
+ blockquote: (value) => blockquoteMarkdown(value),
309
+ jsonFence: (value) => jsonFenceMarkdown(value),
310
+ link: (label, href) => linkMarkdown(label, href),
311
+ omitted: (name, reason) => renderUnknownComponentOmission(name, reason),
312
+ xmlComponent: (name, props, children) => xmlComponentMarkdown(name, props, children)
313
+ };
314
+ return body.children.map((node) => renderNode(node, ctx)).join("").trim();
315
+ };
316
+ const hasH1 = (markdown) => /^#\s+/m.test(markdown);
317
+ const normalizeDescription = (page) => typeof page.description === "string" && page.description.trim() ? page.description.trim() : "";
318
+ const renderAgentMarkdown = (page, collection, path, locale, _options) => {
319
+ const title = typeof page.title === "string" && page.title.trim() ? page.title.trim() : path.split("/").filter(Boolean).pop() || "Index";
320
+ const description = normalizeDescription(page);
321
+ const rendered = renderMarkdownRoot(page.body, { collection, page, path, locale });
322
+ const parts = [];
323
+ if (!hasH1(rendered)) parts.push(`# ${escapeMarkdownText(title)}`);
324
+ if (description && !rendered.includes(description)) parts.push(`> ${description}`);
325
+ if (rendered) parts.push(rendered);
326
+ return parts.join("\n\n").trim() + "\n";
327
+ };
328
+ const toAgentMarkdown = (collection, page, options) => {
329
+ const path = normalizeAgentRoutePath(page.path || page._requestedRoute || page._path);
330
+ const locale = page.locale || page._resolvedLocale || page._locale;
331
+ const title = typeof page.title === "string" && page.title.trim() ? page.title.trim() : path.split("/").filter(Boolean).pop() || "Index";
332
+ const description = normalizeDescription(page);
333
+ return {
334
+ path,
335
+ markdownPath: agentMarkdownPathForRoute(path),
336
+ rawPath: agentRawPathForRoute(path),
337
+ ...locale ? { locale } : {},
338
+ collection,
339
+ title,
340
+ description,
341
+ markdown: renderAgentMarkdown(page, collection, path, locale, options),
342
+ ...page._file ? { sourceFile: page._file } : {},
343
+ canonicalUrl: path,
344
+ ...typeof page.updated === "string" ? { lastModified: page.updated } : {},
345
+ metadataFields: options.metadata,
346
+ includeInIndex: options.includeInIndex,
347
+ includeInFull: options.includeInFull
348
+ };
349
+ };
350
+ const collectionHandle = (name, config) => ({ ...config, name });
351
+ const routeBaseForLocale = (config, locale) => {
352
+ if (!config.route) return "";
353
+ if (typeof config.route === "string") return normalizeAgentRoutePath(config.route);
354
+ const localized = locale ? config.route[locale] : void 0;
355
+ return typeof localized === "string" ? normalizeAgentRoutePath(localized) : "";
356
+ };
357
+ const collectionDefaultLocale = (config) => {
358
+ const collectionI18n = config.i18n && typeof config.i18n === "object" ? config.i18n : void 0;
359
+ return collectionI18n?.defaultLocale || contentConfig().defaultLocale || contentConfig().agent?.site?.defaultLocale;
360
+ };
361
+ const prefixRequestedLocale = (path, locale, defaultLocale2) => {
362
+ const normalized = normalizeAgentRoutePath(path);
363
+ if (!locale || locale === defaultLocale2) return normalized;
364
+ if (normalized === `/${locale}` || normalized.startsWith(`/${locale}/`)) return normalized;
365
+ if (normalized === "/") return `/${locale}`;
366
+ return `/${locale}${normalized}`;
367
+ };
368
+ const publicPathForLocale = (collection, config, rowPath, locale, defaultLocale2) => {
369
+ const normalizedRowPath = normalizeAgentRoutePath(rowPath || "/");
370
+ const base = routeBaseForLocale(config, locale);
371
+ if (base && (normalizedRowPath === base || normalizedRowPath.startsWith(`${base}/`))) {
372
+ return prefixRequestedLocale(normalizedRowPath, locale, defaultLocale2);
373
+ }
374
+ return getCollectionPath(collectionHandle(collection, config), {
375
+ ...locale ? { locale } : {},
376
+ path: normalizedRowPath
377
+ });
378
+ };
379
+ const publicPathForQueryRow = (collection, config, row, locale) => {
380
+ const requested = row.path || row._requestedRoute;
381
+ if (requested) return normalizeAgentRoutePath(requested);
382
+ const defaultLocale2 = collectionDefaultLocale(config);
383
+ const rowPath = normalizeAgentRoutePath(row._path || "/");
384
+ const resolvedLocale = row._resolvedLocale || row._locale;
385
+ if (locale && resolvedLocale && locale !== resolvedLocale) {
386
+ const sourceLocalePath = publicPathForLocale(collection, config, rowPath, resolvedLocale, defaultLocale2);
387
+ return prefixRequestedLocale(sourceLocalePath, locale, defaultLocale2);
388
+ }
389
+ return publicPathForLocale(collection, config, rowPath, locale, defaultLocale2);
390
+ };
391
+ export async function resolveContentMarkdown(event, collection, routeOrPath = "/", options = {}) {
392
+ const config = collectionConfig(collection);
393
+ const agentOptions = resolveAgentMarkdownOptions(config);
394
+ if (!config || !agentOptions) return null;
395
+ const provider = await getContentProvider(event);
396
+ if (!provider.page) return null;
397
+ const page = await provider.page(event, collection, routeOrPath, {
398
+ ...options.locale ? { locale: options.locale } : {}
399
+ });
400
+ if (!page || !isPublicPage(page, config)) return null;
401
+ return toAgentMarkdown(collection, page, agentOptions);
402
+ }
403
+ export async function resolveContentMarkdownByRoute(event, routePath, options = {}) {
404
+ for (const [collection] of markdownEnabledCollectionEntries(options.collections)) {
405
+ const page = await resolveContentMarkdown(event, collection, routePath, {
406
+ ...options.locale ? { locale: options.locale } : {}
407
+ });
408
+ if (page) return page;
409
+ }
410
+ return null;
411
+ }
412
+ const normalizeQueryResult = (value) => Array.isArray(value.result) ? value.result : [];
413
+ export async function queryMarkdownEnabledContent(event, options = {}) {
414
+ const provider = await getContentProvider(event);
415
+ const result = [];
416
+ for (const [collection, config] of markdownEnabledCollectionEntries(options.collections)) {
417
+ const agentOptions = resolveAgentMarkdownOptions(config);
418
+ if (!agentOptions) continue;
419
+ const rows = normalizeQueryResult(await provider.query(event, {
420
+ collection,
421
+ only: ["path", "locale", "localePaths", "_path", "_locale", "_resolvedLocale", "_requestedRoute", "_file", "_draft", "_partial", "_navigation", "title", "description", "updated", "navigation", "robots", "sitemap"],
422
+ ...options.limit ? { limit: options.limit } : {},
423
+ ...options.locale ? { resolveLocale: { locale: options.locale, fallback: true } } : {}
424
+ }));
425
+ for (const row of rows) {
426
+ if (!isPublicPage(row, config)) continue;
427
+ const locale = options.locale || row._resolvedLocale || row._locale;
428
+ const path = publicPathForQueryRow(collection, config, row, locale);
429
+ const title = typeof row.title === "string" && row.title.trim() ? row.title.trim() : path.split("/").filter(Boolean).pop() || "Index";
430
+ const description = normalizeDescription(row);
431
+ result.push({
432
+ path,
433
+ markdownPath: agentMarkdownPathForRoute(path),
434
+ rawPath: agentRawPathForRoute(path),
435
+ ...locale ? { locale } : {},
436
+ collection,
437
+ title,
438
+ description,
439
+ ...row._file ? { sourceFile: row._file } : {},
440
+ canonicalUrl: path,
441
+ ...typeof row.updated === "string" ? { lastModified: row.updated } : {},
442
+ metadataFields: agentOptions.metadata,
443
+ includeInIndex: agentOptions.includeInIndex,
444
+ includeInFull: agentOptions.includeInFull
445
+ });
446
+ }
447
+ }
448
+ return result;
449
+ }
@@ -0,0 +1 @@
1
+ export * from './agent-site.js'
@@ -0,0 +1,38 @@
1
+ import type { H3Event } from 'h3';
2
+ import type { AgentMarkdown } from './agent-markdown';
3
+ import type { AgentMetadataField } from '../../types/config';
4
+ export type AgentPageSource = 'ginko' | 'app-owned';
5
+ export interface AgentPage {
6
+ title: string;
7
+ description: string;
8
+ path: string;
9
+ markdownPath: string;
10
+ rawPath: string;
11
+ url: string;
12
+ markdownUrl: string;
13
+ section: string;
14
+ sectionTitle: string;
15
+ sectionOrder: number;
16
+ locale: string;
17
+ source: AgentPageSource;
18
+ collection?: string;
19
+ updated?: string;
20
+ metadataFields?: readonly AgentMetadataField[];
21
+ includeInIndex: boolean;
22
+ includeInFull: boolean;
23
+ markdown?: string;
24
+ }
25
+ export declare const getAgentLocales: () => any;
26
+ export declare const isSupportedAgentLocale: (locale: string | undefined) => boolean;
27
+ export declare const buildAgentPageIndex: (event: H3Event, locale?: any) => Promise<AgentPage[]>;
28
+ export declare const renderAgentMarkdownFrontmatter: (page: AgentPage) => string;
29
+ export declare const renderAgentMarkdownPage: (page: AgentPage, markdown: string) => string;
30
+ export declare const renderLlmsTxt: (pages: AgentPage[], locale?: any) => string;
31
+ export declare const resolveMarkdownForPublicRoute: (event: H3Event, routePath: string, locale?: any) => Promise<AgentMarkdown | {
32
+ markdown: string;
33
+ } | null>;
34
+ export declare const renderLlmsFullTxt: (event: H3Event, locale?: any) => Promise<string>;
35
+ export declare const localeFromAgentPath: (path: string) => any;
36
+ export declare const routePathFromRawSlug: (slug: string | string[] | undefined) => string;
37
+ export declare const routePathFromIndexSlug: (slug: string | string[] | undefined) => string;
38
+ export declare const collectAgentMarkdownPrerenderRoutes: (event: H3Event) => Promise<string[]>;