@lupinum/ginko-content 0.1.2 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (189) hide show
  1. package/README.md +18 -4
  2. package/compatibility.json +1 -1
  3. package/dist/cli.mjs +317 -294
  4. package/dist/cms-import/index.js +5 -1
  5. package/dist/config.d.mts +1 -1
  6. package/dist/config.mjs +1 -1
  7. package/dist/core/cache-hints.d.ts +9 -1
  8. package/dist/core/content/collection.js +1 -1
  9. package/dist/core/content/path.js +1 -1
  10. package/dist/core/errors.js +5 -1
  11. package/dist/core/provider-errors +1 -0
  12. package/dist/core/provider-errors.d.ts +10 -0
  13. package/dist/core/provider-errors.js +27 -0
  14. package/dist/core/query/execute.js +12 -3
  15. package/dist/core/query/filter.js +2 -2
  16. package/dist/core/query/lower.js +5 -4
  17. package/dist/features/navigation/build.js +14 -6
  18. package/dist/features/navigation/canonical.js +1 -1
  19. package/dist/features/sitemap/query.js +2 -2
  20. package/dist/integrations/nitro/ingest.d.ts +3 -3
  21. package/dist/integrations/nitro/ingest.js +3 -3
  22. package/dist/integrations/nitro/storage.js +3 -1
  23. package/dist/module.d.mts +151 -526
  24. package/dist/module.json +1 -1
  25. package/dist/module.mjs +1091 -722
  26. package/dist/parsers/csv.js +4 -3
  27. package/dist/parsers/index.d.ts +2 -5
  28. package/dist/parsers/index.js +2 -2
  29. package/dist/parsers/markdown-plugins.d.ts +1 -0
  30. package/dist/parsers/markdown-plugins.js +83 -1
  31. package/dist/parsers/markdown.js +1 -1
  32. package/dist/public/client.d.ts +3 -2
  33. package/dist/public/client.js +2 -2
  34. package/dist/public/provider-errors.d.ts +2 -1
  35. package/dist/public/provider-errors.js +1 -18
  36. package/dist/public/provider.d.ts +3 -10
  37. package/dist/public/server.d.ts +6 -1
  38. package/dist/public/server.js +33 -0
  39. package/dist/runtime/agent-paths +1 -0
  40. package/dist/runtime/agent-paths.d.ts +6 -0
  41. package/dist/runtime/agent-paths.js +41 -0
  42. package/dist/runtime/app/components/internal/MarkdownRenderer.d.ts +1 -1
  43. package/dist/runtime/app/composables/head.d.ts +3 -0
  44. package/dist/runtime/app/composables/head.js +2 -1
  45. package/dist/runtime/app/composables/hot-reload +1 -0
  46. package/dist/runtime/app/composables/hot-reload.d.ts +5 -0
  47. package/dist/runtime/app/composables/hot-reload.js +15 -0
  48. package/dist/runtime/app/composables/locale-context.d.ts +1 -1
  49. package/dist/runtime/app/composables/locale-context.js +2 -1
  50. package/dist/runtime/app/composables/locale.js +2 -2
  51. package/dist/runtime/app/composables/search.js +10 -6
  52. package/dist/runtime/app/composables/use-content-document +1 -0
  53. package/dist/runtime/app/composables/use-content-document.d.ts +29 -0
  54. package/dist/runtime/app/composables/use-content-document.js +44 -0
  55. package/dist/runtime/app/composables/use-content-list +1 -0
  56. package/dist/runtime/app/composables/use-content-list.d.ts +43 -0
  57. package/dist/runtime/app/composables/use-content-list.js +98 -0
  58. package/dist/runtime/app/composables/use-content-navigation +1 -0
  59. package/dist/runtime/app/composables/use-content-navigation.d.ts +40 -0
  60. package/dist/runtime/app/composables/use-content-navigation.js +102 -0
  61. package/dist/runtime/app/composables/use-content-page +1 -0
  62. package/dist/runtime/app/composables/use-content-page.d.ts +47 -0
  63. package/dist/runtime/app/composables/use-content-page.js +142 -0
  64. package/dist/runtime/app/composables/use-content-shared +1 -0
  65. package/dist/runtime/app/composables/use-content-shared.d.ts +13 -0
  66. package/dist/runtime/app/composables/use-content-shared.js +29 -0
  67. package/dist/runtime/app/composables/use-content.d.ts +11 -206
  68. package/dist/runtime/app/composables/use-content.js +4 -377
  69. package/dist/runtime/app/plugins/hot-reload +1 -0
  70. package/dist/runtime/app/plugins/hot-reload.js +6 -0
  71. package/dist/runtime/query/backlinks +1 -0
  72. package/dist/runtime/query/backlinks.d.ts +8 -0
  73. package/dist/runtime/query/backlinks.js +90 -0
  74. package/dist/runtime/query/context +1 -0
  75. package/dist/runtime/query/context.d.ts +20 -0
  76. package/dist/runtime/query/context.js +0 -0
  77. package/dist/runtime/query/documents +1 -0
  78. package/dist/runtime/query/documents.d.ts +6 -0
  79. package/dist/runtime/query/documents.js +98 -0
  80. package/dist/runtime/query/errors +1 -0
  81. package/dist/runtime/query/errors.d.ts +1 -0
  82. package/dist/runtime/query/errors.js +4 -0
  83. package/dist/runtime/query/handles +1 -0
  84. package/dist/runtime/query/handles.d.ts +2 -0
  85. package/dist/runtime/query/handles.js +7 -0
  86. package/dist/runtime/query/locale-options +1 -0
  87. package/dist/runtime/query/locale-options.d.ts +5 -0
  88. package/dist/runtime/query/locale-options.js +14 -0
  89. package/dist/runtime/query/localized-docs +1 -0
  90. package/dist/runtime/query/localized-docs.d.ts +17 -0
  91. package/dist/runtime/query/localized-docs.js +44 -0
  92. package/dist/runtime/query/navigation +1 -0
  93. package/dist/runtime/query/navigation.d.ts +22 -0
  94. package/dist/runtime/query/navigation.js +73 -0
  95. package/dist/runtime/query/pagination +1 -0
  96. package/dist/runtime/query/pagination.d.ts +7 -0
  97. package/dist/runtime/query/pagination.js +78 -0
  98. package/dist/runtime/query/populate +1 -0
  99. package/dist/runtime/query/populate.d.ts +9 -0
  100. package/dist/runtime/query/populate.js +106 -0
  101. package/dist/runtime/query/responses +1 -0
  102. package/dist/runtime/query/responses.d.ts +10 -0
  103. package/dist/runtime/query/responses.js +47 -0
  104. package/dist/runtime/query/unified.d.ts +4 -20
  105. package/dist/runtime/query/unified.js +14 -544
  106. package/dist/runtime/query/variants +1 -0
  107. package/dist/runtime/query/variants.d.ts +11 -0
  108. package/dist/runtime/query/variants.js +36 -0
  109. package/dist/runtime/server/agent-http +1 -0
  110. package/dist/runtime/server/agent-http.d.ts +7 -0
  111. package/dist/runtime/server/agent-http.js +36 -0
  112. package/dist/runtime/server/agent-markdown +1 -0
  113. package/dist/runtime/server/agent-markdown.d.ts +83 -0
  114. package/dist/runtime/server/agent-markdown.js +449 -0
  115. package/dist/runtime/server/agent-site +1 -0
  116. package/dist/runtime/server/agent-site.d.ts +38 -0
  117. package/dist/runtime/server/agent-site.js +273 -0
  118. package/dist/runtime/server/api/agent-llms +1 -0
  119. package/dist/runtime/server/api/agent-llms-full +1 -0
  120. package/dist/runtime/server/api/agent-llms-full.d.ts +2 -0
  121. package/dist/runtime/server/api/agent-llms-full.js +13 -0
  122. package/dist/runtime/server/api/agent-llms.d.ts +2 -0
  123. package/dist/runtime/server/api/agent-llms.js +14 -0
  124. package/dist/runtime/server/api/agent-raw +1 -0
  125. package/dist/runtime/server/api/agent-raw.d.ts +2 -0
  126. package/dist/runtime/server/api/agent-raw.js +17 -0
  127. package/dist/runtime/server/api/locales.d.ts +2 -2
  128. package/dist/runtime/server/api/locales.js +4 -2
  129. package/dist/runtime/server/api/navigation.d.ts +1 -1
  130. package/dist/runtime/server/api/query.d.ts +1 -1
  131. package/dist/runtime/server/api/query.js +4 -1
  132. package/dist/runtime/server/api/revalidate.js +2 -2
  133. package/dist/runtime/server/api/site-data.d.ts +1 -1
  134. package/dist/runtime/server/collection-helpers.js +2 -1
  135. package/dist/runtime/server/middleware/agent-link-headers +1 -0
  136. package/dist/runtime/server/middleware/agent-link-headers.d.ts +2 -0
  137. package/dist/runtime/server/middleware/agent-link-headers.js +42 -0
  138. package/dist/runtime/server/middleware/agent-markdown +1 -0
  139. package/dist/runtime/server/middleware/agent-markdown.d.ts +2 -0
  140. package/dist/runtime/server/middleware/agent-markdown.js +43 -0
  141. package/dist/runtime/server/plugins/sitemap.js +1 -0
  142. package/dist/runtime/server/provider-query.d.ts +8 -5
  143. package/dist/runtime/server/provider-query.js +50 -8
  144. package/dist/runtime/server/provider-result.d.ts +19 -2
  145. package/dist/runtime/server/providers/filesystem.js +4 -1
  146. package/dist/runtime/server/providers/index.d.ts +3 -1
  147. package/dist/runtime/server/providers/index.js +2 -2
  148. package/dist/runtime/server/query-api.js +2 -1
  149. package/dist/runtime/server/query-executor.d.ts +3 -2
  150. package/dist/runtime/server/query-executor.js +13 -5
  151. package/dist/runtime/server/search.js +1 -1
  152. package/dist/runtime/server/sitemap-provider.js +40 -1
  153. package/dist/runtime/server/sitemap.js +3 -2
  154. package/dist/runtime/transformers/component-resolver.js +2 -1
  155. package/dist/runtime/utils/content-components.d.ts +0 -5
  156. package/dist/storage/content.d.ts +2 -9
  157. package/dist/storage/references.d.ts +1 -8
  158. package/dist/storage/references.js +9 -4
  159. package/dist/testing/provider-contract.js +3 -4
  160. package/dist/testing/provider-fixture.d.ts +3 -1
  161. package/dist/testing/provider-fixture.js +27 -14
  162. package/dist/types/config.d.ts +77 -0
  163. package/dist/types/config.js +23 -0
  164. package/dist/types/module.d.ts +15 -5
  165. package/dist/types/query-parts/collections +1 -0
  166. package/dist/types/query-parts/collections.d.ts +15 -0
  167. package/dist/types/query-parts/collections.js +0 -0
  168. package/dist/types/query-parts/public +1 -0
  169. package/dist/types/query-parts/public.d.ts +202 -0
  170. package/dist/types/query-parts/public.js +0 -0
  171. package/dist/types/query-parts/results +1 -0
  172. package/dist/types/query-parts/results.d.ts +95 -0
  173. package/dist/types/query-parts/results.js +0 -0
  174. package/dist/types/query-parts/transport +1 -0
  175. package/dist/types/query-parts/transport.d.ts +103 -0
  176. package/dist/types/query-parts/transport.js +0 -0
  177. package/dist/types/query.d.ts +4 -870
  178. package/dist/types/query.js +4 -0
  179. package/dist/types/runtime.d.ts +2 -2
  180. package/dist/types/virtual.d.ts +54 -0
  181. package/dist/types.d.mts +2 -2
  182. package/dist/web-types.json +1 -1
  183. package/package.json +5 -5
  184. package/dist/runtime/app/composables/web-socket +0 -1
  185. package/dist/runtime/app/composables/web-socket.d.ts +0 -3
  186. package/dist/runtime/app/composables/web-socket.js +0 -65
  187. package/dist/runtime/app/plugins/ws +0 -1
  188. package/dist/runtime/app/plugins/ws.js +0 -7
  189. /package/dist/runtime/app/plugins/{ws.d.ts → hot-reload.d.ts} +0 -0
package/dist/module.mjs CHANGED
@@ -1,103 +1,37 @@
1
- import { mkdirSync, writeFileSync } from 'node:fs';
2
- import { join as join$1, basename, resolve as resolve$1, relative as relative$1, dirname } from 'node:path';
3
- import { pathToFileURL } from 'node:url';
4
1
  import { addTypeTemplate, addImports, addServerImports, addComponentsDir, addPlugin, addServerHandler, defineNuxtModule, createResolver, useLogger, addTemplate } from '@nuxt/kit';
5
2
  import { defu } from 'defu';
3
+ import fs, { existsSync } from 'fs';
4
+ import { join, relative, resolve, isAbsolute } from 'pathe';
5
+ import jiti from 'jiti';
6
+ import { createRequire } from 'node:module';
7
+ import { pathToFileURL } from 'node:url';
8
+ import { genSafeVariableName, genImport } from 'knitwork';
6
9
  import { hash } from 'ohash';
7
- import { resolve, join, relative, isAbsolute } from 'pathe';
8
- import { withTrailingSlash } from 'ufo';
9
- import { normalizeContentConfigCollectionNames } from '../dist/types/config';
10
- export { defineCollection, defineContentConfig, reference } from '../dist/types/config';
11
10
  import 'unstorage/drivers/fs';
12
11
  import 'unstorage/drivers/http';
13
12
  import 'unstorage/drivers/github';
14
- import { WebSocketServer } from 'ws';
15
13
  import { consola } from 'consola';
16
- import fs, { existsSync } from 'fs';
17
- import jiti from 'jiti';
18
- import { createRequire } from 'node:module';
19
- import { genSafeVariableName, genImport } from 'knitwork';
20
- import { listen } from 'listhen';
21
14
  import { makeIgnored } from '../dist/core/content/ignore';
22
- import { readFile, readdir } from 'node:fs/promises';
23
- import { resolveContentSitemapSource, GINKO_SITEMAP_SOURCE_NAME } from '../dist/runtime/utils/sitemap-source';
24
15
  import { resolveCollectionI18nConfig } from '../dist/features/localization/config';
16
+ import { readFile, readdir } from 'node:fs/promises';
17
+ import { join as join$1, basename, resolve as resolve$1, relative as relative$1, dirname } from 'node:path';
25
18
  import { globby } from 'globby';
26
- import { transformContent } from '../dist/parsers/index';
27
- import { expandDataLocaleVariants } from '../dist/core/content/locale';
28
19
  import { buildContentGraph } from '../dist/core/content/graph';
20
+ import { expandDataLocaleVariants } from '../dist/core/content/locale';
29
21
  import { normalizeCollectionSources, normalizeCollectionExcludes } from '../dist/core/content/sources';
30
- import { resolveCollectionI18n, prefixPathWithLocale } from '../dist/features/localization/path';
31
22
  import { resolveCollection } from '../dist/core/content/collection';
23
+ import { transformContent } from '../dist/parsers/index';
24
+ import { resolveCollectionI18n, prefixPathWithLocale } from '../dist/features/localization/path';
25
+ import { resolveContentSitemapSource, GINKO_SITEMAP_SOURCE_NAME } from '../dist/runtime/utils/sitemap-source';
32
26
  import { normalizeRouteMounts, normalizeContentPath } from '../dist/core/content/path';
27
+ import { mkdirSync, writeFileSync } from 'node:fs';
28
+ import { withTrailingSlash } from 'ufo';
29
+ import { normalizeContentConfigCollectionNames } from '../dist/types/config';
30
+ export { agentMetadataFields, defineAgentAppPage, defineAgentMarkdownPolicy, defineAgentMetadataFields, defineAgentSection, defineCollection, defineContentConfig, reference } from '../dist/types/config';
33
31
  import { collectTopLevelReferenceFieldsByTarget } from '../dist/core/references/schema';
34
32
 
35
33
  const name = "@lupinum/ginko-content";
