@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.
Files changed (230) hide show
  1. package/compatibility.json +1 -1
  2. package/dist/cli.mjs +2 -2
  3. package/dist/cms-contract/build.js +15 -20
  4. package/dist/cms-contract/types.d.ts +12 -7
  5. package/dist/core/content/document +1 -0
  6. package/dist/core/content/document.d.ts +16 -0
  7. package/dist/core/content/document.js +2 -0
  8. package/dist/core/content/graph.d.ts +3 -1
  9. package/dist/core/content/graph.js +21 -20
  10. package/dist/core/content/locale.d.ts +6 -1
  11. package/dist/core/content/locale.js +17 -6
  12. package/dist/core/content/path.d.ts +5 -5
  13. package/dist/core/content/path.js +8 -8
  14. package/dist/core/content/snapshot +1 -0
  15. package/dist/core/content/snapshot.d.ts +25 -0
  16. package/dist/core/content/snapshot.js +99 -0
  17. package/dist/core/provider-errors.d.ts +1 -1
  18. package/dist/core/provider-errors.js +1 -0
  19. package/dist/core/query/builder.js +3 -3
  20. package/dist/core/query/execute.js +75 -36
  21. package/dist/core/query/filter.js +2 -2
  22. package/dist/core/query/lower.js +79 -3
  23. package/dist/core/query/params.js +7 -7
  24. package/dist/core/query/plan.d.ts +18 -0
  25. package/dist/core/query/plan.js +7 -0
  26. package/dist/core/references/resolve.js +6 -9
  27. package/dist/features/agent/agent-markdown +1 -0
  28. package/dist/features/agent/agent-markdown.d.ts +116 -0
  29. package/dist/features/agent/agent-markdown.js +138 -0
  30. package/dist/features/agent/walker +1 -0
  31. package/dist/features/agent/walker.d.ts +10 -0
  32. package/dist/features/agent/walker.js +174 -0
  33. package/dist/features/collections/resolve.d.ts +2 -2
  34. package/dist/features/collections/resolve.js +19 -16
  35. package/dist/features/localization/links.js +1 -1
  36. package/dist/features/localization/results.d.ts +3 -3
  37. package/dist/features/localization/results.js +46 -41
  38. package/dist/features/localization/translated-slugs.js +5 -5
  39. package/dist/features/navigation/build.d.ts +2 -2
  40. package/dist/features/navigation/build.js +23 -26
  41. package/dist/features/navigation/canonical.d.ts +10 -11
  42. package/dist/features/navigation/canonical.js +26 -21
  43. package/dist/features/navigation/resolve.js +4 -4
  44. package/dist/features/navigation/tree.js +3 -3
  45. package/dist/{runtime → features}/query/backlinks.js +3 -5
  46. package/dist/{runtime → features}/query/documents.js +1 -1
  47. package/dist/{runtime → features}/query/localized-docs.d.ts +1 -1
  48. package/dist/{runtime → features}/query/localized-docs.js +3 -6
  49. package/dist/{runtime → features}/query/navigation.d.ts +1 -1
  50. package/dist/{runtime → features}/query/navigation.js +8 -8
  51. package/dist/{runtime → features}/query/populate.js +6 -14
  52. package/dist/{runtime → features}/query/unified.d.ts +4 -4
  53. package/dist/{runtime → features}/query/unified.js +4 -2
  54. package/dist/{runtime → features}/query/variants.js +4 -4
  55. package/dist/features/search/navigation.js +2 -2
  56. package/dist/features/search/sections.d.ts +1 -1
  57. package/dist/features/search/sections.js +1 -1
  58. package/dist/features/sitemap/query.d.ts +5 -5
  59. package/dist/features/sitemap/query.js +6 -6
  60. package/dist/integrations/nitro/ingest.js +9 -20
  61. package/dist/integrations/nitro/storage.d.ts +1 -0
  62. package/dist/integrations/nitro/storage.js +21 -34
  63. package/dist/module.d.mts +135 -803
  64. package/dist/module.json +1 -1
  65. package/dist/module.mjs +143 -51
  66. package/dist/parsers/csv.js +3 -3
  67. package/dist/parsers/index.js +3 -3
  68. package/dist/parsers/json.js +8 -6
  69. package/dist/parsers/markdown-plugins.js +9 -5
  70. package/dist/parsers/markdown.js +7 -6
  71. package/dist/parsers/path-meta.js +21 -19
  72. package/dist/parsers/reserved +1 -0
  73. package/dist/parsers/reserved.d.ts +15 -0
  74. package/dist/parsers/reserved.js +35 -0
  75. package/dist/parsers/yaml.js +6 -4
  76. package/dist/public/agent +1 -0
  77. package/dist/public/agent.d.ts +17 -0
  78. package/dist/public/agent.js +34 -0
  79. package/dist/public/client.d.ts +2 -3
  80. package/dist/public/client.js +1 -2
  81. package/dist/public/provider-query +1 -0
  82. package/dist/public/provider-query.d.ts +58 -0
  83. package/dist/public/provider-query.js +5 -0
  84. package/dist/public/provider.d.ts +8 -3
  85. package/dist/public/provider.js +2 -0
  86. package/dist/public/server.d.ts +7 -12
  87. package/dist/public/server.js +5 -36
  88. package/dist/runtime/app/components/ContentRenderer.d.vue.ts +3 -3
  89. package/dist/runtime/app/components/ContentRenderer.vue +2 -2
  90. package/dist/runtime/app/components/ContentRenderer.vue.d.ts +3 -3
  91. package/dist/runtime/app/components/ContentRendererInline.d.vue.ts +1 -1
  92. package/dist/runtime/app/components/ContentRendererInline.vue.d.ts +1 -1
  93. package/dist/runtime/app/components/Prose/ProseImg.d.vue.ts +4 -4
  94. package/dist/runtime/app/components/Prose/ProseImg.vue.d.ts +4 -4
  95. package/dist/runtime/app/components/Prose/ProsePre.d.vue.ts +1 -1
  96. package/dist/runtime/app/components/Prose/ProsePre.vue.d.ts +1 -1
  97. package/dist/runtime/app/components/internal/ContentRendererMarkdown.d.vue.ts +1 -1
  98. package/dist/runtime/app/components/internal/ContentRendererMarkdown.vue +4 -4
  99. package/dist/runtime/app/components/internal/ContentRendererMarkdown.vue.d.ts +1 -1
  100. package/dist/runtime/app/components/internal/MarkdownRenderer.d.ts +3 -3
  101. package/dist/runtime/app/composables/preview.d.ts +1 -1
  102. package/dist/runtime/app/composables/query-api.d.ts +5 -5
  103. package/dist/runtime/app/composables/query-api.js +5 -3
  104. package/dist/runtime/app/composables/route.d.ts +1 -1
  105. package/dist/runtime/app/composables/route.js +4 -4
  106. package/dist/runtime/app/composables/search.js +1 -1
  107. package/dist/runtime/app/composables/use-content-document.js +1 -1
  108. package/dist/runtime/app/composables/use-content-list.js +1 -1
  109. package/dist/runtime/app/composables/use-content-navigation.js +2 -2
  110. package/dist/runtime/app/composables/use-content-page.js +4 -4
  111. package/dist/runtime/app/pages/content.vue +2 -1
  112. package/dist/runtime/markdown/plugins.js +6 -8
  113. package/dist/runtime/server/agent-markdown.d.ts +5 -65
  114. package/dist/runtime/server/agent-markdown.js +40 -315
  115. package/dist/runtime/server/agent-site.js +9 -2
  116. package/dist/runtime/server/api/agent-raw.js +1 -1
  117. package/dist/runtime/server/api/cache.d.ts +1 -1
  118. package/dist/runtime/server/api/cache.js +43 -4
  119. package/dist/runtime/server/api/locales.js +3 -3
  120. package/dist/runtime/server/api/navigation.d.ts +1 -1
  121. package/dist/runtime/server/api/navigation.js +3 -1
  122. package/dist/runtime/server/api/query.js +2 -2
  123. package/dist/runtime/server/api/search-index.d.ts +1 -1
  124. package/dist/runtime/server/api/sitemap.d.ts +1 -1
  125. package/dist/runtime/server/cache-adapters.d.ts +18 -0
  126. package/dist/runtime/server/cache-adapters.js +12 -0
  127. package/dist/runtime/server/collection-helpers.d.ts +2 -2
  128. package/dist/runtime/server/collection-helpers.js +15 -11
  129. package/dist/runtime/server/index.d.ts +6 -4
  130. package/dist/runtime/server/index.js +10 -3
  131. package/dist/runtime/server/middleware/agent-link-headers.js +1 -1
  132. package/dist/runtime/server/middleware/agent-markdown.js +1 -1
  133. package/dist/runtime/server/navigation-query.d.ts +2 -2
  134. package/dist/runtime/server/navigation-query.js +52 -7
  135. package/dist/runtime/server/navigation.d.ts +1 -1
  136. package/dist/runtime/server/provider-document +1 -0
  137. package/dist/runtime/server/provider-document.d.ts +59 -0
  138. package/dist/runtime/server/provider-document.js +48 -0
  139. package/dist/runtime/server/provider-query-wire +1 -0
  140. package/dist/runtime/server/provider-query-wire.d.ts +32 -0
  141. package/dist/runtime/server/provider-query-wire.js +27 -0
  142. package/dist/runtime/server/provider-query.d.ts +18 -9
  143. package/dist/runtime/server/provider-query.js +62 -28
  144. package/dist/runtime/server/provider-result.d.ts +4 -4
  145. package/dist/runtime/server/providers/filesystem.js +3 -11
  146. package/dist/runtime/server/providers/index.d.ts +18 -1
  147. package/dist/runtime/server/providers/index.js +72 -20
  148. package/dist/runtime/server/query-api.d.ts +5 -5
  149. package/dist/runtime/server/query-api.js +9 -6
  150. package/dist/runtime/server/query-executor.d.ts +3 -3
  151. package/dist/runtime/server/query-executor.js +43 -59
  152. package/dist/runtime/server/search.d.ts +1 -1
  153. package/dist/runtime/server/search.js +4 -4
  154. package/dist/runtime/server/sitemap.js +5 -5
  155. package/dist/runtime/server/storage-access.d.ts +4 -1
  156. package/dist/runtime/server/storage-access.js +27 -2
  157. package/dist/runtime/transformers/component-resolver.d.ts +1 -1
  158. package/dist/storage/content.d.ts +2 -6
  159. package/dist/storage/content.js +19 -8
  160. package/dist/storage/contents.d.ts +9 -1
  161. package/dist/storage/contents.js +26 -19
  162. package/dist/storage/driver.d.ts +2 -2
  163. package/dist/storage/driver.js +2 -1
  164. package/dist/storage/graph.js +4 -0
  165. package/dist/storage/manifest.js +3 -1
  166. package/dist/storage/references.js +9 -5
  167. package/dist/storage/snapshot-runtime +1 -0
  168. package/dist/storage/snapshot-runtime.d.ts +11 -0
  169. package/dist/storage/snapshot-runtime.js +46 -0
  170. package/dist/storage/validation.d.ts +1 -1
  171. package/dist/storage/validation.js +79 -105
  172. package/dist/testing/provider-contract.d.ts +14 -4
  173. package/dist/testing/provider-contract.js +121 -68
  174. package/dist/testing/provider-fixture.d.ts +1 -1
  175. package/dist/testing/provider-fixture.js +124 -120
  176. package/dist/types/config.d.ts +17 -4
  177. package/dist/types/content.d.ts +104 -70
  178. package/dist/types/fields.js +1 -1
  179. package/dist/types/module.d.ts +1 -1
  180. package/dist/types/query-parts/public.d.ts +14 -5
  181. package/dist/types/query-parts/results.d.ts +6 -2
  182. package/dist/types/query-parts/transport.d.ts +19 -0
  183. package/dist/types/virtual.d.ts +6 -0
  184. package/dist/types.d.mts +2 -2
  185. package/dist/web-types.json +1 -1
  186. package/package.json +39 -9
  187. package/dist/runtime/app/plugins/markdown-components +0 -1
  188. package/dist/runtime/app/plugins/markdown-components.d.ts +0 -2
  189. package/dist/runtime/app/plugins/markdown-components.js +0 -19
  190. /package/dist/{runtime → features/agent}/agent-paths +0 -0
  191. /package/dist/{runtime → features/agent}/agent-paths.d.ts +0 -0
  192. /package/dist/{runtime → features/agent}/agent-paths.js +0 -0
  193. /package/dist/{runtime → features}/query/backlinks +0 -0
  194. /package/dist/{runtime → features}/query/backlinks.d.ts +0 -0
  195. /package/dist/{runtime → features}/query/context +0 -0
  196. /package/dist/{runtime → features}/query/context.d.ts +0 -0
  197. /package/dist/{runtime → features}/query/context.js +0 -0
  198. /package/dist/{runtime → features}/query/documents +0 -0
  199. /package/dist/{runtime → features}/query/documents.d.ts +0 -0
  200. /package/dist/{runtime → features}/query/errors +0 -0
  201. /package/dist/{runtime → features}/query/errors.d.ts +0 -0
  202. /package/dist/{runtime → features}/query/errors.js +0 -0
  203. /package/dist/{runtime → features}/query/handles +0 -0
  204. /package/dist/{runtime → features}/query/handles.d.ts +0 -0
  205. /package/dist/{runtime → features}/query/handles.js +0 -0
  206. /package/dist/{runtime → features}/query/locale-options +0 -0
  207. /package/dist/{runtime → features}/query/locale-options.d.ts +0 -0
  208. /package/dist/{runtime → features}/query/locale-options.js +0 -0
  209. /package/dist/{runtime → features}/query/localized-docs +0 -0
  210. /package/dist/{runtime → features}/query/navigation +0 -0
  211. /package/dist/{runtime → features}/query/pagination +0 -0
  212. /package/dist/{runtime → features}/query/pagination.d.ts +0 -0
  213. /package/dist/{runtime → features}/query/pagination.js +0 -0
  214. /package/dist/{runtime → features}/query/populate +0 -0
  215. /package/dist/{runtime → features}/query/populate.d.ts +0 -0
  216. /package/dist/{runtime → features}/query/public-limits +0 -0
  217. /package/dist/{runtime → features}/query/public-limits.d.ts +0 -0
  218. /package/dist/{runtime → features}/query/public-limits.js +0 -0
  219. /package/dist/{runtime → features}/query/resolve +0 -0
  220. /package/dist/{runtime → features}/query/resolve.d.ts +0 -0
  221. /package/dist/{runtime → features}/query/resolve.js +0 -0
  222. /package/dist/{runtime → features}/query/responses +0 -0
  223. /package/dist/{runtime → features}/query/responses.d.ts +0 -0
  224. /package/dist/{runtime → features}/query/responses.js +0 -0
  225. /package/dist/{runtime → features}/query/routes +0 -0
  226. /package/dist/{runtime → features}/query/routes.d.ts +0 -0
  227. /package/dist/{runtime → features}/query/routes.js +0 -0
  228. /package/dist/{runtime → features}/query/unified +0 -0
  229. /package/dist/{runtime → features}/query/variants +0 -0
  230. /package/dist/{runtime → features}/query/variants.d.ts +0 -0
