@lupinum/ginko-content 0.1.6 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (230) hide show
  1. package/compatibility.json +1 -1
  2. package/dist/cli.mjs +2 -2
  3. package/dist/cms-contract/build.js +15 -20
  4. package/dist/cms-contract/types.d.ts +12 -7
  5. package/dist/core/content/document +1 -0
  6. package/dist/core/content/document.d.ts +16 -0
  7. package/dist/core/content/document.js +2 -0
  8. package/dist/core/content/graph.d.ts +3 -1
  9. package/dist/core/content/graph.js +21 -20
  10. package/dist/core/content/locale.d.ts +6 -1
  11. package/dist/core/content/locale.js +17 -6
  12. package/dist/core/content/path.d.ts +5 -5
  13. package/dist/core/content/path.js +8 -8
  14. package/dist/core/content/snapshot +1 -0
  15. package/dist/core/content/snapshot.d.ts +25 -0
  16. package/dist/core/content/snapshot.js +99 -0
  17. package/dist/core/provider-errors.d.ts +1 -1
  18. package/dist/core/provider-errors.js +1 -0
  19. package/dist/core/query/builder.js +3 -3
  20. package/dist/core/query/execute.js +75 -36
  21. package/dist/core/query/filter.js +2 -2
  22. package/dist/core/query/lower.js +79 -3
  23. package/dist/core/query/params.js +7 -7
  24. package/dist/core/query/plan.d.ts +18 -0
  25. package/dist/core/query/plan.js +7 -0
  26. package/dist/core/references/resolve.js +6 -9
  27. package/dist/features/agent/agent-markdown +1 -0
  28. package/dist/features/agent/agent-markdown.d.ts +116 -0
  29. package/dist/features/agent/agent-markdown.js +138 -0
  30. package/dist/features/agent/walker +1 -0
  31. package/dist/features/agent/walker.d.ts +10 -0
  32. package/dist/features/agent/walker.js +174 -0
  33. package/dist/features/collections/resolve.d.ts +2 -2
  34. package/dist/features/collections/resolve.js +19 -16
  35. package/dist/features/localization/links.js +1 -1
  36. package/dist/features/localization/results.d.ts +3 -3
  37. package/dist/features/localization/results.js +46 -41
  38. package/dist/features/localization/translated-slugs.js +5 -5
  39. package/dist/features/navigation/build.d.ts +2 -2
  40. package/dist/features/navigation/build.js +23 -26
  41. package/dist/features/navigation/canonical.d.ts +10 -11
  42. package/dist/features/navigation/canonical.js +26 -21
  43. package/dist/features/navigation/resolve.js +4 -4
  44. package/dist/features/navigation/tree.js +3 -3
  45. package/dist/{runtime → features}/query/backlinks.js +3 -5
  46. package/dist/{runtime → features}/query/documents.js +1 -1
  47. package/dist/{runtime → features}/query/localized-docs.d.ts +1 -1
  48. package/dist/{runtime → features}/query/localized-docs.js +3 -6
  49. package/dist/{runtime → features}/query/navigation.d.ts +1 -1
  50. package/dist/{runtime → features}/query/navigation.js +8 -8
  51. package/dist/{runtime → features}/query/populate.js +6 -14
  52. package/dist/{runtime → features}/query/unified.d.ts +4 -4
  53. package/dist/{runtime → features}/query/unified.js +4 -2
  54. package/dist/{runtime → features}/query/variants.js +4 -4
  55. package/dist/features/search/navigation.js +2 -2
  56. package/dist/features/search/sections.d.ts +1 -1
  57. package/dist/features/search/sections.js +1 -1
  58. package/dist/features/sitemap/query.d.ts +5 -5
  59. package/dist/features/sitemap/query.js +6 -6
  60. package/dist/integrations/nitro/ingest.js +9 -20
  61. package/dist/integrations/nitro/storage.d.ts +1 -0
  62. package/dist/integrations/nitro/storage.js +22 -8
  63. package/dist/module.d.mts +135 -812
  64. package/dist/module.json +1 -1
  65. package/dist/module.mjs +70 -27
  66. package/dist/parsers/csv.js +3 -3
  67. package/dist/parsers/index.js +3 -3
  68. package/dist/parsers/json.js +8 -6
  69. package/dist/parsers/markdown-plugins.js +9 -5
  70. package/dist/parsers/markdown.js +7 -6
  71. package/dist/parsers/path-meta.js +21 -19
  72. package/dist/parsers/reserved +1 -0
  73. package/dist/parsers/reserved.d.ts +15 -0
  74. package/dist/parsers/reserved.js +35 -0
  75. package/dist/parsers/yaml.js +6 -4
  76. package/dist/public/agent +1 -0
  77. package/dist/public/agent.d.ts +17 -0
  78. package/dist/public/agent.js +34 -0
  79. package/dist/public/client.d.ts +2 -3
  80. package/dist/public/client.js +1 -2
  81. package/dist/public/provider-query +1 -0
  82. package/dist/public/provider-query.d.ts +58 -0
  83. package/dist/public/provider-query.js +5 -0
  84. package/dist/public/provider.d.ts +8 -3
  85. package/dist/public/provider.js +2 -0
  86. package/dist/public/server.d.ts +7 -12
  87. package/dist/public/server.js +5 -36
  88. package/dist/runtime/app/components/ContentRenderer.d.vue.ts +3 -3
  89. package/dist/runtime/app/components/ContentRenderer.vue +2 -2
  90. package/dist/runtime/app/components/ContentRenderer.vue.d.ts +3 -3
  91. package/dist/runtime/app/components/ContentRendererInline.d.vue.ts +1 -1
  92. package/dist/runtime/app/components/ContentRendererInline.vue.d.ts +1 -1
  93. package/dist/runtime/app/components/Prose/ProseImg.d.vue.ts +4 -4
  94. package/dist/runtime/app/components/Prose/ProseImg.vue.d.ts +4 -4
  95. package/dist/runtime/app/components/Prose/ProsePre.d.vue.ts +1 -1
  96. package/dist/runtime/app/components/Prose/ProsePre.vue.d.ts +1 -1
  97. package/dist/runtime/app/components/internal/ContentRendererMarkdown.d.vue.ts +1 -1
  98. package/dist/runtime/app/components/internal/ContentRendererMarkdown.vue +4 -4
  99. package/dist/runtime/app/components/internal/ContentRendererMarkdown.vue.d.ts +1 -1
  100. package/dist/runtime/app/components/internal/MarkdownRenderer.d.ts +3 -3
  101. package/dist/runtime/app/composables/preview.d.ts +1 -1
  102. package/dist/runtime/app/composables/query-api.d.ts +5 -5
  103. package/dist/runtime/app/composables/query-api.js +5 -3
  104. package/dist/runtime/app/composables/route.d.ts +1 -1
  105. package/dist/runtime/app/composables/route.js +4 -4
  106. package/dist/runtime/app/composables/search.js +1 -1
  107. package/dist/runtime/app/composables/use-content-document.js +1 -1
  108. package/dist/runtime/app/composables/use-content-list.js +1 -1
  109. package/dist/runtime/app/composables/use-content-navigation.js +2 -2
  110. package/dist/runtime/app/composables/use-content-page.js +4 -4
  111. package/dist/runtime/app/pages/content.vue +2 -1
  112. package/dist/runtime/markdown/plugins.js +6 -8
  113. package/dist/runtime/server/agent-markdown.d.ts +5 -65
  114. package/dist/runtime/server/agent-markdown.js +40 -315
  115. package/dist/runtime/server/agent-site.js +1 -1
  116. package/dist/runtime/server/api/agent-raw.js +1 -1
  117. package/dist/runtime/server/api/cache.d.ts +1 -1
  118. package/dist/runtime/server/api/cache.js +43 -4
  119. package/dist/runtime/server/api/locales.js +3 -3
  120. package/dist/runtime/server/api/navigation.d.ts +1 -1
  121. package/dist/runtime/server/api/navigation.js +3 -1
  122. package/dist/runtime/server/api/query.js +2 -2
  123. package/dist/runtime/server/api/search-index.d.ts +1 -1
  124. package/dist/runtime/server/api/sitemap.d.ts +1 -1
  125. package/dist/runtime/server/cache-adapters.d.ts +18 -0
  126. package/dist/runtime/server/cache-adapters.js +12 -0
  127. package/dist/runtime/server/collection-helpers.d.ts +2 -2
  128. package/dist/runtime/server/collection-helpers.js +15 -11
  129. package/dist/runtime/server/index.d.ts +6 -4
  130. package/dist/runtime/server/index.js +10 -3
  131. package/dist/runtime/server/middleware/agent-link-headers.js +1 -1
  132. package/dist/runtime/server/middleware/agent-markdown.js +1 -1
  133. package/dist/runtime/server/navigation-query.d.ts +2 -2
  134. package/dist/runtime/server/navigation-query.js +52 -7
  135. package/dist/runtime/server/navigation.d.ts +1 -1
  136. package/dist/runtime/server/provider-document +1 -0
  137. package/dist/runtime/server/provider-document.d.ts +59 -0
  138. package/dist/runtime/server/provider-document.js +48 -0
  139. package/dist/runtime/server/provider-query-wire +1 -0
  140. package/dist/runtime/server/provider-query-wire.d.ts +32 -0
  141. package/dist/runtime/server/provider-query-wire.js +27 -0
  142. package/dist/runtime/server/provider-query.d.ts +18 -9
  143. package/dist/runtime/server/provider-query.js +62 -28
  144. package/dist/runtime/server/provider-result.d.ts +4 -4
  145. package/dist/runtime/server/providers/filesystem.js +3 -11
  146. package/dist/runtime/server/providers/index.d.ts +18 -1
  147. package/dist/runtime/server/providers/index.js +72 -20
  148. package/dist/runtime/server/query-api.d.ts +5 -5
  149. package/dist/runtime/server/query-api.js +9 -6
  150. package/dist/runtime/server/query-executor.d.ts +3 -3
  151. package/dist/runtime/server/query-executor.js +43 -59
  152. package/dist/runtime/server/search.d.ts +1 -1
  153. package/dist/runtime/server/search.js +4 -4
  154. package/dist/runtime/server/sitemap.js +5 -5
  155. package/dist/runtime/server/storage-access.d.ts +4 -1
  156. package/dist/runtime/server/storage-access.js +27 -2
  157. package/dist/runtime/transformers/component-resolver.d.ts +1 -1
  158. package/dist/storage/content.d.ts +2 -6
  159. package/dist/storage/content.js +19 -8
  160. package/dist/storage/contents.d.ts +9 -1
  161. package/dist/storage/contents.js +26 -19
  162. package/dist/storage/driver.d.ts +2 -2
  163. package/dist/storage/driver.js +2 -1
  164. package/dist/storage/graph.js +4 -0
  165. package/dist/storage/manifest.js +3 -1
  166. package/dist/storage/references.js +9 -5
  167. package/dist/storage/snapshot-runtime +1 -0
  168. package/dist/storage/snapshot-runtime.d.ts +11 -0
  169. package/dist/storage/snapshot-runtime.js +46 -0
  170. package/dist/storage/validation.d.ts +1 -1
  171. package/dist/storage/validation.js +47 -102
  172. package/dist/testing/provider-contract.d.ts +14 -4
  173. package/dist/testing/provider-contract.js +121 -68
  174. package/dist/testing/provider-fixture.d.ts +1 -1
  175. package/dist/testing/provider-fixture.js +124 -120
  176. package/dist/types/config.d.ts +8 -4
  177. package/dist/types/content.d.ts +104 -70
  178. package/dist/types/fields.js +1 -1
  179. package/dist/types/module.d.ts +1 -1
  180. package/dist/types/query-parts/public.d.ts +14 -5
  181. package/dist/types/query-parts/results.d.ts +6 -2
  182. package/dist/types/query-parts/transport.d.ts +19 -0
  183. package/dist/types/virtual.d.ts +6 -0
  184. package/dist/types.d.mts +2 -2
  185. package/dist/web-types.json +1 -1
  186. package/package.json +39 -9
  187. package/dist/runtime/app/plugins/markdown-components +0 -1
  188. package/dist/runtime/app/plugins/markdown-components.d.ts +0 -2
  189. package/dist/runtime/app/plugins/markdown-components.js +0 -19
  190. /package/dist/{runtime → features/agent}/agent-paths +0 -0
  191. /package/dist/{runtime → features/agent}/agent-paths.d.ts +0 -0
  192. /package/dist/{runtime → features/agent}/agent-paths.js +0 -0
  193. /package/dist/{runtime → features}/query/backlinks +0 -0
  194. /package/dist/{runtime → features}/query/backlinks.d.ts +0 -0
  195. /package/dist/{runtime → features}/query/context +0 -0
  196. /package/dist/{runtime → features}/query/context.d.ts +0 -0
  197. /package/dist/{runtime → features}/query/context.js +0 -0
  198. /package/dist/{runtime → features}/query/documents +0 -0
  199. /package/dist/{runtime → features}/query/documents.d.ts +0 -0
  200. /package/dist/{runtime → features}/query/errors +0 -0
  201. /package/dist/{runtime → features}/query/errors.d.ts +0 -0
  202. /package/dist/{runtime → features}/query/errors.js +0 -0
  203. /package/dist/{runtime → features}/query/handles +0 -0
  204. /package/dist/{runtime → features}/query/handles.d.ts +0 -0
  205. /package/dist/{runtime → features}/query/handles.js +0 -0
  206. /package/dist/{runtime → features}/query/locale-options +0 -0
  207. /package/dist/{runtime → features}/query/locale-options.d.ts +0 -0
  208. /package/dist/{runtime → features}/query/locale-options.js +0 -0
  209. /package/dist/{runtime → features}/query/localized-docs +0 -0
  210. /package/dist/{runtime → features}/query/navigation +0 -0
  211. /package/dist/{runtime → features}/query/pagination +0 -0
  212. /package/dist/{runtime → features}/query/pagination.d.ts +0 -0
  213. /package/dist/{runtime → features}/query/pagination.js +0 -0
  214. /package/dist/{runtime → features}/query/populate +0 -0
  215. /package/dist/{runtime → features}/query/populate.d.ts +0 -0
  216. /package/dist/{runtime → features}/query/public-limits +0 -0
  217. /package/dist/{runtime → features}/query/public-limits.d.ts +0 -0
  218. /package/dist/{runtime → features}/query/public-limits.js +0 -0
  219. /package/dist/{runtime → features}/query/resolve +0 -0
  220. /package/dist/{runtime → features}/query/resolve.d.ts +0 -0
  221. /package/dist/{runtime → features}/query/resolve.js +0 -0
  222. /package/dist/{runtime → features}/query/responses +0 -0
  223. /package/dist/{runtime → features}/query/responses.d.ts +0 -0
  224. /package/dist/{runtime → features}/query/responses.js +0 -0
  225. /package/dist/{runtime → features}/query/routes +0 -0
  226. /package/dist/{runtime → features}/query/routes.d.ts +0 -0
  227. /package/dist/{runtime → features}/query/routes.js +0 -0
  228. /package/dist/{runtime → features}/query/unified +0 -0
  229. /package/dist/{runtime → features}/query/variants +0 -0
  230. /package/dist/{runtime → features}/query/variants.d.ts +0 -0