36
- const version = "0.1.2";
37
-
38
- const logger = consola.withTag("@lupinum/ginko-content");
39
- const CACHE_VERSION = 3;
40
- const MOUNT_PREFIX = "content:source:";
41
- function useContentMounts(nuxt, storages) {
42
- const key = (path, prefix = "") => `${MOUNT_PREFIX}${path.replace(/[/:]/g, "_")}${prefix.replace(/\//g, ":")}`;
43
- const baseDir = nuxt.options.future?.compatibilityVersion === 4 ? nuxt.options.rootDir : nuxt.options.srcDir;
44
- storages = Object.entries(storages).reduce((mounts, [name, storage]) => {
45
- mounts[key(storage.name || name, storage.prefix)] = storage;
46
- return mounts;
47
- }, {});
48
- const defaultStorage = key("content");
49
- if (!storages[defaultStorage]) {
50
- storages[defaultStorage] = {
51
- name: defaultStorage,
52
- driver: "fs",
53
- base: resolve(baseDir, "content")
54
- };
55
- }
56
- return storages;
57
- }
58
- function createWebSocket() {
59
- const wss = new WebSocketServer({ noServer: true });
60
- const serve = (req, socket = req.socket, head = "") => wss.handleUpgrade(req, socket, head, (client) => wss.emit("connection", client, req));
61
- const broadcast = (data) => {
62
- data = JSON.stringify(data);
63
- for (const client of wss.clients) {
64
- try {
65
- client.send(data);
66
- } catch {
67
- logger.debug("Skipping websocket client that is not ready to receive content refresh events.");
68
- }
69
- }
70
- };
71
- return {
72
- serve,
73
- broadcast,
74
- close: () => {
75
- wss.clients.forEach((client) => client.close());
76
- return new Promise((resolve2) => wss.close(resolve2));
77
- }
78
- };
79
- }
80
- function processMarkdownOptions(options) {
81
- const anchorLinks = typeof options.anchorLinks === "boolean" ? { depth: options.anchorLinks ? 6 : 0, exclude: [] } : { depth: 4, exclude: [1], ...options.anchorLinks };
82
- const plugins = options.plugins?.length ? options.plugins : [
83
- ["toc", { depth: 2, searchDepth: 2 }],
84
- "summary"
85
- ];
86
- return {
87
- ...options,
88
- anchorLinks,
89
- plugins: resolveMarkdownPlugins(plugins)
90
- };
91
- }
92
- function resolveMarkdownPlugins(plugins) {
93
- return (plugins || []).map((plugin) => {
94
- const [name, options] = Array.isArray(plugin) ? plugin : [plugin, {}];
95
- return {
96
- name,
97
- options
98
- };
99
- });
100
- }
34
+ const version = "0.1.3";
101
35
 