@@ -1,324 +1,49 @@
1
- import { kebabCase, pascalCase } from "scule";
2
- import { agentMarkdownPathForRoute, agentRawPathForRoute, normalizeAgentRoutePath } from "../agent-paths.js";
3
- import { getCollectionPath } from "../query/routes.js";
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
- const serializers = /* @__PURE__ */ new Map();
7
- const isRecord = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
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 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 `${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 `![${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
- };
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._draft && !page._partial && !page._navigation && page.navigation !== false && page.robots !== "noindex" && page.sitemap !== false
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 = renderMarkdownRoot(page.body, { collection, page, path, locale });
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._requestedRoute || page._path);
330
- const locale = page.locale || page._resolvedLocale || page._locale;
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._file ? { sourceFile: page._file } : {},
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._requestedRoute;
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._path || "/");
384
- const resolvedLocale = row._resolvedLocale || row._locale;
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", "_path", "_locale", "_resolvedLocale", "_requestedRoute", "_file", "_draft", "_partial", "_navigation", "title", "description", "updated", "navigation", "robots", "sitemap"],
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._resolvedLocale || row._locale;
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._file ? { sourceFile: row._file } : {},
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 "../agent-paths.js";
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: await page.render(ctx)
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 "../../agent-paths.js";
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 contents = [];
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 navigation = await provider.navigationQuery(event, {});
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._canonicalKey === resolved.canonicalKey && typeof doc._locale === "string").sort((left, right) => String(left._locale).localeCompare(String(right._locale))).map((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._locale),
21
- ...typeof doc._path === "string" ? { path: doc._path } : {}
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
- return await provider.navigationQuery(event, query);
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;