@lupinum/ginko-content 0.1.2 → 0.1.4

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 +1083 -730
  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 +272 -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 +41 -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,36 @@
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 { genSafeVariableName, genImport } from 'knitwork';
6
7
  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
8
  import 'unstorage/drivers/fs';
12
9
  import 'unstorage/drivers/http';
13
10
  import 'unstorage/drivers/github';
14
- import { WebSocketServer } from 'ws';
15
11
  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
12
  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
13
  import { resolveCollectionI18nConfig } from '../dist/features/localization/config';
14
+ import { readFile, readdir } from 'node:fs/promises';
15
+ import { join as join$1, basename, resolve as resolve$1, relative as relative$1, dirname } from 'node:path';
25
16
  import { globby } from 'globby';
26
- import { transformContent } from '../dist/parsers/index';
27
- import { expandDataLocaleVariants } from '../dist/core/content/locale';
28
17
  import { buildContentGraph } from '../dist/core/content/graph';
18
+ import { expandDataLocaleVariants } from '../dist/core/content/locale';
29
19
  import { normalizeCollectionSources, normalizeCollectionExcludes } from '../dist/core/content/sources';
30
- import { resolveCollectionI18n, prefixPathWithLocale } from '../dist/features/localization/path';
31
20
  import { resolveCollection } from '../dist/core/content/collection';
21
+ import { transformContent } from '../dist/parsers/index';
22
+ import { resolveCollectionI18n, prefixPathWithLocale } from '../dist/features/localization/path';
23
+ import { resolveContentSitemapSource, GINKO_SITEMAP_SOURCE_NAME } from '../dist/runtime/utils/sitemap-source';
32
24
  import { normalizeRouteMounts, normalizeContentPath } from '../dist/core/content/path';
25
+ import { mkdirSync, writeFileSync } from 'node:fs';
26
+ import { pathToFileURL } from 'node:url';
27
+ import { withTrailingSlash } from 'ufo';
28
+ import { normalizeContentConfigCollectionNames } from '../dist/types/config';
29
+ export { agentMetadataFields, defineAgentAppPage, defineAgentMarkdownPolicy, defineAgentMetadataFields, defineAgentSection, defineCollection, defineContentConfig, reference } from '../dist/types/config';
33
30
  import { collectTopLevelReferenceFieldsByTarget } from '../dist/core/references/schema';
34
31
 
35
32
  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
- }
33
+ const version = "0.1.4";
101
34
 