@@ -39,7 +39,7 @@ const rememberSearchRecords = (cacheKey, records) => {
39
39
  }
40
40
  };
41
41
  const mergeSearchFilter = (filterQuery, locale) => {
42
- const localeFilter = locale ? { _locale: locale } : void 0;
42
+ const localeFilter = locale ? { locale } : void 0;
43
43
  if (filterQuery && localeFilter) {
44
44
  return { $and: [filterQuery, localeFilter] };
45
45
  }
@@ -50,7 +50,7 @@ export async function serverSearchContent(event, filterQuery, locale, collection
50
50
  const collections = resolveSearchCollections(runtimeConfig.content, collectionsOverride);
51
51
  const results = await Promise.all(collections.map(async (collection) => {
52
52
  const loadPages = async (queryLocale) => {
53
- const query = serverQueryCollection(event, collection).select("_path", "_locale", "title", "description", "body");
53
+ const query = serverQueryCollection(event, collection).select("path", "locale", "title", "description", "body");
54
54
  const mergedFilter = mergeSearchFilter(filterQuery, queryLocale);
55
55
  if (mergedFilter) {
56
56
  return await query.where(mergedFilter).find();
@@ -84,7 +84,7 @@ const toSearchRecord = (section) => {
84
84
  content: section.content,
85
85
  headings: section.titles,
86
86
  anchor: anchor || void 0,
87
- locale: typeof section._locale === "string" ? section._locale : void 0
87
+ locale: typeof section.locale === "string" ? section.locale : void 0
88
88
  };
89
89
  };
90
90
  const buildProviderSearchSections = async (event, collections, provider, opts) => {
@@ -92,7 +92,7 @@ const buildProviderSearchSections = async (event, collections, provider, opts) =
92
92
  const sections = await Promise.all(collections.map(async (collection) => {
93
93
  const loadCollectionLocale = async (locale) => (await provider.searchSections(event, collection, {
94
94
  ignoredTags: opts.ignoredTags || [],
95
- extraFields: unique(["_locale", ...opts.extraFields || []]),
95
+ extraFields: unique(["locale", ...opts.extraFields || []]),
96
96
  filterQuery: opts.filterQuery,
97
97
  locale
98
98
  })).map((section) => ({
@@ -6,11 +6,11 @@ import { serverQueryCollection } from "./storage.js";
6
6
  import { contentConfig } from "./storage-access.js";
7
7
  import { resolveCollectionI18n } from "../../features/localization/path";
8
8
  const sitemapPageFields = [
9
- "_path",
10
- "_file",
11
- "_locale",
12
- "_draft",
13
- "_canonicalKey",
9
+ "path",
10
+ "file",
11
+ "locale",
12
+ "draft",
13
+ "canonicalKey",
14
14
  "sitemap",
15
15
  "image",
16
16
  "seo",
@@ -1 +1,4 @@
1
- export { cacheParsedStorage, cacheStorage, contentConfig, contentIgnorePredicate, getContentsIds, resolveStorageId, sourceStorage } from '../../integrations/nitro/storage';
1
+ import type { H3Event } from 'h3';
2
+ import { cacheParsedStorage, cacheStorage, contentConfig, contentIgnorePredicate, getSourceContentIds, resolveStorageId, sourceStorage } from '../../integrations/nitro/storage';
3
+ export declare const getContentsIds: (event: H3Event, prefix?: string) => Promise<string[]>;
4
+ export { cacheParsedStorage, cacheStorage, contentConfig, contentIgnorePredicate, getSourceContentIds, resolveStorageId, sourceStorage };
@@ -1,9 +1,34 @@
1
- export {
1
+ import { splitInlineLocaleVariantId } from "../../core/content/locale";
2
+ import {
2
3
  cacheParsedStorage,
3
4
  cacheStorage,
4
5
  contentConfig,
5
6
  contentIgnorePredicate,
6
- getContentsIds,
7
+ getContentsIds as getStorageContentsIds,
8
+ getSourceContentIds,
7
9
  resolveStorageId,
8
10
  sourceStorage
9
11
  } from "../../integrations/nitro/storage";
12
+ import { getProcessDocuments, usesProcessSnapshot } from "../../storage/snapshot-runtime";
13
+ export const getContentsIds = async (event, prefix) => {
14
+ if (!usesProcessSnapshot) {
15
+ return getStorageContentsIds(event, prefix);
16
+ }
17
+ const ids = /* @__PURE__ */ new Set();
18
+ for (const document of await getProcessDocuments(event)) {
19
+ const { sourceId } = splitInlineLocaleVariantId(document.id);
20
+ if (!prefix || sourceId.startsWith(prefix)) {
21
+ ids.add(sourceId);
22
+ }
23
+ }
24
+ return [...ids];
25
+ };
26
+ export {
27
+ cacheParsedStorage,
28
+ cacheStorage,
29
+ contentConfig,
30
+ contentIgnorePredicate,
31
+ getSourceContentIds,
32
+ resolveStorageId,
33
+ sourceStorage
34
+ };
@@ -6,5 +6,5 @@ export declare function loadComponents(node: ParsedContent['body'] | {
6
6
  props?: Record<string, unknown>;
7
7
  children?: Array<ParsedContent['body']>;
8
8
  } | null, tags: Record<string, string>): [string, unknown][];
9
- declare const _default: import("../../types").ContentTransformer;
9
+ declare const _default: import("../../types/content").ContentTransformer;
10
10
  export default _default;
@@ -1,5 +1,5 @@
1
1
  import type { H3Event } from 'h3';
2
- import type { ParsedContent } from '../types/content';
2
+ import type { ContentDocumentResolution, ParsedContent } from '../types/content';
3
3
  import type { ContentCollectionMap, ContentLocaleEntry, ContentQueryBuilderParams, ContentQueryFetcher, CollectionQueryBuilder, ResolveContentReferenceOptions } from '../types/query';
4
4
  export declare const createServerQueryFetch: <T = ParsedContent>(event: H3Event) => ContentQueryFetcher<T>;
5
5
  /**
@@ -19,11 +19,7 @@ export declare const createServerQueryFetch: <T = ParsedContent>(event: H3Event)
19
19
  * ```
20
20
  */
21
21
  export declare const resolveContentReference: <T = ParsedContent>(event: H3Event, reference: string, options?: ResolveContentReferenceOptions) => Promise<(T & {
22
- _requestedLocale?: string;
23
- _resolvedLocale?: string;
24
- _fallback?: boolean;
25
- _availableLocales?: string[];
26
- _variantPaths?: Record<string, string>;
22
+ resolved?: ContentDocumentResolution;
27
23
  }) | null>;
28
24
  export declare const createServerContentQuery: <T = ParsedContent>(event: H3Event, query?: string | ContentQueryBuilderParams, ...pathParts: string[]) => import("../types/query").ContentQueryBuilder<T>;
29
25
  export declare function serverQueryCollection<K extends keyof ContentCollectionMap & string>(event: H3Event, collection: K): CollectionQueryBuilder<ContentCollectionMap[K]>;
@@ -1,6 +1,7 @@
1
1
  import { joinURL, withLeadingSlash } from "ufo";
2
2
  import { createQuery, wrapQueryBuilder } from "../core/query/builder";
3
3
  import { resolveGraphCanonicalKey, resolveGraphCollectionLocales, resolveGraphVariant } from "../core/content/graph";
4
+ import { sortLocalesCanonically } from "../core/content/locale";
4
5
  import { normalizeReferenceValue } from "../core/references/resolve";
5
6
  import { executeQueryPlan } from "../core/query/execute";
6
7
  import { lowerQueryPlan } from "../core/query/lower";
@@ -27,7 +28,7 @@ export const resolveContentReference = async (event, reference, options = {}) =>
27
28
  if (!canonicalId) {
28
29
  return null;
29
30
  }
30
- const variants = Object.values(graph.byCanonical[canonicalId] || {}).map((entry) => entry.document).filter((document) => !options.collection || document._collection === options.collection);
31
+ const variants = Object.values(graph.byCanonical[canonicalId] || {}).map((entry) => entry.document).filter((document) => !options.collection || document.collection === options.collection);
31
32
  if (!variants.length) {
32
33
  return null;
33
34
  }
@@ -41,17 +42,27 @@ export const resolveContentReference = async (event, reference, options = {}) =>
41
42
  if (!resolved || !resolvedVariant) {
42
43
  return null;
43
44
  }
44
- const availableLocales = Array.from(new Set(variants.map((document) => document._locale).filter(Boolean)));
45
+ const collectionI18n = getServerCollectionI18n(options.collection);
46
+ const availableLocales = sortLocalesCanonically(
47
+ Array.from(new Set(variants.map((document) => document.locale).filter(Boolean))),
48
+ {
49
+ defaultLocale: collectionI18n?.defaultLocale || config.defaultLocale,
50
+ locales: collectionI18n?.locales
51
+ }
52
+ );
45
53
  const variantPaths = Object.fromEntries(
46
- variants.filter((document) => document._locale && document._path).map((document) => [document._locale, document._path])
54
+ variants.filter((document) => document.locale && document.path).map((document) => [document.locale, document.path])
47
55
  );
48
56
  return {
49
57
  ...resolved,
50
- _requestedLocale: resolvedVariant.requestedLocale,
51
- _resolvedLocale: resolvedVariant.resolvedLocale,
52
- _fallback: resolvedVariant.fallback,
53
- _availableLocales: availableLocales,
54
- _variantPaths: variantPaths
58
+ resolved: {
59
+ ...resolved.resolved || {},
60
+ requestedLocale: resolvedVariant.requestedLocale,
61
+ locale: resolvedVariant.resolvedLocale,
62
+ fallback: resolvedVariant.fallback,
63
+ availableLocales,
64
+ variantPaths
65
+ }
55
66
  };
56
67
  };
57
68
  const getServerCollectionI18n = (collection) => {
@@ -1,5 +1,13 @@
1
1
  import type { H3Event } from 'h3';
2
- import type { ParsedContent } from '../types/content';
2
+ import type { MissingDocument, ParsedContent } from '../types/content';
3
3
  export declare function chunksFromArray<T>(arr: T[], n: number): Generator<T[], void>;
4
+ /**
5
+ * Load every locale variant for a single content id.
6
+ *
7
+ * Cache strategy:
8
+ * 1. Dev/prerender validate source metadata before reusing an artifact.
9
+ * 2. Concurrent misses for the same `(storageId, hash)` share one parse.
10
+ */
11
+ export declare const loadContentVariants: (event: H3Event, id: string) => Promise<Array<ParsedContent | MissingDocument>>;
4
12
  export declare const getContentsList: (event: H3Event, prefix?: string) => Promise<ParsedContent[]>;
5
13
  export declare const getContent: (event: H3Event, id: string) => Promise<ParsedContent>;
@@ -1,11 +1,12 @@
1
1
  import { hash as ohash } from "ohash";
2
+ import { isRealDocument } from "../core/content/document";
2
3
  import { splitInlineLocaleVariantId } from "../core/content/locale";
3
4
  import { memoizeRuntimeValue } from "../integrations/nitro/context";
4
5
  import { parseContentVariants } from "../integrations/nitro/ingest";
5
6
  import { cacheStoreFor, getCachedContents, setCachedContents } from "./cache.js";
6
7
  import { contentConfig, contentIgnorePredicate, getContentStorageRuntime, getContentsIds, resolveStorageId } from "./driver.js";
8
+ import { getProcessDocuments, usesProcessSnapshot } from "./snapshot-runtime.js";
7
9
  import { validateContentGraph } from "./validation.js";
8
- const isProduction = process.env.NODE_ENV === "production";
9
10
  const isPrerendering = import.meta.prerender;
10
11
  const shouldValidateAtRuntime = import.meta.dev || isPrerendering;
11
12
  const isContentCacheArtifact = (value) => {
@@ -16,23 +17,22 @@ export function* chunksFromArray(arr, n) {
16
17
  yield arr.slice(i, i + n);
17
18
  }
18
19
  }
19
- const loadContentVariants = async (event, id) => {
20
+ export const loadContentVariants = async (event, id) => {
20
21
  const runtime = getContentStorageRuntime(event);
21
22
  const config = contentConfig();
22
23
  const { sourceId: contentId } = splitInlineLocaleVariantId(id);
23
24
  if (!contentIgnorePredicate(contentId)) {
24
- return [{ _id: contentId, body: null }];
25
+ return [{ id: contentId, body: null, missing: true }];
25
26
  }
26
27
  const storageId = await resolveStorageId(event, contentId);
27
28
  const cachedValue = await runtime.parsedCache.getItem(storageId);
28
29
  const cached = isContentCacheArtifact(cachedValue) ? cachedValue : null;
29
- if (isProduction && !isPrerendering && cached) {
30
- return cached.parsed;
30
+ const body = await runtime.source.getItem(storageId);
31
+ if (body === null) {
32
+ return [{ id: contentId, body: null, missing: true }];
31
33
  }
32
- const meta = await runtime.source.getMeta(storageId);
33
34
  const hash = ohash({
34
- mtime: meta.mtime,
35
- size: meta.size || 0,
35
+ body: ohash(body),
36
36
  version: runtime.config.cacheVersion,
37
37
  integrity: runtime.config.cacheIntegrity,
38
38
  collections: runtime.config.collections,
@@ -46,10 +46,6 @@ const loadContentVariants = async (event, id) => {
46
46
  return cached.parsed;
47
47
  }
48
48
  return cacheStoreFor(event).inflightContents.run(`${storageId}${hash}`, async () => {
49
- const body = await runtime.source.getItem(storageId);
50
- if (body === null) {
51
- return [{ _id: contentId, body: null }];
52
- }
53
49
  const parsed = await parseContentVariants(contentId, body, config, { validate: true });
54
50
  try {
55
51
  await runtime.parsedCache.setItem(storageId, { parsed, hash });
@@ -68,7 +64,7 @@ const loadContents = async (event, prefix) => {
68
64
  const result = await Promise.all(chunk.map((key) => loadContentVariants(event, key)));
69
65
  contents.push(...result.flat());
70
66
  }
71
- const filtered = contents.filter((document) => document && document._path);
67
+ const filtered = contents.filter(isRealDocument).filter((document) => document.path);
72
68
  if (shouldValidateAtRuntime) {
73
69
  const outcome = validateContentGraph(filtered, contentConfig());
74
70
  if (!outcome.ok) {
@@ -77,7 +73,20 @@ const loadContents = async (event, prefix) => {
77
73
  }
78
74
  return filtered;
79
75
  };
76
+ const snapshotDocumentsFor = async (event, prefix) => {
77
+ const documents = await getProcessDocuments(event);
78
+ if (!prefix) {
79
+ return documents;
80
+ }
81
+ return documents.filter((document) => {
82
+ const { sourceId } = splitInlineLocaleVariantId(document.id);
83
+ return sourceId.startsWith(prefix);
84
+ });
85
+ };
80
86
  export const getContentsList = (event, prefix) => {
87
+ if (usesProcessSnapshot) {
88
+ return snapshotDocumentsFor(event, prefix);
89
+ }
81
90
  const runtime = getContentStorageRuntime(event);
82
91
  const cacheKey = JSON.stringify({
83
92
  prefix,
@@ -90,25 +99,23 @@ export const getContentsList = (event, prefix) => {
90
99
  });
91
100
  return memoizeRuntimeValue(event, `contents:${cacheKey}`, async () => {
92
101
  const cached = getCachedContents(event, cacheKey);
93
- if ((isPrerendering || !isProduction) && cached?.length) {
102
+ if (cached?.length) {
94
103
  return cached;
95
104
  }
96
105
  return await cacheStoreFor(event).inflightContentsList.run(cacheKey, async () => {
97
106
  const result = await loadContents(event, prefix);
98
- if (isPrerendering || !isProduction) {
99
- setCachedContents(event, cacheKey, result);
100
- }
107
+ setCachedContents(event, cacheKey, result);
101
108
  return result;
102
109
  });
103
110
  });
104
111
  };
105
112
  export const getContent = async (event, id) => {
106
113
  const { sourceId, locale } = splitInlineLocaleVariantId(id);
107
- const parsed = await loadContentVariants(event, sourceId);
114
+ const parsed = usesProcessSnapshot ? (await getProcessDocuments(event)).filter((document) => splitInlineLocaleVariantId(document.id).sourceId === sourceId) : (await loadContentVariants(event, sourceId)).filter(isRealDocument);
108
115
  if (!locale) {
109
116
  return parsed[0];
110
117
  }
111
- const match = parsed.find((document) => document._locale === locale);
118
+ const match = parsed.find((document) => document.locale === locale);
112
119
  if (!match) {
113
120
  console.warn(`[content] Locale variant "${locale}" not found for "${sourceId}", falling back to default`);
114
121
  }
@@ -1,6 +1,6 @@
1
1
  import type { H3Event } from 'h3';
2
2
  import type { Storage, StorageValue } from 'unstorage';
3
- import { sourceStorage, cacheStorage, cacheParsedStorage, contentConfig, contentIgnorePredicate, getContentsIds, resolveStorageId } from '../integrations/nitro/storage';
3
+ import { sourceStorage, cacheStorage, cacheParsedStorage, contentConfig, contentIgnorePredicate, getContentsIds, getSourceContentIds, resolveStorageId } from '../integrations/nitro/storage';
4
4
  export interface ContentSourceDriver {
5
5
  getKeys: (prefix?: string) => Promise<string[]>;
6
6
  getItem: (key: string) => Promise<StorageValue | null>;
@@ -25,4 +25,4 @@ export interface ContentStorageRuntime {
25
25
  now: () => Date;
26
26
  }
27
27
  export declare const getContentStorageRuntime: (event: H3Event) => ContentStorageRuntime;
28
- export { cacheParsedStorage, cacheStorage, contentConfig, contentIgnorePredicate, getContentsIds, resolveStorageId, sourceStorage };
28
+ export { cacheParsedStorage, cacheStorage, contentConfig, contentIgnorePredicate, getContentsIds, getSourceContentIds, resolveStorageId, sourceStorage };
@@ -1,4 +1,4 @@
1
- import { sourceStorage, cacheStorage, cacheParsedStorage, contentConfig, contentIgnorePredicate, getContentsIds, resolveStorageId } from "../integrations/nitro/storage";
1
+ import { sourceStorage, cacheStorage, cacheParsedStorage, contentConfig, contentIgnorePredicate, getContentsIds, getSourceContentIds, resolveStorageId } from "../integrations/nitro/storage";
2
2
  import { getContentRuntimeContext } from "../integrations/nitro/context";
3
3
  export const getContentStorageRuntime = (event) => {
4
4
  const runtime = getContentRuntimeContext(event);
@@ -17,6 +17,7 @@ export {
17
17
  contentConfig,
18
18
  contentIgnorePredicate,
19
19
  getContentsIds,
20
+ getSourceContentIds,
20
21
  resolveStorageId,
21
22
  sourceStorage
22
23
  };
@@ -2,7 +2,11 @@ import { buildContentGraph } from "../core/content/graph";
2
2
  import { memoizeRuntimeValue } from "../integrations/nitro/context";
3
3
  import { contentConfig } from "./driver.js";
4
4
  import { getContentsList } from "./contents.js";
5
+ import { getProcessGraph, usesProcessSnapshot } from "./snapshot-runtime.js";
5
6
  export const getContentGraph = async (event) => {
7
+ if (usesProcessSnapshot) {
8
+ return getProcessGraph(event);
9
+ }
6
10
  return await memoizeRuntimeValue(event, "graph", async () => {
7
11
  const config = contentConfig();
8
12
  const contents = await getContentsList(event);
@@ -31,6 +31,7 @@ export async function resolveVariant(event, canonicalKey, requestedLocale, optio
31
31
  const config = getContentRuntimeConfig().content;
32
32
  return resolveGraphVariant(await getContentGraph(event), canonicalKey, requestedLocale, {
33
33
  defaultLocale: config.defaultLocale,
34
+ locales: config.locales,
34
35
  fallback: options.fallback,
35
36
  exact: options.exact,
36
37
  localeFallback: config.localeFallback
@@ -40,6 +41,7 @@ export async function resolveRouteVariant(event, routePath, requestedLocale, opt
40
41
  const config = getContentRuntimeConfig().content;
41
42
  return resolveGraphRouteVariant(await getContentGraph(event), routePath, requestedLocale, {
42
43
  defaultLocale: config.defaultLocale,
44
+ locales: config.locales,
43
45
  fallback: options.fallback,
44
46
  exact: options.exact,
45
47
  localeFallback: config.localeFallback
@@ -56,7 +58,7 @@ export async function getIndexedContentsList(event, query) {
56
58
  return await getContentsList(event);
57
59
  }
58
60
  const params = query.params();
59
- const paths = collectQueryWhere(params?.where, (where) => typeof where._path !== "undefined").map((where) => where._path).filter((path) => typeof path === "string" || path instanceof RegExp);
61
+ const paths = collectQueryWhere(params?.where, (where) => typeof where.path !== "undefined").map((where) => where.path).filter((path) => typeof path === "string" || path instanceof RegExp);
60
62
  const graph = await getContentGraph(event);
61
63
  if (!paths.length && !params?.collection) {
62
64
  return graph.documents;
@@ -17,7 +17,7 @@ const isConfiguredQuickLink = (href) => {
17
17
  return Boolean(links?.[namespace]?.[key]);
18
18
  };
19
19
  const resolveDocumentRefLinks = async (event, content, requestedLocale) => {
20
- if (!content || content._type !== "markdown" || !content.body) {
20
+ if (!content || content.type !== "markdown" || !content.body) {
21
21
  return void 0;
22
22
  }
23
23
  const hrefs = collectMarkdownRefLinks(content.body);
@@ -35,19 +35,19 @@ const resolveDocumentRefLinks = async (event, content, requestedLocale) => {
35
35
  return [href, href];
36
36
  }
37
37
  if (import.meta.dev) {
38
- console.warn(`[content] Could not resolve markdown ref "${href}" in "${content._file || content._id}"`);
38
+ console.warn(`[content] Could not resolve markdown ref "${href}" in "${content.file?.path || content.id}"`);
39
39
  }
40
40
  return [href, href];
41
41
  }
42
42
  const variant = await resolveVariant(event, canonicalKey, requestedLocale);
43
43
  if (!variant?.path) {
44
44
  if (import.meta.dev) {
45
- console.warn(`[content] Could not resolve markdown ref "${href}" in "${content._file || content._id}"`);
45
+ console.warn(`[content] Could not resolve markdown ref "${href}" in "${content.file?.path || content.id}"`);
46
46
  }
47
47
  return [href, href];
48
48
  }
49
49
  if (import.meta.dev && requestedLocale && variant.resolvedLocale && variant.resolvedLocale !== requestedLocale) {
50
- console.warn(`[content] Markdown ref "${href}" in "${content._file || content._id}" fell back from locale "${requestedLocale}" to "${variant.resolvedLocale}"`);
50
+ console.warn(`[content] Markdown ref "${href}" in "${content.file?.path || content.id}" fell back from locale "${requestedLocale}" to "${variant.resolvedLocale}"`);
51
51
  }
52
52
  const routeLocale = variant.fallback && requestedLocale ? requestedLocale : variant.resolvedLocale;
53
53
  return [href, `${projectContentPathToLocale(variant.path, routeLocale, contentConfig().defaultLocale)}${parsed.hash}`];
@@ -63,9 +63,13 @@ export const withResolvedRefs = async (event, content, requestedLocale) => {
63
63
  if (!resolvedRefs) {
64
64
  return content;
65
65
  }
66
+ const existingResolution = content.resolved;
66
67
  return {
67
68
  ...content,
68
- _resolvedRefs: resolvedRefs
69
+ resolved: {
70
+ ...existingResolution || {},
71
+ resolvedRefs
72
+ }
69
73
  };
70
74
  };
71
75
  export const withResolvedRefsList = async (event, items, requestedLocale) => {
@@ -0,0 +1 @@
1
+ export * from './snapshot-runtime.js'
@@ -0,0 +1,11 @@
1
+ import type { H3Event } from 'h3';
2
+ import type { ParsedContent } from '../types/content';
3
+ import type { ContentGraph } from '../core/content/graph';
4
+ /**
5
+ * Single source of truth for "serve from the process-scoped snapshot".
6
+ * Prerender must stay on the request-scoped dev path — it is the phase that
7
+ * produces the snapshot in the first place.
8
+ */
9
+ export declare const usesProcessSnapshot: boolean;
10
+ export declare const getProcessGraph: (event: H3Event) => Promise<ContentGraph>;
11
+ export declare const getProcessDocuments: (event: H3Event) => Promise<ParsedContent[]>;
@@ -0,0 +1,46 @@
1
+ import { buildContentGraph } from "../core/content/graph";
2
+ import { isContentSnapshot } from "../core/content/snapshot";
3
+ import { cacheStorage, contentConfig } from "./driver.js";
4
+ export const usesProcessSnapshot = process.env.NODE_ENV === "production" && !import.meta.prerender;
5
+ let state = null;
6
+ export const getProcessGraph = async (event) => {
7
+ const snapshotState = await getProcessSnapshotState(event);
8
+ return snapshotState.graph;
9
+ };
10
+ export const getProcessDocuments = async (event) => {
11
+ const snapshotState = await getProcessSnapshotState(event);
12
+ return snapshotState.documents;
13
+ };
14
+ const getProcessSnapshotState = (event) => {
15
+ const config = contentConfig();
16
+ const integrity = config.cacheIntegrity;
17
+ if (state && state.integrity === integrity) {
18
+ return state.promise;
19
+ }
20
+ const promise = loadSnapshotState(event, integrity);
21
+ promise.catch(() => {
22
+ if (state?.promise === promise) {
23
+ state = null;
24
+ }
25
+ });
26
+ state = { integrity, promise };
27
+ return promise;
28
+ };
29
+ const loadSnapshotState = async (event, integrity) => {
30
+ const raw = await cacheStorage(event).getItem("snapshot.json");
31
+ if (!isContentSnapshot(raw)) {
32
+ throw new Error("[content] production snapshot missing or invalid \u2014 the site was built without a content snapshot. Rebuild with this package version.");
33
+ }
34
+ if (raw.integrity !== integrity) {
35
+ throw new Error(`[content] snapshot integrity mismatch (built: ${raw.integrity}, runtime: ${integrity}) \u2014 stale build artifact.`);
36
+ }
37
+ const config = contentConfig();
38
+ const graph = buildContentGraph(raw.documents, {
39
+ locales: config.locales,
40
+ defaultLocale: config.defaultLocale
41
+ });
42
+ return {
43
+ graph,
44
+ documents: raw.documents
45
+ };
46
+ };
@@ -16,7 +16,7 @@ export declare const getCanonicalContentId: (document: ParsedContent, locales?:
16
16
  * - Strict collections (default): schema errors produce a `SCHEMA_VALIDATION_FAILED`
17
17
  * failure.
18
18
  *
19
- * Also chains into `validateNavigationDocument` for `_navigation: true` files,
19
+ * Also chains into `validateNavigationDocument` for `navigationFile: true` files,
20
20
  * which can fail with `INVALID_NAVIGATION_YAML`.
21
21
  */
22
22
  export declare const validateCollectionDocument: (document: ParsedContent, collections?: Record<string, ContentCollectionConfig>) => Result<ParsedContent, ContentError>;