@lupinum/ginko-content 0.1.5 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/compatibility.json +1 -1
- package/dist/cli.mjs +2 -2
- package/dist/cms-contract/build.js +15 -20
- package/dist/cms-contract/types.d.ts +12 -7
- package/dist/core/content/document +1 -0
- package/dist/core/content/document.d.ts +16 -0
- package/dist/core/content/document.js +2 -0
- package/dist/core/content/graph.d.ts +3 -1
- package/dist/core/content/graph.js +21 -20
- package/dist/core/content/locale.d.ts +6 -1
- package/dist/core/content/locale.js +17 -6
- package/dist/core/content/path.d.ts +5 -5
- package/dist/core/content/path.js +8 -8
- package/dist/core/content/snapshot +1 -0
- package/dist/core/content/snapshot.d.ts +25 -0
- package/dist/core/content/snapshot.js +99 -0
- package/dist/core/provider-errors.d.ts +1 -1
- package/dist/core/provider-errors.js +1 -0
- package/dist/core/query/builder.js +3 -3
- package/dist/core/query/execute.js +75 -36
- package/dist/core/query/filter.js +2 -2
- package/dist/core/query/lower.js +79 -3
- package/dist/core/query/params.js +7 -7
- package/dist/core/query/plan.d.ts +18 -0
- package/dist/core/query/plan.js +7 -0
- package/dist/core/references/resolve.js +6 -9
- package/dist/features/agent/agent-markdown +1 -0
- package/dist/features/agent/agent-markdown.d.ts +116 -0
- package/dist/features/agent/agent-markdown.js +138 -0
- package/dist/features/agent/walker +1 -0
- package/dist/features/agent/walker.d.ts +10 -0
- package/dist/features/agent/walker.js +174 -0
- package/dist/features/collections/resolve.d.ts +2 -2
- package/dist/features/collections/resolve.js +19 -16
- package/dist/features/localization/links.js +1 -1
- package/dist/features/localization/results.d.ts +3 -3
- package/dist/features/localization/results.js +46 -41
- package/dist/features/localization/translated-slugs.js +5 -5
- package/dist/features/navigation/build.d.ts +2 -2
- package/dist/features/navigation/build.js +23 -26
- package/dist/features/navigation/canonical.d.ts +10 -11
- package/dist/features/navigation/canonical.js +26 -21
- package/dist/features/navigation/resolve.js +4 -4
- package/dist/features/navigation/tree.js +3 -3
- package/dist/{runtime → features}/query/backlinks.js +3 -5
- package/dist/{runtime → features}/query/documents.js +1 -1
- package/dist/{runtime → features}/query/localized-docs.d.ts +1 -1
- package/dist/{runtime → features}/query/localized-docs.js +3 -6
- package/dist/{runtime → features}/query/navigation.d.ts +1 -1
- package/dist/{runtime → features}/query/navigation.js +8 -8
- package/dist/{runtime → features}/query/populate.js +6 -14
- package/dist/{runtime → features}/query/unified.d.ts +4 -4
- package/dist/{runtime → features}/query/unified.js +4 -2
- package/dist/{runtime → features}/query/variants.js +4 -4
- package/dist/features/search/navigation.js +2 -2
- package/dist/features/search/sections.d.ts +1 -1
- package/dist/features/search/sections.js +1 -1
- package/dist/features/sitemap/query.d.ts +5 -5
- package/dist/features/sitemap/query.js +6 -6
- package/dist/integrations/nitro/ingest.js +9 -20
- package/dist/integrations/nitro/storage.d.ts +1 -0
- package/dist/integrations/nitro/storage.js +21 -34
- package/dist/module.d.mts +135 -803
- package/dist/module.json +1 -1
- package/dist/module.mjs +143 -51
- package/dist/parsers/csv.js +3 -3
- package/dist/parsers/index.js +3 -3
- package/dist/parsers/json.js +8 -6
- package/dist/parsers/markdown-plugins.js +9 -5
- package/dist/parsers/markdown.js +7 -6
- package/dist/parsers/path-meta.js +21 -19
- package/dist/parsers/reserved +1 -0
- package/dist/parsers/reserved.d.ts +15 -0
- package/dist/parsers/reserved.js +35 -0
- package/dist/parsers/yaml.js +6 -4
- package/dist/public/agent +1 -0
- package/dist/public/agent.d.ts +17 -0
- package/dist/public/agent.js +34 -0
- package/dist/public/client.d.ts +2 -3
- package/dist/public/client.js +1 -2
- package/dist/public/provider-query +1 -0
- package/dist/public/provider-query.d.ts +58 -0
- package/dist/public/provider-query.js +5 -0
- package/dist/public/provider.d.ts +8 -3
- package/dist/public/provider.js +2 -0
- package/dist/public/server.d.ts +7 -12
- package/dist/public/server.js +5 -36
- package/dist/runtime/app/components/ContentRenderer.d.vue.ts +3 -3
- package/dist/runtime/app/components/ContentRenderer.vue +2 -2
- package/dist/runtime/app/components/ContentRenderer.vue.d.ts +3 -3
- package/dist/runtime/app/components/ContentRendererInline.d.vue.ts +1 -1
- package/dist/runtime/app/components/ContentRendererInline.vue.d.ts +1 -1
- package/dist/runtime/app/components/Prose/ProseImg.d.vue.ts +4 -4
- package/dist/runtime/app/components/Prose/ProseImg.vue.d.ts +4 -4
- package/dist/runtime/app/components/Prose/ProsePre.d.vue.ts +1 -1
- package/dist/runtime/app/components/Prose/ProsePre.vue.d.ts +1 -1
- package/dist/runtime/app/components/internal/ContentRendererMarkdown.d.vue.ts +1 -1
- package/dist/runtime/app/components/internal/ContentRendererMarkdown.vue +4 -4
- package/dist/runtime/app/components/internal/ContentRendererMarkdown.vue.d.ts +1 -1
- package/dist/runtime/app/components/internal/MarkdownRenderer.d.ts +3 -3
- package/dist/runtime/app/composables/preview.d.ts +1 -1
- package/dist/runtime/app/composables/query-api.d.ts +5 -5
- package/dist/runtime/app/composables/query-api.js +5 -3
- package/dist/runtime/app/composables/route.d.ts +1 -1
- package/dist/runtime/app/composables/route.js +4 -4
- package/dist/runtime/app/composables/search.js +1 -1
- package/dist/runtime/app/composables/use-content-document.js +1 -1
- package/dist/runtime/app/composables/use-content-list.js +1 -1
- package/dist/runtime/app/composables/use-content-navigation.js +2 -2
- package/dist/runtime/app/composables/use-content-page.js +4 -4
- package/dist/runtime/app/pages/content.vue +2 -1
- package/dist/runtime/markdown/plugins.js +6 -8
- package/dist/runtime/server/agent-markdown.d.ts +5 -65
- package/dist/runtime/server/agent-markdown.js +40 -315
- package/dist/runtime/server/agent-site.js +9 -2
- package/dist/runtime/server/api/agent-raw.js +1 -1
- package/dist/runtime/server/api/cache.d.ts +1 -1
- package/dist/runtime/server/api/cache.js +43 -4
- package/dist/runtime/server/api/locales.js +3 -3
- package/dist/runtime/server/api/navigation.d.ts +1 -1
- package/dist/runtime/server/api/navigation.js +3 -1
- package/dist/runtime/server/api/query.js +2 -2
- package/dist/runtime/server/api/search-index.d.ts +1 -1
- package/dist/runtime/server/api/sitemap.d.ts +1 -1
- package/dist/runtime/server/cache-adapters.d.ts +18 -0
- package/dist/runtime/server/cache-adapters.js +12 -0
- package/dist/runtime/server/collection-helpers.d.ts +2 -2
- package/dist/runtime/server/collection-helpers.js +15 -11
- package/dist/runtime/server/index.d.ts +6 -4
- package/dist/runtime/server/index.js +10 -3
- package/dist/runtime/server/middleware/agent-link-headers.js +1 -1
- package/dist/runtime/server/middleware/agent-markdown.js +1 -1
- package/dist/runtime/server/navigation-query.d.ts +2 -2
- package/dist/runtime/server/navigation-query.js +52 -7
- package/dist/runtime/server/navigation.d.ts +1 -1
- package/dist/runtime/server/provider-document +1 -0
- package/dist/runtime/server/provider-document.d.ts +59 -0
- package/dist/runtime/server/provider-document.js +48 -0
- package/dist/runtime/server/provider-query-wire +1 -0
- package/dist/runtime/server/provider-query-wire.d.ts +32 -0
- package/dist/runtime/server/provider-query-wire.js +27 -0
- package/dist/runtime/server/provider-query.d.ts +18 -9
- package/dist/runtime/server/provider-query.js +62 -28
- package/dist/runtime/server/provider-result.d.ts +4 -4
- package/dist/runtime/server/providers/filesystem.js +3 -11
- package/dist/runtime/server/providers/index.d.ts +18 -1
- package/dist/runtime/server/providers/index.js +72 -20
- package/dist/runtime/server/query-api.d.ts +5 -5
- package/dist/runtime/server/query-api.js +9 -6
- package/dist/runtime/server/query-executor.d.ts +3 -3
- package/dist/runtime/server/query-executor.js +43 -59
- package/dist/runtime/server/search.d.ts +1 -1
- package/dist/runtime/server/search.js +4 -4
- package/dist/runtime/server/sitemap.js +5 -5
- package/dist/runtime/server/storage-access.d.ts +4 -1
- package/dist/runtime/server/storage-access.js +27 -2
- package/dist/runtime/transformers/component-resolver.d.ts +1 -1
- package/dist/storage/content.d.ts +2 -6
- package/dist/storage/content.js +19 -8
- package/dist/storage/contents.d.ts +9 -1
- package/dist/storage/contents.js +26 -19
- package/dist/storage/driver.d.ts +2 -2
- package/dist/storage/driver.js +2 -1
- package/dist/storage/graph.js +4 -0
- package/dist/storage/manifest.js +3 -1
- package/dist/storage/references.js +9 -5
- package/dist/storage/snapshot-runtime +1 -0
- package/dist/storage/snapshot-runtime.d.ts +11 -0
- package/dist/storage/snapshot-runtime.js +46 -0
- package/dist/storage/validation.d.ts +1 -1
- package/dist/storage/validation.js +79 -105
- package/dist/testing/provider-contract.d.ts +14 -4
- package/dist/testing/provider-contract.js +121 -68
- package/dist/testing/provider-fixture.d.ts +1 -1
- package/dist/testing/provider-fixture.js +124 -120
- package/dist/types/config.d.ts +17 -4
- package/dist/types/content.d.ts +104 -70
- package/dist/types/fields.js +1 -1
- package/dist/types/module.d.ts +1 -1
- package/dist/types/query-parts/public.d.ts +14 -5
- package/dist/types/query-parts/results.d.ts +6 -2
- package/dist/types/query-parts/transport.d.ts +19 -0
- package/dist/types/virtual.d.ts +6 -0
- package/dist/types.d.mts +2 -2
- package/dist/web-types.json +1 -1
- package/package.json +39 -9
- package/dist/runtime/app/plugins/markdown-components +0 -1
- package/dist/runtime/app/plugins/markdown-components.d.ts +0 -2
- package/dist/runtime/app/plugins/markdown-components.js +0 -19
- /package/dist/{runtime → features/agent}/agent-paths +0 -0
- /package/dist/{runtime → features/agent}/agent-paths.d.ts +0 -0
- /package/dist/{runtime → features/agent}/agent-paths.js +0 -0
- /package/dist/{runtime → features}/query/backlinks +0 -0
- /package/dist/{runtime → features}/query/backlinks.d.ts +0 -0
- /package/dist/{runtime → features}/query/context +0 -0
- /package/dist/{runtime → features}/query/context.d.ts +0 -0
- /package/dist/{runtime → features}/query/context.js +0 -0
- /package/dist/{runtime → features}/query/documents +0 -0
- /package/dist/{runtime → features}/query/documents.d.ts +0 -0
- /package/dist/{runtime → features}/query/errors +0 -0
- /package/dist/{runtime → features}/query/errors.d.ts +0 -0
- /package/dist/{runtime → features}/query/errors.js +0 -0
- /package/dist/{runtime → features}/query/handles +0 -0
- /package/dist/{runtime → features}/query/handles.d.ts +0 -0
- /package/dist/{runtime → features}/query/handles.js +0 -0
- /package/dist/{runtime → features}/query/locale-options +0 -0
- /package/dist/{runtime → features}/query/locale-options.d.ts +0 -0
- /package/dist/{runtime → features}/query/locale-options.js +0 -0
- /package/dist/{runtime → features}/query/localized-docs +0 -0
- /package/dist/{runtime → features}/query/navigation +0 -0
- /package/dist/{runtime → features}/query/pagination +0 -0
- /package/dist/{runtime → features}/query/pagination.d.ts +0 -0
- /package/dist/{runtime → features}/query/pagination.js +0 -0
- /package/dist/{runtime → features}/query/populate +0 -0
- /package/dist/{runtime → features}/query/populate.d.ts +0 -0
- /package/dist/{runtime → features}/query/public-limits +0 -0
- /package/dist/{runtime → features}/query/public-limits.d.ts +0 -0
- /package/dist/{runtime → features}/query/public-limits.js +0 -0
- /package/dist/{runtime → features}/query/resolve +0 -0
- /package/dist/{runtime → features}/query/resolve.d.ts +0 -0
- /package/dist/{runtime → features}/query/resolve.js +0 -0
- /package/dist/{runtime → features}/query/responses +0 -0
- /package/dist/{runtime → features}/query/responses.d.ts +0 -0
- /package/dist/{runtime → features}/query/responses.js +0 -0
- /package/dist/{runtime → features}/query/routes +0 -0
- /package/dist/{runtime → features}/query/routes.d.ts +0 -0
- /package/dist/{runtime → features}/query/routes.js +0 -0
- /package/dist/{runtime → features}/query/unified +0 -0
- /package/dist/{runtime → features}/query/variants +0 -0
- /package/dist/{runtime → features}/query/variants.d.ts +0 -0
|
@@ -1,324 +1,49 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import {
|
|
2
|
+
createAgentMarkdownRegistry,
|
|
3
|
+
resolveAgentMarkdownOptions
|
|
4
|
+
} from "../../features/agent/agent-markdown";
|
|
5
|
+
import { renderAgentMarkdownBody } from "../../features/agent/walker";
|
|
6
|
+
import { agentMarkdownPathForRoute, agentRawPathForRoute, normalizeAgentRoutePath } from "../../features/agent/agent-paths";
|
|
7
|
+
import { getCollectionPath } from "../../features/query/routes";
|
|
4
8
|
import { getContentProvider } from "./providers/index.js";
|
|
9
|
+
import { createProviderQuery } from "./provider-query.js";
|
|
5
10
|
import { contentConfig } from "./storage-access.js";
|
|
6
|
-
|
|
7
|
-
|
|
11
|
+
export * from "../../features/agent/agent-markdown";
|
|
12
|
+
export { renderAgentMarkdownBody } from "../../features/agent/walker";
|
|
13
|
+
const appRegistry = createAgentMarkdownRegistry();
|
|
14
|
+
export const getAgentMarkdownRegistry = () => appRegistry;
|
|
15
|
+
export const registerAgentMarkdownSerializer = (name, serializer, options) => appRegistry.register(name, serializer, options);
|
|
16
|
+
export const registerAgentMarkdownSerializers = (entries, options) => appRegistry.registerMany(entries, options);
|
|
17
|
+
export const registerAgentMarkdownComponent = (name, component, options) => appRegistry.registerComponent(name, component, options);
|
|
18
|
+
export const registerAgentMarkdownComponents = (entries, options) => appRegistry.registerComponents(entries, options);
|
|
19
|
+
export const clearAgentMarkdownSerializers = () => appRegistry.clear();
|
|
8
20
|
const defaultLocale = () => contentConfig().defaultLocale || contentConfig().locales?.[0] || contentConfig().agent?.site?.defaultLocale || "en";
|
|
9
21
|
const configuredLocales = () => {
|
|
10
22
|
const locales = contentConfig().agent?.site?.locales?.length ? contentConfig().agent?.site?.locales : contentConfig().locales;
|
|
11
23
|
return locales?.length ? locales : [defaultLocale()];
|
|
12
24
|
};
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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 `${agentRawPathForRoute(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 `})`;
|
|
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, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">");
|
|
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
|
-
};
|
|
25
|
+
const buildRenderContext = (collection, page, path, locale) => ({
|
|
26
|
+
collection,
|
|
27
|
+
page,
|
|
28
|
+
path,
|
|
29
|
+
locale,
|
|
30
|
+
registry: appRegistry,
|
|
31
|
+
tagAliases: contentConfig().markdown?.tags || {},
|
|
32
|
+
defaultLocale: defaultLocale(),
|
|
33
|
+
locales: configuredLocales()
|
|
34
|
+
});
|
|
166
35
|
const collectionConfig = (collection) => contentConfig().collections?.[collection];
|
|
167
36
|
const markdownEnabledCollectionEntries = (collections) => Object.entries(contentConfig().collections || {}).filter(([name, config]) => (!collections?.length || collections.includes(name)) && resolveAgentMarkdownOptions(config));
|
|
168
37
|
const isPublicPage = (page, config) => Boolean(
|
|
169
|
-
page && config && config.type !== "data" && config.sitemap !== false && !page.
|
|
38
|
+
page && config && config.type !== "data" && config.sitemap !== false && !page.draft && !page.partial && !page.navigationFile && page.navigation !== false && page.robots !== "noindex" && page.sitemap !== false
|
|
170
39
|
);
|
|
171
40
|
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
41
|
const hasH1 = (markdown) => /^#\s+/m.test(markdown);
|
|
317
42
|
const normalizeDescription = (page) => typeof page.description === "string" && page.description.trim() ? page.description.trim() : "";
|
|
318
43
|
const renderAgentMarkdown = (page, collection, path, locale, _options) => {
|
|
319
44
|
const title = typeof page.title === "string" && page.title.trim() ? page.title.trim() : path.split("/").filter(Boolean).pop() || "Index";
|
|
320
45
|
const description = normalizeDescription(page);
|
|
321
|
-
const rendered =
|
|
46
|
+
const rendered = renderAgentMarkdownBody(page.body, buildRenderContext(collection, page, path, locale));
|
|
322
47
|
const parts = [];
|
|
323
48
|
if (!hasH1(rendered)) parts.push(`# ${escapeMarkdownText(title)}`);
|
|
324
49
|
if (description && !rendered.includes(description)) parts.push(`> ${description}`);
|
|
@@ -326,8 +51,8 @@ const renderAgentMarkdown = (page, collection, path, locale, _options) => {
|
|
|
326
51
|
return parts.join("\n\n").trim() + "\n";
|
|
327
52
|
};
|
|
328
53
|
const toAgentMarkdown = (collection, page, options) => {
|
|
329
|
-
const path = normalizeAgentRoutePath(page.path || page.
|
|
330
|
-
const locale = page.locale || page.
|
|
54
|
+
const path = normalizeAgentRoutePath(page.path || page.resolved?.requestedRoute);
|
|
55
|
+
const locale = page.locale || page.resolved?.locale || page.locale;
|
|
331
56
|
const title = typeof page.title === "string" && page.title.trim() ? page.title.trim() : path.split("/").filter(Boolean).pop() || "Index";
|
|
332
57
|
const description = normalizeDescription(page);
|
|
333
58
|
return {
|
|
@@ -339,7 +64,7 @@ const toAgentMarkdown = (collection, page, options) => {
|
|
|
339
64
|
title,
|
|
340
65
|
description,
|
|
341
66
|
markdown: renderAgentMarkdown(page, collection, path, locale, options),
|
|
342
|
-
...page.
|
|
67
|
+
...page.file?.path ? { sourceFile: page.file?.path } : {},
|
|
343
68
|
canonicalUrl: path,
|
|
344
69
|
...typeof page.updated === "string" ? { lastModified: page.updated } : {},
|
|
345
70
|
metadataFields: options.metadata,
|
|
@@ -377,11 +102,11 @@ const publicPathForLocale = (collection, config, rowPath, locale, defaultLocale2
|
|
|
377
102
|
});
|
|
378
103
|
};
|
|
379
104
|
const publicPathForQueryRow = (collection, config, row, locale) => {
|
|
380
|
-
const requested = row.path || row.
|
|
105
|
+
const requested = row.path || row.resolved?.requestedRoute;
|
|
381
106
|
if (requested) return normalizeAgentRoutePath(requested);
|
|
382
107
|
const defaultLocale2 = collectionDefaultLocale(config);
|
|
383
|
-
const rowPath = normalizeAgentRoutePath(row.
|
|
384
|
-
const resolvedLocale = row.
|
|
108
|
+
const rowPath = normalizeAgentRoutePath(row.path || "/");
|
|
109
|
+
const resolvedLocale = row.resolved?.locale || row.locale;
|
|
385
110
|
if (locale && resolvedLocale && locale !== resolvedLocale) {
|
|
386
111
|
const sourceLocalePath = publicPathForLocale(collection, config, rowPath, resolvedLocale, defaultLocale2);
|
|
387
112
|
return prefixRequestedLocale(sourceLocalePath, locale, defaultLocale2);
|
|
@@ -416,15 +141,15 @@ export async function queryMarkdownEnabledContent(event, options = {}) {
|
|
|
416
141
|
for (const [collection, config] of markdownEnabledCollectionEntries(options.collections)) {
|
|
417
142
|
const agentOptions = resolveAgentMarkdownOptions(config);
|
|
418
143
|
if (!agentOptions) continue;
|
|
419
|
-
const rows = normalizeQueryResult(await provider.query(event, {
|
|
144
|
+
const rows = normalizeQueryResult(await provider.query(event, createProviderQuery({
|
|
420
145
|
collection,
|
|
421
|
-
only: ["path", "locale", "localePaths", "
|
|
146
|
+
only: ["path", "locale", "localePaths", "resolved", "file", "draft", "partial", "navigationFile", "title", "description", "updated", "navigation", "robots", "sitemap"],
|
|
422
147
|
...options.limit ? { limit: options.limit } : {},
|
|
423
148
|
...options.locale ? { resolveLocale: { locale: options.locale, fallback: true } } : {}
|
|
424
|
-
}));
|
|
149
|
+
})));
|
|
425
150
|
for (const row of rows) {
|
|
426
151
|
if (!isPublicPage(row, config)) continue;
|
|
427
|
-
const locale = options.locale || row.
|
|
152
|
+
const locale = options.locale || row.resolved?.locale || row.locale;
|
|
428
153
|
const path = publicPathForQueryRow(collection, config, row, locale);
|
|
429
154
|
const title = typeof row.title === "string" && row.title.trim() ? row.title.trim() : path.split("/").filter(Boolean).pop() || "Index";
|
|
430
155
|
const description = normalizeDescription(row);
|
|
@@ -436,7 +161,7 @@ export async function queryMarkdownEnabledContent(event, options = {}) {
|
|
|
436
161
|
collection,
|
|
437
162
|
title,
|
|
438
163
|
description,
|
|
439
|
-
...row.
|
|
164
|
+
...row.file?.path ? { sourceFile: row.file?.path } : {},
|
|
440
165
|
canonicalUrl: path,
|
|
441
166
|
...typeof row.updated === "string" ? { lastModified: row.updated } : {},
|
|
442
167
|
metadataFields: agentOptions.metadata,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { getRequestURL } from "h3";
|
|
2
2
|
import { linkMarkdown, queryMarkdownEnabledContent, resolveContentMarkdownByRoute } from "./agent-markdown.js";
|
|
3
|
-
import { agentMarkdownPathForRoute, agentRawPathForRoute, agentRoutePathFromIndexSlug, agentRoutePathFromRawSlug, normalizeAgentRoutePath } from "
|
|
3
|
+
import { agentMarkdownPathForRoute, agentRawPathForRoute, agentRoutePathFromIndexSlug, agentRoutePathFromRawSlug, normalizeAgentRoutePath } from "../../features/agent/agent-paths";
|
|
4
4
|
import { contentConfig } from "./storage-access.js";
|
|
5
5
|
const localizedValue = (value, locale, fallback = "") => {
|
|
6
6
|
if (typeof value === "string") return value;
|
|
@@ -49,6 +49,12 @@ const resolveLocalizedMaybeFunction = async (value, ctx) => {
|
|
|
49
49
|
if (typeof value === "function") return await value(ctx);
|
|
50
50
|
return localizedValue(value, ctx.locale);
|
|
51
51
|
};
|
|
52
|
+
const resolveAppPageMarkdown = async (page, ctx) => {
|
|
53
|
+
if (typeof page.render === "function") {
|
|
54
|
+
return await page.render(ctx);
|
|
55
|
+
}
|
|
56
|
+
return localizedValue(page.markdown, ctx.locale);
|
|
57
|
+
};
|
|
52
58
|
const resolveAppPageRoute = (page, locale) => normalizeAgentRoutePath(localizedValue(page.route, locale, "/"));
|
|
53
59
|
const createAppPageContext = (locale, siteUrl) => ({
|
|
54
60
|
locale,
|
|
@@ -96,6 +102,7 @@ const createAppOwnedAgentPages = async (locale, siteUrl) => {
|
|
|
96
102
|
const path = resolveAppPageRoute(page, locale);
|
|
97
103
|
const rawPath = agentRawPathForRoute(path);
|
|
98
104
|
const section = resolveSection(page.section, locale);
|
|
105
|
+
const markdown = await resolveAppPageMarkdown(page, ctx);
|
|
99
106
|
result.push({
|
|
100
107
|
title: await resolveLocalizedMaybeFunction(page.title, ctx),
|
|
101
108
|
description: await resolveLocalizedMaybeFunction(page.description, ctx),
|
|
@@ -113,7 +120,7 @@ const createAppOwnedAgentPages = async (locale, siteUrl) => {
|
|
|
113
120
|
metadataFields: page.metadata,
|
|
114
121
|
includeInIndex: page.includeInIndex !== false,
|
|
115
122
|
includeInFull: page.includeInFull !== false,
|
|
116
|
-
markdown
|
|
123
|
+
markdown
|
|
117
124
|
});
|
|
118
125
|
}
|
|
119
126
|
return result;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createError, defineEventHandler } from "h3";
|
|
2
2
|
import { localeFromAgentPath, resolveMarkdownForPublicRoute, routePathFromRawSlug } from "../agent-site.js";
|
|
3
3
|
import { setAgentMarkdownHeaders } from "../agent-http.js";
|
|
4
|
-
import { isUnsafeAgentRoutePath } from "
|
|
4
|
+
import { isUnsafeAgentRoutePath } from "../../../features/agent/agent-paths";
|
|
5
5
|
export default defineEventHandler(async (event) => {
|
|
6
6
|
const routePath = routePathFromRawSlug(event.context.params?.slug);
|
|
7
7
|
if (isUnsafeAgentRoutePath(routePath)) {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { NavItem } from '../../../types/content';
|
|
2
2
|
declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<{
|
|
3
|
-
contents: unknown[];
|
|
4
3
|
navigation: NavItem[];
|
|
5
4
|
generatedAt: number;
|
|
5
|
+
documentCount: number;
|
|
6
6
|
generateTime: number;
|
|
7
7
|
}>>;
|
|
8
8
|
export default _default;
|
|
@@ -1,26 +1,65 @@
|
|
|
1
1
|
import { defineEventHandler } from "h3";
|
|
2
|
+
import { isRealDocument } from "../../../core/content/document";
|
|
3
|
+
import { assertSnapshotComplete, buildContentSnapshot } from "../../../core/content/snapshot";
|
|
4
|
+
import { chunksFromArray, loadContentVariants } from "../../../storage/contents";
|
|
2
5
|
import { getContentProvider } from "../providers/index.js";
|
|
6
|
+
import { createProviderNavigationQuery } from "../provider-query.js";
|
|
3
7
|
import { createContentProviderError } from "../../../public/provider-errors";
|
|
4
|
-
import { cacheStorage } from "../storage-access.js";
|
|
8
|
+
import { cacheStorage, contentConfig, getSourceContentIds } from "../storage-access.js";
|
|
9
|
+
const hasRoutePath = (document) => typeof document.path === "string" && document.path.length > 0;
|
|
10
|
+
const describeExcludedSource = (variants) => {
|
|
11
|
+
if (variants.every((variant) => !isRealDocument(variant))) {
|
|
12
|
+
return "unreadable (source missing or failed to parse)";
|
|
13
|
+
}
|
|
14
|
+
return "no route path (parsed, but every variant lacks a path)";
|
|
15
|
+
};
|
|
5
16
|
export default defineEventHandler(async (event) => {
|
|
6
17
|
const now = Date.now();
|
|
7
|
-
const
|
|
18
|
+
const config = contentConfig();
|
|
19
|
+
const sourceIds = await getSourceContentIds(event);
|
|
20
|
+
const documents = [];
|
|
21
|
+
const excluded = /* @__PURE__ */ new Map();
|
|
22
|
+
for (const chunk of chunksFromArray(sourceIds, 10)) {
|
|
23
|
+
const results = await Promise.all(chunk.map(async (id) => [id, await loadContentVariants(event, id)]));
|
|
24
|
+
for (const [id, variants] of results) {
|
|
25
|
+
const routable = variants.filter(isRealDocument).filter(hasRoutePath);
|
|
26
|
+
if (routable.length === 0) {
|
|
27
|
+
excluded.set(id, describeExcludedSource(variants));
|
|
28
|
+
}
|
|
29
|
+
documents.push(...routable);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
if (excluded.size > 0) {
|
|
33
|
+
const details = [...excluded.entries()].slice(0, 20).map(([id, reason]) => `${id}: ${reason}`).join("; ");
|
|
34
|
+
throw new Error(
|
|
35
|
+
`[content] snapshot build failed: ${excluded.size} source document(s) produced no servable content and would silently 404 in production. First ${Math.min(excluded.size, 20)} \u2014 ${details}`
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
const snapshot = buildContentSnapshot({
|
|
39
|
+
integrity: config.cacheIntegrity,
|
|
40
|
+
documents,
|
|
41
|
+
sourceIds,
|
|
42
|
+
now
|
|
43
|
+
});
|
|
44
|
+
assertSnapshotComplete(snapshot, sourceIds);
|
|
45
|
+
await cacheStorage(event).setItem("snapshot.json", snapshot);
|
|
8
46
|
const provider = await getContentProvider(event);
|
|
9
47
|
if (!provider.navigationQuery) {
|
|
10
48
|
throw createContentProviderError("unsupported_provider_operation", `${provider.name} does not support navigation queries`, {
|
|
11
49
|
provider: provider.name
|
|
12
50
|
});
|
|
13
51
|
}
|
|
14
|
-
const
|
|
52
|
+
const { query: navQuery, options: navOptions } = createProviderNavigationQuery({});
|
|
53
|
+
const navigation = await provider.navigationQuery(event, navQuery, navOptions);
|
|
15
54
|
await cacheStorage(event).setItem("_nav.json", navigation);
|
|
16
55
|
const meta = {
|
|
17
56
|
generatedAt: now,
|
|
57
|
+
documentCount: snapshot.documentIds.length,
|
|
18
58
|
generateTime: Date.now() - now
|
|
19
59
|
};
|
|
20
60
|
await cacheStorage(event).setItem("_meta.json", meta);
|
|
21
61
|
return {
|
|
22
62
|
...meta,
|
|
23
|
-
contents,
|
|
24
63
|
navigation
|
|
25
64
|
};
|
|
26
65
|
});
|
|
@@ -15,10 +15,10 @@ export default defineEventHandler(async (event) => {
|
|
|
15
15
|
collection,
|
|
16
16
|
exact: false
|
|
17
17
|
});
|
|
18
|
-
const locales = resolved ? resolved.variants.filter((doc) => doc.
|
|
18
|
+
const locales = resolved ? resolved.variants.filter((doc) => doc.canonicalKey === resolved.canonicalKey && typeof doc.locale === "string").sort((left, right) => String(left.locale).localeCompare(String(right.locale))).map((doc) => ({
|
|
19
19
|
canonicalKey: resolved.canonicalKey,
|
|
20
|
-
locale: String(doc.
|
|
21
|
-
...typeof doc.
|
|
20
|
+
locale: String(doc.locale),
|
|
21
|
+
...typeof doc.path === "string" ? { path: doc.path } : {}
|
|
22
22
|
})) : [];
|
|
23
23
|
if (!locales.length) {
|
|
24
24
|
throw createError({
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<import("../../../types").NavItem[]>>;
|
|
1
|
+
declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<import("../../../types/content").NavItem[]>>;
|
|
2
2
|
export default _default;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { defineEventHandler, getQuery } from "h3";
|
|
2
2
|
import { getContentQuery } from "../../utils/query.js";
|
|
3
3
|
import { getContentProvider } from "../providers/index.js";
|
|
4
|
+
import { createProviderNavigationQuery } from "../provider-query.js";
|
|
4
5
|
import { createContentProviderError } from "../../../public/provider-errors";
|
|
5
6
|
export default defineEventHandler(async (event) => {
|
|
6
7
|
const query = getContentQuery(event);
|
|
@@ -17,5 +18,6 @@ export default defineEventHandler(async (event) => {
|
|
|
17
18
|
provider: provider.name
|
|
18
19
|
});
|
|
19
20
|
}
|
|
20
|
-
|
|
21
|
+
const { query: providerQuery, options } = createProviderNavigationQuery(query);
|
|
22
|
+
return await provider.navigationQuery(event, providerQuery, options);
|
|
21
23
|
});
|
|
@@ -1,9 +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
|
+
import { createProviderQuery, normalizeProviderQueryResponse } from "../provider-query.js";
|
|
5
5
|
export default defineEventHandler(async (event) => {
|
|
6
6
|
const query = getContentQuery(event);
|
|
7
7
|
const provider = await getContentProvider(event);
|
|
8
|
-
return normalizeProviderQueryResponse(query, await provider.query(event, query), provider.name);
|
|
8
|
+
return normalizeProviderQueryResponse(query, await provider.query(event, createProviderQuery(query)), provider.name);
|
|
9
9
|
});
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<import("../../../types").ContentSearchIndexRecord[]>>;
|
|
1
|
+
declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<import("../../../types/search").ContentSearchIndexRecord[]>>;
|
|
2
2
|
export default _default;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<import("../../../types").ContentSitemapEntry[]>>;
|
|
1
|
+
declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<import("../../../types/query").ContentSitemapEntry[]>>;
|
|
2
2
|
export default _default;
|
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
import type { ContentCacheAdapter, ContentCacheHint } from '../../public/provider';
|
|
2
|
+
/**
|
|
3
|
+
* Choosing a cache adapter:
|
|
4
|
+
* - `noopContentCache` — the default. Both `apply` and `invalidate` are intentionally
|
|
5
|
+
* inert: the runtime still computes and stores a cache hint per request, but nothing
|
|
6
|
+
* is written to the response and no upstream cache is purged. Use when caching is
|
|
7
|
+
* handled entirely outside this module (a CDN/edge config you own) or disabled.
|
|
8
|
+
* - `vercelContentCache` — `apply` is intentionally inert (Vercel ISR revalidation is a
|
|
9
|
+
* pull, not a per-response header this module sets); only `invalidate` does work,
|
|
10
|
+
* firing prerender-revalidate HEAD requests. Use on Vercel ISR deployments.
|
|
11
|
+
* - `headersContentCache` — the active-`apply` adapter: writes `Cache-Control`/`ETag`/
|
|
12
|
+
* `Last-Modified` onto every content response from the hint. Use when this module
|
|
13
|
+
* should own response cache headers directly (self-hosted / generic CDN).
|
|
14
|
+
*/
|
|
2
15
|
export declare const noopContentCache: () => ContentCacheAdapter;
|
|
16
|
+
/**
|
|
17
|
+
* Emits the per-request cache hint as HTTP response headers via {@link contentCacheHeaders}.
|
|
18
|
+
* `invalidate` is a no-op: header-based caching has no upstream store to purge.
|
|
19
|
+
*/
|
|
20
|
+
export declare const headersContentCache: () => ContentCacheAdapter;
|
|
3
21
|
export interface VercelContentCacheOptions {
|
|
4
22
|
origin: string;
|
|
5
23
|
bypassToken: string;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { setHeader } from "h3";
|
|
1
2
|
export const noopContentCache = () => ({
|
|
2
3
|
name: "noop",
|
|
3
4
|
apply: () => {
|
|
@@ -5,6 +6,17 @@ export const noopContentCache = () => ({
|
|
|
5
6
|
invalidate: async () => {
|
|
6
7
|
}
|
|
7
8
|
});
|
|
9
|
+
export const headersContentCache = () => ({
|
|
10
|
+
name: "headers",
|
|
11
|
+
apply: (event, hint) => {
|
|
12
|
+
const headers = contentCacheHeaders(hint);
|
|
13
|
+
headers.forEach((value, key) => {
|
|
14
|
+
setHeader(event, key, value);
|
|
15
|
+
});
|
|
16
|
+
},
|
|
17
|
+
invalidate: async () => {
|
|
18
|
+
}
|
|
19
|
+
});
|
|
8
20
|
export const vercelContentCache = (options) => {
|
|
9
21
|
const origin = options.origin.replace(/\/$/, "");
|
|
10
22
|
const fetchImpl = options.fetch || globalThis.fetch;
|