102
35
  const CONFIG_FILES = [
103
36
  "content.config.ts",
@@ -119,8 +52,6 @@ async function loadContentConfig(nuxt) {
119
52
  return loaded?.default || loaded || {};
120
53
  }
121
54
 
122
- const require$1 = createRequire(import.meta.url);
123
- const jitiImportSpecifier = pathToFileURL(require$1.resolve("jiti")).href;
124
55
  const createVirtualContentTemplates = (contentContext, nuxt, contentConfigPath, addTemplateImpl) => {
125
56
  const transformersTemplate = addTemplateImpl({
126
57
  filename: "content/virtual-transformers.mjs",
@@ -143,10 +74,8 @@ const createVirtualContentTemplates = (contentContext, nuxt, contentConfigPath,
143
74
  filename: "content/virtual-config.mjs",
144
75
  write: true,
145
76
  getContents: () => contentConfigPath ? [
146
- `import jiti from ${JSON.stringify(jitiImportSpecifier)}`,
147
- `const importer = jiti(import.meta.url, { interopDefault: true })`,
148
- `const config = await importer.import(${JSON.stringify(contentConfigPath)})`,
149
- "export default config?.default || config || {}"
77
+ `import config from ${JSON.stringify(contentConfigPath)}`,
78
+ "export default config || {}"
150
79
  ].join("\n") : "export default {}"
151
80
  }).dst;
152
81
  const virtualProvidersTemplate = addTemplateImpl({
@@ -175,7 +104,7 @@ const createVirtualContentTemplates = (contentContext, nuxt, contentConfigPath,
175
104
  write: true,
176
105
  getContents: () => {
177
106
  const cache = contentContext.cache;
178
- if (!cache || cache === false) {
107
+ if (!cache) {
179
108
  return [
180
109
  "export const loadContentCacheAdapter = () => undefined",
181
110
  "export default {}"
@@ -211,14 +140,66 @@ const registerVirtualContentAliases = (nuxt, transformersTemplate, virtualConfig
211
140
  });
212
141
  };
213
142
 
143
+ consola.withTag("@lupinum/ginko-content");
144
+ const CACHE_VERSION = 3;
145
+ const MOUNT_PREFIX = "content:source:";
146
+ function useContentMounts(nuxt, storages) {
147
+ const key = (path, prefix = "") => `${MOUNT_PREFIX}${path.replace(/[/:]/g, "_")}${prefix.replace(/\//g, ":")}`;
148
+ const baseDir = nuxt.options.future?.compatibilityVersion === 4 ? nuxt.options.rootDir : nuxt.options.srcDir;
149
+ storages = Object.entries(storages).reduce((mounts, [name, storage]) => {
150
+ mounts[key(storage.name || name, storage.prefix)] = storage;
151
+ return mounts;
152
+ }, {});
153
+ const defaultStorage = key("content");
154
+ if (!storages[defaultStorage]) {
155
+ storages[defaultStorage] = {
156
+ name: defaultStorage,
157
+ driver: "fs",
158
+ base: resolve(baseDir, "content")
159
+ };
160
+ }
161
+ return storages;
162
+ }
163
+ function processMarkdownOptions(options) {
164
+ const anchorLinks = typeof options.anchorLinks === "boolean" ? { depth: options.anchorLinks ? 6 : 0, exclude: [] } : { depth: 4, exclude: [1], ...options.anchorLinks };
165
+ const plugins = options.plugins?.length ? options.plugins : [
166
+ ["toc", { depth: 2, searchDepth: 2 }],
167
+ "summary"
168
+ ];
169
+ return {
170
+ anchorLinks,
171
+ tags: options.tags || {},
172
+ image: options.image,
173
+ plugins: resolveMarkdownPlugins(plugins)
174
+ };
175
+ }
176
+ function resolveMarkdownPlugins(plugins) {
177
+ return (plugins || []).map((plugin) => {
178
+ const [name, options] = Array.isArray(plugin) ? plugin : [plugin, {}];
179
+ return {
180
+ name,
181
+ options
182
+ };
183
+ });
184
+ }
185
+
214
186
  const registerContentDevRuntime = (nuxt, options, contentContext) => {
215
187
  const isIgnored = makeIgnored(contentContext.ignores);
188
+ let viteServer;
189
+ if (options.watch !== false) {
190
+ nuxt.options.vite ||= {};
191
+ nuxt.options.vite.plugins ||= [];
192
+ nuxt.options.vite.plugins.push({
193
+ name: "ginko-content-hmr",
194
+ configureServer(server) {
195
+ viteServer = server;
196
+ }
197
+ });
198
+ }
216
199
  nuxt.hook("nitro:init", async (nitro) => {
217
- if (!options.watch || !options.watch.ws) {
200
+ if (options.watch === false) {
218
201
  return;
219
202
  }
220
- const ws = createWebSocket();
221
- const { server, url } = await listen(() => "Ginko", options.watch.ws);
222
203
  const unwatch = await nitro.storage.watch(async (event, key) => {
223
204
  if (!key.startsWith(MOUNT_PREFIX) || isIgnored(key)) {
224
205
  return;
@@ -227,56 +208,85 @@ const registerContentDevRuntime = (nuxt, options, contentContext) => {
227
208
  await nitro.storage.removeItem("cache:content:_manifest.json");
228
209
  await nitro.storage.removeItem("cache:content:_nav.json");
229
210
  await nitro.storage.removeItem("cache:content:_meta.json");
230
- ws.broadcast({ event, key });
211
+ await nitro.storage.removeItem(`cache:content:parsed:${key}`);
212
+ const payload = { event, key };
213
+ viteServer?.ws.send({
214
+ type: "custom",
215
+ event: "ginko-content:update",
216
+ data: payload
217
+ });
231
218
  });
232
219
  nitro.hooks.hook("close", async () => {
233
220
  await unwatch();
234
- await ws.close();
235
- await server.close();
236
221
  });
237
- server.on("upgrade", ws.serve);
238
- nitro.options.runtimeConfig.public.content.wsUrl = url.replace("http", "ws");
239
222
  await nitro.storage.removeItem("cache:content:_manifest.json");
240
223
  await nitro.storage.removeItem("cache:content:_nav.json");
241
224
  await nitro.storage.removeItem("cache:content:_meta.json");
242
225
  });
243
226
  };
244
227
 
228
+ const runtimeAppImportSpecs = [
229
+ { name: "getCollectionPath", as: "getCollectionPath", from: "./query/routes.js" },
230
+ { name: "useContentHead", as: "useContentHead", from: "./app/composables/head.js" },
231
+ { name: "useContentPage", as: "useContentPage", from: "./app/composables/use-content.js" },
232
+ { name: "useContentOne", as: "useContentOne", from: "./app/composables/use-content.js" },
233
+ { name: "useContentMany", as: "useContentMany", from: "./app/composables/use-content.js" },
234
+ { name: "useContentPagination", as: "useContentPagination", from: "./app/composables/use-content.js" },
235
+ { name: "useContentBacklinks", as: "useContentBacklinks", from: "./app/composables/use-content.js" },
236
+ { name: "useContentResolveOne", as: "useContentResolveOne", from: "./app/composables/use-content.js" },
237
+ { name: "useContentVariants", as: "useContentVariants", from: "./app/composables/use-content.js" },
238
+ { name: "useContentTree", as: "useContentTree", from: "./app/composables/use-content.js" },
239
+ { name: "useContentNavigation", as: "useContentNavigation", from: "./app/composables/use-content.js" },
240
+ { name: "useContentNeighbors", as: "useContentNeighbors", from: "./app/composables/use-content.js" },
241
+ { name: "useContentSwitchLocalePath", as: "useContentSwitchLocalePath", from: "./app/composables/route.js" },
242
+ { name: "useContentSearchData", as: "useContentSearchData", from: "./app/composables/search.js" },
243
+ { name: "useContentSearchResults", as: "useContentSearchResults", from: "./app/composables/search.js" },
244
+ { name: "querySiteData", as: "querySiteData", from: "./app/composables/site-data.js" }
245
+ ];
246
+ const runtimeServerImportSpecs = [
247
+ { name: "one", as: "one" },
248
+ { name: "many", as: "many" },
249
+ { name: "paginate", as: "paginate" },
250
+ { name: "backlinks", as: "backlinks" },
251
+ { name: "resolveOne", as: "resolveOne" },
252
+ { name: "variants", as: "variants" },
253
+ { name: "tree", as: "tree" },
254
+ { name: "neighbors", as: "neighbors" },
255
+ { name: "getCollectionPath", as: "getCollectionPath" },
256
+ { name: "queryCollectionsSitemapEntries", as: "queryCollectionsSitemapEntries" }
257
+ ];
258
+ const generatedContentServerValueNames = [
259
+ ...runtimeServerImportSpecs.map((spec) => spec.name),
260
+ "contentCacheHeaders",
261
+ "noopContentCache",
262
+ "vercelContentCache",
263
+ "clearContentCacheHint",
264
+ "collectContentCacheHint",
265
+ "getContentCacheHint",
266
+ "withContentCache",
267
+ "createContentProviderError"
268
+ ];
269
+ const generatedContentServerTypeSpecs = [
270
+ { local: "ContentCacheAdapter", exported: "ContentCacheAdapter" },
271
+ { local: "ContentCacheHint", exported: "ContentCacheHint" },
272
+ { local: "ContentCacheHintInput", exported: "ContentCacheHintInput" },
273
+ { local: "ContentCacheInvalidateInput", exported: "ContentCacheInvalidateInput" },
274
+ { local: "ContentProvider", exported: "ContentProvider" },
275
+ { local: "ContentProviderCapabilities", exported: "ContentProviderCapabilities" },
276
+ { local: "ContentProviderResult<T>", exported: "ContentProviderResult<T>" },
277
+ { local: "MaybeContentProviderResult<T>", exported: "MaybeContentProviderResult<T>" },
278
+ { local: "ContentProviderErrorCode", exported: "ContentProviderErrorCode" },
279
+ { local: "VercelContentCacheOptions", exported: "VercelContentCacheOptions" }
280
+ ];
245
281
  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
- ]);
282
+ addImports(runtimeAppImportSpecs.map((spec) => ({
283
+ ...spec,
284
+ from: resolveRuntimeModule(spec.from)
285
+ })));
286
+ addServerImports(runtimeServerImportSpecs.map((spec) => ({
287
+ ...spec,
288
+ from: resolveRuntimeModule("./server/index.js")
289
+ })));
280
290
  };
281
291
  const registerRuntimeComponents = (resolve) => {
282
292
  addComponentsDir({
@@ -332,11 +342,11 @@ const registerContentI18nTemplate = (addTemplateImpl, hasNuxtI18nModule) => {
332
342
  ].join("\n")
333
343
  });
334
344
  };
335
- const registerUserContentComponents = async (nuxt, resolve) => {
345
+ const registerUserContentComponents = async (nuxt, _resolve) => {
336
346
  const layers = [...nuxt.options._layers];
337
347
  for (const layer of layers) {
338
348
  const srcDir = layer.config.srcDir;
339
- const globalComponents = resolve(srcDir, "components/content");
349
+ const globalComponents = join(srcDir, "components/content");
340
350
  const dirStat = await fs.promises.stat(globalComponents).catch((error) => {
341
351
  if (error.code === "ENOENT") {
342
352
  return null;
@@ -362,6 +372,12 @@ const registerGeneratedTypes = (contentConfigPath, resolveRuntimeModuleRoot, col
362
372
  const key = JSON.stringify(name);
363
373
  return ` ${key}: StrictParsedContent & __GeneratedCollectionSchema<__ContentCollectionExport<${key}>>`;
364
374
  });
375
+ const contentServerValueDeclarations = generatedContentServerValueNames.map(
376
+ (name) => ` const ${name}: typeof import('${resolveRuntimeModuleRoot("./server")}').${name}`
377
+ );
378
+ const contentServerTypeDeclarations = generatedContentServerTypeSpecs.map(
379
+ (spec) => ` type ${spec.local} = import('${resolveRuntimeModuleRoot("./server")}').${spec.exported}`
380
+ );
365
381
  return addTypeTemplate({
366
382
  filename: "types/content.d.ts",
367
383
  getContents: () => [
@@ -418,42 +434,36 @@ const registerGeneratedTypes = (contentConfigPath, resolveRuntimeModuleRoot, col
418
434
  " interface ContentCollectionI18nMap extends Pick<__GeneratedContentCollectionMap, __GeneratedI18nCollectionNames> {}",
419
435
  "}",
420
436
  "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`,
437
+ ...contentServerValueDeclarations,
438
+ ...contentServerTypeDeclarations,
448
439
  "}"
449
440
  ].filter(Boolean).join("\n")
450
441
  });
451
442
  };
452
443
  const registerDevRuntimePlugin = (resolveRuntimeModule) => {
453
- addPlugin(resolveRuntimeModule("./app/plugins/ws.js"));
444
+ addPlugin(resolveRuntimeModule("./app/plugins/hot-reload.js"));
445
+ };
446
+
447
+ const normalizeAgentRouteOptions = (options) => {
448
+ if (options.agent === false) {
449
+ return {
450
+ routes: false,
451
+ linkHeaders: false,
452
+ markdownNegotiation: false,
453
+ prerender: false
454
+ };
455
+ }
456
+ return {
457
+ routes: options.agent?.routes !== false,
458
+ linkHeaders: options.agent?.linkHeaders !== false,
459
+ markdownNegotiation: options.agent?.markdownNegotiation !== false,
460
+ prerender: options.agent?.prerender !== false
461
+ };
454
462
  };
455
463
 
456
464
  const registerContentServerHandlers = (nuxt, options, resolveRuntimeModule, buildIntegrity) => {
465
+ const revalidate = options.revalidate === false ? void 0 : options.revalidate;
466
+ const sitemap = options.sitemap === false ? false : options.sitemap === true ? { path: "/sitemap" } : options.sitemap;
457
467
  addServerHandler({
458
468
  method: "get",
459
469
  route: `${options.api.baseURL}/query/**:params`,
@@ -474,7 +484,7 @@ const registerContentServerHandlers = (nuxt, options, resolveRuntimeModule, buil
474
484
  route: `${options.api.baseURL}/site-data`,
475
485
  handler: resolveRuntimeModule("./server/api/site-data.js")
476
486
  });
477
- if (options.revalidate && options.revalidate !== false && options.revalidate.token) {
487
+ if (revalidate?.token) {
478
488
  addServerHandler({
479
489
  method: "post",
480
490
  route: `${options.api.baseURL}/revalidate`,
@@ -486,10 +496,10 @@ const registerContentServerHandlers = (nuxt, options, resolveRuntimeModule, buil
486
496
  route: nuxt.options.dev ? `${options.api.baseURL}/cache.json` : `${options.api.baseURL}/cache.${buildIntegrity}.json`,
487
497
  handler: resolveRuntimeModule("./server/api/cache.js")
488
498
  });
489
- if (options.sitemap !== false) {
499
+ if (sitemap !== false) {
490
500
  addServerHandler({
491
501
  method: "get",
492
- route: `${options.api.baseURL}${options.sitemap.path || "/sitemap"}`,
502
+ route: `${options.api.baseURL}${sitemap.path || "/sitemap"}`,
493
503
  handler: resolveRuntimeModule("./server/api/sitemap.js")
494
504
  });
495
505
  }
@@ -505,6 +515,46 @@ const registerContentServerHandlers = (nuxt, options, resolveRuntimeModule, buil
505
515
  handler: resolveRuntimeModule("./server/api/navigation.js")
506
516
  });
507
517
  }
518
+ const agent = normalizeAgentRouteOptions(options);
519
+ if (agent.routes) {
520
+ addServerHandler({
521
+ method: "get",
522
+ route: "/llms.txt",
523
+ handler: resolveRuntimeModule("./server/api/agent-llms.js")
524
+ });
525
+ addServerHandler({
526
+ method: "get",
527
+ route: "/llms-full.txt",
528
+ handler: resolveRuntimeModule("./server/api/agent-llms-full.js")
529
+ });
530
+ addServerHandler({
531
+ method: "get",
532
+ route: "/:locale/llms.txt",
533
+ handler: resolveRuntimeModule("./server/api/agent-llms.js")
534
+ });
535
+ addServerHandler({
536
+ method: "get",
537
+ route: "/:locale/llms-full.txt",
538
+ handler: resolveRuntimeModule("./server/api/agent-llms-full.js")
539
+ });
540
+ addServerHandler({
541
+ method: "get",
542
+ route: "/raw/**:slug",
543
+ handler: resolveRuntimeModule("./server/api/agent-raw.js")
544
+ });
545
+ }
546
+ if (agent.markdownNegotiation) {
547
+ addServerHandler({
548
+ middleware: true,
549
+ handler: resolveRuntimeModule("./server/middleware/agent-markdown.js")
550
+ });
551
+ }
552
+ if (agent.linkHeaders) {
553
+ addServerHandler({
554
+ middleware: true,
555
+ handler: resolveRuntimeModule("./server/middleware/agent-link-headers.js")
556
+ });
557
+ }
508
558
  };
509
559
  const registerContentSearchServerHandlers = (apiBaseURL, search, resolveRuntimeModule) => {
510
560
  const searchBaseURL = search.apiBaseURL || `${apiBaseURL.replace(/\/$/, "")}/search`;
@@ -523,12 +573,58 @@ const registerContentSearchServerHandlers = (apiBaseURL, search, resolveRuntimeM
523
573
  }
524
574
  };
525
575
 
526
- const SITEMAP_INDEX = "sitemap_index.xml";
527
- const assert = (condition, message) => {
528
- if (!condition) {
529
- throw new Error(message);
530
- }
576
+ const registerContentComponentsTemplate = (addTemplateImpl) => {
577
+ addTemplateImpl({
578
+ filename: "content-components.mjs",
579
+ getContents({ app, nuxt }) {
580
+ const componentsMap = app.components.filter((component) => {
581
+ if (component.island) {
582
+ return false;
583
+ }
584
+ if (component.filePath.endsWith(".css")) {
585
+ return false;
586
+ }
587
+ if (component.pascalName === "ClientOnly" || component.filePath.includes("nuxt/dist/") || component.filePath.includes("NuxtWelcome")) {
588
+ return false;
589
+ }
590
+ return true;
591
+ }).reduce((map, component) => {
592
+ const importPath = isAbsolute(component.filePath) ? "./" + relative(nuxt.options.buildDir, component.filePath).replace(/\b\.(?!vue)\w+$/g, "") : component.filePath.replace(/\b\.(?!vue)\w+$/g, "");
593
+ map[component.pascalName] = map[component.pascalName] || [
594
+ component.pascalName,
595
+ importPath,
596
+ component.global,
597
+ component.export || "default"
598
+ ];
599
+ return map;
600
+ }, {});
601
+ const componentsList = Object.values(componentsMap);
602
+ const globalComponents = componentsList.filter((component) => component[2]).map((component) => component[0]);
603
+ const localComponents = componentsList.filter((component) => !component[2]);
604
+ return [
605
+ "const pickExport = (mod, exportName, componentName, path) => {",
606
+ " const resolved = exportName === 'default' ? mod?.default || mod : mod?.[exportName]",
607
+ " if (!resolved) {",
608
+ ' throw new Error(`[nuxt-content] Missing export "${exportName}" for component "${componentName}" in "${path}".`)',
609
+ " }",
610
+ " return resolved",
611
+ "}",
612
+ "export const localComponentLoaders = {",
613
+ ...localComponents.map(([pascalName, path, , exp]) => {
614
+ const pathLiteral = JSON.stringify(path);
615
+ const exportLiteral = JSON.stringify(exp);
616
+ const nameLiteral = JSON.stringify(pascalName);
617
+ return ` ${pascalName}: () => import(${pathLiteral}).then(mod => pickExport(mod, ${exportLiteral}, ${nameLiteral}, ${pathLiteral})),`;
618
+ }),
619
+ "}",
620
+ `export const globalComponents = ${JSON.stringify(globalComponents)}`,
621
+ `export const localComponents = ${JSON.stringify(localComponents.map((component) => component[0]))}`
622
+ ].join("\n");
623
+ }
624
+ });
531
625
  };
626
+
627
+ const SITEMAP_INDEX = "sitemap_index.xml";
532
628
  const countTag = (xml, tag) => (xml.match(new RegExp(`<${tag}>`, "g")) || []).length;
533
629
  const extractLocValues = (xml) => {
534
630
  return Array.from(xml.matchAll(/<loc>([^<]+)<\/loc>/g), (match) => match[1]);
@@ -595,10 +691,9 @@ const discoverSitemapsFromDisk = async (outputPublicDir) => {
595
691
  }
596
692
  const rootSitemapPath = join$1(outputPublicDir, "sitemap.xml");
597
693
  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
- );
694
+ if (!(countTag(rootSitemapXml, "url") > 0 || rootSitemapXml.includes("<urlset"))) {
695
+ throw new Error(`Content sitemap assertion failed: no child sitemap files were discovered in ${indexPath} or ${sitemapDir}, and ${rootSitemapPath} is not a urlset sitemap.`);
696
+ }
602
697
  return [{
603
698
  name: "sitemap",
604
699
  path: rootSitemapPath,
@@ -633,7 +728,9 @@ async function assertGeneratedSitemaps({
633
728
  logger
634
729
  }) {
635
730
  const discoveredSitemaps = targets || (outputPublicDir ? await discoverSitemapsFromDisk(outputPublicDir) : []);
636
- assert(discoveredSitemaps.length > 0, "Content sitemap assertion failed: no sitemap targets were available for validation.");
731
+ if (discoveredSitemaps.length === 0) {
732
+ throw new Error("Content sitemap assertion failed: no sitemap targets were available for validation.");
733
+ }
637
734
  const targetMap = new Map(discoveredSitemaps.map((target) => [target.name, target]));
638
735
  const failures = [];
639
736
  for (const sitemapName of Object.keys(options.sitemaps)) {
@@ -675,7 +772,7 @@ async function assertGeneratedSitemaps({
675
772
  failures.push([
676
773
  `- Placeholder sitemap URLs found: ${placeholderSiteUrls.join(", ")}`,
677
774
  "Expected production URLs in generated sitemap loc values.",
678
- "Set site.url or runtimeConfig.public.siteUrl to the deployed origin for production release checks."
775
+ "Set site.url or runtimeConfig.public.content.siteUrl to the deployed origin for production release checks."
679
776
  ].join(" "));
680
777
  }
681
778
  }
@@ -707,283 +804,14 @@ const createSitemapAssertionTargetsFromPrerenderedSitemaps = (sitemaps) => {
707
804
  }
708
805
  return true;
709
806
  });
710
- assert(targets.length > 0, "Content sitemap assertion failed: no sitemap urlsets were available from sitemap:prerender:done.");
807
+ if (targets.length === 0) {
808
+ throw new Error("Content sitemap assertion failed: no sitemap urlsets were available from sitemap:prerender:done.");
809
+ }
711
810
  return targets;
712
811
  };
713
812
 
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) {
813
+ const resolveSitemapCollections = (collections, sitemap) => {
814
+ if (!sitemap) {
987
815
  return [];
988
816
  }
989
817
  const include = sitemap.include?.length ? sitemap.include : Object.keys(collections);
@@ -997,7 +825,7 @@ const toTranslatedSlugSourcePattern = (source) => {
997
825
  return match ? `${match[1]}.*${match[2] || ""}` : segment;
998
826
  }).join("/");
999
827
  };
1000
- const parseCollectionFiles = async (rootDir, collections, collectionNames, contentContext) => {
828
+ const parseDerivedCollectionFiles = async (rootDir, collections, collectionNames, contentContext) => {
1001
829
  const contentDir = resolve$1(rootDir, "content");
1002
830
  const patterns = Array.from(new Set(
1003
831
  collectionNames.flatMap((name) => {
@@ -1028,7 +856,7 @@ const parseCollectionFiles = async (rootDir, collections, collectionNames, conte
1028
856
  for (const id of ids) {
1029
857
  const file = resolve$1(contentDir, id);
1030
858
  const content = await readFile(file, "utf8");
1031
- const normalizedId = `content:${relative$1(contentDir, file).replaceAll("\\", "/")}`;
859
+ const normalizedId = `content:${relative$1(contentDir, file).replace(/\\/g, "/")}`;
1032
860
  const parsed = await transformContent(normalizedId, content, {
1033
861
  markdown: contentContext.markdown,
1034
862
  yaml: contentContext.yaml,
@@ -1042,29 +870,36 @@ const parseCollectionFiles = async (rootDir, collections, collectionNames, conte
1042
870
  collectionResolver: (filePath) => resolveCollection(filePath, collections, contentContext.locales || [])
1043
871
  }
1044
872
  });
1045
- const collection = parsed._collection ? collections[parsed._collection] : void 0;
1046
- documents.push(...expandDataLocaleVariants(parsed, collection?.i18n));
873
+ const collection = typeof parsed._collection === "string" ? collections[parsed._collection] : void 0;
874
+ documents.push(...expandDataLocaleVariants(parsed, collection?.i18n === true ? void 0 : collection?.i18n));
1047
875
  }
1048
876
  return documents;
1049
877
  };
1050
- const collectPrerenderRoutes = async (rootDir, contentContext) => {
878
+ const collectDerivedDocuments = async (rootDir, contentContext) => {
1051
879
  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 [];
880
+ const includedCollections = resolveSitemapCollections(collections, contentContext.sitemap);
881
+ if (!includedCollections.length) {
882
+ return { collections, includedCollections, graph: buildContentGraph([], { locales: contentContext.locales, defaultLocale: contentContext.defaultLocale }) };
1058
883
  }
1059
- const documents = await parseCollectionFiles(rootDir, collections, includeCollections, contentContext);
884
+ const documents = await parseDerivedCollectionFiles(rootDir, collections, includedCollections, contentContext);
1060
885
  const graph = buildContentGraph(documents, {
1061
886
  locales: contentContext.locales,
1062
887
  defaultLocale: contentContext.defaultLocale
1063
888
  });
889
+ return { collections, includedCollections, graph };
890
+ };
891
+ const collectDerivedPrerenderRoutes = async (rootDir, contentContext) => {
892
+ if (!contentContext.sitemap) {
893
+ return [];
894
+ }
895
+ const { includedCollections, graph } = await collectDerivedDocuments(rootDir, contentContext);
896
+ if (!includedCollections.length) {
897
+ return [];
898
+ }
1064
899
  const routes = /* @__PURE__ */ new Set();
1065
- for (const collection of includeCollections) {
900
+ for (const collection of includedCollections) {
1066
901
  const { defaultLocale } = resolveCollectionI18n(collection, contentContext);
1067
- const includeDrafts = contentContext.sitemap && contentContext.sitemap !== false ? Boolean(contentContext.sitemap.includeDrafts) : false;
902
+ const includeDrafts = Boolean(contentContext.sitemap.includeDrafts);
1068
903
  const seenCanonicalKeys = /* @__PURE__ */ new Set();
1069
904
  for (const contentId of graph.byCollection[collection] || []) {
1070
905
  const document = graph.byId[contentId];
@@ -1091,18 +926,12 @@ const collectPrerenderRoutes = async (rootDir, contentContext) => {
1091
926
  return Array.from(routes).sort();
1092
927
  };
1093
928
  const collectSitemapCollectionRouteCounts = async (rootDir, contentContext) => {
1094
- const collections = contentContext.collections || {};
1095
- const includedCollections = resolveSitemapCollections(collections, contentContext.sitemap);
929
+ const { includedCollections, graph } = await collectDerivedDocuments(rootDir, contentContext);
1096
930
  if (!includedCollections.length) {
1097
931
  return {};
1098
932
  }
1099
- const documents = await parseCollectionFiles(rootDir, collections, includedCollections, contentContext);
1100
- const graph = buildContentGraph(documents, {
1101
- locales: contentContext.locales,
1102
- defaultLocale: contentContext.defaultLocale
1103
- });
1104
933
  const counts = Object.fromEntries(includedCollections.map((collection) => [collection, 0]));
1105
- const includeDrafts = contentContext.sitemap && contentContext.sitemap !== false ? Boolean(contentContext.sitemap.includeDrafts) : false;
934
+ const includeDrafts = contentContext.sitemap ? Boolean(contentContext.sitemap.includeDrafts) : false;
1106
935
  for (const collection of includedCollections) {
1107
936
  const seenCanonicalKeys = /* @__PURE__ */ new Set();
1108
937
  for (const contentId of graph.byCollection[collection] || []) {
@@ -1126,12 +955,32 @@ const collectSitemapCollectionRouteCounts = async (rootDir, contentContext) => {
1126
955
  }
1127
956
  return counts;
1128
957
  };
958
+
959
+ const appendHook = (hooks, name, handler) => {
960
+ const existing = hooks[name];
961
+ if (!existing) {
962
+ hooks[name] = handler;
963
+ return;
964
+ }
965
+ hooks[name] = Array.isArray(existing) ? [...existing, handler] : [existing, handler];
966
+ };
1129
967
  const registerContentNitroIntegrationHooks = (nitroConfig, options, contentContext) => {
1130
- const usesFilesystemProvider = !contentContext.provider || contentContext.provider === "filesystem";
968
+ const getHookContentContext = () => {
969
+ if (!options.resolveContentContext) {
970
+ return contentContext;
971
+ }
972
+ try {
973
+ return options.resolveContentContext();
974
+ } catch {
975
+ return contentContext;
976
+ }
977
+ };
1131
978
  nitroConfig.hooks ||= {};
1132
- if (contentContext.sitemap?.assert?.enabled) {
979
+ if (contentContext.sitemap && contentContext.sitemap.assert?.enabled) {
1133
980
  appendHook(nitroConfig.hooks, "compiled", async (nitro) => {
1134
- const assertOptions = contentContext.sitemap?.assert;
981
+ const hookContentContext = getHookContentContext();
982
+ const hookUsesFilesystemProvider = !hookContentContext.provider || hookContentContext.provider === "filesystem";
983
+ const assertOptions = hookContentContext.sitemap ? hookContentContext.sitemap.assert : void 0;
1135
984
  if (!assertOptions || !shouldRunSitemapAssertionOnCompiled(assertOptions, nitro)) {
1136
985
  return;
1137
986
  }
@@ -1139,11 +988,11 @@ const registerContentNitroIntegrationHooks = (nitroConfig, options, contentConte
1139
988
  await assertGeneratedSitemaps({
1140
989
  outputPublicDir: nitro.options.output.publicDir,
1141
990
  options: assertOptions,
1142
- collectionRouteCounts: usesFilesystemProvider ? await collectSitemapCollectionRouteCounts(options.rootDir, contentContext) : {},
991
+ collectionRouteCounts: hookUsesFilesystemProvider ? await collectSitemapCollectionRouteCounts(options.rootDir, hookContentContext) : {},
1143
992
  logger: nitro.logger
1144
993
  });
1145
994
  } catch (error) {
1146
- if (error?.code === "ENOENT") {
995
+ if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
1147
996
  return;
1148
997
  }
1149
998
  throw error;
@@ -1151,8 +1000,10 @@ const registerContentNitroIntegrationHooks = (nitroConfig, options, contentConte
1151
1000
  });
1152
1001
  }
1153
1002
  appendHook(nitroConfig.hooks, "prerender:routes", async (routes) => {
1154
- if (usesFilesystemProvider) {
1155
- for (const route of await collectPrerenderRoutes(options.rootDir, contentContext)) {
1003
+ const hookContentContext = getHookContentContext();
1004
+ const hookUsesFilesystemProvider = !hookContentContext.provider || hookContentContext.provider === "filesystem";
1005
+ if (hookUsesFilesystemProvider) {
1006
+ for (const route of await collectDerivedPrerenderRoutes(options.rootDir, hookContentContext)) {
1156
1007
  routes.add(route);
1157
1008
  }
1158
1009
  }
@@ -1163,12 +1014,157 @@ const registerContentNitroIntegrationHooks = (nitroConfig, options, contentConte
1163
1014
  });
1164
1015
  };
1165
1016
 
1166
- const getContentPageMeta = (page) => {
1167
- const content = page.meta?.content;
1168
- return content && typeof content === "object" ? content : void 0;
1169
- };
1170
- const isRouteConfig = (value) => typeof value === "string" || value !== null && typeof value === "object" && Object.values(value).every((entry) => typeof entry === "string");
1171
- const describePage = (page) => page.file || page.path || "<unknown page>";
1017
+ function hasNuxtModule(modules = [], name) {
1018
+ return modules.some((entry) => {
1019
+ if (typeof entry === "string") {
1020
+ return entry === name;
1021
+ }
1022
+ if (Array.isArray(entry) && typeof entry[0] === "string") {
1023
+ return entry[0] === name;
1024
+ }
1025
+ return false;
1026
+ });
1027
+ }
1028
+ function hasNuxtI18nModule(modules = []) {
1029
+ return hasNuxtModule(modules, "@nuxtjs/i18n");
1030
+ }
1031
+ function hasNuxtSitemapModule(modules = []) {
1032
+ return hasNuxtModule(modules, "@nuxtjs/sitemap");
1033
+ }
1034
+ function configureNuxtSitemapSource(nuxt, apiBaseURL, sitemapPath = "/sitemap") {
1035
+ if (!hasNuxtSitemapModule(nuxt.options.modules)) {
1036
+ return;
1037
+ }
1038
+ const source = {
1039
+ context: {
1040
+ name: GINKO_SITEMAP_SOURCE_NAME
1041
+ },
1042
+ fetch: resolveContentSitemapSource(apiBaseURL, sitemapPath)
1043
+ };
1044
+ const sitemap = nuxt.options.sitemap ??= {};
1045
+ const sources = Array.isArray(sitemap.sources) ? sitemap.sources : [];
1046
+ sitemap.sources = [
1047
+ ...sources.filter((item) => {
1048
+ if (typeof item === "string") return item !== source.fetch;
1049
+ return item?.fetch !== source.fetch && item?.context?.name !== GINKO_SITEMAP_SOURCE_NAME;
1050
+ }),
1051
+ source
1052
+ ];
1053
+ }
1054
+ function resolveNuxtSitemapPrerenderRoutes(nuxt) {
1055
+ if (!hasNuxtSitemapModule(nuxt.options.modules)) {
1056
+ return [];
1057
+ }
1058
+ const nuxtSitemap = nuxt.options.sitemap;
1059
+ if (nuxtSitemap?.sitemaps === false) {
1060
+ return ["/sitemap.xml"];
1061
+ }
1062
+ const nuxtI18n = nuxt.options.i18n || {};
1063
+ if (!hasNuxtI18nModule(nuxt.options.modules) || !Array.isArray(nuxtI18n.locales) || nuxtI18n.locales.length === 0) {
1064
+ return ["/sitemap.xml"];
1065
+ }
1066
+ const childRoutes = nuxtI18n.locales.map((locale) => typeof locale === "string" ? locale : locale.language || locale.code).filter(Boolean).map((locale) => `/__sitemap__/${locale}.xml`);
1067
+ return Array.from(/* @__PURE__ */ new Set(["/sitemap.xml", "/sitemap_index.xml", ...childRoutes]));
1068
+ }
1069
+ function resolveModuleI18nOptions(options, nuxt) {
1070
+ if (options.i18n === false) {
1071
+ return {
1072
+ locales: [],
1073
+ defaultLocale: void 0,
1074
+ fallback: {},
1075
+ translatedSlugs: false,
1076
+ strictTranslatedSlugs: false
1077
+ };
1078
+ }
1079
+ const moduleI18n = options.i18n === true ? {} : options.i18n || {};
1080
+ const nuxtI18n = nuxt.options.i18n || {};
1081
+ const nuxtLocales = Array.isArray(nuxtI18n.locales) ? nuxtI18n.locales.map((locale) => typeof locale === "string" ? locale : locale.code).filter(Boolean) : [];
1082
+ const locales = Array.from(new Set([
1083
+ moduleI18n.defaultLocale,
1084
+ nuxtI18n.defaultLocale,
1085
+ ...moduleI18n.locales || [],
1086
+ ...nuxtLocales
1087
+ ].filter(Boolean)));
1088
+ const defaultLocale = moduleI18n.defaultLocale || nuxtI18n.defaultLocale || locales[0];
1089
+ return {
1090
+ locales,
1091
+ defaultLocale,
1092
+ fallback: defu({}, moduleI18n.fallback),
1093
+ translatedSlugs: moduleI18n.translatedSlugs ?? false,
1094
+ strictTranslatedSlugs: moduleI18n.strictTranslatedSlugs ?? false
1095
+ };
1096
+ }
1097
+ function normalizeSitemapOptions(options) {
1098
+ if (options.sitemap === false) {
1099
+ return false;
1100
+ }
1101
+ const sitemap = options.sitemap === true ? {} : options.sitemap;
1102
+ return {
1103
+ path: sitemap.path || "/sitemap",
1104
+ include: sitemap.include,
1105
+ exclude: sitemap.exclude || [],
1106
+ includeDrafts: sitemap.includeDrafts,
1107
+ assert: normalizeContentSitemapAssertOptions(sitemap.assert)
1108
+ };
1109
+ }
1110
+ function normalizeSearchOptions(options) {
1111
+ if (options.search === false) {
1112
+ return false;
1113
+ }
1114
+ return {
1115
+ engine: options.search?.engine || "minisearch",
1116
+ ignoredTags: options.search?.ignoredTags || ["script", "style", "pre"],
1117
+ filterQuery: options.search?.filterQuery || { _draft: false, _partial: false },
1118
+ collections: options.search?.collections,
1119
+ extraFields: options.search?.extraFields || [],
1120
+ apiBaseURL: options.search?.apiBaseURL,
1121
+ minisearch: normalizeMiniSearchOptions(options.search?.minisearch)
1122
+ };
1123
+ }
1124
+ const defaultMiniSearchOptions = {
1125
+ fields: ["title", "content", "headings"],
1126
+ storeFields: ["path", "title", "excerpt", "anchor", "locale", "collection"],
1127
+ boost: {
1128
+ title: 4,
1129
+ headings: 2,
1130
+ content: 1
1131
+ },
1132
+ fuzzy: 0.2,
1133
+ prefix: true
1134
+ };
1135
+ const requiredMiniSearchStoreFields = ["path", "title", "excerpt"];
1136
+ function normalizeMiniSearchOptions(options = {}) {
1137
+ const fields = options?.fields?.filter((field) => typeof field === "string" && field.length > 0);
1138
+ const storeFields = options?.storeFields?.filter((field) => typeof field === "string" && field.length > 0);
1139
+ const boost = Object.fromEntries(
1140
+ Object.entries(options?.boost || {}).filter((entry) => typeof entry[0] === "string" && entry[0].length > 0 && typeof entry[1] === "number" && Number.isFinite(entry[1]))
1141
+ );
1142
+ const resolvedFields = fields?.length ? fields : [...defaultMiniSearchOptions.fields];
1143
+ const resolvedStoreFields = storeFields?.length ? storeFields : defaultMiniSearchOptions.storeFields;
1144
+ return {
1145
+ fields: resolvedFields,
1146
+ storeFields: Array.from(/* @__PURE__ */ new Set([...requiredMiniSearchStoreFields, ...resolvedStoreFields])),
1147
+ boost: Object.keys(boost).length ? boost : { ...defaultMiniSearchOptions.boost },
1148
+ fuzzy: typeof options?.fuzzy === "boolean" || typeof options?.fuzzy === "number" ? options.fuzzy : defaultMiniSearchOptions.fuzzy,
1149
+ prefix: typeof options?.prefix === "boolean" ? options.prefix : defaultMiniSearchOptions.prefix
1150
+ };
1151
+ }
1152
+ function createSearchRuntimeConfig(search, apiBaseURL) {
1153
+ const resolvedApiBaseURL = search.apiBaseURL || `${apiBaseURL.replace(/\/$/, "")}/search`;
1154
+ return {
1155
+ apiBaseURL: resolvedApiBaseURL,
1156
+ indexURL: `${resolvedApiBaseURL.replace(/\/$/, "")}/index.json`,
1157
+ engine: search.engine || "minisearch",
1158
+ minisearch: normalizeMiniSearchOptions(search.minisearch)
1159
+ };
1160
+ }
1161
+
1162
+ const getContentPageMeta = (page) => {
1163
+ const content = page.meta?.content;
1164
+ return content && typeof content === "object" ? content : void 0;
1165
+ };
1166
+ const isRouteConfig = (value) => typeof value === "string" || value !== null && typeof value === "object" && Object.values(value).every((entry) => typeof entry === "string");
1167
+ const describePage = (page) => page.file || page.path || "<unknown page>";
1172
1168
  const expectedLocalesFor = (collection, locales, defaultLocale) => {
1173
1169
  const collectionI18n = collection.i18n && typeof collection.i18n === "object" ? collection.i18n : void 0;
1174
1170
  return collectionI18n?.locales?.length ? collectionI18n.locales : locales.length ? locales : defaultLocale ? [defaultLocale] : [];
@@ -1218,6 +1214,556 @@ function validatePage(page, contentMeta, collections, options) {
1218
1214
  }
1219
1215
  }
1220
1216
 
1217
+ const hasAgentSurface = (config) => Boolean(
1218
+ config.agent || Object.values(config.collections || {}).some((collection) => Boolean(collection.agent?.markdown))
1219
+ );
1220
+ const agentSectionIds = (sections) => new Set((sections || []).map((section) => section.id).filter(Boolean));
1221
+ const validateAgentConfig = (config, options, context) => {
1222
+ if (!hasAgentSurface(config)) return;
1223
+ const agentRoutes = normalizeAgentRouteOptions(options);
1224
+ if (!context.dev && agentRoutes.routes && agentRoutes.prerender && !config.agent?.site?.url) {
1225
+ throw new Error(
1226
+ "@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."
1227
+ );
1228
+ }
1229
+ const sections = agentSectionIds(config.agent?.sections);
1230
+ const seenSections = /* @__PURE__ */ new Set();
1231
+ for (const section of config.agent?.sections || []) {
1232
+ if (!section.id) {
1233
+ throw new Error("@lupinum/ginko-content agent section ids must be non-empty strings.");
1234
+ }
1235
+ if (seenSections.has(section.id)) {
1236
+ throw new Error(`Duplicate Ginko agent section id "${section.id}". Agent section ids must be unique.`);
1237
+ }
1238
+ seenSections.add(section.id);
1239
+ }
1240
+ const assertSection = (id, owner) => {
1241
+ if (!id || id === "content") return;
1242
+ if (!sections.has(id)) {
1243
+ throw new Error(
1244
+ `${owner} references unknown Ginko agent section "${id}". Declare it in agent.sections or use the default "content" section.`
1245
+ );
1246
+ }
1247
+ };
1248
+ for (const [name, collection] of Object.entries(config.collections || {})) {
1249
+ if (collection.agent?.markdown || collection.agent?.section) {
1250
+ assertSection(collection.agent.section, `Collection "${name}"`);
1251
+ }
1252
+ }
1253
+ for (const page of config.agent?.pages || []) {
1254
+ assertSection(page.section, `Agent app page "${page.id}"`);
1255
+ }
1256
+ };
1257
+
1258
+ const normalizeStaticRoutePath = (path) => {
1259
+ if (!path || path === "/") return "/";
1260
+ return `/${path.replace(/\/+/g, "/").replace(/^\/+|\/+$/g, "")}`;
1261
+ };
1262
+ const collectRawMarkdownLinksFromLlms = (markdown, siteUrl) => {
1263
+ const links = /* @__PURE__ */ new Set();
1264
+ const pattern = /\[[^\]]*\]\(([^)]+)\)/g;
1265
+ for (const match of markdown.matchAll(pattern)) {
1266
+ const href = match[1];
1267
+ if (!href) continue;
1268
+ try {
1269
+ const url = /^[a-z][a-z0-9+.-]*:/i.test(href) ? new URL(href) : new URL(href, siteUrl || "http://localhost:3000");
1270
+ if (url.pathname.startsWith("/raw/") && url.pathname.endsWith(".md")) links.add(url.pathname);
1271
+ } catch {
1272
+ if (href.startsWith("/raw/") && href.endsWith(".md")) links.add(href);
1273
+ }
1274
+ }
1275
+ return Array.from(links);
1276
+ };
1277
+ const rawMarkdownRouteForPageRoute = (route) => {
1278
+ const normalized = normalizeStaticRoutePath(route);
1279
+ return normalized === "/" ? "/raw/index.md" : `/raw${normalized}.md`;
1280
+ };
1281
+ const collectRawMarkdownRoutesFromGeneratedFrontmatter = (markdown) => {
1282
+ const links = /* @__PURE__ */ new Set();
1283
+ const addRoutePath = (route) => {
1284
+ links.add(rawMarkdownRouteForPageRoute(route));
1285
+ };
1286
+ const addFrontmatterRoute = (frontmatter) => {
1287
+ const route = /^route:\s*"([^"]+)"/m.exec(frontmatter || "")?.[1];
1288
+ if (!route) return;
1289
+ addRoutePath(route);
1290
+ };
1291
+ const addSourceRoute = (source) => {
1292
+ if (!source) return;
1293
+ try {
1294
+ addRoutePath(new URL(source, "http://localhost:3000").pathname);
1295
+ } catch {
1296
+ addRoutePath(source);
1297
+ }
1298
+ };
1299
+ const sourcePattern = /^Source:\s+(\S+)/gm;
1300
+ for (const match of markdown.matchAll(sourcePattern)) {
1301
+ addSourceRoute(match[1]);
1302
+ }
1303
+ const pageFrontmatterPattern = /^Source:\s+[^\n]+\n\n---\n([\s\S]*?)\n---/gm;
1304
+ for (const match of markdown.matchAll(pageFrontmatterPattern)) {
1305
+ addFrontmatterRoute(match[1]);
1306
+ }
1307
+ const firstFrontmatter = /^---\n([\s\S]*?)\n---/.exec(markdown)?.[1];
1308
+ addFrontmatterRoute(firstFrontmatter);
1309
+ return Array.from(links);
1310
+ };
1311
+ const publicOutputPath = (publicDir, route) => join(publicDir, normalizeStaticRoutePath(route).replace(/^\//, ""));
1312
+
1313
+ const hookNuxtBoundary$2 = (nuxt, name, callback) => {
1314
+ const hook = nuxt.hook;
1315
+ hook(name, callback);
1316
+ };
1317
+ const registerStaticOutputGeneration = ({
1318
+ nuxt,
1319
+ options,
1320
+ appContentConfig,
1321
+ contentContext,
1322
+ resolvedI18n,
1323
+ resolveRuntimeModule,
1324
+ getSearchRuntime
1325
+ }) => {
1326
+ if (nuxt.options.dev) return;
1327
+ hookNuxtBoundary$2(nuxt, "nitro:build:before", (nitro) => {
1328
+ nitro.hooks.hook("prerender:init", (prerenderer) => {
1329
+ prerenderer.hooks.hook("compiled", async () => {
1330
+ const searchRuntime = getSearchRuntime();
1331
+ const publicDir = nitro.options.output.publicDir || nuxt.options.nitro?.output?.publicDir || resolve$1(nuxt.options.rootDir, ".output/public");
1332
+ const serverFilename = typeof prerenderer.options.rollupConfig?.output?.entryFileNames === "string" ? prerenderer.options.rollupConfig.output.entryFileNames : "index.mjs";
1333
+ const serverEntrypoint = resolve$1(prerenderer.options.output.serverDir, serverFilename);
1334
+ const { localFetch } = await import(pathToFileURL(serverEntrypoint).href);
1335
+ if (searchRuntime !== false && searchRuntime.engine !== "cms" && (!contentContext.provider || contentContext.provider === "filesystem")) {
1336
+ const response = await localFetch(searchRuntime.indexURL);
1337
+ if (!response.ok) {
1338
+ throw new Error(`Failed to generate search index: [${response.status}] ${response.statusText}`);
1339
+ }
1340
+ const json = await response.text();
1341
+ const indexPath = publicOutputPath(publicDir, searchRuntime.indexURL);
1342
+ mkdirSync(dirname(indexPath), { recursive: true });
1343
+ writeFileSync(indexPath, json, "utf8");
1344
+ if (searchRuntime.engine === "pagefind") {
1345
+ const records = JSON.parse(json);
1346
+ const { writePagefindIndex } = await import(resolveRuntimeModule("./server/pagefind.js"));
1347
+ await writePagefindIndex(records, resolve$1(publicDir, "pagefind"));
1348
+ }
1349
+ }
1350
+ const agentRoutes = normalizeAgentRouteOptions(options);
1351
+ if (agentRoutes.routes && agentRoutes.prerender && appContentConfig.agent) {
1352
+ const defaultLocale = appContentConfig.agent.site?.defaultLocale || resolvedI18n.defaultLocale;
1353
+ const locales = appContentConfig.agent.site?.locales?.length ? appContentConfig.agent.site.locales : resolvedI18n.locales;
1354
+ const llmsRoutes = [
1355
+ "/llms.txt",
1356
+ "/llms-full.txt",
1357
+ ...locales.filter((locale) => locale && locale !== defaultLocale).flatMap((locale) => [`/${locale}/llms.txt`, `/${locale}/llms-full.txt`])
1358
+ ];
1359
+ const markdownRoutes = /* @__PURE__ */ new Set();
1360
+ for (const route of llmsRoutes) {
1361
+ const response = await localFetch(route);
1362
+ if (!response.ok) {
1363
+ throw new Error(`Failed to generate agent markdown route ${route}: [${response.status}] ${response.statusText}`);
1364
+ }
1365
+ const body = await response.text();
1366
+ const outputPath = publicOutputPath(publicDir, route);
1367
+ mkdirSync(dirname(outputPath), { recursive: true });
1368
+ writeFileSync(outputPath, body, "utf8");
1369
+ if (/\/llms\.txt$/i.test(route)) {
1370
+ collectRawMarkdownLinksFromLlms(body, appContentConfig.agent.site?.url).forEach((link) => markdownRoutes.add(link));
1371
+ } else if (/\/llms-full\.txt$/i.test(route)) {
1372
+ collectRawMarkdownRoutesFromGeneratedFrontmatter(body).forEach((link) => markdownRoutes.add(link));
1373
+ }
1374
+ }
1375
+ for (const route of markdownRoutes) {
1376
+ const response = await localFetch(route);
1377
+ if (!response.ok) {
1378
+ throw new Error(`Failed to generate agent markdown route ${route}: [${response.status}] ${response.statusText}`);
1379
+ }
1380
+ const body = await response.text();
1381
+ const outputPath = publicOutputPath(publicDir, route);
1382
+ mkdirSync(dirname(outputPath), { recursive: true });
1383
+ writeFileSync(outputPath, body, "utf8");
1384
+ }
1385
+ }
1386
+ });
1387
+ });
1388
+ });
1389
+ };
1390
+
1391
+ const hookNuxtBoundary$1 = (nuxt, name, callback) => {
1392
+ const hook = nuxt.hook;
1393
+ hook(name, callback);
1394
+ };
1395
+ const registerContentNitroConfig = ({
1396
+ nuxt,
1397
+ options,
1398
+ appContentConfig,
1399
+ contentContext,
1400
+ runtimeInlineDependencies,
1401
+ buildIntegrity,
1402
+ resolvedI18n,
1403
+ resolveRuntimeModule,
1404
+ resolveModuleFile,
1405
+ getResolvedContentContext,
1406
+ getSearchRuntime
1407
+ }) => {
1408
+ hookNuxtBoundary$1(nuxt, "nitro:config", (nitroConfig) => {
1409
+ const searchRuntime = getSearchRuntime();
1410
+ nitroConfig.prerender = nitroConfig.prerender || {};
1411
+ nitroConfig.prerender.routes = nitroConfig.prerender.routes || [];
1412
+ const usesFilesystemProvider = !contentContext.provider || contentContext.provider === "filesystem";
1413
+ if (!nuxt.options.dev && usesFilesystemProvider) {
1414
+ nitroConfig.prerender.routes.unshift(`${options.api.baseURL}/cache.${buildIntegrity}.json`);
1415
+ }
1416
+ const sources = useContentMounts(nuxt, contentContext.sources);
1417
+ nitroConfig.devStorage = Object.assign(nitroConfig.devStorage || {}, sources);
1418
+ nitroConfig.devStorage["cache:content"] = {
1419
+ driver: "fs",
1420
+ base: resolve$1(nuxt.options.buildDir, "content-cache")
1421
+ };
1422
+ for (const source of Object.values(sources)) {
1423
+ if (source.driver === "fs" && typeof source.base === "string" && source.base.includes(nuxt.options.srcDir)) {
1424
+ const wildcard = join(source.base, "**/*").replace(withTrailingSlash(nuxt.options.srcDir), "");
1425
+ nuxt.options.ignore.push(wildcard, `!${wildcard}.vue`);
1426
+ }
1427
+ }
1428
+ nitroConfig.bundledStorage = nitroConfig.bundledStorage || [];
1429
+ nitroConfig.bundledStorage.push("cache:content");
1430
+ nitroConfig.externals = defu(typeof nitroConfig.externals === "object" ? nitroConfig.externals : {}, {
1431
+ inline: [
1432
+ resolveModuleFile("./runtime"),
1433
+ ...runtimeInlineDependencies
1434
+ ]
1435
+ });
1436
+ if (searchRuntime !== false && searchRuntime.engine !== "cms" && usesFilesystemProvider) {
1437
+ nitroConfig.routeRules = nitroConfig.routeRules || {};
1438
+ nitroConfig.routeRules[searchRuntime.indexURL] = {
1439
+ prerender: true,
1440
+ headers: { "Content-Type": "application/json; charset=utf-8" }
1441
+ };
1442
+ }
1443
+ if (contentContext.sitemap !== false) {
1444
+ nitroConfig.plugins ||= [];
1445
+ const sitemapPlugin = resolveRuntimeModule("server/plugins/sitemap.js");
1446
+ if (!nitroConfig.plugins.includes(sitemapPlugin)) {
1447
+ nitroConfig.plugins.push(sitemapPlugin);
1448
+ }
1449
+ }
1450
+ if (contentContext.cache) {
1451
+ nitroConfig.plugins ||= [];
1452
+ const cachePlugin = resolveRuntimeModule("server/plugins/cache.js");
1453
+ if (!nitroConfig.plugins.includes(cachePlugin)) {
1454
+ nitroConfig.plugins.push(cachePlugin);
1455
+ }
1456
+ }
1457
+ registerContentNitroIntegrationHooks(nitroConfig, {
1458
+ rootDir: nuxt.options.rootDir,
1459
+ sitemapPrerenderRoutes: () => contentContext.sitemap === false ? [] : resolveNuxtSitemapPrerenderRoutes(nuxt),
1460
+ resolveContentContext: getResolvedContentContext
1461
+ }, {
1462
+ collections: contentContext.collections,
1463
+ locales: contentContext.locales,
1464
+ defaultLocale: contentContext.defaultLocale,
1465
+ translatedSlugs: contentContext.translatedSlugs,
1466
+ respectPathCase: contentContext.respectPathCase,
1467
+ markdown: processMarkdownOptions(contentContext.markdown),
1468
+ yaml: contentContext.yaml,
1469
+ csv: contentContext.csv,
1470
+ sitemap: contentContext.sitemap,
1471
+ provider: contentContext.provider
1472
+ });
1473
+ const agentRoutes = normalizeAgentRouteOptions(options);
1474
+ if (agentRoutes.routes && agentRoutes.prerender && appContentConfig.agent) {
1475
+ nitroConfig.prerender.routes.push("/llms.txt", "/llms-full.txt");
1476
+ for (const locale of appContentConfig.agent.site?.locales || resolvedI18n.locales || []) {
1477
+ if (locale && locale !== (appContentConfig.agent.site?.defaultLocale || resolvedI18n.defaultLocale)) {
1478
+ nitroConfig.prerender.routes.push(`/${locale}/llms.txt`, `/${locale}/llms-full.txt`);
1479
+ }
1480
+ }
1481
+ for (const page of appContentConfig.agent.pages || []) {
1482
+ const routes = typeof page.route === "string" ? [page.route] : Object.values(page.route);
1483
+ for (const route of routes) {
1484
+ const normalized = route === "/" ? "/" : `/${route.replace(/^\/+|\/+$/g, "")}`;
1485
+ nitroConfig.prerender.routes.push(
1486
+ normalized === "/" ? "/raw/index.md" : `/raw${normalized}.md`
1487
+ );
1488
+ }
1489
+ }
1490
+ }
1491
+ });
1492
+ };
1493
+
1494
+ const BUILTIN_PLUGIN_PEER_DEPS = {
1495
+ highlight: "shiki",
1496
+ math: "katex",
1497
+ mermaid: "beautiful-mermaid"
1498
+ };
1499
+ function assertConfiguredProviderAvailable(contentContext) {
1500
+ const provider = contentContext.provider || "filesystem";
1501
+ if (provider === "filesystem") return;
1502
+ if (contentContext.providers?.[provider]) return;
1503
+ if (provider === "cms") {
1504
+ 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".');
1505
+ }
1506
+ 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.`);
1507
+ }
1508
+ function validateCollectionNames(collections) {
1509
+ normalizeContentConfigCollectionNames(collections);
1510
+ }
1511
+ function validateRemovedMarkdownOptions(options) {
1512
+ if (options.highlight !== void 0) {
1513
+ throw new Error("`content.highlight` was removed. Enable syntax highlighting with `content.markdown.plugins`, for example `[['highlight', { ...options }]]`.");
1514
+ }
1515
+ const markdown = options.markdown || {};
1516
+ const removedOptions = ["mdc", "remarkPlugins", "rehypePlugins", "toc"];
1517
+ const removed = removedOptions.filter((key) => typeof markdown[key] !== "undefined");
1518
+ if (removed.length) {
1519
+ throw new Error(`Removed markdown options: ${removed.map((option) => `content.markdown.${String(option)}`).join(", ")}. Use ordered \`content.markdown.plugins\` entries instead.`);
1520
+ }
1521
+ }
1522
+ async function validateBuiltinMarkdownPlugins(plugins, resolvePath) {
1523
+ for (const plugin of plugins) {
1524
+ const peerDependency = BUILTIN_PLUGIN_PEER_DEPS[plugin.name];
1525
+ if (!peerDependency) {
1526
+ continue;
1527
+ }
1528
+ try {
1529
+ await resolvePath(peerDependency);
1530
+ } catch (error) {
1531
+ const next = new Error(`Markdown plugin "${plugin.name}" requires "${peerDependency}" to be installed.`);
1532
+ next.cause = error;
1533
+ throw next;
1534
+ }
1535
+ }
1536
+ }
1537
+
1538
+ const contentModuleDefaults = {
1539
+ api: {
1540
+ baseURL: "/api/_content"
1541
+ },
1542
+ i18n: true,
1543
+ sitemap: true,
1544
+ search: {
1545
+ engine: "minisearch",
1546
+ ignoredTags: ["script", "style", "pre"],
1547
+ filterQuery: { _draft: false, _partial: false },
1548
+ extraFields: [],
1549
+ minisearch: {
1550
+ fields: [...defaultMiniSearchOptions.fields],
1551
+ storeFields: [...defaultMiniSearchOptions.storeFields],
1552
+ boost: { ...defaultMiniSearchOptions.boost },
1553
+ fuzzy: defaultMiniSearchOptions.fuzzy,
1554
+ prefix: defaultMiniSearchOptions.prefix
1555
+ }
1556
+ },
1557
+ watch: true,
1558
+ sources: {},
1559
+ ignores: [],
1560
+ collections: {},
1561
+ markdown: {
1562
+ plugins: [],
1563
+ tags: {
1564
+ code: "ProseCode",
1565
+ img: "ProseImg",
1566
+ pre: "ProsePre"
1567
+ },
1568
+ anchorLinks: {
1569
+ depth: 4,
1570
+ exclude: [1]
1571
+ },
1572
+ image: "auto"
1573
+ },
1574
+ yaml: {},
1575
+ csv: {
1576
+ delimiter: ",",
1577
+ json: true
1578
+ },
1579
+ navigation: {
1580
+ fields: []
1581
+ },
1582
+ agent: {
1583
+ routes: true,
1584
+ linkHeaders: true,
1585
+ markdownNegotiation: true,
1586
+ prerender: true
1587
+ },
1588
+ contentHead: true,
1589
+ respectPathCase: false,
1590
+ experimental: {
1591
+ stripQueryParameters: false
1592
+ }
1593
+ };
1594
+
1595
+ const resolveNuxtSiteUrl = (nuxt) => {
1596
+ const publicRuntime = nuxt.options.runtimeConfig.public;
1597
+ const publicContentRuntime = publicRuntime.content;
1598
+ const configuredSiteUrl = publicContentRuntime?.siteUrl || publicRuntime.siteUrl;
1599
+ if (typeof configuredSiteUrl === "string" && configuredSiteUrl.length > 0) {
1600
+ return configuredSiteUrl;
1601
+ }
1602
+ const nuxtSite = nuxt.options.site;
1603
+ return typeof nuxtSite?.url === "string" && nuxtSite.url.length > 0 ? nuxtSite.url : void 0;
1604
+ };
1605
+ const sanitizePublicMarkdownPluginValue = (value) => {
1606
+ if (typeof value === "function" || typeof value === "symbol") {
1607
+ return void 0;
1608
+ }
1609
+ if (Array.isArray(value)) {
1610
+ return value.map((item) => sanitizePublicMarkdownPluginValue(item)).filter((item) => item !== void 0);
1611
+ }
1612
+ if (typeof value !== "object" || value === null) {
1613
+ return value;
1614
+ }
1615
+ const prototype = Object.getPrototypeOf(value);
1616
+ if (prototype !== Object.prototype && prototype !== null) {
1617
+ return value;
1618
+ }
1619
+ return Object.fromEntries(Object.entries(value).filter(([key]) => key !== "transformers").map(([key, item]) => [key, sanitizePublicMarkdownPluginValue(item)]).filter(([, item]) => item !== void 0));
1620
+ };
1621
+ const sanitizePublicMarkdownPlugins = (plugins) => plugins.map((plugin) => ({
1622
+ name: plugin.name,
1623
+ options: sanitizePublicMarkdownPluginValue(plugin.options || {})
1624
+ }));
1625
+ const sanitizePrivateMarkdownPluginValue = (value) => {
1626
+ if (typeof value === "function" || typeof value === "symbol") {
1627
+ return void 0;
1628
+ }
1629
+ if (Array.isArray(value)) {
1630
+ return value.map((item) => sanitizePrivateMarkdownPluginValue(item)).filter((item) => item !== void 0);
1631
+ }
1632
+ if (typeof value !== "object" || value === null) {
1633
+ return value;
1634
+ }
1635
+ const prototype = Object.getPrototypeOf(value);
1636
+ if (prototype !== Object.prototype && prototype !== null) {
1637
+ return value;
1638
+ }
1639
+ return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, sanitizePrivateMarkdownPluginValue(item)]).filter(([, item]) => item !== void 0));
1640
+ };
1641
+ const sanitizePrivateMarkdownPlugins = (plugins) => plugins.map((plugin) => ({
1642
+ name: plugin.name,
1643
+ options: sanitizePrivateMarkdownPluginValue(plugin.options || {})
1644
+ }));
1645
+ const applyContentRuntimeConfig = (nuxt, options, contentContext, runtimeCollections, buildIntegrity, cacheIntegrity) => {
1646
+ const revalidate = options.revalidate === false ? void 0 : options.revalidate;
1647
+ const searchRuntime = contentContext.search === false ? false : {
1648
+ apiBaseURL: contentContext.search.apiBaseURL || `${options.api.baseURL.replace(/\/$/, "")}/search`,
1649
+ indexURL: `${(contentContext.search.apiBaseURL || `${options.api.baseURL.replace(/\/$/, "")}/search`).replace(/\/$/, "")}/index.json`,
1650
+ engine: contentContext.search.engine || "minisearch",
1651
+ minisearch: normalizeMiniSearchOptions(contentContext.search.minisearch)
1652
+ };
1653
+ const siteUrl = resolveNuxtSiteUrl(nuxt);
1654
+ const contentRuntime = defu(nuxt.options.runtimeConfig.public.content, {
1655
+ ...siteUrl ? { siteUrl } : {},
1656
+ locales: contentContext.locales,
1657
+ provider: contentContext.provider || "filesystem",
1658
+ providers: contentContext.providers || {},
1659
+ defaultLocale: contentContext.defaultLocale || void 0,
1660
+ localeFallback: contentContext.localeFallback || {},
1661
+ translatedSlugs: contentContext.translatedSlugs ?? false,
1662
+ strictTranslatedSlugs: contentContext.strictTranslatedSlugs ?? false,
1663
+ collections: runtimeCollections,
1664
+ integrity: buildIntegrity,
1665
+ experimental: {
1666
+ stripQueryParameters: options.experimental.stripQueryParameters
1667
+ },
1668
+ respectPathCase: options.respectPathCase ?? false,
1669
+ api: {
1670
+ baseURL: options.api.baseURL
1671
+ },
1672
+ markdown: {
1673
+ plugins: sanitizePublicMarkdownPlugins(contentContext.markdown.plugins),
1674
+ tags: contentContext.markdown.tags,
1675
+ anchorLinks: contentContext.markdown.anchorLinks,
1676
+ image: contentContext.markdown.image || "auto"
1677
+ },
1678
+ sitemap: contentContext.sitemap ? {
1679
+ path: contentContext.sitemap.path || "/sitemap",
1680
+ ...contentContext.sitemap.include?.length ? { include: contentContext.sitemap.include } : {},
1681
+ ...contentContext.sitemap.exclude?.length ? { exclude: contentContext.sitemap.exclude } : {},
1682
+ ...typeof contentContext.sitemap.includeDrafts === "boolean" ? { includeDrafts: contentContext.sitemap.includeDrafts } : {}
1683
+ } : false,
1684
+ search: searchRuntime,
1685
+ navigation: contentContext.navigation,
1686
+ contentHead: options.contentHead ?? true
1687
+ });
1688
+ const privateContentRuntime = {
1689
+ ...contentContext,
1690
+ markdown: {
1691
+ ...contentContext.markdown,
1692
+ plugins: sanitizePrivateMarkdownPlugins(contentContext.markdown.plugins)
1693
+ }
1694
+ };
1695
+ nuxt.options.runtimeConfig.public.content = contentRuntime;
1696
+ nuxt.options.runtimeConfig.content = defu(nuxt.options.runtimeConfig.content, {
1697
+ cacheVersion: CACHE_VERSION,
1698
+ cacheIntegrity,
1699
+ revalidate: revalidate?.token ? {
1700
+ token: revalidate.token,
1701
+ allowUnsigned: revalidate.allowUnsigned === true
1702
+ } : false,
1703
+ ...privateContentRuntime,
1704
+ collections: runtimeCollections
1705
+ });
1706
+ };
1707
+
1708
+ const registerContentContextFinalization = ({
1709
+ nuxt,
1710
+ options,
1711
+ contentContext,
1712
+ buildIntegrity,
1713
+ resolvePath,
1714
+ resolveRuntimeModule,
1715
+ onResolved
1716
+ }) => {
1717
+ nuxt.hook("modules:done", async () => {
1718
+ await nuxt.callHook("content:providers", contentContext.providers ||= {});
1719
+ assertConfiguredProviderAvailable(contentContext);
1720
+ await nuxt.callHook("content:context", contentContext);
1721
+ if (contentContext.search !== false) {
1722
+ registerContentSearchServerHandlers(options.api.baseURL, contentContext.search, resolveRuntimeModule);
1723
+ }
1724
+ contentContext.defaultLocale = contentContext.defaultLocale || contentContext.locales[0];
1725
+ const resolvedContentContext = {
1726
+ ...contentContext,
1727
+ markdown: processMarkdownOptions(contentContext.markdown)
1728
+ };
1729
+ onResolved(resolvedContentContext);
1730
+ await validateBuiltinMarkdownPlugins(resolvedContentContext.markdown.plugins, resolvePath);
1731
+ const runtimeCollections = Object.fromEntries(Object.entries(options.collections || {}).map(([name, collection]) => {
1732
+ const references = collectTopLevelReferenceFieldsByTarget(collection.schema);
1733
+ return [
1734
+ name,
1735
+ {
1736
+ ...collection.source ? { source: collection.source } : {},
1737
+ ...collection.exclude ? { exclude: collection.exclude } : {},
1738
+ ...collection.type ? { type: collection.type } : {},
1739
+ strict: collection.strict ?? true,
1740
+ ...collection.route ? { route: collection.route } : {},
1741
+ ...typeof collection.sitemap === "boolean" ? { sitemap: collection.sitemap } : {},
1742
+ ...collection.i18n && collection.i18n !== true ? { i18n: collection.i18n } : {},
1743
+ ...Object.keys(references).length ? { references } : {}
1744
+ }
1745
+ ];
1746
+ }));
1747
+ const cacheIntegrity = hash({
1748
+ locales: resolvedContentContext.locales,
1749
+ defaultLocale: resolvedContentContext.defaultLocale,
1750
+ localeFallback: resolvedContentContext.localeFallback,
1751
+ translatedSlugs: resolvedContentContext.translatedSlugs,
1752
+ strictTranslatedSlugs: resolvedContentContext.strictTranslatedSlugs,
1753
+ respectPathCase: resolvedContentContext.respectPathCase,
1754
+ collections: runtimeCollections,
1755
+ markdown: resolvedContentContext.markdown,
1756
+ yaml: resolvedContentContext.yaml,
1757
+ csv: resolvedContentContext.csv
1758
+ });
1759
+ applyContentRuntimeConfig(nuxt, options, resolvedContentContext, runtimeCollections, buildIntegrity, cacheIntegrity);
1760
+ });
1761
+ };
1762
+
1763
+ const hookNuxtBoundary = (nuxt, name2, callback) => {
1764
+ const hook = nuxt.hook;
1765
+ hook(name2, callback);
1766
+ };
1221
1767
  const module$1 = defineNuxtModule({
1222
1768
  meta: {
1223
1769
  name,
@@ -1236,75 +1782,14 @@ const module$1 = defineNuxtModule({
1236
1782
  }
1237
1783
  }
1238
1784
  },
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
- },
1785
+ defaults: contentModuleDefaults,
1302
1786
  async setup(options, nuxt) {
1303
1787
  const { resolve, resolvePath } = createResolver(import.meta.url);
1304
1788
  const logger = useLogger(name);
1305
1789
  const resolveRuntimeModule = (path) => resolve("./runtime", path);
1306
1790
  const runtimeInlineDependencies = ["comark", "@comark/vue"];
1307
1791
  validateRemovedMarkdownOptions(options);
1792
+ nuxt.options.experimental.payloadExtraction ??= false;
1308
1793
  nuxt.options.build.transpile ||= [];
1309
1794
  for (const dependency of runtimeInlineDependencies) {
1310
1795
  if (!nuxt.options.build.transpile.includes(dependency)) {
@@ -1317,6 +1802,10 @@ const module$1 = defineNuxtModule({
1317
1802
  });
1318
1803
  const contentConfigPath = resolveContentConfigPath(nuxt);
1319
1804
  const appContentConfig = await loadContentConfig(nuxt);
1805
+ const externalGinkoContentConfig = nuxt.options.ginkoContent;
1806
+ if (externalGinkoContentConfig?.agent && options.agent !== false) {
1807
+ options.agent = defu(options.agent || {}, externalGinkoContentConfig.agent);
1808
+ }
1320
1809
  if (!contentConfigPath || !appContentConfig.collections || !Object.keys(appContentConfig.collections).length) {
1321
1810
  throw new Error("@lupinum/ginko-content requires a content.config.ts with at least one collection. Define collections with defineContentConfig({ collections: { ... } }).");
1322
1811
  }
@@ -1324,6 +1813,10 @@ const module$1 = defineNuxtModule({
1324
1813
  const resolvedSitemap = normalizeSitemapOptions(options);
1325
1814
  const resolvedSearch = normalizeSearchOptions(options);
1326
1815
  validateCollectionNames(appContentConfig.collections);
1816
+ if (!hasAgentSurface(appContentConfig)) {
1817
+ options.agent = false;
1818
+ }
1819
+ validateAgentConfig(appContentConfig, options, { dev: nuxt.options.dev });
1327
1820
  options.collections = Object.fromEntries(Object.entries(appContentConfig.collections).map(([name2, collection]) => [
1328
1821
  name2,
1329
1822
  {
@@ -1353,25 +1846,32 @@ const module$1 = defineNuxtModule({
1353
1846
  sitemap: resolvedSitemap,
1354
1847
  search: resolvedSearch
1355
1848
  };
1849
+ let resolvedContentContext;
1850
+ const getResolvedContentContext = () => {
1851
+ if (!resolvedContentContext) {
1852
+ throw new Error("Content runtime config was read before content context resolution completed.");
1853
+ }
1854
+ return resolvedContentContext;
1855
+ };
1356
1856
  if (resolvedSitemap !== false) {
1357
1857
  configureNuxtSitemapSource(nuxt, options.api.baseURL, resolvedSitemap.path);
1358
1858
  }
1359
1859
  nuxt.hook("pages:extend", (pages) => {
1360
- validateContentPageRouteMetadata(pages, options.collections, {
1860
+ validateContentPageRouteMetadata(pages, options.collections || {}, {
1361
1861
  locales: resolvedI18n.locales,
1362
1862
  defaultLocale: resolvedI18n.defaultLocale
1363
1863
  });
1364
1864
  });
1365
1865
  if (resolvedSitemap && resolvedSitemap.assert.enabled) {
1366
- nuxt.hook("sitemap:prerender:done", async ({ sitemaps }) => {
1367
- const assertOptions = contentContext.sitemap?.assert;
1866
+ hookNuxtBoundary(nuxt, "sitemap:prerender:done", async ({ sitemaps }) => {
1867
+ const assertOptions = resolvedSitemap ? resolvedSitemap.assert : void 0;
1368
1868
  if (!assertOptions || !shouldRunSitemapAssertionOnPrerenderedSitemaps(assertOptions)) {
1369
1869
  return;
1370
1870
  }
1371
1871
  await assertGeneratedSitemaps({
1372
1872
  options: assertOptions,
1373
1873
  targets: createSitemapAssertionTargetsFromPrerenderedSitemaps(sitemaps),
1374
- collectionRouteCounts: await collectSitemapCollectionRouteCounts(nuxt.options.rootDir, contentContext),
1874
+ collectionRouteCounts: await collectSitemapCollectionRouteCounts(nuxt.options.rootDir, getResolvedContentContext()),
1375
1875
  logger
1376
1876
  });
1377
1877
  });
@@ -1389,146 +1889,40 @@ const module$1 = defineNuxtModule({
1389
1889
  Object.keys(appContentConfig.collections),
1390
1890
  Object.entries(appContentConfig.collections).filter(([, collection]) => Boolean(collection.i18n)).map(([name2]) => name2)
1391
1891
  );
1392
- await registerUserContentComponents(nuxt, resolve);
1892
+ await registerUserContentComponents(nuxt);
1393
1893
  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
- });
1894
+ registerContentNitroConfig({
1895
+ nuxt,
1896
+ options,
1897
+ appContentConfig,
1898
+ contentContext,
1899
+ runtimeInlineDependencies,
1900
+ buildIntegrity,
1901
+ resolvedI18n,
1902
+ resolveRuntimeModule,
1903
+ resolveModuleFile: resolve,
1904
+ getResolvedContentContext,
1905
+ getSearchRuntime
1463
1906
  });
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);
1907
+ registerStaticOutputGeneration({
1908
+ nuxt,
1909
+ options,
1910
+ appContentConfig,
1911
+ contentContext,
1912
+ resolvedI18n,
1913
+ resolveRuntimeModule,
1914
+ getSearchRuntime
1915
+ });
1916
+ registerContentContextFinalization({
1917
+ nuxt,
1918
+ options,
1919
+ contentContext,
1920
+ buildIntegrity,
1921
+ resolvePath,
1922
+ resolveRuntimeModule,
1923
+ onResolved: (context) => {
1924
+ resolvedContentContext = context;
1499
1925
  }
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
1926
  });
1533
1927
  if (nuxt.options.dev) {
1534
1928
  registerDevRuntimePlugin(resolveRuntimeModule);
@@ -1536,46 +1930,5 @@ const module$1 = defineNuxtModule({
1536
1930
  }
1537
1931
  }
1538
1932
  });
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
1933
 
1581
1934
  export { module$1 as default };