@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
@@ -23,12 +23,13 @@ export default defineTransformer({
23
23
  name: "csv",
24
24
  extensions: [".csv"],
25
25
  parse: async (_id, content, options = {}) => {
26
- const tree = fromCSV(content, {
26
+ const csvOptions = typeof options === "object" && options !== null ? options : {};
27
+ const tree = fromCSV(String(content ?? ""), {
27
28
  delimiter: ",",
28
29
  json: true,
29
- ...options
30
+ ...csvOptions
30
31
  });
31
- const result = options.json === false ? toJsonArray(tree) : toJsonObject(tree);
32
+ const result = csvOptions.json === false ? toJsonArray(tree) : toJsonObject(tree);
32
33
  return {
33
34
  _id,
34
35
  _type: "csv",
@@ -1,10 +1,7 @@
1
1
  import type { StorageValue } from 'unstorage';
2
- import type { TransformContentOptions } from '../types/content';
2
+ import type { ParsedContent, TransformContentOptions } from '../types/content';
3
3
  /**
4
4
  * Parse content file using registered plugins
5
5
  */
6
- export declare function transformContent(id: string, content: StorageValue, options?: TransformContentOptions): Promise<{
7
- _id: string;
8
- body: StorageValue;
9
- }>;
6
+ export declare function transformContent(id: string, content: StorageValue, options?: TransformContentOptions): Promise<ParsedContent>;
10
7
  export { defineTransformer } from './utils';
@@ -31,8 +31,8 @@ export async function transformContent(id, content, options = {}) {
31
31
  const ext = extname(id);
32
32
  const parser = getParser(ext, transformers);
33
33
  if (!parser) {
34
- console.warn(`${ext} files are not supported, "${id}" falling back to raw content`);
35
- return file;
34
+ console.warn(`${ext} files are not supported, "${id}" storing body as null`);
35
+ return { _id: file._id, body: null };
36
36
  }
37
37
  const parserOptions = options[camelCase(parser.name)] || {};
38
38
  const parsed = await parser.parse(file._id, file.body, parserOptions);
@@ -1,3 +1,4 @@
1
1
  import type { ComarkPlugin } from 'comark';
2
2
  import type { ResolvedMarkdownPlugin } from '../types/content';
3
+ export declare const normalizeMarkdownPluginOptions: (plugin: ResolvedMarkdownPlugin) => import("../types/content").MarkdownPluginOptions;
3
4
  export declare function resolveMarkdownPlugins(plugins: ResolvedMarkdownPlugin[]): Promise<ComarkPlugin[]>;
@@ -1,6 +1,85 @@
1
1
  import highlightPlugin from "comark/plugins/highlight";
2
2
  import summaryPlugin from "comark/plugins/summary";
3
3
  import tocPlugin from "comark/plugins/toc";
4
+ import footnotesPlugin from "comark/plugins/footnotes";
5
+ import materialThemeLighter from "shiki/dist/themes/material-theme-lighter.mjs";
6
+ import materialThemePalenight from "shiki/dist/themes/material-theme-palenight.mjs";
7
+ const shikiTransformerFactories = {
8
+ "@shikijs/transformers:notation-diff": "transformerNotationDiff",
9
+ "@shikijs/transformers:notation-highlight": "transformerNotationHighlight"
10
+ };
11
+ const cloneMarkdownPluginOptionValue = (value) => {
12
+ if (typeof value !== "object" || value === null) {
13
+ return value;
14
+ }
15
+ if (Array.isArray(value)) {
16
+ return value.map((item) => cloneMarkdownPluginOptionValue(item));
17
+ }
18
+ const prototype = Object.getPrototypeOf(value);
19
+ if (prototype !== Object.prototype && prototype !== null) {
20
+ return value;
21
+ }
22
+ return Object.fromEntries(Object.entries(value).map(([key, item]) => [
23
+ key,
24
+ cloneMarkdownPluginOptionValue(item)
25
+ ]));
26
+ };
27
+ const defaultHighlightThemes = () => ({
28
+ light: cloneMarkdownPluginOptionValue(materialThemeLighter),
29
+ dark: cloneMarkdownPluginOptionValue(materialThemePalenight)
30
+ });
31
+ const restoreSerializedShikiTransformers = async (transformers) => {
32
+ if (!Array.isArray(transformers)) {
33
+ return transformers;
34
+ }
35
+ let shikiTransformersModule;
36
+ return await Promise.all(transformers.map(async (transformer) => {
37
+ if (typeof transformer !== "object" || transformer === null || typeof transformer.code === "function") {
38
+ return transformer;
39
+ }
40
+ const factoryName = shikiTransformerFactories[transformer.name || ""];
41
+ if (!factoryName) {
42
+ return transformer;
43
+ }
44
+ shikiTransformersModule ||= await import("@shikijs/transformers");
45
+ const factory = shikiTransformersModule[factoryName];
46
+ if (typeof factory !== "function") {
47
+ throw new TypeError(`[ginko-content] Failed to restore serialized Shiki transformer "${transformer.name}". Install @shikijs/transformers in the app or remove the transformer from content.markdown.plugins.`);
48
+ }
49
+ return factory();
50
+ }));
51
+ };
52
+ export const normalizeMarkdownPluginOptions = (plugin) => {
53
+ if (plugin.name !== "highlight") {
54
+ return plugin.options;
55
+ }
56
+ const options = plugin.options || {};
57
+ const normalizedOptions = cloneMarkdownPluginOptionValue(options);
58
+ const themes = options.themes;
59
+ if (typeof themes !== "object" || themes === null) {
60
+ return {
61
+ ...normalizedOptions,
62
+ registerDefaultThemes: false,
63
+ themes: defaultHighlightThemes()
64
+ };
65
+ }
66
+ return {
67
+ ...normalizedOptions,
68
+ registerDefaultThemes: false,
69
+ themes: cloneMarkdownPluginOptionValue(themes)
70
+ };
71
+ };
72
+ const resolveMarkdownPluginOptions = async (plugin) => {
73
+ const normalized = normalizeMarkdownPluginOptions(plugin);
74
+ if (plugin.name !== "highlight" || typeof normalized !== "object" || normalized === null) {
75
+ return normalized;
76
+ }
77
+ const options = normalized;
78
+ return {
79
+ ...options,
80
+ transformers: await restoreSerializedShikiTransformers(options.transformers)
81
+ };
82
+ };
4
83
  const builtinMarkdownPlugins = {
5
84
  breaks: {
6
85
  load: () => loadModule("comark/plugins/breaks")
@@ -8,6 +87,9 @@ const builtinMarkdownPlugins = {
8
87
  emoji: {
9
88
  load: () => loadModule("comark/plugins/emoji")
10
89
  },
90
+ footnotes: {
91
+ load: async () => footnotesPlugin
92
+ },
11
93
  highlight: {
12
94
  load: async () => highlightPlugin
13
95
  },
@@ -36,7 +118,7 @@ const builtinMarkdownPlugins = {
36
118
  export async function resolveMarkdownPlugins(plugins) {
37
119
  return await Promise.all(plugins.map(async (plugin) => {
38
120
  const factory = await loadMarkdownPluginFactory(plugin.name);
39
- return factory(plugin.options);
121
+ return factory(await resolveMarkdownPluginOptions(plugin));
40
122
  }));
41
123
  }
42
124
  async function loadMarkdownPluginFactory(name) {
@@ -8,7 +8,7 @@ export default defineTransformer({
8
8
  name: "markdown",
9
9
  extensions: [".md"],
10
10
  parse: async (_id, content, options = {}) => {
11
- const config = { ...options };
11
+ const config = { ...typeof options === "object" && options !== null ? options : {} };
12
12
  const plugins = await resolveMarkdownPlugins(config.plugins || []);
13
13
  const tree = await parse(content, {
14
14
  plugins
@@ -7,10 +7,11 @@
7
7
  export { one, many, paginate, backlinks, resolveOne, variants, tree, neighbors } from '../runtime/app/composables/query-api.js';
8
8
  export { getCollectionPath } from '../runtime/query/routes.js';
9
9
  export type { CollectionPathOptions } from '../runtime/query/routes.js';
10
+ export { agentMarkdownPathForRoute, agentRawPathForRoute, normalizeAgentRoutePath } from '../runtime/agent-paths.js';
10
11
  export { useContentHead } from '../runtime/app/composables/head.js';
11
- export { useContentPage, useContentOne, useContentMany, useContentPagination, useContentBacklinks, useContentResolveOne, useContentVariants, useContentTree, useContentNavigation, useContentNeighbors, useContentLocaleSwitch } from '../runtime/app/composables/use-content.js';
12
+ export { useContentPage, useContentOne, useContentMany, useContentPagination, useContentBacklinks, useContentResolveOne, useContentVariants, useContentTree, useContentNavigation, useContentNeighbors } from '../runtime/app/composables/use-content.js';
12
13
  export type { UseContentPageOptions } from '../runtime/app/composables/use-content.js';
13
- export type { QueryWhere, QueryOperators, ContentSelector, ContentResolvedMeta, BacklinkFields, BacklinkSource, BacklinksOptions, BacklinksResult, LocaleFallback, DocumentFromHandle, OneOptions, ManyOptions, PaginationOptions, PaginationResult, PopulateSpec, PopulatedDocument, ResolveOneOptions, ResolveOneResult, LocalizedDoc, LocalePathEntry, NeighborsOptions, NeighborsResult, ResolutionEnvelope, SortSpec, TreeOptions, VariantsOptions, ContentVariant, ContentNavigationItem, ContentCollectionName, ContentCollectionStringName, ContentCollectionTarget, ContentTreeItem, ContentRouteMeta, ContentSearchSection } from '../types/query.js';
14
+ export type { QueryWhere, QueryOperators, ContentSelector, ContentResolvedMeta, BacklinkFields, BacklinkSource, BacklinksOptions, BacklinksResult, LocaleFallback, DocumentFromHandle, OneOptions, ManyOptions, PaginationOptions, PaginationResult, PopulateSpec, PopulatedDocument, ResolveOneOptions, ResolveOneResult, LocalizedDoc, LocalizedContentDocument, LocalePathEntry, NeighborsOptions, NeighborsResult, ResolutionEnvelope, SortSpec, TreeOptions, VariantsOptions, ContentVariant, ContentNavigationItem, ContentCollectionName, ContentCollectionStringName, ContentCollectionTarget, ContentTreeItem, ContentRouteMeta, ContentSearchSection } from '../types/query.js';
14
15
  export type { ContentNavigationNode } from '../runtime/app/composables/use-content.js';
15
16
  export { useContentSearch, useContentSearchData, useContentSearchResults } from '../runtime/app/composables/search.js';
16
17
  export type { UseContentSearchDataOptions, UseContentSearchDataResult, UseContentSearchOptions, UseContentSearchResult, UseContentSearchResultsOptions, UseContentSearchResultsResult } from '../runtime/app/composables/search.js';
@@ -9,6 +9,7 @@ export {
9
9
  neighbors
10
10
  } from "../runtime/app/composables/query-api.js";
11
11
  export { getCollectionPath } from "../runtime/query/routes.js";
12
+ export { agentMarkdownPathForRoute, agentRawPathForRoute, normalizeAgentRoutePath } from "../runtime/agent-paths.js";
12
13
  export { useContentHead } from "../runtime/app/composables/head.js";
13
14
  export {
14
15
  useContentPage,
@@ -20,8 +21,7 @@ export {
20
21
  useContentVariants,
21
22
  useContentTree,
22
23
  useContentNavigation,
23
- useContentNeighbors,
24
- useContentLocaleSwitch
24
+ useContentNeighbors
25
25
  } from "../runtime/app/composables/use-content.js";
26
26
  export { useContentSearch, useContentSearchData, useContentSearchResults } from "../runtime/app/composables/search.js";
27
27
  export { querySiteData } from "../runtime/app/composables/site-data.js";
@@ -1,4 +1,5 @@
1
- export type ContentProviderErrorCode = 'unknown_provider' | 'unknown_collection' | 'unsupported_provider_operation' | 'unsupported_query_operator' | 'unsupported_query_shape' | 'data_collection_route_access' | 'data_collection_search_access' | 'data_collection_sitemap_access' | 'missing_locale_route' | 'provider_config_missing' | 'provider_module_missing' | 'provider_module_invalid' | 'unsupported_provider_search_index' | 'unsupported_provider_search' | 'unsupported_provider_site_data' | 'unsupported_provider_prerender';
1
+ import { type ContentProviderErrorCode } from '../core/provider-errors';
2
+ export type { ContentProviderErrorCode } from '../core/provider-errors';
2
3
  export declare const createContentProviderError: (code: ContentProviderErrorCode, message: string, details?: Record<string, unknown>) => import("h3").H3Error<{
3
4
  code: ContentProviderErrorCode;
4
5
  }>;
@@ -1,22 +1,5 @@
1
1
  import { createError } from "h3";
2
- const statusForProviderError = {
3
- unknown_provider: 400,
4
- unknown_collection: 400,
5
- unsupported_provider_operation: 400,
6
- unsupported_query_operator: 400,
7
- unsupported_query_shape: 400,
8
- data_collection_route_access: 400,
9
- data_collection_search_access: 400,
10
- data_collection_sitemap_access: 400,
11
- missing_locale_route: 404,
12
- provider_config_missing: 500,
13
- provider_module_missing: 500,
14
- provider_module_invalid: 500,
15
- unsupported_provider_search_index: 400,
16
- unsupported_provider_search: 400,
17
- unsupported_provider_site_data: 400,
18
- unsupported_provider_prerender: 400
19
- };
2
+ import { statusForProviderError } from "../core/provider-errors";
20
3
  export const createContentProviderError = (code, message, details = {}) => createError({
21
4
  statusCode: statusForProviderError[code],
22
5
  statusMessage: code,
@@ -5,15 +5,8 @@ import type { ContentProviderName } from '../types/config';
5
5
  import type { ContentCollectionItemSurroundingsOptions, ContentCollectionNavigationOptions, ContentCollectionPageOptions, ContentCollectionRouteMetaOptions, ContentCollectionSearchSectionsOptions, ContentPageResult, ContentQueryBuilderParams, ContentRouteMeta, ContentSearchSection, ContentSitemapEntry } from '../types/query';
6
6
  import type { QueryCollectionsSitemapEntriesOptions } from '../features/sitemap/query';
7
7
  import type { ContentProviderSearchRequest, ContentSearchResult } from '../types/search';
8
- export interface ContentCacheHint {
9
- tags?: string[];
10
- paths?: string[];
11
- maxAge?: number;
12
- swr?: number;
13
- etag?: string;
14
- lastModified?: Date;
15
- }
16
- export type ContentCacheHintInput = ContentCacheHint | false;
8
+ import type { ContentCacheHint, ContentCacheHintInput } from '../core/cache-hints';
9
+ export type { ContentCacheHint, ContentCacheHintInput } from '../core/cache-hints';
17
10
  export declare const contentProviderResultMarker = "__ginkoContentProviderResult";
18
11
  export interface ContentProviderResult<T = unknown> {
19
12
  readonly [contentProviderResultMarker]: true;
@@ -60,7 +53,7 @@ export interface ContentProviderCapabilities {
60
53
  export interface ContentProvider {
61
54
  name: ContentProviderName;
62
55
  capabilities: ContentProviderCapabilities;
63
- query: <T = ParsedContent>(event: H3Event, query: ContentQueryBuilderParams) => Promise<MaybeContentProviderResult<ContentQueryResponse<T> | T[] | T | number | undefined>>;
56
+ query: <T = ParsedContent>(event: H3Event, query: ContentQueryBuilderParams) => Promise<MaybeContentProviderResult<ContentQueryResponse<T>>>;
64
57
  navigationQuery?: (event: H3Event, query: ContentQueryBuilderParams) => Promise<MaybeContentProviderResult<NavItem[]>>;
65
58
  navigation?: (event: H3Event, collection: string, options?: string[] | ContentCollectionNavigationOptions) => Promise<MaybeContentProviderResult<NavItem[]>>;
66
59
  surroundings?: (event: H3Event, collection: string, path: string, options?: ContentCollectionItemSurroundingsOptions) => Promise<MaybeContentProviderResult<Array<NavItem | null>>>;
@@ -8,7 +8,12 @@
8
8
  export { one, many, paginate, backlinks, resolveOne, variants, tree, neighbors, createServerContentQueryContext } from '../runtime/server/query-api.js';
9
9
  export { getCollectionPath } from '../runtime/query/routes.js';
10
10
  export type { CollectionPathOptions } from '../runtime/query/routes.js';
11
- export type { QueryWhere, QueryOperators, ContentSelector, ContentPageResult, ContentQueryBuilderParams, ContentRouteMeta, ContentSearchSection, ContentResolvedMeta, ContentSitemapEntry, BacklinkFields, BacklinkSource, BacklinksOptions, BacklinksResult, LocaleFallback, DocumentFromHandle, OneOptions, ManyOptions, PaginationOptions, PaginationResult, PopulateSpec, PopulatedDocument, ResolveOneOptions, ResolveOneResult, LocalizedDoc, LocalePathEntry, NeighborsOptions, NeighborsResult, SortSpec, TreeOptions, VariantsOptions, ContentVariant } from '../types/query.js';
11
+ export { agentMarkdownPathForRoute, agentRawPathForRoute, normalizeAgentRoutePath } from '../runtime/agent-paths.js';
12
+ export { clearAgentMarkdownSerializers, blockquoteMarkdown, defineAgentMarkdownComponent, getMarkdownProp, jsonFenceMarkdown, linkMarkdown, queryMarkdownEnabledContent, registerAgentMarkdownComponent, registerAgentMarkdownComponents, registerAgentMarkdownSerializer, registerAgentMarkdownSerializers, renderMarkdownChildren, resolveAgentMarkdownOptions, resolveContentMarkdown, resolveContentMarkdownByRoute, xmlComponentMarkdown } from '../runtime/server/agent-markdown.js';
13
+ export { buildAgentPageIndex, collectAgentMarkdownPrerenderRoutes, getAgentLocales, isSupportedAgentLocale, localeFromAgentPath, renderAgentMarkdownFrontmatter, renderAgentMarkdownPage, renderLlmsFullTxt, renderLlmsTxt, resolveMarkdownForPublicRoute, routePathFromIndexSlug, routePathFromRawSlug } from '../runtime/server/agent-site.js';
14
+ export type { AgentMarkdown, AgentMarkdownComponent, AgentMarkdownComponentMap, AgentMarkdownContext, AgentMarkdownMeta, AgentMarkdownPublicSignals, AgentMarkdownSerializer, AgentMarkdownSerializerMap, ResolvedAgentMarkdownOptions } from '../runtime/server/agent-markdown.js';
15
+ export type { AgentPage, AgentPageSource } from '../runtime/server/agent-site.js';
16
+ export type { QueryWhere, QueryOperators, ContentSelector, ContentPageResult, ContentQueryBuilderParams, ContentRouteMeta, ContentSearchSection, ContentResolvedMeta, ContentSitemapEntry, BacklinkFields, BacklinkSource, BacklinksOptions, BacklinksResult, LocaleFallback, DocumentFromHandle, OneOptions, ManyOptions, PaginationOptions, PaginationResult, PopulateSpec, PopulatedDocument, ResolveOneOptions, ResolveOneResult, LocalizedDoc, LocalizedContentDocument, LocalePathEntry, NeighborsOptions, NeighborsResult, SortSpec, TreeOptions, VariantsOptions, ContentVariant } from '../types/query.js';
12
17
  export type { ContentQueryResponse } from '../types/api.js';
13
18
  export declare const queryCollectionsSitemapEntries: typeof import('../runtime/server/sitemap-provider.js').queryCollectionsSitemapEntries;
14
19
  export { createContentProviderError } from './provider-errors.js';
@@ -10,6 +10,39 @@ export {
10
10
  createServerContentQueryContext
11
11
  } from "../runtime/server/query-api.js";
12
12
  export { getCollectionPath } from "../runtime/query/routes.js";
13
+ export { agentMarkdownPathForRoute, agentRawPathForRoute, normalizeAgentRoutePath } from "../runtime/agent-paths.js";
14
+ export {
15
+ clearAgentMarkdownSerializers,
16
+ blockquoteMarkdown,
17
+ defineAgentMarkdownComponent,
18
+ getMarkdownProp,
19
+ jsonFenceMarkdown,
20
+ linkMarkdown,
21
+ queryMarkdownEnabledContent,
22
+ registerAgentMarkdownComponent,
23
+ registerAgentMarkdownComponents,
24
+ registerAgentMarkdownSerializer,
25
+ registerAgentMarkdownSerializers,
26
+ renderMarkdownChildren,
27
+ resolveAgentMarkdownOptions,
28
+ resolveContentMarkdown,
29
+ resolveContentMarkdownByRoute,
30
+ xmlComponentMarkdown
31
+ } from "../runtime/server/agent-markdown.js";
32
+ export {
33
+ buildAgentPageIndex,
34
+ collectAgentMarkdownPrerenderRoutes,
35
+ getAgentLocales,
36
+ isSupportedAgentLocale,
37
+ localeFromAgentPath,
38
+ renderAgentMarkdownFrontmatter,
39
+ renderAgentMarkdownPage,
40
+ renderLlmsFullTxt,
41
+ renderLlmsTxt,
42
+ resolveMarkdownForPublicRoute,
43
+ routePathFromIndexSlug,
44
+ routePathFromRawSlug
45
+ } from "../runtime/server/agent-site.js";
13
46
  export const queryCollectionsSitemapEntries = async (...args) => {
14
47
  const { queryCollectionsSitemapEntries: queryCollectionsSitemapEntries2 } = await import("../runtime/server/sitemap-provider.js");
15
48
  return await queryCollectionsSitemapEntries2(...args);
@@ -0,0 +1 @@
1
+ export * from './agent-paths.js'
@@ -0,0 +1,6 @@
1
+ export declare const isUnsafeAgentRoutePath: (path: string) => boolean;
2
+ export declare const normalizeAgentRoutePath: (path: string | undefined) => string;
3
+ export declare const agentMarkdownPathForRoute: (path: string) => string;
4
+ export declare const agentRawPathForRoute: (path: string) => string;
5
+ export declare const agentRoutePathFromRawSlug: (slug: string | string[] | undefined) => string;
6
+ export declare const agentRoutePathFromIndexSlug: (slug: string | string[] | undefined) => string;
@@ -0,0 +1,41 @@
1
+ const trimSlashes = (value) => value.replace(/^\/+|\/+$/g, "");
2
+ const decodeRoutePath = (path) => {
3
+ let decoded = path;
4
+ for (let i = 0; i < 3; i++) {
5
+ try {
6
+ const next = decodeURIComponent(decoded);
7
+ if (next === decoded) return decoded;
8
+ decoded = next;
9
+ } catch {
10
+ return decoded;
11
+ }
12
+ }
13
+ return decoded;
14
+ };
15
+ export const isUnsafeAgentRoutePath = (path) => {
16
+ const decoded = decodeRoutePath(path);
17
+ if (decoded.includes("\0")) return true;
18
+ return decoded.split("/").includes("..");
19
+ };
20
+ export const normalizeAgentRoutePath = (path) => {
21
+ if (!path || path === "/") return "/";
22
+ return `/${trimSlashes(path.replace(/\/+/g, "/"))}`;
23
+ };
24
+ export const agentMarkdownPathForRoute = (path) => {
25
+ const normalized = normalizeAgentRoutePath(path);
26
+ return normalized === "/" ? "/index.md" : `${normalized}/index.md`;
27
+ };
28
+ export const agentRawPathForRoute = (path) => {
29
+ const normalized = normalizeAgentRoutePath(path);
30
+ return normalized === "/" ? "/raw/index.md" : `/raw${normalized}.md`;
31
+ };
32
+ export const agentRoutePathFromRawSlug = (slug) => {
33
+ const joined = Array.isArray(slug) ? slug.join("/") : slug || "";
34
+ const withoutExtension = joined.replace(/\.md$/i, "");
35
+ if (withoutExtension === "index") return "/";
36
+ return normalizeAgentRoutePath(withoutExtension.replace(/\/index$/i, "") || "/");
37
+ };
38
+ export const agentRoutePathFromIndexSlug = (slug) => {
39
+ const joined = Array.isArray(slug) ? slug.join("/") : slug || "";
40
+ return normalizeAgentRoutePath(joined || "/");
41
+ };
@@ -78,9 +78,9 @@ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropT
78
78
  default: () => never[];
79
79
  };
80
80
  }>> & Readonly<{}>, {
81
+ locale: string;
81
82
  defaultLocale: string;
82
83
  locales: string[];
83
- locale: string;
84
84
  class: string | unknown[] | Record<string, any>;
85
85
  components: Record<string, any>;
86
86
  prose: boolean | undefined;
@@ -20,6 +20,9 @@ type ContentHeadRuntimeConfig = {
20
20
  baseURL: string;
21
21
  };
22
22
  public: {
23
+ content?: {
24
+ siteUrl?: unknown;
25
+ };
23
26
  siteUrl?: unknown;
24
27
  };
25
28
  };
@@ -35,7 +35,8 @@ export const resolveContentHead = (data, to, config) => {
35
35
  });
36
36
  }
37
37
  }
38
- const siteUrl = typeof config.public.siteUrl === "string" ? withoutTrailingSlash(config.public.siteUrl) : void 0;
38
+ const configuredSiteUrl = config.public.content?.siteUrl ?? config.public.siteUrl;
39
+ const siteUrl = typeof configuredSiteUrl === "string" ? withoutTrailingSlash(configuredSiteUrl) : void 0;
39
40
  if (import.meta.server && siteUrl) {
40
41
  const url = withoutTrailingSlash(joinURL(siteUrl, appConfig.baseURL, to.fullPath));
41
42
  if (!head.meta.some((m) => m.property === "og:url")) {
@@ -0,0 +1 @@
1
+ export * from './hot-reload.js'
@@ -0,0 +1,5 @@
1
+ type ContentHot = {
2
+ on: (event: 'ginko-content:update', callback: (data: unknown) => void) => void;
3
+ };
4
+ export declare function registerContentHotReload(hot?: ContentHot | undefined, isClient?: boolean | undefined): void;
5
+ export {};
@@ -0,0 +1,15 @@
1
+ import { refreshNuxtData } from "#imports";
2
+ let registered = false;
3
+ const onContentUpdate = (data) => {
4
+ if (!data || typeof data !== "object") {
5
+ return;
6
+ }
7
+ refreshNuxtData();
8
+ };
9
+ export function registerContentHotReload(hot = import.meta.hot, isClient = import.meta.client) {
10
+ if (!hot || !isClient || registered) {
11
+ return;
12
+ }
13
+ registered = true;
14
+ hot.on("ginko-content:update", onContentUpdate);
15
+ }
@@ -1,5 +1,5 @@
1
1
  export declare function getLocaleContext(): {
2
2
  route: any;
3
- nuxtApp: any;
3
+ i18nLocale: any;
4
4
  resolvedLocaleState: any;
5
5
  };
@@ -1,8 +1,9 @@
1
1
  import { useNuxtApp, useRoute, useState } from "#imports";
2
2
  export function getLocaleContext() {
3
+ const nuxtApp = useNuxtApp();
3
4
  return {
4
5
  route: useRoute(),
5
- nuxtApp: useNuxtApp(),
6
+ i18nLocale: nuxtApp.$i18n?.locale,
6
7
  resolvedLocaleState: useState("$si18n:resolved-locale")
7
8
  };
8
9
  }
@@ -2,8 +2,8 @@ import { getLocaleContext } from "./locale-context.js";
2
2
  import { normalizeContentPath, resolveRouteContent as resolveLocalizedRouteContent } from "../../../features/localization/path";
3
3
  export { normalizeContentPath };
4
4
  export const resolveActiveLocale = (locales, defaultLocale) => {
5
- const { route, nuxtApp, resolvedLocaleState } = getLocaleContext();
6
- const i18nLocale = nuxtApp.$i18n?.locale;
5
+ const { route, i18nLocale: capturedI18nLocale, resolvedLocaleState } = getLocaleContext();
6
+ const i18nLocale = capturedI18nLocale || resolvedLocaleState.value;
7
7
  const explicitLocale = typeof i18nLocale === "string" ? i18nLocale : i18nLocale?.value || resolvedLocaleState.value;
8
8
  if (explicitLocale && locales.includes(explicitLocale)) {
9
9
  return explicitLocale;
@@ -100,7 +100,7 @@ export async function useContentSearchData(collection, options = {}) {
100
100
  const name = collectionName(collection);
101
101
  const searchTerm = ref("");
102
102
  const locale = computed(() => toValue(options.locale));
103
- const payload = await useAsyncData(computed(() => `content-search-data:${name}:${locale.value || "default"}`), async () => {
103
+ const payloadPromise = useAsyncData(computed(() => `content-search-data:${name}:${locale.value || "default"}`), async () => {
104
104
  const locale2 = toValue(options.locale);
105
105
  const [navigation, files2] = await Promise.all([
106
106
  tree(name, locale2 ? { locale: locale2 } : {}),
@@ -113,6 +113,7 @@ export async function useContentSearchData(collection, options = {}) {
113
113
  }, {
114
114
  watch: [locale]
115
115
  });
116
+ const payload = await payloadPromise;
116
117
  const files = computed(() => payload.data.value?.files || []);
117
118
  const searchNavigation = computed(() => payload.data.value?.navigation || []);
118
119
  return {
@@ -217,7 +218,8 @@ export const useContentSearch = async (options = {}) => {
217
218
  const useMiniSearch = async (search, indexURL, minisearch, options) => {
218
219
  const locale = computed(() => toValue(options.locale));
219
220
  const requestUrl = computed(() => locale.value ? `${indexURL}?locale=${encodeURIComponent(locale.value)}` : indexURL);
220
- const { data, pending, error } = await useFetch(requestUrl);
221
+ const fetchData = useFetch(requestUrl);
222
+ const { data, pending, error } = await fetchData;
221
223
  const results = computed(() => searchRecords(data.value || [], toValue(search), locale.value, minisearch));
222
224
  return {
223
225
  results,
@@ -239,7 +241,8 @@ const useCmsSearch = async (search, apiBaseURL, options) => {
239
241
  const query = params.toString();
240
242
  return query ? `${apiBaseURL}?${query}` : apiBaseURL;
241
243
  });
242
- const { data, pending, error } = await useFetch(requestUrl);
244
+ const fetchData = useFetch(requestUrl);
245
+ const { data, pending, error } = await fetchData;
243
246
  return {
244
247
  results: computed(() => (data.value || []).map((result) => ({
245
248
  ...result,
@@ -294,15 +297,16 @@ const usePagefindSearch = (search, pagefindUrl, options) => {
294
297
  const response = await pagefind.search(term);
295
298
  const normalized = await Promise.all((response?.results || []).map(async (result) => {
296
299
  const data = await result.data();
300
+ const meta = data?.meta;
297
301
  const [path = "", anchor] = String(data?.url || "").split("#");
298
302
  return {
299
303
  path,
300
- collection: typeof data?.meta?.collection === "string" ? data.meta.collection : "",
301
- title: data?.meta?.title || path,
304
+ collection: typeof meta?.collection === "string" ? meta.collection : "",
305
+ title: typeof meta?.title === "string" ? meta.title : path,
302
306
  excerpt: data?.excerpt || "",
303
307
  score: result.score,
304
308
  anchor: anchor || void 0,
305
- locale: data?.meta?.locale || deriveLocale(path, contentConfig?.locales || [])
309
+ locale: typeof meta?.locale === "string" ? meta.locale : deriveLocale(path, contentConfig?.locales || [])
306
310
  };
307
311
  }));
308
312
  if (!cancelled) {
@@ -0,0 +1 @@
1
+ export * from './use-content-document.js'
@@ -0,0 +1,29 @@
1
+ import type { ComputedRef } from 'vue';
2
+ import type { ContentCollectionTarget, DocumentFromHandle, LocalizedDoc, OneOptions, PopulateFromOptions, PopulateSpec, PopulatedDocument, ResolveOneOptions, ResolveOneResult } from '../../../types/query';
3
+ import { type Reactive } from './use-content-shared';
4
+ type DocFromHandle<H> = DocumentFromHandle<H>;
5
+ interface UseContentOneReturn<T> {
6
+ data: ComputedRef<LocalizedDoc<T> | null>;
7
+ pending: ComputedRef<boolean>;
8
+ status: ComputedRef<string>;
9
+ error: ComputedRef<unknown>;
10
+ refresh: () => Promise<void>;
11
+ }
12
+ /**
13
+ * Reactively resolve a single document. Reruns when any reactive source on
14
+ * `options` changes. Caching follows Nuxt's standard `useAsyncData` semantics
15
+ * keyed by `${collection}:${stableHash(options)}`.
16
+ */
17
+ export declare function useContentOne<const H extends ContentCollectionTarget, O extends OneOptions<H, PopulateSpec | undefined>>(handle: H, options: Reactive<O>): Promise<UseContentOneReturn<PopulatedDocument<DocFromHandle<H>, PopulateFromOptions<O>>>>;
18
+ interface UseContentResolveOneReturn<T> {
19
+ doc: ComputedRef<LocalizedDoc<T> | null>;
20
+ explain: ComputedRef<ResolveOneResult<T>['explain'] | null>;
21
+ status: ComputedRef<string>;
22
+ error: ComputedRef<unknown>;
23
+ refresh: () => Promise<void>;
24
+ }
25
+ /**
26
+ * Reactively resolve a single document with a first-class diagnostic envelope.
27
+ */
28
+ export declare function useContentResolveOne<const H extends ContentCollectionTarget, O extends ResolveOneOptions<H, PopulateSpec | undefined>>(handle: H, options: Reactive<O>): Promise<UseContentResolveOneReturn<PopulatedDocument<DocFromHandle<H>, PopulateFromOptions<O>>>>;
29
+ export {};
@@ -0,0 +1,44 @@
1
+ import { computed } from "vue";
2
+ import { useAsyncData } from "#imports";
3
+ import { createClientContentQueryContext } from "./query-api.js";
4
+ import {
5
+ one as oneWithContext,
6
+ resolveOne as resolveOneWithContext
7
+ } from "../../query/unified.js";
8
+ import { contentCollectionName, resolveOptions, stableKey } from "./use-content-shared.js";
9
+ export async function useContentOne(handle, options) {
10
+ const resolved = computed(() => resolveOptions(options));
11
+ const key = computed(() => stableKey("content-one", contentCollectionName(handle), resolved.value));
12
+ const context = createClientContentQueryContext();
13
+ const asyncDataPromise = useAsyncData(
14
+ key,
15
+ () => oneWithContext(context, handle, resolved.value),
16
+ { watch: [resolved], default: () => null }
17
+ );
18
+ const asyncData = await asyncDataPromise;
19
+ return {
20
+ data: computed(() => asyncData.data.value),
21
+ pending: computed(() => asyncData.pending.value),
22
+ status: computed(() => asyncData.status.value),
23
+ error: computed(() => asyncData.error.value),
24
+ refresh: () => asyncData.refresh()
25
+ };
26
+ }
27
+ export async function useContentResolveOne(handle, options) {
28
+ const resolved = computed(() => resolveOptions(options));
29
+ const key = computed(() => stableKey("content-resolve-one", contentCollectionName(handle), resolved.value));
30
+ const context = createClientContentQueryContext();
31
+ const asyncDataPromise = useAsyncData(
32
+ key,
33
+ () => resolveOneWithContext(context, handle, resolved.value),
34
+ { watch: [resolved] }
35
+ );
36
+ const asyncData = await asyncDataPromise;
37
+ return {
38
+ doc: computed(() => asyncData.data.value?.doc ?? null),
39
+ explain: computed(() => asyncData.data.value?.explain ?? null),
40
+ status: computed(() => asyncData.status.value),
41
+ error: computed(() => asyncData.error.value),
42
+ refresh: () => asyncData.refresh()
43
+ };
44
+ }
@@ -0,0 +1 @@
1
+ export * from './use-content-list.js'