102
36
  const CONFIG_FILES = [
103
37
  "content.config.ts",
@@ -175,7 +109,7 @@ const createVirtualContentTemplates = (contentContext, nuxt, contentConfigPath,
175
109
  write: true,
176
110
  getContents: () => {
177
111
  const cache = contentContext.cache;
178
- if (!cache || cache === false) {
112
+ if (!cache) {
179
113
  return [
180
114
  "export const loadContentCacheAdapter = () => undefined",
181
115
  "export default {}"
@@ -211,14 +145,66 @@ const registerVirtualContentAliases = (nuxt, transformersTemplate, virtualConfig
211
145
  });
212
146
  };
213
147
 
148
+ consola.withTag("@lupinum/ginko-content");
149
+ const CACHE_VERSION = 3;
150
+ const MOUNT_PREFIX = "content:source:";
151
+ function useContentMounts(nuxt, storages) {
152
+ const key = (path, prefix = "") => `${MOUNT_PREFIX}${path.replace(/[/:]/g, "_")}${prefix.replace(/\//g, ":")}`;
153
+ const baseDir = nuxt.options.future?.compatibilityVersion === 4 ? nuxt.options.rootDir : nuxt.options.srcDir;
154
+ storages = Object.entries(storages).reduce((mounts, [name, storage]) => {
155
+ mounts[key(storage.name || name, storage.prefix)] = storage;
156
+ return mounts;
157
+ }, {});
158
+ const defaultStorage = key("content");
159
+ if (!storages[defaultStorage]) {
160
+ storages[defaultStorage] = {
161
+ name: defaultStorage,
162
+ driver: "fs",
163
+ base: resolve(baseDir, "content")
164
+ };
165
+ }
166
+ return storages;
167
+ }
168
+ function processMarkdownOptions(options) {
169
+ const anchorLinks = typeof options.anchorLinks === "boolean" ? { depth: options.anchorLinks ? 6 : 0, exclude: [] } : { depth: 4, exclude: [1], ...options.anchorLinks };
170
+ const plugins = options.plugins?.length ? options.plugins : [
171
+ ["toc", { depth: 2, searchDepth: 2 }],
172
+ "summary"
173
+ ];
174
+ return {
175
+ anchorLinks,
176
+ tags: options.tags || {},
177
+ image: options.image,
178
+ plugins: resolveMarkdownPlugins(plugins)
179
+ };
180
+ }
181
+ function resolveMarkdownPlugins(plugins) {
182
+ return (plugins || []).map((plugin) => {
183
+ const [name, options] = Array.isArray(plugin) ? plugin : [plugin, {}];
184
+ return {
185
+ name,
186
+ options
187
+ };
188
+ });
189
+ }
190
+
214
191
  const registerContentDevRuntime = (nuxt, options, contentContext) => {
215
192
  const isIgnored = makeIgnored(contentContext.ignores);
193
+ let viteServer;
194
+ if (options.watch !== false) {
195
+ nuxt.options.vite ||= {};
196
+ nuxt.options.vite.plugins ||= [];
197
+ nuxt.options.vite.plugins.push({
198
+ name: "ginko-content-hmr",
199
+ configureServer(server) {
200
+ viteServer = server;
201
+ }
202
+ });
203
+ }
216
204
  nuxt.hook("nitro:init", async (nitro) => {
217
- if (!options.watch || !options.watch.ws) {
205
+ if (options.watch === false) {
218
206
  return;
219
207
  }
220
- const ws = createWebSocket();
221
- const { server, url } = await listen(() => "Ginko", options.watch.ws);
222
208
  const unwatch = await nitro.storage.watch(async (event, key) => {
223
209
  if (!key.startsWith(MOUNT_PREFIX) || isIgnored(key)) {
224
210
  return;
@@ -227,56 +213,85 @@ const registerContentDevRuntime = (nuxt, options, contentContext) => {
227
213
  await nitro.storage.removeItem("cache:content:_manifest.json");
228
214
  await nitro.storage.removeItem("cache:content:_nav.json");
229
215
  await nitro.storage.removeItem("cache:content:_meta.json");
230
- ws.broadcast({ event, key });
216
+ await nitro.storage.removeItem(`cache:content:parsed:${key}`);
217
+ const payload = { event, key };
218
+ viteServer?.ws.send({
219
+ type: "custom",
220
+ event: "ginko-content:update",
221
+ data: payload
222
+ });
231
223
  });
232
224
  nitro.hooks.hook("close", async () => {
233
225
  await unwatch();
234
- await ws.close();
235
- await server.close();
236
226
  });
237
- server.on("upgrade", ws.serve);
238
- nitro.options.runtimeConfig.public.content.wsUrl = url.replace("http", "ws");
239
227
  await nitro.storage.removeItem("cache:content:_manifest.json");
240
228
  await nitro.storage.removeItem("cache:content:_nav.json");
241
229
  await nitro.storage.removeItem("cache:content:_meta.json");
242
230
  });
243
231
  };
244
232
 
233
+ const runtimeAppImportSpecs = [
234
+ { name: "getCollectionPath", as: "getCollectionPath", from: "./query/routes.js" },
235
+ { name: "useContentHead", as: "useContentHead", from: "./app/composables/head.js" },
236
+ { name: "useContentPage", as: "useContentPage", from: "./app/composables/use-content.js" },
237
+ { name: "useContentOne", as: "useContentOne", from: "./app/composables/use-content.js" },
238
+ { name: "useContentMany", as: "useContentMany", from: "./app/composables/use-content.js" },
239
+ { name: "useContentPagination", as: "useContentPagination", from: "./app/composables/use-content.js" },
240
+ { name: "useContentBacklinks", as: "useContentBacklinks", from: "./app/composables/use-content.js" },
241
+ { name: "useContentResolveOne", as: "useContentResolveOne", from: "./app/composables/use-content.js" },
242
+ { name: "useContentVariants", as: "useContentVariants", from: "./app/composables/use-content.js" },
243
+ { name: "useContentTree", as: "useContentTree", from: "./app/composables/use-content.js" },
244
+ { name: "useContentNavigation", as: "useContentNavigation", from: "./app/composables/use-content.js" },
245
+ { name: "useContentNeighbors", as: "useContentNeighbors", from: "./app/composables/use-content.js" },
246
+ { name: "useContentSwitchLocalePath", as: "useContentSwitchLocalePath", from: "./app/composables/route.js" },
247
+ { name: "useContentSearchData", as: "useContentSearchData", from: "./app/composables/search.js" },
248
+ { name: "useContentSearchResults", as: "useContentSearchResults", from: "./app/composables/search.js" },
249
+ { name: "querySiteData", as: "querySiteData", from: "./app/composables/site-data.js" }
250
+ ];
251
+ const runtimeServerImportSpecs = [
252
+ { name: "one", as: "one" },
253
+ { name: "many", as: "many" },
254
+ { name: "paginate", as: "paginate" },
255
+ { name: "backlinks", as: "backlinks" },
256
+ { name: "resolveOne", as: "resolveOne" },
257
+ { name: "variants", as: "variants" },
258
+ { name: "tree", as: "tree" },
259
+ { name: "neighbors", as: "neighbors" },
260
+ { name: "getCollectionPath", as: "getCollectionPath" },
261
+ { name: "queryCollectionsSitemapEntries", as: "queryCollectionsSitemapEntries" }
262
+ ];
263
+ const generatedContentServerValueNames = [
264
+ ...runtimeServerImportSpecs.map((spec) => spec.name),
265
+ "contentCacheHeaders",
266
+ "noopContentCache",
267
+ "vercelContentCache",
268
+ "clearContentCacheHint",
269
+ "collectContentCacheHint",
270
+ "getContentCacheHint",
271
+ "withContentCache",
272
+ "createContentProviderError"
273
+ ];
274
+ const generatedContentServerTypeSpecs = [
275
+ { local: "ContentCacheAdapter", exported: "ContentCacheAdapter" },
276
+ { local: "ContentCacheHint", exported: "ContentCacheHint" },
277
+ { local: "ContentCacheHintInput", exported: "ContentCacheHintInput" },
278
+ { local: "ContentCacheInvalidateInput", exported: "ContentCacheInvalidateInput" },
279
+ { local: "ContentProvider", exported: "ContentProvider" },
280
+ { local: "ContentProviderCapabilities", exported: "ContentProviderCapabilities" },
281
+ { local: "ContentProviderResult<T>", exported: "ContentProviderResult<T>" },
282
+ { local: "MaybeContentProviderResult<T>", exported: "MaybeContentProviderResult<T>" },
283
+ { local: "ContentProviderErrorCode", exported: "ContentProviderErrorCode" },
284
+ { local: "VercelContentCacheOptions", exported: "VercelContentCacheOptions" }
285
+ ];
245
286
  const registerRuntimeImports = (resolveRuntimeModule) => {
246
- addImports([
247
- { name: "getCollectionPath", as: "getCollectionPath", from: resolveRuntimeModule("./query/routes.js") },
248
- { name: "useContentHead", as: "useContentHead", from: resolveRuntimeModule("./app/composables/head.js") },
249
- // Vue composables — reactive wrappers over the same options shape.
250
- { name: "useContentPage", as: "useContentPage", from: resolveRuntimeModule("./app/composables/use-content.js") },
251
- { name: "useContentOne", as: "useContentOne", from: resolveRuntimeModule("./app/composables/use-content.js") },
252
- { name: "useContentMany", as: "useContentMany", from: resolveRuntimeModule("./app/composables/use-content.js") },
253
- { name: "useContentPagination", as: "useContentPagination", from: resolveRuntimeModule("./app/composables/use-content.js") },
254
- { name: "useContentBacklinks", as: "useContentBacklinks", from: resolveRuntimeModule("./app/composables/use-content.js") },
255
- { name: "useContentResolveOne", as: "useContentResolveOne", from: resolveRuntimeModule("./app/composables/use-content.js") },
256
- { name: "useContentVariants", as: "useContentVariants", from: resolveRuntimeModule("./app/composables/use-content.js") },
257
- { name: "useContentTree", as: "useContentTree", from: resolveRuntimeModule("./app/composables/use-content.js") },
258
- { name: "useContentNavigation", as: "useContentNavigation", from: resolveRuntimeModule("./app/composables/use-content.js") },
259
- { name: "useContentNeighbors", as: "useContentNeighbors", from: resolveRuntimeModule("./app/composables/use-content.js") },
260
- { name: "useContentLocaleSwitch", as: "useContentLocaleSwitch", from: resolveRuntimeModule("./app/composables/use-content.js") },
261
- { name: "useContentSwitchLocalePath", as: "useContentSwitchLocalePath", from: resolveRuntimeModule("./app/composables/route.js") },
262
- // Search & site data — keep the headless useContentSearch export explicit
263
- // because Nuxt UI owns the same auto-import name.
264
- { name: "useContentSearchData", as: "useContentSearchData", from: resolveRuntimeModule("./app/composables/search.js") },
265
- { name: "useContentSearchResults", as: "useContentSearchResults", from: resolveRuntimeModule("./app/composables/search.js") },
266
- { name: "querySiteData", as: "querySiteData", from: resolveRuntimeModule("./app/composables/site-data.js") }
267
- ]);
268
- addServerImports([
269
- { name: "one", as: "one", from: resolveRuntimeModule("./server/index.js") },
270
- { name: "many", as: "many", from: resolveRuntimeModule("./server/index.js") },
271
- { name: "paginate", as: "paginate", from: resolveRuntimeModule("./server/index.js") },
272
- { name: "backlinks", as: "backlinks", from: resolveRuntimeModule("./server/index.js") },
273
- { name: "resolveOne", as: "resolveOne", from: resolveRuntimeModule("./server/index.js") },
274
- { name: "variants", as: "variants", from: resolveRuntimeModule("./server/index.js") },
275
- { name: "tree", as: "tree", from: resolveRuntimeModule("./server/index.js") },
276
- { name: "neighbors", as: "neighbors", from: resolveRuntimeModule("./server/index.js") },
277
- { name: "getCollectionPath", as: "getCollectionPath", from: resolveRuntimeModule("./server/index.js") },
278
- { name: "queryCollectionsSitemapEntries", as: "queryCollectionsSitemapEntries", from: resolveRuntimeModule("./server/index.js") }
279
- ]);
287
+ addImports(runtimeAppImportSpecs.map((spec) => ({
288
+ ...spec,
289
+ from: resolveRuntimeModule(spec.from)
290
+ })));
291
+ addServerImports(runtimeServerImportSpecs.map((spec) => ({
292
+ ...spec,
293
+ from: resolveRuntimeModule("./server/index.js")
294
+ })));
280
295
  };
281
296
  const registerRuntimeComponents = (resolve) => {
282
297
  addComponentsDir({
@@ -332,11 +347,11 @@ const registerContentI18nTemplate = (addTemplateImpl, hasNuxtI18nModule) => {
332
347
  ].join("\n")
333
348
  });
334
349
  };
335
- const registerUserContentComponents = async (nuxt, resolve) => {
350
+ const registerUserContentComponents = async (nuxt, _resolve) => {
336
351
  const layers = [...nuxt.options._layers];
337
352
  for (const layer of layers) {
338
353
  const srcDir = layer.config.srcDir;
339
- const globalComponents = resolve(srcDir, "components/content");
354
+ const globalComponents = join(srcDir, "components/content");
340
355
  const dirStat = await fs.promises.stat(globalComponents).catch((error) => {
341
356
  if (error.code === "ENOENT") {
342
357
  return null;
@@ -362,6 +377,12 @@ const registerGeneratedTypes = (contentConfigPath, resolveRuntimeModuleRoot, col
362
377
  const key = JSON.stringify(name);
363
378
  return ` ${key}: StrictParsedContent & __GeneratedCollectionSchema<__ContentCollectionExport<${key}>>`;
364
379
  });
380
+ const contentServerValueDeclarations = generatedContentServerValueNames.map(
381
+ (name) => ` const ${name}: typeof import('${resolveRuntimeModuleRoot("./server")}').${name}`
382
+ );
383
+ const contentServerTypeDeclarations = generatedContentServerTypeSpecs.map(
384
+ (spec) => ` type ${spec.local} = import('${resolveRuntimeModuleRoot("./server")}').${spec.exported}`
385
+ );
365
386
  return addTypeTemplate({
366
387
  filename: "types/content.d.ts",
367
388
  getContents: () => [
@@ -418,42 +439,36 @@ const registerGeneratedTypes = (contentConfigPath, resolveRuntimeModuleRoot, col
418
439
  " interface ContentCollectionI18nMap extends Pick<__GeneratedContentCollectionMap, __GeneratedI18nCollectionNames> {}",
419
440
  "}",
420
441
  "declare module '#content/server' {",
421
- ` const one: typeof import('${resolveRuntimeModuleRoot("./server")}').one`,
422
- ` const many: typeof import('${resolveRuntimeModuleRoot("./server")}').many`,
423
- ` const paginate: typeof import('${resolveRuntimeModuleRoot("./server")}').paginate`,
424
- ` const backlinks: typeof import('${resolveRuntimeModuleRoot("./server")}').backlinks`,
425
- ` const resolveOne: typeof import('${resolveRuntimeModuleRoot("./server")}').resolveOne`,
426
- ` const variants: typeof import('${resolveRuntimeModuleRoot("./server")}').variants`,
427
- ` const tree: typeof import('${resolveRuntimeModuleRoot("./server")}').tree`,
428
- ` const neighbors: typeof import('${resolveRuntimeModuleRoot("./server")}').neighbors`,
429
- ` const queryCollectionsSitemapEntries: typeof import('${resolveRuntimeModuleRoot("./server")}').queryCollectionsSitemapEntries`,
430
- ` const contentCacheHeaders: typeof import('${resolveRuntimeModuleRoot("./server")}').contentCacheHeaders`,
431
- ` const noopContentCache: typeof import('${resolveRuntimeModuleRoot("./server")}').noopContentCache`,
432
- ` const vercelContentCache: typeof import('${resolveRuntimeModuleRoot("./server")}').vercelContentCache`,
433
- ` const clearContentCacheHint: typeof import('${resolveRuntimeModuleRoot("./server")}').clearContentCacheHint`,
434
- ` const collectContentCacheHint: typeof import('${resolveRuntimeModuleRoot("./server")}').collectContentCacheHint`,
435
- ` const getContentCacheHint: typeof import('${resolveRuntimeModuleRoot("./server")}').getContentCacheHint`,
436
- ` const withContentCache: typeof import('${resolveRuntimeModuleRoot("./server")}').withContentCache`,
437
- ` const createContentProviderError: typeof import('${resolveRuntimeModuleRoot("./server")}').createContentProviderError`,
438
- ` type ContentCacheAdapter = import('${resolveRuntimeModuleRoot("./server")}').ContentCacheAdapter`,
439
- ` type ContentCacheHint = import('${resolveRuntimeModuleRoot("./server")}').ContentCacheHint`,
440
- ` type ContentCacheHintInput = import('${resolveRuntimeModuleRoot("./server")}').ContentCacheHintInput`,
441
- ` type ContentCacheInvalidateInput = import('${resolveRuntimeModuleRoot("./server")}').ContentCacheInvalidateInput`,
442
- ` type ContentProvider = import('${resolveRuntimeModuleRoot("./server")}').ContentProvider`,
443
- ` type ContentProviderCapabilities = import('${resolveRuntimeModuleRoot("./server")}').ContentProviderCapabilities`,
444
- ` type ContentProviderResult<T> = import('${resolveRuntimeModuleRoot("./server")}').ContentProviderResult<T>`,
445
- ` type MaybeContentProviderResult<T> = import('${resolveRuntimeModuleRoot("./server")}').MaybeContentProviderResult<T>`,
446
- ` type ContentProviderErrorCode = import('${resolveRuntimeModuleRoot("./server")}').ContentProviderErrorCode`,
447
- ` type VercelContentCacheOptions = import('${resolveRuntimeModuleRoot("./server")}').VercelContentCacheOptions`,
442
+ ...contentServerValueDeclarations,
443
+ ...contentServerTypeDeclarations,
448
444
  "}"
449
445
  ].filter(Boolean).join("\n")
450
446
  });
451
447
  };
452
448
  const registerDevRuntimePlugin = (resolveRuntimeModule) => {
453
- addPlugin(resolveRuntimeModule("./app/plugins/ws.js"));
449
+ addPlugin(resolveRuntimeModule("./app/plugins/hot-reload.js"));
450
+ };
451
+
452
+ const normalizeAgentRouteOptions = (options) => {
453
+ if (options.agent === false) {
454
+ return {
455
+ routes: false,
456
+ linkHeaders: false,
457
+ markdownNegotiation: false,
458
+ prerender: false
459
+ };
460
+ }
461
+ return {
462
+ routes: options.agent?.routes !== false,
463
+ linkHeaders: options.agent?.linkHeaders !== false,
464
+ markdownNegotiation: options.agent?.markdownNegotiation !== false,
465
+ prerender: options.agent?.prerender !== false
466
+ };
454
467
  };
455
468
 
456
469
  const registerContentServerHandlers = (nuxt, options, resolveRuntimeModule, buildIntegrity) => {
470
+ const revalidate = options.revalidate === false ? void 0 : options.revalidate;
471
+ const sitemap = options.sitemap === false ? false : options.sitemap === true ? { path: "/sitemap" } : options.sitemap;
457
472
  addServerHandler({
458
473
  method: "get",
459
474
  route: `${options.api.baseURL}/query/**:params`,
@@ -474,7 +489,7 @@ const registerContentServerHandlers = (nuxt, options, resolveRuntimeModule, buil
474
489
  route: `${options.api.baseURL}/site-data`,
475
490
  handler: resolveRuntimeModule("./server/api/site-data.js")
476
491
  });
477
- if (options.revalidate && options.revalidate !== false && options.revalidate.token) {
492
+ if (revalidate?.token) {
478
493
  addServerHandler({
479
494
  method: "post",
480
495
  route: `${options.api.baseURL}/revalidate`,
@@ -486,10 +501,10 @@ const registerContentServerHandlers = (nuxt, options, resolveRuntimeModule, buil
486
501
  route: nuxt.options.dev ? `${options.api.baseURL}/cache.json` : `${options.api.baseURL}/cache.${buildIntegrity}.json`,
487
502
  handler: resolveRuntimeModule("./server/api/cache.js")
488
503
  });
489
- if (options.sitemap !== false) {
504
+ if (sitemap !== false) {
490
505
  addServerHandler({
491
506
  method: "get",
492
- route: `${options.api.baseURL}${options.sitemap.path || "/sitemap"}`,
507
+ route: `${options.api.baseURL}${sitemap.path || "/sitemap"}`,
493
508
  handler: resolveRuntimeModule("./server/api/sitemap.js")
494
509
  });
495
510
  }
@@ -505,6 +520,46 @@ const registerContentServerHandlers = (nuxt, options, resolveRuntimeModule, buil
505
520
  handler: resolveRuntimeModule("./server/api/navigation.js")
506
521
  });
507
522
  }
523
+ const agent = normalizeAgentRouteOptions(options);
524
+ if (agent.routes) {
525
+ addServerHandler({
526
+ method: "get",
527
+ route: "/llms.txt",
528
+ handler: resolveRuntimeModule("./server/api/agent-llms.js")
529
+ });
530
+ addServerHandler({
531
+ method: "get",
532
+ route: "/llms-full.txt",
533
+ handler: resolveRuntimeModule("./server/api/agent-llms-full.js")
534
+ });
535
+ addServerHandler({
536
+ method: "get",
537
+ route: "/:locale/llms.txt",
538
+ handler: resolveRuntimeModule("./server/api/agent-llms.js")
539
+ });
540
+ addServerHandler({
541
+ method: "get",
542
+ route: "/:locale/llms-full.txt",
543
+ handler: resolveRuntimeModule("./server/api/agent-llms-full.js")
544
+ });
545
+ addServerHandler({
546
+ method: "get",
547
+ route: "/raw/**:slug",
548
+ handler: resolveRuntimeModule("./server/api/agent-raw.js")
549
+ });
550
+ }
551
+ if (agent.markdownNegotiation) {
552
+ addServerHandler({
553
+ middleware: true,
554
+ handler: resolveRuntimeModule("./server/middleware/agent-markdown.js")
555
+ });
556
+ }
557
+ if (agent.linkHeaders) {
558
+ addServerHandler({
559
+ middleware: true,
560
+ handler: resolveRuntimeModule("./server/middleware/agent-link-headers.js")
561
+ });
562
+ }
508
563
  };
509
564
  const registerContentSearchServerHandlers = (apiBaseURL, search, resolveRuntimeModule) => {
510
565
  const searchBaseURL = search.apiBaseURL || `${apiBaseURL.replace(/\/$/, "")}/search`;
@@ -523,12 +578,58 @@ const registerContentSearchServerHandlers = (apiBaseURL, search, resolveRuntimeM
523
578
  }
524
579
  };
525
580
 
526
- const SITEMAP_INDEX = "sitemap_index.xml";
527
- const assert = (condition, message) => {
528
- if (!condition) {
529
- throw new Error(message);
530
- }
581
+ const registerContentComponentsTemplate = (addTemplateImpl) => {
582
+ addTemplateImpl({
583
+ filename: "content-components.mjs",
584
+ getContents({ app, nuxt }) {
585
+ const componentsMap = app.components.filter((component) => {
586
+ if (component.island) {
587
+ return false;
588
+ }
589
+ if (component.filePath.endsWith(".css")) {
590
+ return false;
591
+ }
592
+ if (component.pascalName === "ClientOnly" || component.filePath.includes("nuxt/dist/") || component.filePath.includes("NuxtWelcome")) {
593
+ return false;
594
+ }
595
+ return true;
596
+ }).reduce((map, component) => {
597
+ const importPath = isAbsolute(component.filePath) ? "./" + relative(nuxt.options.buildDir, component.filePath).replace(/\b\.(?!vue)\w+$/g, "") : component.filePath.replace(/\b\.(?!vue)\w+$/g, "");
598
+ map[component.pascalName] = map[component.pascalName] || [
599
+ component.pascalName,
600
+ importPath,
601
+ component.global,
602
+ component.export || "default"
603
+ ];
604
+ return map;
605
+ }, {});
606
+ const componentsList = Object.values(componentsMap);
607
+ const globalComponents = componentsList.filter((component) => component[2]).map((component) => component[0]);
608
+ const localComponents = componentsList.filter((component) => !component[2]);
609
+ return [
610
+ "const pickExport = (mod, exportName, componentName, path) => {",
611
+ " const resolved = exportName === 'default' ? mod?.default || mod : mod?.[exportName]",
612
+ " if (!resolved) {",
613
+ ' throw new Error(`[nuxt-content] Missing export "${exportName}" for component "${componentName}" in "${path}".`)',
614
+ " }",
615
+ " return resolved",
616
+ "}",
617
+ "export const localComponentLoaders = {",
618
+ ...localComponents.map(([pascalName, path, , exp]) => {
619
+ const pathLiteral = JSON.stringify(path);
620
+ const exportLiteral = JSON.stringify(exp);
621
+ const nameLiteral = JSON.stringify(pascalName);
622
+ return ` ${pascalName}: () => import(${pathLiteral}).then(mod => pickExport(mod, ${exportLiteral}, ${nameLiteral}, ${pathLiteral})),`;
623
+ }),
624
+ "}",
625
+ `export const globalComponents = ${JSON.stringify(globalComponents)}`,
626
+ `export const localComponents = ${JSON.stringify(localComponents.map((component) => component[0]))}`
627
+ ].join("\n");
628
+ }
629
+ });
531
630
  };
631
+
632
+ const SITEMAP_INDEX = "sitemap_index.xml";
532
633
  const countTag = (xml, tag) => (xml.match(new RegExp(`<${tag}>`, "g")) || []).length;
533
634
  const extractLocValues = (xml) => {
534
635
  return Array.from(xml.matchAll(/<loc>([^<]+)<\/loc>/g), (match) => match[1]);
@@ -595,10 +696,9 @@ const discoverSitemapsFromDisk = async (outputPublicDir) => {
595
696
  }
596
697
  const rootSitemapPath = join$1(outputPublicDir, "sitemap.xml");
597
698
  const rootSitemapXml = await readFile(rootSitemapPath, "utf8");
598
- assert(
599
- countTag(rootSitemapXml, "url") > 0 || rootSitemapXml.includes("<urlset"),
600
- `Content sitemap assertion failed: no child sitemap files were discovered in ${indexPath} or ${sitemapDir}, and ${rootSitemapPath} is not a urlset sitemap.`
601
- );
699
+ if (!(countTag(rootSitemapXml, "url") > 0 || rootSitemapXml.includes("<urlset"))) {
700
+ throw new Error(`Content sitemap assertion failed: no child sitemap files were discovered in ${indexPath} or ${sitemapDir}, and ${rootSitemapPath} is not a urlset sitemap.`);
701
+ }
602
702
  return [{
603
703
  name: "sitemap",
604
704
  path: rootSitemapPath,
@@ -633,7 +733,9 @@ async function assertGeneratedSitemaps({
633
733
  logger
634
734
  }) {
635
735
  const discoveredSitemaps = targets || (outputPublicDir ? await discoverSitemapsFromDisk(outputPublicDir) : []);
636
- assert(discoveredSitemaps.length > 0, "Content sitemap assertion failed: no sitemap targets were available for validation.");
736
+ if (discoveredSitemaps.length === 0) {
737
+ throw new Error("Content sitemap assertion failed: no sitemap targets were available for validation.");
738
+ }
637
739
  const targetMap = new Map(discoveredSitemaps.map((target) => [target.name, target]));
638
740
  const failures = [];
639
741
  for (const sitemapName of Object.keys(options.sitemaps)) {
@@ -675,7 +777,7 @@ async function assertGeneratedSitemaps({
675
777
  failures.push([
676
778
  `- Placeholder sitemap URLs found: ${placeholderSiteUrls.join(", ")}`,
677
779
  "Expected production URLs in generated sitemap loc values.",
678
- "Set site.url or runtimeConfig.public.siteUrl to the deployed origin for production release checks."
780
+ "Set site.url or runtimeConfig.public.content.siteUrl to the deployed origin for production release checks."
679
781
  ].join(" "));
680
782
  }
681
783
  }
@@ -707,283 +809,14 @@ const createSitemapAssertionTargetsFromPrerenderedSitemaps = (sitemaps) => {
707
809
  }
708
810
  return true;
709
811
  });
710
- assert(targets.length > 0, "Content sitemap assertion failed: no sitemap urlsets were available from sitemap:prerender:done.");
812
+ if (targets.length === 0) {
813
+ throw new Error("Content sitemap assertion failed: no sitemap urlsets were available from sitemap:prerender:done.");
814
+ }
711
815
  return targets;
712
816
  };
713
817
 
714
- function hasNuxtModule(modules = [], name) {
715
- return modules.some((entry) => {
716
- if (typeof entry === "string") {
717
- return entry === name;
718
- }
719
- if (Array.isArray(entry) && typeof entry[0] === "string") {
720
- return entry[0] === name;
721
- }
722
- return false;
723
- });
724
- }
725
- function hasNuxtI18nModule(modules = []) {
726
- return hasNuxtModule(modules, "@nuxtjs/i18n");
727
- }
728
- function hasNuxtSitemapModule(modules = []) {
729
- return hasNuxtModule(modules, "@nuxtjs/sitemap");
730
- }
731
- function configureNuxtSitemapSource(nuxt, apiBaseURL, sitemapPath = "/sitemap") {
732
- if (!hasNuxtSitemapModule(nuxt.options.modules)) {
733
- return;
734
- }
735
- const source = {
736
- context: {
737
- name: GINKO_SITEMAP_SOURCE_NAME
738
- },
739
- fetch: resolveContentSitemapSource(apiBaseURL, sitemapPath)
740
- };
741
- const sitemap = nuxt.options.sitemap ??= {};
742
- const sources = Array.isArray(sitemap.sources) ? sitemap.sources : [];
743
- sitemap.sources = [
744
- ...sources.filter((item) => {
745
- if (typeof item === "string") return item !== source.fetch;
746
- return item?.fetch !== source.fetch && item?.context?.name !== GINKO_SITEMAP_SOURCE_NAME;
747
- }),
748
- source
749
- ];
750
- }
751
- function resolveNuxtSitemapPrerenderRoutes(nuxt) {
752
- if (!hasNuxtSitemapModule(nuxt.options.modules)) {
753
- return [];
754
- }
755
- const nuxtSitemap = nuxt.options.sitemap;
756
- if (nuxtSitemap?.sitemaps === false) {
757
- return ["/sitemap.xml"];
758
- }
759
- const nuxtI18n = nuxt.options.i18n || {};
760
- if (!hasNuxtI18nModule(nuxt.options.modules) || !Array.isArray(nuxtI18n.locales) || nuxtI18n.locales.length === 0) {
761
- return ["/sitemap.xml"];
762
- }
763
- const childRoutes = nuxtI18n.locales.map((locale) => typeof locale === "string" ? locale : locale.language || locale.code).filter(Boolean).map((locale) => `/__sitemap__/${locale}.xml`);
764
- return Array.from(/* @__PURE__ */ new Set(["/sitemap.xml", "/sitemap_index.xml", ...childRoutes]));
765
- }
766
- function resolveModuleI18nOptions(options, nuxt) {
767
- if (options.i18n === false) {
768
- return {
769
- locales: [],
770
- defaultLocale: void 0,
771
- fallback: {},
772
- translatedSlugs: false,
773
- strictTranslatedSlugs: false
774
- };
775
- }
776
- const moduleI18n = options.i18n === true ? {} : options.i18n || {};
777
- const nuxtI18n = nuxt.options.i18n || {};
778
- const nuxtLocales = Array.isArray(nuxtI18n.locales) ? nuxtI18n.locales.map((locale) => typeof locale === "string" ? locale : locale.code).filter(Boolean) : [];
779
- const locales = Array.from(new Set([
780
- moduleI18n.defaultLocale,
781
- nuxtI18n.defaultLocale,
782
- ...moduleI18n.locales || [],
783
- ...nuxtLocales
784
- ].filter(Boolean)));
785
- const defaultLocale = moduleI18n.defaultLocale || nuxtI18n.defaultLocale || locales[0];
786
- return {
787
- locales,
788
- defaultLocale,
789
- fallback: defu({}, moduleI18n.fallback),
790
- translatedSlugs: moduleI18n.translatedSlugs ?? false,
791
- strictTranslatedSlugs: moduleI18n.strictTranslatedSlugs ?? false
792
- };
793
- }
794
- function normalizeSitemapOptions(options) {
795
- if (options.sitemap === false) {
796
- return false;
797
- }
798
- const sitemap = options.sitemap === true ? {} : options.sitemap;
799
- return {
800
- path: sitemap.path || "/sitemap",
801
- include: sitemap.include,
802
- exclude: sitemap.exclude || [],
803
- includeDrafts: sitemap.includeDrafts,
804
- assert: normalizeContentSitemapAssertOptions(sitemap.assert)
805
- };
806
- }
807
- function normalizeSearchOptions(options) {
808
- if (options.search === false) {
809
- return false;
810
- }
811
- return {
812
- engine: options.search?.engine || "minisearch",
813
- ignoredTags: options.search?.ignoredTags || ["script", "style", "pre"],
814
- filterQuery: options.search?.filterQuery || { _draft: false, _partial: false },
815
- collections: options.search?.collections,
816
- extraFields: options.search?.extraFields || [],
817
- apiBaseURL: options.search?.apiBaseURL,
818
- minisearch: normalizeMiniSearchOptions(options.search?.minisearch)
819
- };
820
- }
821
- const defaultMiniSearchOptions = {
822
- fields: ["title", "content", "headings"],
823
- storeFields: ["path", "title", "excerpt", "anchor", "locale", "collection"],
824
- boost: {
825
- title: 4,
826
- headings: 2,
827
- content: 1
828
- },
829
- fuzzy: 0.2,
830
- prefix: true
831
- };
832
- const requiredMiniSearchStoreFields = ["path", "title", "excerpt"];
833
- function normalizeMiniSearchOptions(options = {}) {
834
- const fields = options?.fields?.filter((field) => typeof field === "string" && field.length > 0);
835
- const storeFields = options?.storeFields?.filter((field) => typeof field === "string" && field.length > 0);
836
- const boost = Object.fromEntries(
837
- Object.entries(options?.boost || {}).filter((entry) => typeof entry[0] === "string" && entry[0].length > 0 && typeof entry[1] === "number" && Number.isFinite(entry[1]))
838
- );
839
- const resolvedFields = fields?.length ? fields : [...defaultMiniSearchOptions.fields];
840
- const resolvedStoreFields = storeFields?.length ? storeFields : defaultMiniSearchOptions.storeFields;
841
- return {
842
- fields: resolvedFields,
843
- storeFields: Array.from(/* @__PURE__ */ new Set([...requiredMiniSearchStoreFields, ...resolvedStoreFields])),
844
- boost: Object.keys(boost).length ? boost : { ...defaultMiniSearchOptions.boost },
845
- fuzzy: typeof options?.fuzzy === "boolean" || typeof options?.fuzzy === "number" ? options.fuzzy : defaultMiniSearchOptions.fuzzy,
846
- prefix: typeof options?.prefix === "boolean" ? options.prefix : defaultMiniSearchOptions.prefix
847
- };
848
- }
849
- function createSearchRuntimeConfig(search, apiBaseURL) {
850
- const resolvedApiBaseURL = search.apiBaseURL || `${apiBaseURL.replace(/\/$/, "")}/search`;
851
- return {
852
- apiBaseURL: resolvedApiBaseURL,
853
- indexURL: `${resolvedApiBaseURL.replace(/\/$/, "")}/index.json`,
854
- engine: search.engine || "minisearch",
855
- minisearch: normalizeMiniSearchOptions(search.minisearch)
856
- };
857
- }
858
-
859
- const resolveNuxtSiteUrl = (nuxt) => {
860
- const publicRuntime = nuxt.options.runtimeConfig.public;
861
- const configuredSiteUrl = publicRuntime.siteUrl;
862
- if (typeof configuredSiteUrl === "string" && configuredSiteUrl.length > 0) {
863
- return configuredSiteUrl;
864
- }
865
- const nuxtSite = nuxt.options.site;
866
- return typeof nuxtSite?.url === "string" && nuxtSite.url.length > 0 ? nuxtSite.url : void 0;
867
- };
868
- const applyContentRuntimeConfig = (nuxt, options, contentContext, runtimeCollections, buildIntegrity, cacheIntegrity) => {
869
- const searchRuntime = contentContext.search === false ? false : {
870
- apiBaseURL: contentContext.search.apiBaseURL || `${options.api.baseURL.replace(/\/$/, "")}/search`,
871
- indexURL: `${(contentContext.search.apiBaseURL || `${options.api.baseURL.replace(/\/$/, "")}/search`).replace(/\/$/, "")}/index.json`,
872
- engine: contentContext.search.engine || "minisearch",
873
- minisearch: normalizeMiniSearchOptions(contentContext.search.minisearch)
874
- };
875
- const contentRuntime = defu(nuxt.options.runtimeConfig.public.content, {
876
- locales: contentContext.locales,
877
- provider: contentContext.provider || "filesystem",
878
- providers: contentContext.providers || {},
879
- defaultLocale: contentContext.defaultLocale || void 0,
880
- localeFallback: contentContext.localeFallback || {},
881
- translatedSlugs: contentContext.translatedSlugs ?? false,
882
- strictTranslatedSlugs: contentContext.strictTranslatedSlugs ?? false,
883
- collections: runtimeCollections,
884
- integrity: buildIntegrity,
885
- experimental: {
886
- stripQueryParameters: options.experimental.stripQueryParameters
887
- },
888
- respectPathCase: options.respectPathCase ?? false,
889
- api: {
890
- baseURL: options.api.baseURL
891
- },
892
- markdown: {
893
- plugins: contentContext.markdown.plugins,
894
- tags: contentContext.markdown.tags,
895
- anchorLinks: contentContext.markdown.anchorLinks,
896
- image: contentContext.markdown.image || "auto"
897
- },
898
- sitemap: contentContext.sitemap ? {
899
- path: contentContext.sitemap.path || "/sitemap",
900
- ...contentContext.sitemap.include?.length ? { include: contentContext.sitemap.include } : {},
901
- ...contentContext.sitemap.exclude?.length ? { exclude: contentContext.sitemap.exclude } : {},
902
- ...typeof contentContext.sitemap.includeDrafts === "boolean" ? { includeDrafts: contentContext.sitemap.includeDrafts } : {}
903
- } : false,
904
- search: searchRuntime,
905
- navigation: contentContext.navigation,
906
- wsUrl: "",
907
- contentHead: options.contentHead ?? true
908
- });
909
- const siteUrl = resolveNuxtSiteUrl(nuxt);
910
- if (siteUrl) {
911
- nuxt.options.runtimeConfig.public.siteUrl = siteUrl;
912
- }
913
- nuxt.options.runtimeConfig.public.content = contentRuntime;
914
- nuxt.options.runtimeConfig.content = defu(nuxt.options.runtimeConfig.content, {
915
- cacheVersion: CACHE_VERSION,
916
- cacheIntegrity,
917
- revalidate: options.revalidate && options.revalidate !== false && options.revalidate.token ? {
918
- token: options.revalidate.token,
919
- allowUnsigned: options.revalidate.allowUnsigned === true
920
- } : false,
921
- ...contentContext,
922
- collections: runtimeCollections
923
- });
924
- };
925
-
926
- const registerContentComponentsTemplate = (addTemplateImpl) => {
927
- addTemplateImpl({
928
- filename: "content-components.mjs",
929
- getContents({ app, nuxt }) {
930
- const componentsMap = app.components.filter((component) => {
931
- if (component.island) {
932
- return false;
933
- }
934
- if (component.filePath.endsWith(".css")) {
935
- return false;
936
- }
937
- if (component.pascalName === "ClientOnly" || component.filePath.includes("nuxt/dist/") || component.filePath.includes("NuxtWelcome")) {
938
- return false;
939
- }
940
- return true;
941
- }).reduce((map, component) => {
942
- const importPath = isAbsolute(component.filePath) ? "./" + relative(nuxt.options.buildDir, component.filePath).replace(/\b\.(?!vue)\w+$/g, "") : component.filePath.replace(/\b\.(?!vue)\w+$/g, "");
943
- map[component.pascalName] = map[component.pascalName] || [
944
- component.pascalName,
945
- importPath,
946
- component.global,
947
- component.export || "default"
948
- ];
949
- return map;
950
- }, {});
951
- const componentsList = Object.values(componentsMap);
952
- const globalComponents = componentsList.filter((component) => component[2]).map((component) => component[0]);
953
- const localComponents = componentsList.filter((component) => !component[2]);
954
- return [
955
- "const pickExport = (mod, exportName, componentName, path) => {",
956
- " const resolved = exportName === 'default' ? mod?.default || mod : mod?.[exportName]",
957
- " if (!resolved) {",
958
- ' throw new Error(`[nuxt-content] Missing export "${exportName}" for component "${componentName}" in "${path}".`)',
959
- " }",
960
- " return resolved",
961
- "}",
962
- "export const localComponentLoaders = {",
963
- ...localComponents.map(([pascalName, path, , exp]) => {
964
- const pathLiteral = JSON.stringify(path);
965
- const exportLiteral = JSON.stringify(exp);
966
- const nameLiteral = JSON.stringify(pascalName);
967
- return ` ${pascalName}: () => import(${pathLiteral}).then(mod => pickExport(mod, ${exportLiteral}, ${nameLiteral}, ${pathLiteral})),`;
968
- }),
969
- "}",
970
- `export const globalComponents = ${JSON.stringify(globalComponents)}`,
971
- `export const localComponents = ${JSON.stringify(localComponents.map((component) => component[0]))}`
972
- ].join("\n");
973
- }
974
- });
975
- };
976
-
977
- const appendHook = (hooks, name, handler) => {
978
- const existing = hooks[name];
979
- if (!existing) {
980
- hooks[name] = handler;
981
- return;
982
- }
983
- hooks[name] = Array.isArray(existing) ? [...existing, handler] : [existing, handler];
984
- };
985
- const resolveSitemapCollections = (collections, sitemap) => {
986
- if (!sitemap || sitemap === false) {
818
+ const resolveSitemapCollections = (collections, sitemap) => {
819
+ if (!sitemap) {
987
820
  return [];
988
821
  }
989
822
  const include = sitemap.include?.length ? sitemap.include : Object.keys(collections);
@@ -997,7 +830,7 @@ const toTranslatedSlugSourcePattern = (source) => {
997
830
  return match ? `${match[1]}.*${match[2] || ""}` : segment;
998
831
  }).join("/");
999
832
  };
1000
- const parseCollectionFiles = async (rootDir, collections, collectionNames, contentContext) => {
833
+ const parseDerivedCollectionFiles = async (rootDir, collections, collectionNames, contentContext) => {
1001
834
  const contentDir = resolve$1(rootDir, "content");
1002
835
  const patterns = Array.from(new Set(
1003
836
  collectionNames.flatMap((name) => {
@@ -1028,7 +861,7 @@ const parseCollectionFiles = async (rootDir, collections, collectionNames, conte
1028
861
  for (const id of ids) {
1029
862
  const file = resolve$1(contentDir, id);
1030
863
  const content = await readFile(file, "utf8");
1031
- const normalizedId = `content:${relative$1(contentDir, file).replaceAll("\\", "/")}`;
864
+ const normalizedId = `content:${relative$1(contentDir, file).replace(/\\/g, "/")}`;
1032
865
  const parsed = await transformContent(normalizedId, content, {
1033
866
  markdown: contentContext.markdown,
1034
867
  yaml: contentContext.yaml,
@@ -1042,29 +875,36 @@ const parseCollectionFiles = async (rootDir, collections, collectionNames, conte
1042
875
  collectionResolver: (filePath) => resolveCollection(filePath, collections, contentContext.locales || [])
1043
876
  }
1044
877
  });
1045
- const collection = parsed._collection ? collections[parsed._collection] : void 0;
1046
- documents.push(...expandDataLocaleVariants(parsed, collection?.i18n));
878
+ const collection = typeof parsed._collection === "string" ? collections[parsed._collection] : void 0;
879
+ documents.push(...expandDataLocaleVariants(parsed, collection?.i18n === true ? void 0 : collection?.i18n));
1047
880
  }
1048
881
  return documents;
1049
882
  };
1050
- const collectPrerenderRoutes = async (rootDir, contentContext) => {
883
+ const collectDerivedDocuments = async (rootDir, contentContext) => {
1051
884
  const collections = contentContext.collections || {};
1052
- if (!contentContext.sitemap || contentContext.sitemap === false) {
1053
- return [];
1054
- }
1055
- const includeCollections = resolveSitemapCollections(collections, contentContext.sitemap);
1056
- if (!includeCollections.length) {
1057
- return [];
885
+ const includedCollections = resolveSitemapCollections(collections, contentContext.sitemap);
886
+ if (!includedCollections.length) {
887
+ return { collections, includedCollections, graph: buildContentGraph([], { locales: contentContext.locales, defaultLocale: contentContext.defaultLocale }) };
1058
888
  }
1059
- const documents = await parseCollectionFiles(rootDir, collections, includeCollections, contentContext);
889
+ const documents = await parseDerivedCollectionFiles(rootDir, collections, includedCollections, contentContext);
1060
890
  const graph = buildContentGraph(documents, {
1061
891
  locales: contentContext.locales,
1062
892
  defaultLocale: contentContext.defaultLocale
1063
893
  });
894
+ return { collections, includedCollections, graph };
895
+ };
896
+ const collectDerivedPrerenderRoutes = async (rootDir, contentContext) => {
897
+ if (!contentContext.sitemap) {
898
+ return [];
899
+ }
900
+ const { includedCollections, graph } = await collectDerivedDocuments(rootDir, contentContext);
901
+ if (!includedCollections.length) {
902
+ return [];
903
+ }
1064
904
  const routes = /* @__PURE__ */ new Set();
1065
- for (const collection of includeCollections) {
905
+ for (const collection of includedCollections) {
1066
906
  const { defaultLocale } = resolveCollectionI18n(collection, contentContext);
1067
- const includeDrafts = contentContext.sitemap && contentContext.sitemap !== false ? Boolean(contentContext.sitemap.includeDrafts) : false;
907
+ const includeDrafts = Boolean(contentContext.sitemap.includeDrafts);
1068
908
  const seenCanonicalKeys = /* @__PURE__ */ new Set();
1069
909
  for (const contentId of graph.byCollection[collection] || []) {
1070
910
  const document = graph.byId[contentId];
@@ -1091,18 +931,12 @@ const collectPrerenderRoutes = async (rootDir, contentContext) => {
1091
931
  return Array.from(routes).sort();
1092
932
  };
1093
933
  const collectSitemapCollectionRouteCounts = async (rootDir, contentContext) => {
1094
- const collections = contentContext.collections || {};
1095
- const includedCollections = resolveSitemapCollections(collections, contentContext.sitemap);
934
+ const { includedCollections, graph } = await collectDerivedDocuments(rootDir, contentContext);
1096
935
  if (!includedCollections.length) {
1097
936
  return {};
1098
937
  }
1099
- const documents = await parseCollectionFiles(rootDir, collections, includedCollections, contentContext);
1100
- const graph = buildContentGraph(documents, {
1101
- locales: contentContext.locales,
1102
- defaultLocale: contentContext.defaultLocale
1103
- });
1104
938
  const counts = Object.fromEntries(includedCollections.map((collection) => [collection, 0]));
1105
- const includeDrafts = contentContext.sitemap && contentContext.sitemap !== false ? Boolean(contentContext.sitemap.includeDrafts) : false;
939
+ const includeDrafts = contentContext.sitemap ? Boolean(contentContext.sitemap.includeDrafts) : false;
1106
940
  for (const collection of includedCollections) {
1107
941
  const seenCanonicalKeys = /* @__PURE__ */ new Set();
1108
942
  for (const contentId of graph.byCollection[collection] || []) {
@@ -1126,12 +960,32 @@ const collectSitemapCollectionRouteCounts = async (rootDir, contentContext) => {
1126
960
  }
1127
961
  return counts;
1128
962
  };
963
+
964
+ const appendHook = (hooks, name, handler) => {
965
+ const existing = hooks[name];
966
+ if (!existing) {
967
+ hooks[name] = handler;
968
+ return;
969
+ }
970
+ hooks[name] = Array.isArray(existing) ? [...existing, handler] : [existing, handler];
971
+ };
1129
972
  const registerContentNitroIntegrationHooks = (nitroConfig, options, contentContext) => {
1130
- const usesFilesystemProvider = !contentContext.provider || contentContext.provider === "filesystem";
973
+ const getHookContentContext = () => {
974
+ if (!options.resolveContentContext) {
975
+ return contentContext;
976
+ }
977
+ try {
978
+ return options.resolveContentContext();
979
+ } catch {
980
+ return contentContext;
981
+ }
982
+ };
1131
983
  nitroConfig.hooks ||= {};
1132
- if (contentContext.sitemap?.assert?.enabled) {
984
+ if (contentContext.sitemap && contentContext.sitemap.assert?.enabled) {
1133
985
  appendHook(nitroConfig.hooks, "compiled", async (nitro) => {
1134
- const assertOptions = contentContext.sitemap?.assert;
986
+ const hookContentContext = getHookContentContext();
987
+ const hookUsesFilesystemProvider = !hookContentContext.provider || hookContentContext.provider === "filesystem";
988
+ const assertOptions = hookContentContext.sitemap ? hookContentContext.sitemap.assert : void 0;
1135
989
  if (!assertOptions || !shouldRunSitemapAssertionOnCompiled(assertOptions, nitro)) {
1136
990
  return;
1137
991
  }
@@ -1139,11 +993,11 @@ const registerContentNitroIntegrationHooks = (nitroConfig, options, contentConte
1139
993
  await assertGeneratedSitemaps({
1140
994
  outputPublicDir: nitro.options.output.publicDir,
1141
995
  options: assertOptions,
1142
- collectionRouteCounts: usesFilesystemProvider ? await collectSitemapCollectionRouteCounts(options.rootDir, contentContext) : {},
996
+ collectionRouteCounts: hookUsesFilesystemProvider ? await collectSitemapCollectionRouteCounts(options.rootDir, hookContentContext) : {},
1143
997
  logger: nitro.logger
1144
998
  });
1145
999
  } catch (error) {
1146
- if (error?.code === "ENOENT") {
1000
+ if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
1147
1001
  return;
1148
1002
  }
1149
1003
  throw error;
@@ -1151,8 +1005,10 @@ const registerContentNitroIntegrationHooks = (nitroConfig, options, contentConte
1151
1005
  });
1152
1006
  }
1153
1007
  appendHook(nitroConfig.hooks, "prerender:routes", async (routes) => {
1154
- if (usesFilesystemProvider) {
1155
- for (const route of await collectPrerenderRoutes(options.rootDir, contentContext)) {
1008
+ const hookContentContext = getHookContentContext();
1009
+ const hookUsesFilesystemProvider = !hookContentContext.provider || hookContentContext.provider === "filesystem";
1010
+ if (hookUsesFilesystemProvider) {
1011
+ for (const route of await collectDerivedPrerenderRoutes(options.rootDir, hookContentContext)) {
1156
1012
  routes.add(route);
1157
1013
  }
1158
1014
  }
@@ -1163,10 +1019,155 @@ const registerContentNitroIntegrationHooks = (nitroConfig, options, contentConte
1163
1019
  });
1164
1020
  };
1165
1021
 
1166
- const getContentPageMeta = (page) => {
1167
- const content = page.meta?.content;
1168
- return content && typeof content === "object" ? content : void 0;
1169
- };
1022
+ function hasNuxtModule(modules = [], name) {
1023
+ return modules.some((entry) => {
1024
+ if (typeof entry === "string") {
1025
+ return entry === name;
1026
+ }
1027
+ if (Array.isArray(entry) && typeof entry[0] === "string") {
1028
+ return entry[0] === name;
1029
+ }
1030
+ return false;
1031
+ });
1032
+ }
1033
+ function hasNuxtI18nModule(modules = []) {
1034
+ return hasNuxtModule(modules, "@nuxtjs/i18n");
1035
+ }
1036
+ function hasNuxtSitemapModule(modules = []) {
1037
+ return hasNuxtModule(modules, "@nuxtjs/sitemap");
1038
+ }
1039
+ function configureNuxtSitemapSource(nuxt, apiBaseURL, sitemapPath = "/sitemap") {
1040
+ if (!hasNuxtSitemapModule(nuxt.options.modules)) {
1041
+ return;
1042
+ }
1043
+ const source = {
1044
+ context: {
1045
+ name: GINKO_SITEMAP_SOURCE_NAME
1046
+ },
1047
+ fetch: resolveContentSitemapSource(apiBaseURL, sitemapPath)
1048
+ };
1049
+ const sitemap = nuxt.options.sitemap ??= {};
1050
+ const sources = Array.isArray(sitemap.sources) ? sitemap.sources : [];
1051
+ sitemap.sources = [
1052
+ ...sources.filter((item) => {
1053
+ if (typeof item === "string") return item !== source.fetch;
1054
+ return item?.fetch !== source.fetch && item?.context?.name !== GINKO_SITEMAP_SOURCE_NAME;
1055
+ }),
1056
+ source
1057
+ ];
1058
+ }
1059
+ function resolveNuxtSitemapPrerenderRoutes(nuxt) {
1060
+ if (!hasNuxtSitemapModule(nuxt.options.modules)) {
1061
+ return [];
1062
+ }
1063
+ const nuxtSitemap = nuxt.options.sitemap;
1064
+ if (nuxtSitemap?.sitemaps === false) {
1065
+ return ["/sitemap.xml"];
1066
+ }
1067
+ const nuxtI18n = nuxt.options.i18n || {};
1068
+ if (!hasNuxtI18nModule(nuxt.options.modules) || !Array.isArray(nuxtI18n.locales) || nuxtI18n.locales.length === 0) {
1069
+ return ["/sitemap.xml"];
1070
+ }
1071
+ const childRoutes = nuxtI18n.locales.map((locale) => typeof locale === "string" ? locale : locale.language || locale.code).filter(Boolean).map((locale) => `/__sitemap__/${locale}.xml`);
1072
+ return Array.from(/* @__PURE__ */ new Set(["/sitemap.xml", "/sitemap_index.xml", ...childRoutes]));
1073
+ }
1074
+ function resolveModuleI18nOptions(options, nuxt) {
1075
+ if (options.i18n === false) {
1076
+ return {
1077
+ locales: [],
1078
+ defaultLocale: void 0,
1079
+ fallback: {},
1080
+ translatedSlugs: false,
1081
+ strictTranslatedSlugs: false
1082
+ };
1083
+ }
1084
+ const moduleI18n = options.i18n === true ? {} : options.i18n || {};
1085
+ const nuxtI18n = nuxt.options.i18n || {};
1086
+ const nuxtLocales = Array.isArray(nuxtI18n.locales) ? nuxtI18n.locales.map((locale) => typeof locale === "string" ? locale : locale.code).filter(Boolean) : [];
1087
+ const locales = Array.from(new Set([
1088
+ moduleI18n.defaultLocale,
1089
+ nuxtI18n.defaultLocale,
1090
+ ...moduleI18n.locales || [],
1091
+ ...nuxtLocales
1092
+ ].filter(Boolean)));
1093
+ const defaultLocale = moduleI18n.defaultLocale || nuxtI18n.defaultLocale || locales[0];
1094
+ return {
1095
+ locales,
1096
+ defaultLocale,
1097
+ fallback: defu({}, moduleI18n.fallback),
1098
+ translatedSlugs: moduleI18n.translatedSlugs ?? false,
1099
+ strictTranslatedSlugs: moduleI18n.strictTranslatedSlugs ?? false
1100
+ };
1101
+ }
1102
+ function normalizeSitemapOptions(options) {
1103
+ if (options.sitemap === false) {
1104
+ return false;
1105
+ }
1106
+ const sitemap = options.sitemap === true ? {} : options.sitemap;
1107
+ return {
1108
+ path: sitemap.path || "/sitemap",
1109
+ include: sitemap.include,
1110
+ exclude: sitemap.exclude || [],
1111
+ includeDrafts: sitemap.includeDrafts,
1112
+ assert: normalizeContentSitemapAssertOptions(sitemap.assert)
1113
+ };
1114
+ }
1115
+ function normalizeSearchOptions(options) {
1116
+ if (options.search === false) {
1117
+ return false;
1118
+ }
1119
+ return {
1120
+ engine: options.search?.engine || "minisearch",
1121
+ ignoredTags: options.search?.ignoredTags || ["script", "style", "pre"],
1122
+ filterQuery: options.search?.filterQuery || { _draft: false, _partial: false },
1123
+ collections: options.search?.collections,
1124
+ extraFields: options.search?.extraFields || [],
1125
+ apiBaseURL: options.search?.apiBaseURL,
1126
+ minisearch: normalizeMiniSearchOptions(options.search?.minisearch)
1127
+ };
1128
+ }
1129
+ const defaultMiniSearchOptions = {
1130
+ fields: ["title", "content", "headings"],
1131
+ storeFields: ["path", "title", "excerpt", "anchor", "locale", "collection"],
1132
+ boost: {
1133
+ title: 4,
1134
+ headings: 2,
1135
+ content: 1
1136
+ },
1137
+ fuzzy: 0.2,
1138
+ prefix: true
1139
+ };
1140
+ const requiredMiniSearchStoreFields = ["path", "title", "excerpt"];
1141
+ function normalizeMiniSearchOptions(options = {}) {
1142
+ const fields = options?.fields?.filter((field) => typeof field === "string" && field.length > 0);
1143
+ const storeFields = options?.storeFields?.filter((field) => typeof field === "string" && field.length > 0);
1144
+ const boost = Object.fromEntries(
1145
+ Object.entries(options?.boost || {}).filter((entry) => typeof entry[0] === "string" && entry[0].length > 0 && typeof entry[1] === "number" && Number.isFinite(entry[1]))
1146
+ );
1147
+ const resolvedFields = fields?.length ? fields : [...defaultMiniSearchOptions.fields];
1148
+ const resolvedStoreFields = storeFields?.length ? storeFields : defaultMiniSearchOptions.storeFields;
1149
+ return {
1150
+ fields: resolvedFields,
1151
+ storeFields: Array.from(/* @__PURE__ */ new Set([...requiredMiniSearchStoreFields, ...resolvedStoreFields])),
1152
+ boost: Object.keys(boost).length ? boost : { ...defaultMiniSearchOptions.boost },
1153
+ fuzzy: typeof options?.fuzzy === "boolean" || typeof options?.fuzzy === "number" ? options.fuzzy : defaultMiniSearchOptions.fuzzy,
1154
+ prefix: typeof options?.prefix === "boolean" ? options.prefix : defaultMiniSearchOptions.prefix
1155
+ };
1156
+ }
1157
+ function createSearchRuntimeConfig(search, apiBaseURL) {
1158
+ const resolvedApiBaseURL = search.apiBaseURL || `${apiBaseURL.replace(/\/$/, "")}/search`;
1159
+ return {
1160
+ apiBaseURL: resolvedApiBaseURL,
1161
+ indexURL: `${resolvedApiBaseURL.replace(/\/$/, "")}/index.json`,
1162
+ engine: search.engine || "minisearch",
1163
+ minisearch: normalizeMiniSearchOptions(search.minisearch)
1164
+ };
1165
+ }
1166
+
1167
+ const getContentPageMeta = (page) => {
1168
+ const content = page.meta?.content;
1169
+ return content && typeof content === "object" ? content : void 0;
1170
+ };
1170
1171
  const isRouteConfig = (value) => typeof value === "string" || value !== null && typeof value === "object" && Object.values(value).every((entry) => typeof entry === "string");
1171
1172
  const describePage = (page) => page.file || page.path || "<unknown page>";
1172
1173
  const expectedLocalesFor = (collection, locales, defaultLocale) => {
@@ -1218,6 +1219,568 @@ function validatePage(page, contentMeta, collections, options) {
1218
1219
  }
1219
1220
  }
1220
1221
 
1222
+ const hasAgentSurface = (config) => Boolean(
1223
+ config.agent || Object.values(config.collections || {}).some((collection) => Boolean(collection.agent?.markdown))
1224
+ );
1225
+ const agentSectionIds = (sections) => new Set((sections || []).map((section) => section.id).filter(Boolean));
1226
+ const validateAgentConfig = (config, options, context) => {
1227
+ if (!hasAgentSurface(config)) return;
1228
+ const agentRoutes = normalizeAgentRouteOptions(options);
1229
+ if (!context.dev && agentRoutes.routes && agentRoutes.prerender && !config.agent?.site?.url) {
1230
+ throw new Error(
1231
+ "@lupinum/ginko-content agent prerender requires agent.site.url for non-dev builds. Set agent.site.url in content.config.ts or disable content.agent.prerender."
1232
+ );
1233
+ }
1234
+ const sections = agentSectionIds(config.agent?.sections);
1235
+ const seenSections = /* @__PURE__ */ new Set();
1236
+ for (const section of config.agent?.sections || []) {
1237
+ if (!section.id) {
1238
+ throw new Error("@lupinum/ginko-content agent section ids must be non-empty strings.");
1239
+ }
1240
+ if (seenSections.has(section.id)) {
1241
+ throw new Error(`Duplicate Ginko agent section id "${section.id}". Agent section ids must be unique.`);
1242
+ }
1243
+ seenSections.add(section.id);
1244
+ }
1245
+ const assertSection = (id, owner) => {
1246
+ if (!id || id === "content") return;
1247
+ if (!sections.has(id)) {
1248
+ throw new Error(
1249
+ `${owner} references unknown Ginko agent section "${id}". Declare it in agent.sections or use the default "content" section.`
1250
+ );
1251
+ }
1252
+ };
1253
+ for (const [name, collection] of Object.entries(config.collections || {})) {
1254
+ if (collection.agent?.markdown || collection.agent?.section) {
1255
+ assertSection(collection.agent.section, `Collection "${name}"`);
1256
+ }
1257
+ }
1258
+ for (const page of config.agent?.pages || []) {
1259
+ assertSection(page.section, `Agent app page "${page.id}"`);
1260
+ }
1261
+ };
1262
+
1263
+ const normalizeStaticRoutePath = (path) => {
1264
+ if (!path || path === "/") return "/";
1265
+ return `/${path.replace(/\/+/g, "/").replace(/^\/+|\/+$/g, "")}`;
1266
+ };
1267
+ const collectRawMarkdownLinksFromLlms = (markdown, siteUrl) => {
1268
+ const links = /* @__PURE__ */ new Set();
1269
+ const pattern = /\[[^\]]*\]\(([^)]+)\)/g;
1270
+ for (const match of markdown.matchAll(pattern)) {
1271
+ const href = match[1];
1272
+ if (!href) continue;
1273
+ try {
1274
+ const url = /^[a-z][a-z0-9+.-]*:/i.test(href) ? new URL(href) : new URL(href, siteUrl || "http://localhost:3000");
1275
+ if (url.pathname.startsWith("/raw/") && url.pathname.endsWith(".md")) links.add(url.pathname);
1276
+ } catch {
1277
+ if (href.startsWith("/raw/") && href.endsWith(".md")) links.add(href);
1278
+ }
1279
+ }
1280
+ return Array.from(links);
1281
+ };
1282
+ const rawMarkdownRouteForPageRoute = (route) => {
1283
+ const normalized = normalizeStaticRoutePath(route);
1284
+ return normalized === "/" ? "/raw/index.md" : `/raw${normalized}.md`;
1285
+ };
1286
+ const collectRawMarkdownRoutesFromGeneratedFrontmatter = (markdown) => {
1287
+ const links = /* @__PURE__ */ new Set();
1288
+ const addRoutePath = (route) => {
1289
+ links.add(rawMarkdownRouteForPageRoute(route));
1290
+ };
1291
+ const addFrontmatterRoute = (frontmatter) => {
1292
+ const route = /^route:\s*"([^"]+)"/m.exec(frontmatter || "")?.[1];
1293
+ if (!route) return;
1294
+ addRoutePath(route);
1295
+ };
1296
+ const addSourceRoute = (source) => {
1297
+ if (!source) return;
1298
+ try {
1299
+ addRoutePath(new URL(source, "http://localhost:3000").pathname);
1300
+ } catch {
1301
+ addRoutePath(source);
1302
+ }
1303
+ };
1304
+ const sourcePattern = /^Source:\s+(\S+)/gm;
1305
+ for (const match of markdown.matchAll(sourcePattern)) {
1306
+ addSourceRoute(match[1]);
1307
+ }
1308
+ const pageFrontmatterPattern = /^Source:\s+[^\n]+\n\n---\n([\s\S]*?)\n---/gm;
1309
+ for (const match of markdown.matchAll(pageFrontmatterPattern)) {
1310
+ addFrontmatterRoute(match[1]);
1311
+ }
1312
+ const firstFrontmatter = /^---\n([\s\S]*?)\n---/.exec(markdown)?.[1];
1313
+ addFrontmatterRoute(firstFrontmatter);
1314
+ return Array.from(links);
1315
+ };
1316
+ const publicOutputPath = (publicDir, route) => join(publicDir, normalizeStaticRoutePath(route).replace(/^\//, ""));
1317
+ const rawMarkdownIndexRouteForRawRoute = (route) => {
1318
+ if (!route.startsWith("/raw/") || !route.endsWith(".md")) return null;
1319
+ const pageRoute = route.replace(/^\/raw/, "").replace(/\.md$/, "");
1320
+ return pageRoute === "/index" ? "/index.md" : `${pageRoute}/index.md`;
1321
+ };
1322
+
1323
+ const hookNuxtBoundary$2 = (nuxt, name, callback) => {
1324
+ const hook = nuxt.hook;
1325
+ hook(name, callback);
1326
+ };
1327
+ const registerStaticOutputGeneration = ({
1328
+ nuxt,
1329
+ options,
1330
+ appContentConfig,
1331
+ contentContext,
1332
+ resolvedI18n,
1333
+ resolveRuntimeModule,
1334
+ getSearchRuntime
1335
+ }) => {
1336
+ if (nuxt.options.dev) return;
1337
+ hookNuxtBoundary$2(nuxt, "nitro:build:before", (nitro) => {
1338
+ nitro.hooks.hook("prerender:init", (prerenderer) => {
1339
+ prerenderer.hooks.hook("compiled", async () => {
1340
+ const searchRuntime = getSearchRuntime();
1341
+ const publicDir = nitro.options.output.publicDir || nuxt.options.nitro?.output?.publicDir || resolve$1(nuxt.options.rootDir, ".output/public");
1342
+ const serverFilename = typeof prerenderer.options.rollupConfig?.output?.entryFileNames === "string" ? prerenderer.options.rollupConfig.output.entryFileNames : "index.mjs";
1343
+ const serverEntrypoint = resolve$1(prerenderer.options.output.serverDir, serverFilename);
1344
+ const { localFetch } = await import(pathToFileURL(serverEntrypoint).href);
1345
+ if (searchRuntime !== false && searchRuntime.engine !== "cms" && (!contentContext.provider || contentContext.provider === "filesystem")) {
1346
+ const response = await localFetch(searchRuntime.indexURL);
1347
+ if (!response.ok) {
1348
+ throw new Error(`Failed to generate search index: [${response.status}] ${response.statusText}`);
1349
+ }
1350
+ const json = await response.text();
1351
+ const indexPath = publicOutputPath(publicDir, searchRuntime.indexURL);
1352
+ mkdirSync(dirname(indexPath), { recursive: true });
1353
+ writeFileSync(indexPath, json, "utf8");
1354
+ if (searchRuntime.engine === "pagefind") {
1355
+ const records = JSON.parse(json);
1356
+ const { writePagefindIndex } = await import(resolveRuntimeModule("./server/pagefind.js"));
1357
+ await writePagefindIndex(records, resolve$1(publicDir, "pagefind"));
1358
+ }
1359
+ }
1360
+ const agentRoutes = normalizeAgentRouteOptions(options);
1361
+ if (agentRoutes.routes && agentRoutes.prerender && appContentConfig.agent) {
1362
+ const defaultLocale = appContentConfig.agent.site?.defaultLocale || resolvedI18n.defaultLocale;
1363
+ const locales = appContentConfig.agent.site?.locales?.length ? appContentConfig.agent.site.locales : resolvedI18n.locales;
1364
+ const llmsRoutes = [
1365
+ "/llms.txt",
1366
+ "/llms-full.txt",
1367
+ ...locales.filter((locale) => locale && locale !== defaultLocale).flatMap((locale) => [`/${locale}/llms.txt`, `/${locale}/llms-full.txt`])
1368
+ ];
1369
+ const markdownRoutes = /* @__PURE__ */ new Set();
1370
+ for (const route of llmsRoutes) {
1371
+ const response = await localFetch(route);
1372
+ if (!response.ok) {
1373
+ throw new Error(`Failed to generate agent markdown route ${route}: [${response.status}] ${response.statusText}`);
1374
+ }
1375
+ const body = await response.text();
1376
+ const outputPath = publicOutputPath(publicDir, route);
1377
+ mkdirSync(dirname(outputPath), { recursive: true });
1378
+ writeFileSync(outputPath, body, "utf8");
1379
+ if (/\/llms\.txt$/i.test(route)) {
1380
+ collectRawMarkdownLinksFromLlms(body, appContentConfig.agent.site?.url).forEach((link) => markdownRoutes.add(link));
1381
+ } else if (/\/llms-full\.txt$/i.test(route)) {
1382
+ collectRawMarkdownRoutesFromGeneratedFrontmatter(body).forEach((link) => markdownRoutes.add(link));
1383
+ }
1384
+ }
1385
+ for (const route of markdownRoutes) {
1386
+ const response = await localFetch(route);
1387
+ if (!response.ok) {
1388
+ throw new Error(`Failed to generate agent markdown route ${route}: [${response.status}] ${response.statusText}`);
1389
+ }
1390
+ const body = await response.text();
1391
+ const outputPath = publicOutputPath(publicDir, route);
1392
+ mkdirSync(dirname(outputPath), { recursive: true });
1393
+ writeFileSync(outputPath, body, "utf8");
1394
+ const indexRoute = rawMarkdownIndexRouteForRawRoute(route);
1395
+ if (indexRoute) {
1396
+ const indexPath = publicOutputPath(publicDir, indexRoute);
1397
+ mkdirSync(dirname(indexPath), { recursive: true });
1398
+ writeFileSync(indexPath, body, "utf8");
1399
+ }
1400
+ }
1401
+ }
1402
+ });
1403
+ });
1404
+ });
1405
+ };
1406
+
1407
+ const hookNuxtBoundary$1 = (nuxt, name, callback) => {
1408
+ const hook = nuxt.hook;
1409
+ hook(name, callback);
1410
+ };
1411
+ const registerContentNitroConfig = ({
1412
+ nuxt,
1413
+ options,
1414
+ appContentConfig,
1415
+ contentContext,
1416
+ runtimeInlineDependencies,
1417
+ buildIntegrity,
1418
+ resolvedI18n,
1419
+ resolveRuntimeModule,
1420
+ resolveModuleFile,
1421
+ getResolvedContentContext,
1422
+ getSearchRuntime
1423
+ }) => {
1424
+ hookNuxtBoundary$1(nuxt, "nitro:config", (nitroConfig) => {
1425
+ const searchRuntime = getSearchRuntime();
1426
+ nitroConfig.prerender = nitroConfig.prerender || {};
1427
+ nitroConfig.prerender.routes = nitroConfig.prerender.routes || [];
1428
+ const usesFilesystemProvider = !contentContext.provider || contentContext.provider === "filesystem";
1429
+ if (!nuxt.options.dev && usesFilesystemProvider) {
1430
+ nitroConfig.prerender.routes.unshift(`${options.api.baseURL}/cache.${buildIntegrity}.json`);
1431
+ }
1432
+ const sources = useContentMounts(nuxt, contentContext.sources);
1433
+ nitroConfig.devStorage = Object.assign(nitroConfig.devStorage || {}, sources);
1434
+ nitroConfig.devStorage["cache:content"] = {
1435
+ driver: "fs",
1436
+ base: resolve$1(nuxt.options.buildDir, "content-cache")
1437
+ };
1438
+ for (const source of Object.values(sources)) {
1439
+ if (source.driver === "fs" && typeof source.base === "string" && source.base.includes(nuxt.options.srcDir)) {
1440
+ const wildcard = join(source.base, "**/*").replace(withTrailingSlash(nuxt.options.srcDir), "");
1441
+ nuxt.options.ignore.push(wildcard, `!${wildcard}.vue`);
1442
+ }
1443
+ }
1444
+ nitroConfig.bundledStorage = nitroConfig.bundledStorage || [];
1445
+ nitroConfig.bundledStorage.push("cache:content");
1446
+ nitroConfig.externals = defu(typeof nitroConfig.externals === "object" ? nitroConfig.externals : {}, {
1447
+ inline: [
1448
+ resolveModuleFile("./runtime"),
1449
+ ...runtimeInlineDependencies
1450
+ ]
1451
+ });
1452
+ if (searchRuntime !== false && searchRuntime.engine !== "cms" && usesFilesystemProvider) {
1453
+ nitroConfig.routeRules = nitroConfig.routeRules || {};
1454
+ nitroConfig.routeRules[searchRuntime.indexURL] = {
1455
+ prerender: true,
1456
+ headers: { "Content-Type": "application/json; charset=utf-8" }
1457
+ };
1458
+ }
1459
+ if (contentContext.sitemap !== false) {
1460
+ nitroConfig.plugins ||= [];
1461
+ const sitemapPlugin = resolveRuntimeModule("server/plugins/sitemap.js");
1462
+ if (!nitroConfig.plugins.includes(sitemapPlugin)) {
1463
+ nitroConfig.plugins.push(sitemapPlugin);
1464
+ }
1465
+ }
1466
+ if (contentContext.cache) {
1467
+ nitroConfig.plugins ||= [];
1468
+ const cachePlugin = resolveRuntimeModule("server/plugins/cache.js");
1469
+ if (!nitroConfig.plugins.includes(cachePlugin)) {
1470
+ nitroConfig.plugins.push(cachePlugin);
1471
+ }
1472
+ }
1473
+ registerContentNitroIntegrationHooks(nitroConfig, {
1474
+ rootDir: nuxt.options.rootDir,
1475
+ sitemapPrerenderRoutes: () => contentContext.sitemap === false ? [] : resolveNuxtSitemapPrerenderRoutes(nuxt),
1476
+ resolveContentContext: getResolvedContentContext
1477
+ }, {
1478
+ collections: contentContext.collections,
1479
+ locales: contentContext.locales,
1480
+ defaultLocale: contentContext.defaultLocale,
1481
+ translatedSlugs: contentContext.translatedSlugs,
1482
+ respectPathCase: contentContext.respectPathCase,
1483
+ markdown: processMarkdownOptions(contentContext.markdown),
1484
+ yaml: contentContext.yaml,
1485
+ csv: contentContext.csv,
1486
+ sitemap: contentContext.sitemap,
1487
+ provider: contentContext.provider
1488
+ });
1489
+ const agentRoutes = normalizeAgentRouteOptions(options);
1490
+ if (agentRoutes.routes && agentRoutes.prerender && appContentConfig.agent) {
1491
+ nitroConfig.prerender.routes.push("/llms.txt", "/llms-full.txt");
1492
+ for (const locale of appContentConfig.agent.site?.locales || resolvedI18n.locales || []) {
1493
+ if (locale && locale !== (appContentConfig.agent.site?.defaultLocale || resolvedI18n.defaultLocale)) {
1494
+ nitroConfig.prerender.routes.push(`/${locale}/llms.txt`, `/${locale}/llms-full.txt`);
1495
+ }
1496
+ }
1497
+ for (const page of appContentConfig.agent.pages || []) {
1498
+ const routes = typeof page.route === "string" ? [page.route] : Object.values(page.route);
1499
+ for (const route of routes) {
1500
+ const normalized = route === "/" ? "/" : `/${route.replace(/^\/+|\/+$/g, "")}`;
1501
+ nitroConfig.prerender.routes.push(
1502
+ normalized === "/" ? "/raw/index.md" : `/raw${normalized}.md`,
1503
+ normalized === "/" ? "/index.md" : `${normalized}/index.md`
1504
+ );
1505
+ }
1506
+ }
1507
+ }
1508
+ });
1509
+ };
1510
+
1511
+ const BUILTIN_PLUGIN_PEER_DEPS = {
1512
+ highlight: "shiki",
1513
+ math: "katex",
1514
+ mermaid: "beautiful-mermaid"
1515
+ };
1516
+ function assertConfiguredProviderAvailable(contentContext) {
1517
+ const provider = contentContext.provider || "filesystem";
1518
+ if (provider === "filesystem") return;
1519
+ if (contentContext.providers?.[provider]) return;
1520
+ if (provider === "cms") {
1521
+ throw new Error('content.config.ts sets provider "cms", but no CMS provider module registered it. Add @lupinum/ginko-cms to nuxt.config.ts modules or remove provider: "cms".');
1522
+ }
1523
+ throw new Error(`content.config.ts sets provider "${provider}", but no provider module registered it. Register a module for "${provider}" or add it to content providers.`);
1524
+ }
1525
+ function validateCollectionNames(collections) {
1526
+ normalizeContentConfigCollectionNames(collections);
1527
+ }
1528
+ function validateRemovedMarkdownOptions(options) {
1529
+ if (options.highlight !== void 0) {
1530
+ throw new Error("`content.highlight` was removed. Enable syntax highlighting with `content.markdown.plugins`, for example `[['highlight', { ...options }]]`.");
1531
+ }
1532
+ const markdown = options.markdown || {};
1533
+ const removedOptions = ["mdc", "remarkPlugins", "rehypePlugins", "toc"];
1534
+ const removed = removedOptions.filter((key) => typeof markdown[key] !== "undefined");
1535
+ if (removed.length) {
1536
+ throw new Error(`Removed markdown options: ${removed.map((option) => `content.markdown.${String(option)}`).join(", ")}. Use ordered \`content.markdown.plugins\` entries instead.`);
1537
+ }
1538
+ }
1539
+ async function validateBuiltinMarkdownPlugins(plugins, resolvePath) {
1540
+ for (const plugin of plugins) {
1541
+ const peerDependency = BUILTIN_PLUGIN_PEER_DEPS[plugin.name];
1542
+ if (!peerDependency) {
1543
+ continue;
1544
+ }
1545
+ try {
1546
+ await resolvePath(peerDependency);
1547
+ } catch (error) {
1548
+ const next = new Error(`Markdown plugin "${plugin.name}" requires "${peerDependency}" to be installed.`);
1549
+ next.cause = error;
1550
+ throw next;
1551
+ }
1552
+ }
1553
+ }
1554
+
1555
+ const contentModuleDefaults = {
1556
+ api: {
1557
+ baseURL: "/api/_content"
1558
+ },
1559
+ i18n: true,
1560
+ sitemap: true,
1561
+ search: {
1562
+ engine: "minisearch",
1563
+ ignoredTags: ["script", "style", "pre"],
1564
+ filterQuery: { _draft: false, _partial: false },
1565
+ extraFields: [],
1566
+ minisearch: {
1567
+ fields: [...defaultMiniSearchOptions.fields],
1568
+ storeFields: [...defaultMiniSearchOptions.storeFields],
1569
+ boost: { ...defaultMiniSearchOptions.boost },
1570
+ fuzzy: defaultMiniSearchOptions.fuzzy,
1571
+ prefix: defaultMiniSearchOptions.prefix
1572
+ }
1573
+ },
1574
+ watch: true,
1575
+ sources: {},
1576
+ ignores: [],
1577
+ collections: {},
1578
+ markdown: {
1579
+ plugins: [],
1580
+ tags: {
1581
+ code: "ProseCode",
1582
+ img: "ProseImg",
1583
+ pre: "ProsePre"
1584
+ },
1585
+ anchorLinks: {
1586
+ depth: 4,
1587
+ exclude: [1]
1588
+ },
1589
+ image: "auto"
1590
+ },
1591
+ yaml: {},
1592
+ csv: {
1593
+ delimiter: ",",
1594
+ json: true
1595
+ },
1596
+ navigation: {
1597
+ fields: []
1598
+ },
1599
+ agent: {
1600
+ routes: true,
1601
+ linkHeaders: true,
1602
+ markdownNegotiation: true,
1603
+ prerender: true
1604
+ },
1605
+ contentHead: true,
1606
+ respectPathCase: false,
1607
+ experimental: {
1608
+ stripQueryParameters: false
1609
+ }
1610
+ };
1611
+
1612
+ const resolveNuxtSiteUrl = (nuxt) => {
1613
+ const publicRuntime = nuxt.options.runtimeConfig.public;
1614
+ const publicContentRuntime = publicRuntime.content;
1615
+ const configuredSiteUrl = publicContentRuntime?.siteUrl || publicRuntime.siteUrl;
1616
+ if (typeof configuredSiteUrl === "string" && configuredSiteUrl.length > 0) {
1617
+ return configuredSiteUrl;
1618
+ }
1619
+ const nuxtSite = nuxt.options.site;
1620
+ return typeof nuxtSite?.url === "string" && nuxtSite.url.length > 0 ? nuxtSite.url : void 0;
1621
+ };
1622
+ const sanitizePublicMarkdownPluginValue = (value) => {
1623
+ if (typeof value === "function" || typeof value === "symbol") {
1624
+ return void 0;
1625
+ }
1626
+ if (Array.isArray(value)) {
1627
+ return value.map((item) => sanitizePublicMarkdownPluginValue(item)).filter((item) => item !== void 0);
1628
+ }
1629
+ if (typeof value !== "object" || value === null) {
1630
+ return value;
1631
+ }
1632
+ const prototype = Object.getPrototypeOf(value);
1633
+ if (prototype !== Object.prototype && prototype !== null) {
1634
+ return value;
1635
+ }
1636
+ return Object.fromEntries(Object.entries(value).filter(([key]) => key !== "transformers").map(([key, item]) => [key, sanitizePublicMarkdownPluginValue(item)]).filter(([, item]) => item !== void 0));
1637
+ };
1638
+ const sanitizePublicMarkdownPlugins = (plugins) => plugins.map((plugin) => ({
1639
+ name: plugin.name,
1640
+ options: sanitizePublicMarkdownPluginValue(plugin.options || {})
1641
+ }));
1642
+ const sanitizePrivateMarkdownPluginValue = (value) => {
1643
+ if (typeof value === "function" || typeof value === "symbol") {
1644
+ return void 0;
1645
+ }
1646
+ if (Array.isArray(value)) {
1647
+ return value.map((item) => sanitizePrivateMarkdownPluginValue(item)).filter((item) => item !== void 0);
1648
+ }
1649
+ if (typeof value !== "object" || value === null) {
1650
+ return value;
1651
+ }
1652
+ const prototype = Object.getPrototypeOf(value);
1653
+ if (prototype !== Object.prototype && prototype !== null) {
1654
+ return value;
1655
+ }
1656
+ return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, sanitizePrivateMarkdownPluginValue(item)]).filter(([, item]) => item !== void 0));
1657
+ };
1658
+ const sanitizePrivateMarkdownPlugins = (plugins) => plugins.map((plugin) => ({
1659
+ name: plugin.name,
1660
+ options: sanitizePrivateMarkdownPluginValue(plugin.options || {})
1661
+ }));
1662
+ const applyContentRuntimeConfig = (nuxt, options, contentContext, runtimeCollections, buildIntegrity, cacheIntegrity) => {
1663
+ const revalidate = options.revalidate === false ? void 0 : options.revalidate;
1664
+ const searchRuntime = contentContext.search === false ? false : {
1665
+ apiBaseURL: contentContext.search.apiBaseURL || `${options.api.baseURL.replace(/\/$/, "")}/search`,
1666
+ indexURL: `${(contentContext.search.apiBaseURL || `${options.api.baseURL.replace(/\/$/, "")}/search`).replace(/\/$/, "")}/index.json`,
1667
+ engine: contentContext.search.engine || "minisearch",
1668
+ minisearch: normalizeMiniSearchOptions(contentContext.search.minisearch)
1669
+ };
1670
+ const siteUrl = resolveNuxtSiteUrl(nuxt);
1671
+ const contentRuntime = defu(nuxt.options.runtimeConfig.public.content, {
1672
+ ...siteUrl ? { siteUrl } : {},
1673
+ locales: contentContext.locales,
1674
+ provider: contentContext.provider || "filesystem",
1675
+ providers: contentContext.providers || {},
1676
+ defaultLocale: contentContext.defaultLocale || void 0,
1677
+ localeFallback: contentContext.localeFallback || {},
1678
+ translatedSlugs: contentContext.translatedSlugs ?? false,
1679
+ strictTranslatedSlugs: contentContext.strictTranslatedSlugs ?? false,
1680
+ collections: runtimeCollections,
1681
+ integrity: buildIntegrity,
1682
+ experimental: {
1683
+ stripQueryParameters: options.experimental.stripQueryParameters
1684
+ },
1685
+ respectPathCase: options.respectPathCase ?? false,
1686
+ api: {
1687
+ baseURL: options.api.baseURL
1688
+ },
1689
+ markdown: {
1690
+ plugins: sanitizePublicMarkdownPlugins(contentContext.markdown.plugins),
1691
+ tags: contentContext.markdown.tags,
1692
+ anchorLinks: contentContext.markdown.anchorLinks,
1693
+ image: contentContext.markdown.image || "auto"
1694
+ },
1695
+ sitemap: contentContext.sitemap ? {
1696
+ path: contentContext.sitemap.path || "/sitemap",
1697
+ ...contentContext.sitemap.include?.length ? { include: contentContext.sitemap.include } : {},
1698
+ ...contentContext.sitemap.exclude?.length ? { exclude: contentContext.sitemap.exclude } : {},
1699
+ ...typeof contentContext.sitemap.includeDrafts === "boolean" ? { includeDrafts: contentContext.sitemap.includeDrafts } : {}
1700
+ } : false,
1701
+ search: searchRuntime,
1702
+ navigation: contentContext.navigation,
1703
+ contentHead: options.contentHead ?? true
1704
+ });
1705
+ const privateContentRuntime = {
1706
+ ...contentContext,
1707
+ markdown: {
1708
+ ...contentContext.markdown,
1709
+ plugins: sanitizePrivateMarkdownPlugins(contentContext.markdown.plugins)
1710
+ }
1711
+ };
1712
+ nuxt.options.runtimeConfig.public.content = contentRuntime;
1713
+ nuxt.options.runtimeConfig.content = defu(nuxt.options.runtimeConfig.content, {
1714
+ cacheVersion: CACHE_VERSION,
1715
+ cacheIntegrity,
1716
+ revalidate: revalidate?.token ? {
1717
+ token: revalidate.token,
1718
+ allowUnsigned: revalidate.allowUnsigned === true
1719
+ } : false,
1720
+ ...privateContentRuntime,
1721
+ collections: runtimeCollections
1722
+ });
1723
+ };
1724
+
1725
+ const registerContentContextFinalization = ({
1726
+ nuxt,
1727
+ options,
1728
+ contentContext,
1729
+ buildIntegrity,
1730
+ resolvePath,
1731
+ resolveRuntimeModule,
1732
+ onResolved
1733
+ }) => {
1734
+ nuxt.hook("modules:done", async () => {
1735
+ await nuxt.callHook("content:providers", contentContext.providers ||= {});
1736
+ assertConfiguredProviderAvailable(contentContext);
1737
+ await nuxt.callHook("content:context", contentContext);
1738
+ if (contentContext.search !== false) {
1739
+ registerContentSearchServerHandlers(options.api.baseURL, contentContext.search, resolveRuntimeModule);
1740
+ }
1741
+ contentContext.defaultLocale = contentContext.defaultLocale || contentContext.locales[0];
1742
+ const resolvedContentContext = {
1743
+ ...contentContext,
1744
+ markdown: processMarkdownOptions(contentContext.markdown)
1745
+ };
1746
+ onResolved(resolvedContentContext);
1747
+ await validateBuiltinMarkdownPlugins(resolvedContentContext.markdown.plugins, resolvePath);
1748
+ const runtimeCollections = Object.fromEntries(Object.entries(options.collections || {}).map(([name, collection]) => {
1749
+ const references = collectTopLevelReferenceFieldsByTarget(collection.schema);
1750
+ return [
1751
+ name,
1752
+ {
1753
+ ...collection.source ? { source: collection.source } : {},
1754
+ ...collection.exclude ? { exclude: collection.exclude } : {},
1755
+ ...collection.type ? { type: collection.type } : {},
1756
+ strict: collection.strict ?? true,
1757
+ ...collection.route ? { route: collection.route } : {},
1758
+ ...typeof collection.sitemap === "boolean" ? { sitemap: collection.sitemap } : {},
1759
+ ...collection.i18n && collection.i18n !== true ? { i18n: collection.i18n } : {},
1760
+ ...Object.keys(references).length ? { references } : {}
1761
+ }
1762
+ ];
1763
+ }));
1764
+ const cacheIntegrity = hash({
1765
+ locales: resolvedContentContext.locales,
1766
+ defaultLocale: resolvedContentContext.defaultLocale,
1767
+ localeFallback: resolvedContentContext.localeFallback,
1768
+ translatedSlugs: resolvedContentContext.translatedSlugs,
1769
+ strictTranslatedSlugs: resolvedContentContext.strictTranslatedSlugs,
1770
+ respectPathCase: resolvedContentContext.respectPathCase,
1771
+ collections: runtimeCollections,
1772
+ markdown: resolvedContentContext.markdown,
1773
+ yaml: resolvedContentContext.yaml,
1774
+ csv: resolvedContentContext.csv
1775
+ });
1776
+ applyContentRuntimeConfig(nuxt, options, resolvedContentContext, runtimeCollections, buildIntegrity, cacheIntegrity);
1777
+ });
1778
+ };
1779
+
1780
+ const hookNuxtBoundary = (nuxt, name2, callback) => {
1781
+ const hook = nuxt.hook;
1782
+ hook(name2, callback);
1783
+ };
1221
1784
  const module$1 = defineNuxtModule({
1222
1785
  meta: {
1223
1786
  name,
@@ -1236,69 +1799,7 @@ const module$1 = defineNuxtModule({
1236
1799
  }
1237
1800
  }
1238
1801
  },
1239
- defaults: {
1240
- api: {
1241
- baseURL: "/api/_content"
1242
- },
1243
- i18n: true,
1244
- sitemap: true,
1245
- search: {
1246
- engine: "minisearch",
1247
- ignoredTags: ["script", "style", "pre"],
1248
- filterQuery: { _draft: false, _partial: false },
1249
- extraFields: [],
1250
- minisearch: {
1251
- fields: ["title", "content", "headings"],
1252
- storeFields: ["path", "title", "excerpt", "anchor", "locale"],
1253
- boost: {
1254
- title: 4,
1255
- headings: 2,
1256
- content: 1
1257
- },
1258
- fuzzy: 0.2,
1259
- prefix: true
1260
- }
1261
- },
1262
- watch: {
1263
- ws: {
1264
- port: {
1265
- port: 4e3,
1266
- portRange: [4e3, 4040]
1267
- },
1268
- hostname: "localhost",
1269
- showURL: false
1270
- }
1271
- },
1272
- sources: {},
1273
- ignores: [],
1274
- collections: {},
1275
- markdown: {
1276
- plugins: [],
1277
- tags: {
1278
- code: "ProseCode",
1279
- img: "ProseImg",
1280
- pre: "ProsePre"
1281
- },
1282
- anchorLinks: {
1283
- depth: 4,
1284
- exclude: [1]
1285
- },
1286
- image: "auto"
1287
- },
1288
- yaml: {},
1289
- csv: {
1290
- delimiter: ",",
1291
- json: true
1292
- },
1293
- navigation: {
1294
- fields: []
1295
- },
1296
- contentHead: true,
1297
- respectPathCase: false,
1298
- experimental: {
1299
- stripQueryParameters: false
1300
- }
1301
- },
1802
+ defaults: contentModuleDefaults,
1302
1803
  async setup(options, nuxt) {
1303
1804
  const { resolve, resolvePath } = createResolver(import.meta.url);
1304
1805
  const logger = useLogger(name);
@@ -1317,6 +1818,10 @@ const module$1 = defineNuxtModule({
1317
1818
  });
1318
1819
  const contentConfigPath = resolveContentConfigPath(nuxt);
1319
1820
  const appContentConfig = await loadContentConfig(nuxt);
1821
+ const externalGinkoContentConfig = nuxt.options.ginkoContent;
1822
+ if (externalGinkoContentConfig?.agent && options.agent !== false) {
1823
+ options.agent = defu(options.agent || {}, externalGinkoContentConfig.agent);
1824
+ }
1320
1825
  if (!contentConfigPath || !appContentConfig.collections || !Object.keys(appContentConfig.collections).length) {
1321
1826
  throw new Error("@lupinum/ginko-content requires a content.config.ts with at least one collection. Define collections with defineContentConfig({ collections: { ... } }).");
1322
1827
  }
@@ -1324,6 +1829,10 @@ const module$1 = defineNuxtModule({
1324
1829
  const resolvedSitemap = normalizeSitemapOptions(options);
1325
1830
  const resolvedSearch = normalizeSearchOptions(options);
1326
1831
  validateCollectionNames(appContentConfig.collections);
1832
+ if (!hasAgentSurface(appContentConfig)) {
1833
+ options.agent = false;
1834
+ }
1835
+ validateAgentConfig(appContentConfig, options, { dev: nuxt.options.dev });
1327
1836
  options.collections = Object.fromEntries(Object.entries(appContentConfig.collections).map(([name2, collection]) => [
1328
1837
  name2,
1329
1838
  {
@@ -1353,25 +1862,32 @@ const module$1 = defineNuxtModule({
1353
1862
  sitemap: resolvedSitemap,
1354
1863
  search: resolvedSearch
1355
1864
  };
1865
+ let resolvedContentContext;
1866
+ const getResolvedContentContext = () => {
1867
+ if (!resolvedContentContext) {
1868
+ throw new Error("Content runtime config was read before content context resolution completed.");
1869
+ }
1870
+ return resolvedContentContext;
1871
+ };
1356
1872
  if (resolvedSitemap !== false) {
1357
1873
  configureNuxtSitemapSource(nuxt, options.api.baseURL, resolvedSitemap.path);
1358
1874
  }
1359
1875
  nuxt.hook("pages:extend", (pages) => {
1360
- validateContentPageRouteMetadata(pages, options.collections, {
1876
+ validateContentPageRouteMetadata(pages, options.collections || {}, {
1361
1877
  locales: resolvedI18n.locales,
1362
1878
  defaultLocale: resolvedI18n.defaultLocale
1363
1879
  });
1364
1880
  });
1365
1881
  if (resolvedSitemap && resolvedSitemap.assert.enabled) {
1366
- nuxt.hook("sitemap:prerender:done", async ({ sitemaps }) => {
1367
- const assertOptions = contentContext.sitemap?.assert;
1882
+ hookNuxtBoundary(nuxt, "sitemap:prerender:done", async ({ sitemaps }) => {
1883
+ const assertOptions = resolvedSitemap ? resolvedSitemap.assert : void 0;
1368
1884
  if (!assertOptions || !shouldRunSitemapAssertionOnPrerenderedSitemaps(assertOptions)) {
1369
1885
  return;
1370
1886
  }
1371
1887
  await assertGeneratedSitemaps({
1372
1888
  options: assertOptions,
1373
1889
  targets: createSitemapAssertionTargetsFromPrerenderedSitemaps(sitemaps),
1374
- collectionRouteCounts: await collectSitemapCollectionRouteCounts(nuxt.options.rootDir, contentContext),
1890
+ collectionRouteCounts: await collectSitemapCollectionRouteCounts(nuxt.options.rootDir, getResolvedContentContext()),
1375
1891
  logger
1376
1892
  });
1377
1893
  });
@@ -1389,146 +1905,40 @@ const module$1 = defineNuxtModule({
1389
1905
  Object.keys(appContentConfig.collections),
1390
1906
  Object.entries(appContentConfig.collections).filter(([, collection]) => Boolean(collection.i18n)).map(([name2]) => name2)
1391
1907
  );
1392
- await registerUserContentComponents(nuxt, resolve);
1908
+ await registerUserContentComponents(nuxt);
1393
1909
  const getSearchRuntime = () => contentContext.search === false ? false : createSearchRuntimeConfig(contentContext.search, options.api.baseURL);
1394
- nuxt.hook("nitro:config", (nitroConfig) => {
1395
- const searchRuntime = getSearchRuntime();
1396
- nitroConfig.prerender = nitroConfig.prerender || {};
1397
- nitroConfig.prerender.routes = nitroConfig.prerender.routes || [];
1398
- const usesFilesystemProvider = !contentContext.provider || contentContext.provider === "filesystem";
1399
- if (!nuxt.options.dev && usesFilesystemProvider) {
1400
- nitroConfig.prerender.routes.unshift(`${options.api.baseURL}/cache.${buildIntegrity}.json`);
1401
- }
1402
- const sources = useContentMounts(nuxt, contentContext.sources);
1403
- nitroConfig.devStorage = Object.assign(nitroConfig.devStorage || {}, sources);
1404
- nitroConfig.devStorage["cache:content"] = {
1405
- driver: "fs",
1406
- base: resolve(nuxt.options.buildDir, "content-cache")
1407
- };
1408
- for (const source of Object.values(sources)) {
1409
- if (source.driver === "fs" && source.base.includes(nuxt.options.srcDir)) {
1410
- const wildcard = join(source.base, "**/*").replace(withTrailingSlash(nuxt.options.srcDir), "");
1411
- nuxt.options.ignore.push(
1412
- // Remove `srcDir` from the path
1413
- wildcard,
1414
- `!${wildcard}.vue`
1415
- );
1416
- }
1417
- }
1418
- nitroConfig.bundledStorage = nitroConfig.bundledStorage || [];
1419
- nitroConfig.bundledStorage.push("cache:content");
1420
- nitroConfig.externals = defu(typeof nitroConfig.externals === "object" ? nitroConfig.externals : {}, {
1421
- inline: [
1422
- resolve("./runtime"),
1423
- ...runtimeInlineDependencies
1424
- ]
1425
- });
1426
- if (searchRuntime !== false && searchRuntime.engine !== "cms" && usesFilesystemProvider) {
1427
- nitroConfig.routeRules = nitroConfig.routeRules || {};
1428
- nitroConfig.routeRules[searchRuntime.indexURL] = {
1429
- prerender: true,
1430
- headers: { "Content-Type": "application/json; charset=utf-8" }
1431
- };
1432
- }
1433
- if (contentContext.sitemap !== false) {
1434
- nitroConfig.plugins ||= [];
1435
- const sitemapPlugin = resolveRuntimeModule("server/plugins/sitemap.js");
1436
- if (!nitroConfig.plugins.includes(sitemapPlugin)) {
1437
- nitroConfig.plugins.push(sitemapPlugin);
1438
- }
1439
- }
1440
- if (contentContext.cache && contentContext.cache !== false) {
1441
- nitroConfig.plugins ||= [];
1442
- const cachePlugin = resolveRuntimeModule("server/plugins/cache.js");
1443
- if (!nitroConfig.plugins.includes(cachePlugin)) {
1444
- nitroConfig.plugins.push(cachePlugin);
1445
- }
1446
- }
1447
- registerContentNitroIntegrationHooks(nitroConfig, {
1448
- rootDir: nuxt.options.rootDir,
1449
- sitemapPrerenderRoutes: () => contentContext.sitemap === false ? [] : resolveNuxtSitemapPrerenderRoutes(nuxt)
1450
- }, {
1451
- collections: contentContext.collections,
1452
- locales: contentContext.locales,
1453
- defaultLocale: contentContext.defaultLocale,
1454
- translatedSlugs: contentContext.translatedSlugs,
1455
- strictTranslatedSlugs: contentContext.strictTranslatedSlugs,
1456
- respectPathCase: contentContext.respectPathCase,
1457
- markdown: contentContext.markdown,
1458
- yaml: contentContext.yaml,
1459
- csv: contentContext.csv,
1460
- sitemap: contentContext.sitemap,
1461
- provider: contentContext.provider
1462
- });
1910
+ registerContentNitroConfig({
1911
+ nuxt,
1912
+ options,
1913
+ appContentConfig,
1914
+ contentContext,
1915
+ runtimeInlineDependencies,
1916
+ buildIntegrity,
1917
+ resolvedI18n,
1918
+ resolveRuntimeModule,
1919
+ resolveModuleFile: resolve,
1920
+ getResolvedContentContext,
1921
+ getSearchRuntime
1463
1922
  });
1464
- if (!nuxt.options.dev) {
1465
- nuxt.hook("nitro:build:before", (nitro) => {
1466
- nitro.hooks.hook("prerender:init", (prerenderer) => {
1467
- prerenderer.hooks.hook("compiled", async () => {
1468
- const searchRuntime = getSearchRuntime();
1469
- if (searchRuntime === false || searchRuntime.engine === "cms" || contentContext.provider && contentContext.provider !== "filesystem") {
1470
- return;
1471
- }
1472
- const publicDir = nitro.options.output.publicDir || nuxt.options.nitro.output?.publicDir || resolve$1(nuxt.options.rootDir, ".output/public");
1473
- const serverFilename = typeof prerenderer.options.rollupConfig?.output?.entryFileNames === "string" ? prerenderer.options.rollupConfig.output.entryFileNames : "index.mjs";
1474
- const serverEntrypoint = resolve$1(prerenderer.options.output.serverDir, serverFilename);
1475
- const { localFetch } = await import(pathToFileURL(serverEntrypoint).href);
1476
- const response = await localFetch(searchRuntime.indexURL);
1477
- if (!response.ok) {
1478
- throw new Error(`Failed to generate search index: [${response.status}] ${response.statusText}`);
1479
- }
1480
- const json = await response.text();
1481
- const indexPath = join(publicDir, searchRuntime.indexURL.replace(/^\//, ""));
1482
- mkdirSync(dirname(indexPath), { recursive: true });
1483
- writeFileSync(indexPath, json, "utf8");
1484
- if (searchRuntime.engine === "pagefind") {
1485
- const records = JSON.parse(json);
1486
- const { writePagefindIndex } = await import(resolveRuntimeModule("./server/pagefind.js"));
1487
- await writePagefindIndex(records, resolve$1(publicDir, "pagefind"));
1488
- }
1489
- });
1490
- });
1491
- });
1492
- }
1493
- nuxt.hook("modules:done", async () => {
1494
- await nuxt.callHook("content:providers", contentContext.providers ||= {});
1495
- assertConfiguredProviderAvailable(contentContext);
1496
- await nuxt.callHook("content:context", contentContext);
1497
- if (contentContext.search !== false) {
1498
- registerContentSearchServerHandlers(options.api.baseURL, contentContext.search, resolveRuntimeModule);
1923
+ registerStaticOutputGeneration({
1924
+ nuxt,
1925
+ options,
1926
+ appContentConfig,
1927
+ contentContext,
1928
+ resolvedI18n,
1929
+ resolveRuntimeModule,
1930
+ getSearchRuntime
1931
+ });
1932
+ registerContentContextFinalization({
1933
+ nuxt,
1934
+ options,
1935
+ contentContext,
1936
+ buildIntegrity,
1937
+ resolvePath,
1938
+ resolveRuntimeModule,
1939
+ onResolved: (context) => {
1940
+ resolvedContentContext = context;
1499
1941
  }
1500
- contentContext.defaultLocale = contentContext.defaultLocale || contentContext.locales[0];
1501
- contentContext.markdown = processMarkdownOptions(contentContext.markdown);
1502
- await validateBuiltinMarkdownPlugins(contentContext.markdown.plugins, resolvePath);
1503
- const runtimeCollections = Object.fromEntries(Object.entries(options.collections || {}).map(([name2, collection]) => {
1504
- const references = collectTopLevelReferenceFieldsByTarget(collection.schema);
1505
- return [
1506
- name2,
1507
- {
1508
- ...collection.source ? { source: collection.source } : {},
1509
- ...collection.exclude ? { exclude: collection.exclude } : {},
1510
- ...collection.type ? { type: collection.type } : {},
1511
- strict: collection.strict ?? true,
1512
- ...collection.route ? { route: collection.route } : {},
1513
- ...typeof collection.sitemap === "boolean" ? { sitemap: collection.sitemap } : {},
1514
- ...collection.i18n && collection.i18n !== true ? { i18n: collection.i18n } : {},
1515
- ...Object.keys(references).length ? { references } : {}
1516
- }
1517
- ];
1518
- }));
1519
- const cacheIntegrity = hash({
1520
- locales: contentContext.locales,
1521
- defaultLocale: contentContext.defaultLocale,
1522
- localeFallback: contentContext.localeFallback,
1523
- translatedSlugs: contentContext.translatedSlugs,
1524
- strictTranslatedSlugs: contentContext.strictTranslatedSlugs,
1525
- respectPathCase: contentContext.respectPathCase,
1526
- collections: runtimeCollections,
1527
- markdown: contentContext.markdown,
1528
- yaml: contentContext.yaml,
1529
- csv: contentContext.csv
1530
- });
1531
- applyContentRuntimeConfig(nuxt, options, contentContext, runtimeCollections, buildIntegrity, cacheIntegrity);
1532
1942
  });
1533
1943
  if (nuxt.options.dev) {
1534
1944
  registerDevRuntimePlugin(resolveRuntimeModule);
@@ -1536,46 +1946,5 @@ const module$1 = defineNuxtModule({
1536
1946
  }
1537
1947
  }
1538
1948
  });
1539
- function assertConfiguredProviderAvailable(contentContext) {
1540
- const provider = contentContext.provider || "filesystem";
1541
- if (provider === "filesystem") return;
1542
- if (contentContext.providers?.[provider]) return;
1543
- if (provider === "cms") {
1544
- throw new Error('content.config.ts sets provider "cms", but no CMS provider module registered it. Add @lupinum/ginko-cms to nuxt.config.ts modules or remove provider: "cms".');
1545
- }
1546
- throw new Error(`content.config.ts sets provider "${provider}", but no provider module registered it. Register a module for "${provider}" or add it to content providers.`);
1547
- }
1548
- function validateCollectionNames(collections) {
1549
- normalizeContentConfigCollectionNames(collections);
1550
- }
1551
- function validateRemovedMarkdownOptions(options) {
1552
- if (options.highlight !== void 0) {
1553
- throw new Error("`content.highlight` was removed. Enable syntax highlighting with `content.markdown.plugins`, for example `[['highlight', { ...options }]]`.");
1554
- }
1555
- const markdown = options.markdown || {};
1556
- const removedOptions = ["mdc", "remarkPlugins", "rehypePlugins", "toc"];
1557
- const removed = removedOptions.filter((key) => typeof markdown[key] !== "undefined");
1558
- if (removed.length) {
1559
- throw new Error(`Removed markdown options: ${removed.map((option) => `content.markdown.${String(option)}`).join(", ")}. Use ordered \`content.markdown.plugins\` entries instead.`);
1560
- }
1561
- }
1562
- async function validateBuiltinMarkdownPlugins(plugins, resolvePath) {
1563
- for (const plugin of plugins) {
1564
- const peerDependency = BUILTIN_PLUGIN_PEER_DEPS[plugin.name];
1565
- if (!peerDependency) {
1566
- continue;
1567
- }
1568
- try {
1569
- await resolvePath(peerDependency);
1570
- } catch (error) {
1571
- throw new Error(`Markdown plugin "${plugin.name}" requires "${peerDependency}" to be installed.`, { cause: error });
1572
- }
1573
- }
1574
- }
1575
- const BUILTIN_PLUGIN_PEER_DEPS = {
1576
- highlight: "shiki",
1577
- math: "katex",
1578
- mermaid: "beautiful-mermaid"
1579
- };
1580
1949
 
1581
1950
  export { module$1 as default };