@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
@@ -0,0 +1,43 @@
1
+ import type { ComputedRef } from 'vue';
2
+ import type { BacklinksOptions, BacklinksResult, BacklinkSource, ContentCollectionTarget, ContentVariant, DocumentFromHandle, ManyOptions, LocalizedDoc, PaginationOptions, PopulateSpec, PopulateFromOptions, PopulatedDocument, VariantsOptions } from '../../../types/query';
3
+ import { type Reactive, type ReactiveValue } from './use-content-shared';
4
+ type DocFromHandle<H> = DocumentFromHandle<H>;
5
+ interface UseContentManyReturn<T> {
6
+ data: ComputedRef<Array<LocalizedDoc<T>>>;
7
+ status: ComputedRef<string>;
8
+ error: ComputedRef<unknown>;
9
+ refresh: () => Promise<void>;
10
+ }
11
+ interface UseContentPaginationReturn<T> {
12
+ data: ComputedRef<Array<LocalizedDoc<T>>>;
13
+ page: ComputedRef<number>;
14
+ limit: ComputedRef<number>;
15
+ total: ComputedRef<number>;
16
+ pageCount: ComputedRef<number>;
17
+ hasNext: ComputedRef<boolean>;
18
+ hasPrev: ComputedRef<boolean>;
19
+ nextPage: ComputedRef<number | null>;
20
+ prevPage: ComputedRef<number | null>;
21
+ status: ComputedRef<string>;
22
+ error: ComputedRef<unknown>;
23
+ refresh: () => Promise<void>;
24
+ }
25
+ export declare function useContentMany<const H extends ContentCollectionTarget, O extends ManyOptions<H, PopulateSpec | undefined>>(handle: H, options?: Reactive<O>): Promise<UseContentManyReturn<PopulatedDocument<DocFromHandle<H>, PopulateFromOptions<O>>>>;
26
+ export declare function useContentPagination<const H extends ContentCollectionTarget, O extends PaginationOptions<H, PopulateSpec | undefined>>(handle: H, options: Reactive<O>): Promise<UseContentPaginationReturn<PopulatedDocument<DocFromHandle<H>, PopulateFromOptions<O>>>>;
27
+ interface UseContentBacklinksReturn<T> {
28
+ data: ComputedRef<T[]>;
29
+ status: ComputedRef<string>;
30
+ error: ComputedRef<unknown>;
31
+ refresh: () => Promise<void>;
32
+ }
33
+ export declare function useContentBacklinks<const Target extends ContentCollectionTarget, const Source extends BacklinkSource | ReadonlyArray<BacklinkSource>, P extends PopulateSpec | undefined = undefined>(handle: Target, options: Reactive<BacklinksOptions<Target, Source, P>> & {
34
+ from: ReactiveValue<Source>;
35
+ }): Promise<UseContentBacklinksReturn<BacklinksResult<Source, P>[number]>>;
36
+ interface UseContentVariantsReturn<T> {
37
+ data: ComputedRef<Array<ContentVariant<T>>>;
38
+ status: ComputedRef<string>;
39
+ error: ComputedRef<unknown>;
40
+ refresh: () => Promise<void>;
41
+ }
42
+ export declare function useContentVariants<H extends ContentCollectionTarget>(handle: H, options: Reactive<VariantsOptions<H>>): Promise<UseContentVariantsReturn<DocFromHandle<H>>>;
43
+ export {};
@@ -0,0 +1,98 @@
1
+ import { computed } from "vue";
2
+ import { useAsyncData } from "#imports";
3
+ import { createClientContentQueryContext } from "./query-api.js";
4
+ import {
5
+ backlinks as backlinksWithContext,
6
+ many as manyWithContext,
7
+ paginate as paginateWithContext,
8
+ variants as variantsWithContext
9
+ } from "../../query/unified.js";
10
+ import { contentCollectionName, resolveOptions, stableKey } from "./use-content-shared.js";
11
+ export async function useContentMany(handle, options = {}) {
12
+ const resolved = computed(() => resolveOptions(options));
13
+ const key = computed(() => stableKey("content-many", contentCollectionName(handle), resolved.value));
14
+ const context = createClientContentQueryContext();
15
+ const asyncDataPromise = useAsyncData(
16
+ key,
17
+ () => manyWithContext(context, handle, resolved.value),
18
+ { watch: [resolved], default: () => [] }
19
+ );
20
+ const asyncData = await asyncDataPromise;
21
+ return {
22
+ data: computed(() => asyncData.data.value || []),
23
+ status: computed(() => asyncData.status.value),
24
+ error: computed(() => asyncData.error.value),
25
+ refresh: () => asyncData.refresh()
26
+ };
27
+ }
28
+ const emptyPagination = () => ({
29
+ data: [],
30
+ page: 1,
31
+ limit: 10,
32
+ total: 0,
33
+ pageCount: 0,
34
+ hasNext: false,
35
+ hasPrev: false,
36
+ nextPage: null,
37
+ prevPage: null
38
+ });
39
+ export async function useContentPagination(handle, options) {
40
+ const resolved = computed(() => resolveOptions(options));
41
+ const key = computed(() => stableKey("content-pagination", contentCollectionName(handle), resolved.value));
42
+ const context = createClientContentQueryContext();
43
+ const asyncDataPromise = useAsyncData(
44
+ key,
45
+ () => paginateWithContext(context, handle, resolved.value),
46
+ { watch: [resolved], default: () => emptyPagination() }
47
+ );
48
+ const asyncData = await asyncDataPromise;
49
+ const result = computed(() => asyncData.data.value || emptyPagination());
50
+ return {
51
+ data: computed(() => result.value.data),
52
+ page: computed(() => result.value.page),
53
+ limit: computed(() => result.value.limit),
54
+ total: computed(() => result.value.total),
55
+ pageCount: computed(() => result.value.pageCount),
56
+ hasNext: computed(() => result.value.hasNext),
57
+ hasPrev: computed(() => result.value.hasPrev),
58
+ nextPage: computed(() => result.value.nextPage),
59
+ prevPage: computed(() => result.value.prevPage),
60
+ status: computed(() => asyncData.status.value),
61
+ error: computed(() => asyncData.error.value),
62
+ refresh: () => asyncData.refresh()
63
+ };
64
+ }
65
+ export async function useContentBacklinks(handle, options) {
66
+ const resolved = computed(() => resolveOptions(options));
67
+ const key = computed(() => stableKey("content-backlinks", contentCollectionName(handle), resolved.value));
68
+ const context = createClientContentQueryContext();
69
+ const asyncDataPromise = useAsyncData(
70
+ key,
71
+ () => backlinksWithContext(context, handle, resolved.value),
72
+ { watch: [resolved], default: () => [] }
73
+ );
74
+ const asyncData = await asyncDataPromise;
75
+ return {
76
+ data: computed(() => asyncData.data.value || []),
77
+ status: computed(() => asyncData.status.value),
78
+ error: computed(() => asyncData.error.value),
79
+ refresh: () => asyncData.refresh()
80
+ };
81
+ }
82
+ export async function useContentVariants(handle, options) {
83
+ const resolved = computed(() => resolveOptions(options));
84
+ const key = computed(() => stableKey("content-variants", contentCollectionName(handle), resolved.value));
85
+ const context = createClientContentQueryContext();
86
+ const asyncDataPromise = useAsyncData(
87
+ key,
88
+ () => variantsWithContext(context, handle, resolved.value),
89
+ { watch: [resolved], default: () => [] }
90
+ );
91
+ const asyncData = await asyncDataPromise;
92
+ return {
93
+ data: computed(() => asyncData.data.value || []),
94
+ status: computed(() => asyncData.status.value),
95
+ error: computed(() => asyncData.error.value),
96
+ refresh: () => asyncData.refresh()
97
+ };
98
+ }
@@ -0,0 +1 @@
1
+ export * from './use-content-navigation.js'
@@ -0,0 +1,40 @@
1
+ import type { ComputedRef } from 'vue';
2
+ import type { ContentCollectionTarget, ContentTreeItem, DocumentFromHandle, NeighborsOptions, NeighborsResult, TreeOptions } from '../../../types/query';
3
+ import { type Reactive } from './use-content-shared';
4
+ export type ContentNavigationNode<T> = Omit<ContentTreeItem<T>, 'children'> & {
5
+ id: string;
6
+ path: string;
7
+ title: string;
8
+ children: Array<ContentNavigationNode<T>>;
9
+ };
10
+ export declare const normalizeContentNavigation: <T>(items?: Array<ContentTreeItem<T>>) => Array<ContentNavigationNode<T>>;
11
+ export declare const findFirstContentNavigationPage: <T>(items: Array<ContentNavigationNode<T>>) => ContentNavigationNode<T> | null;
12
+ export declare const collectContentNavigationPaths: <T>(items: Array<ContentNavigationNode<T>>, paths?: Set<string>) => Set<string>;
13
+ type DocFromHandle<H> = DocumentFromHandle<H>;
14
+ interface UseContentTreeReturn<T> {
15
+ data: ComputedRef<T[]>;
16
+ pending: ComputedRef<boolean>;
17
+ status: ComputedRef<string>;
18
+ error: ComputedRef<unknown>;
19
+ refresh: () => Promise<void>;
20
+ }
21
+ interface UseContentNavigationReturn<T> {
22
+ data: ComputedRef<Array<ContentNavigationNode<T>>>;
23
+ firstPage: ComputedRef<ContentNavigationNode<T> | null>;
24
+ paths: ComputedRef<Set<string>>;
25
+ pending: ComputedRef<boolean>;
26
+ status: ComputedRef<string>;
27
+ error: ComputedRef<unknown>;
28
+ refresh: () => Promise<void>;
29
+ }
30
+ export declare function useContentTree<H extends ContentCollectionTarget>(handle: H, options?: Reactive<TreeOptions<H>>): Promise<UseContentTreeReturn<ContentTreeItem<DocFromHandle<H>>>>;
31
+ export declare function useContentNavigation<H extends ContentCollectionTarget>(handle: H, options?: Reactive<TreeOptions<H>>): Promise<UseContentNavigationReturn<DocFromHandle<H>>>;
32
+ interface UseContentNeighborsReturn<T> {
33
+ data: ComputedRef<NeighborsResult<T>>;
34
+ pending: ComputedRef<boolean>;
35
+ status: ComputedRef<string>;
36
+ error: ComputedRef<unknown>;
37
+ refresh: () => Promise<void>;
38
+ }
39
+ export declare function useContentNeighbors<H extends ContentCollectionTarget>(handle: H, options: Reactive<NeighborsOptions<H>>): Promise<UseContentNeighborsReturn<DocFromHandle<H>>>;
40
+ export {};
@@ -0,0 +1,102 @@
1
+ import { computed } from "vue";
2
+ import { useAsyncData } from "#imports";
3
+ import { resolveCollectionI18n } from "../../../features/localization/path";
4
+ import { createClientContentQueryContext } from "./query-api.js";
5
+ import { neighbors as neighborsWithContext, tree as treeWithContext } from "../../query/unified.js";
6
+ import { getContentRoute, getContentRuntime } from "./runtime.js";
7
+ import { contentCollectionName, resolveLocaleFromRoutePath, resolveOptions, stableKey } from "./use-content-shared.js";
8
+ const navigationNodeId = (item) => {
9
+ const id = item.id ?? item._id ?? item._canonicalKey ?? item.path ?? item._path ?? item.title;
10
+ return typeof id === "string" && id.length ? id : item.title;
11
+ };
12
+ export const normalizeContentNavigation = (items = []) => {
13
+ return items.map((item) => {
14
+ const source = item;
15
+ return {
16
+ ...item,
17
+ id: navigationNodeId(source),
18
+ path: item.path,
19
+ title: item.title,
20
+ children: normalizeContentNavigation(item.children || [])
21
+ };
22
+ });
23
+ };
24
+ export const findFirstContentNavigationPage = (items) => {
25
+ for (const item of items) {
26
+ if (item.path) return item;
27
+ const child = findFirstContentNavigationPage(item.children);
28
+ if (child) return child;
29
+ }
30
+ return null;
31
+ };
32
+ export const collectContentNavigationPaths = (items, paths = /* @__PURE__ */ new Set()) => {
33
+ for (const item of items) {
34
+ if (item.path) paths.add(item.path);
35
+ collectContentNavigationPaths(item.children, paths);
36
+ }
37
+ return paths;
38
+ };
39
+ export async function useContentTree(handle, options = {}) {
40
+ const resolved = computed(() => resolveOptions(options));
41
+ const key = computed(() => stableKey("content-tree", contentCollectionName(handle), resolved.value));
42
+ const context = createClientContentQueryContext();
43
+ const asyncDataPromise = useAsyncData(
44
+ key,
45
+ () => treeWithContext(context, handle, resolved.value),
46
+ { watch: [resolved] }
47
+ );
48
+ const asyncData = await asyncDataPromise;
49
+ return {
50
+ data: computed(() => asyncData.data.value || []),
51
+ pending: computed(() => asyncData.pending.value),
52
+ status: computed(() => asyncData.status.value),
53
+ error: computed(() => asyncData.error.value),
54
+ refresh: () => asyncData.refresh()
55
+ };
56
+ }
57
+ export async function useContentNavigation(handle, options = {}) {
58
+ const route = getContentRoute();
59
+ const runtime = getContentRuntime();
60
+ const collectionI18n = resolveCollectionI18n(contentCollectionName(handle), runtime);
61
+ const routeAwareOptions = computed(() => {
62
+ const resolved = resolveOptions(options);
63
+ if (resolved.locale) {
64
+ return resolved;
65
+ }
66
+ const { locales, defaultLocale } = collectionI18n;
67
+ if (!locales.length && !defaultLocale) {
68
+ return resolved;
69
+ }
70
+ const locale = resolveLocaleFromRoutePath(route.path, locales, defaultLocale);
71
+ return locale ? { ...resolved, locale } : resolved;
72
+ });
73
+ const tree = await useContentTree(handle, routeAwareOptions);
74
+ const data = computed(() => normalizeContentNavigation(tree.data.value));
75
+ return {
76
+ data,
77
+ firstPage: computed(() => findFirstContentNavigationPage(data.value)),
78
+ paths: computed(() => collectContentNavigationPaths(data.value)),
79
+ pending: tree.pending,
80
+ status: tree.status,
81
+ error: tree.error,
82
+ refresh: tree.refresh
83
+ };
84
+ }
85
+ export async function useContentNeighbors(handle, options) {
86
+ const resolved = computed(() => resolveOptions(options));
87
+ const key = computed(() => stableKey("content-neighbors", contentCollectionName(handle), resolved.value));
88
+ const context = createClientContentQueryContext();
89
+ const asyncDataPromise = useAsyncData(
90
+ key,
91
+ () => neighborsWithContext(context, handle, resolved.value),
92
+ { watch: [resolved], default: () => ({ prev: null, next: null }) }
93
+ );
94
+ const asyncData = await asyncDataPromise;
95
+ return {
96
+ data: computed(() => asyncData.data.value || { prev: null, next: null }),
97
+ pending: computed(() => asyncData.pending.value),
98
+ status: computed(() => asyncData.status.value),
99
+ error: computed(() => asyncData.error.value),
100
+ refresh: () => asyncData.refresh()
101
+ };
102
+ }
@@ -0,0 +1 @@
1
+ export * from './use-content-page.js'
@@ -0,0 +1,47 @@
1
+ import type { ComputedRef } from 'vue';
2
+ import { createError } from '#imports';
3
+ import type { ContentCollectionTarget, ContentTreeItem, DocumentFromHandle, LocaleFallback, LocalizedDoc, NeighborsOptions, OneOptions, PopulateSpec, PopulatedDocument } from '../../../types/query';
4
+ import { type Reactive } from './use-content-shared';
5
+ type DocFromHandle<H> = DocumentFromHandle<H>;
6
+ type ContentPageSurroundOptions<H> = boolean | Omit<NeighborsOptions<H>, 'by' | 'locale' | 'fallback'>;
7
+ type ContentPageOneOptions<H, P extends PopulateSpec | undefined> = Omit<OneOptions<H, P>, 'by' | 'locale' | 'fallback'> & {
8
+ /**
9
+ * Optional locale override. Route pages may omit this even for typed i18n
10
+ * handles because the route selector is the source of truth for the active
11
+ * public URL.
12
+ */
13
+ locale?: string;
14
+ fallback?: LocaleFallback;
15
+ };
16
+ export type UseContentPageOptions<H = unknown, P extends PopulateSpec | undefined = undefined> = Reactive<ContentPageOneOptions<H, P>> & {
17
+ /**
18
+ * Load previous/next items alongside the page.
19
+ *
20
+ * Pass `true` for the default surround query, or an options object to choose
21
+ * fields. The page query remains one request unless this is enabled.
22
+ */
23
+ surround?: ContentPageSurroundOptions<H>;
24
+ /**
25
+ * Override the error raised when the current route does not resolve to a
26
+ * content page. Set `false` to render a local empty/not-found state.
27
+ */
28
+ notFound?: false | (() => Parameters<typeof createError>[0]);
29
+ };
30
+ interface UseContentPageReturn<T> {
31
+ data: ComputedRef<LocalizedDoc<T> | undefined>;
32
+ page: ComputedRef<LocalizedDoc<T> | undefined>;
33
+ previous: ComputedRef<ContentTreeItem<T> | null>;
34
+ next: ComputedRef<ContentTreeItem<T> | null>;
35
+ surround: ComputedRef<Array<ContentTreeItem<T>>>;
36
+ pending: ComputedRef<boolean>;
37
+ status: ComputedRef<string>;
38
+ error: ComputedRef<unknown>;
39
+ refresh: () => Promise<void>;
40
+ }
41
+ /**
42
+ * Ergonomic route-page helper. This is the product-level default for Nuxt page
43
+ * components; the explicit `useContentOne` selector remains the lower-level
44
+ * primitive for custom reads.
45
+ */
46
+ export declare function useContentPage<const H extends ContentCollectionTarget, P extends PopulateSpec | undefined = undefined>(handle: H, options?: UseContentPageOptions<H, P>): Promise<UseContentPageReturn<PopulatedDocument<DocFromHandle<H>, P>>>;
47
+ export {};
@@ -0,0 +1,142 @@
1
+ import { computed, shallowRef, toValue, watch } from "vue";
2
+ import { createError, useRoute } from "#imports";
3
+ import { resolveCollectionI18n } from "../../../features/localization/path";
4
+ import { useContentRoute } from "./route.js";
5
+ import { getContentRuntime } from "./runtime.js";
6
+ import { contentCollectionName, resolveLocaleFromRoutePath } from "./use-content-shared.js";
7
+ import { useContentOne } from "./use-content-document.js";
8
+ import { useContentNeighbors } from "./use-content-navigation.js";
9
+ const defaultPageNotFound = () => createError({
10
+ statusCode: 404,
11
+ statusMessage: "Page not found",
12
+ fatal: true
13
+ });
14
+ const normalizePageNotFound = (notFound) => {
15
+ if (notFound === false) return void 0;
16
+ return createError(notFound ? notFound() : defaultPageNotFound());
17
+ };
18
+ const normalizePageSurround = (surround) => {
19
+ if (!surround) return void 0;
20
+ return surround === true ? {} : surround;
21
+ };
22
+ const normalizeRoutePath = (path) => {
23
+ if (typeof path !== "string") return void 0;
24
+ const normalized = path.replace(/\/+$/, "");
25
+ return normalized || "/";
26
+ };
27
+ const localePathMatches = (entry, path) => {
28
+ const normalizedPath = normalizeRoutePath(path);
29
+ if (typeof entry === "string") return normalizeRoutePath(entry) === normalizedPath;
30
+ return Boolean(entry && typeof entry === "object" && "path" in entry && normalizeRoutePath(entry.path) === normalizedPath);
31
+ };
32
+ const routeMetaMatchesPath = (value, path) => {
33
+ if (!value) return false;
34
+ const normalizedPath = normalizeRoutePath(path);
35
+ const legacyPath = value;
36
+ if (normalizeRoutePath(legacyPath._path) === normalizedPath || normalizeRoutePath(legacyPath._requestedPath) === normalizedPath || normalizeRoutePath(legacyPath._requestedRoute) === normalizedPath) return true;
37
+ const requestedRoute = value.resolved?.requestedRoute ?? value._requestedRoute;
38
+ return normalizeRoutePath(value.path) === normalizedPath || normalizeRoutePath(value.canonicalPath) === normalizedPath || normalizeRoutePath(requestedRoute) === normalizedPath || Object.values(value.localePaths || {}).some((entry) => localePathMatches(entry, path));
39
+ };
40
+ export async function useContentPage(handle, options = {}) {
41
+ const route = useRoute();
42
+ const isBrowser = import.meta.client && typeof window !== "undefined";
43
+ const { notFound, surround, ...oneOptions } = options;
44
+ const routeSelector = { route: () => normalizeRoutePath(route.path) };
45
+ const runtime = getContentRuntime();
46
+ const collectionI18n = resolveCollectionI18n(contentCollectionName(handle), runtime);
47
+ const activeLocale = computed(() => {
48
+ const explicitLocale = toValue(oneOptions.locale);
49
+ if (explicitLocale) {
50
+ return explicitLocale;
51
+ }
52
+ const { locales, defaultLocale } = collectionI18n;
53
+ if (!locales.length && !defaultLocale) {
54
+ return void 0;
55
+ }
56
+ return resolveLocaleFromRoutePath(route.path, locales, defaultLocale);
57
+ });
58
+ const rawPage = shallowRef(null);
59
+ const resolvingRoute = shallowRef(false);
60
+ const sawRouteQueryPending = shallowRef(false);
61
+ const page = computed(() => {
62
+ const current = rawPage.value;
63
+ return routeMetaMatchesPath(current, route.path) ? current : void 0;
64
+ });
65
+ useContentRoute(page);
66
+ const pageResultPromise = useContentOne(handle, {
67
+ ...oneOptions,
68
+ locale: () => activeLocale.value,
69
+ by: routeSelector
70
+ });
71
+ const surroundOptions = normalizePageSurround(surround);
72
+ const surroundResultPromise = surroundOptions ? useContentNeighbors(handle, {
73
+ ...surroundOptions,
74
+ locale: () => activeLocale.value,
75
+ ..."fallback" in oneOptions ? { fallback: oneOptions.fallback } : {},
76
+ by: routeSelector
77
+ }) : void 0;
78
+ const pageResult = await pageResultPromise;
79
+ rawPage.value = pageResult.data.value;
80
+ if (isBrowser && rawPage.value && !routeMetaMatchesPath(rawPage.value, route.path)) {
81
+ resolvingRoute.value = true;
82
+ }
83
+ watch(() => route.path, () => {
84
+ resolvingRoute.value = true;
85
+ sawRouteQueryPending.value = false;
86
+ }, { flush: "sync" });
87
+ watch(pageResult.data, (value) => {
88
+ rawPage.value = value;
89
+ const resolved = value;
90
+ if (resolved && routeMetaMatchesPath(resolved, route.path)) {
91
+ resolvingRoute.value = false;
92
+ }
93
+ });
94
+ watch(() => pageResult.pending.value, (pending2) => {
95
+ if (pending2) {
96
+ sawRouteQueryPending.value = true;
97
+ return;
98
+ }
99
+ if (sawRouteQueryPending.value) {
100
+ const resolved = pageResult.data.value;
101
+ if (!resolved || routeMetaMatchesPath(resolved, route.path)) {
102
+ resolvingRoute.value = false;
103
+ }
104
+ }
105
+ });
106
+ const surroundResult = surroundResultPromise ? await surroundResultPromise : void 0;
107
+ const pending = computed(() => pageResult.pending.value || Boolean(surroundResult?.pending.value));
108
+ const pageError = computed(() => {
109
+ if (pageResult.error.value) return pageResult.error.value;
110
+ if (surroundResult?.error.value) return surroundResult.error.value;
111
+ if (pending.value || resolvingRoute.value || page.value || notFound === false) return void 0;
112
+ return normalizePageNotFound(notFound);
113
+ });
114
+ if (!isBrowser && pageError.value) {
115
+ throw pageError.value;
116
+ }
117
+ const previous = computed(() => {
118
+ if (!page.value || !surroundResult) return null;
119
+ return surroundResult.data.value.prev;
120
+ });
121
+ const next = computed(() => {
122
+ if (!page.value || !surroundResult) return null;
123
+ return surroundResult.data.value.next;
124
+ });
125
+ const pageSurround = computed(() => {
126
+ return [previous.value, next.value].filter((item) => Boolean(item));
127
+ });
128
+ return {
129
+ data: page,
130
+ page,
131
+ surround: pageSurround,
132
+ previous,
133
+ next,
134
+ pending,
135
+ status: computed(() => pageResult.status.value),
136
+ error: pageError,
137
+ refresh: async () => {
138
+ await pageResult.refresh();
139
+ if (surroundResult) await surroundResult.refresh();
140
+ }
141
+ };
142
+ }
@@ -0,0 +1 @@
1
+ export * from './use-content-shared.js'
@@ -0,0 +1,13 @@
1
+ import type { MaybeRefOrGetter } from 'vue';
2
+ import type { ContentCollectionTarget } from '../../../types/query';
3
+ export type ReactiveLeaf<T> = T | MaybeRefOrGetter<T>;
4
+ export type ReactiveValue<V> = V extends ((...args: never[]) => unknown) | readonly unknown[] ? ReactiveLeaf<V> : V extends object | undefined ? ReactiveLeaf<V> | {
5
+ [P in keyof NonNullable<V>]?: ReactiveLeaf<NonNullable<V>[P]>;
6
+ } : ReactiveLeaf<V>;
7
+ export type Reactive<T> = {
8
+ [K in keyof T]: ReactiveValue<T[K]>;
9
+ };
10
+ export declare const resolveOptions: <T extends Record<string, unknown>>(reactive: Reactive<T>) => T;
11
+ export declare const stableKey: (prefix: string, name: string, options: unknown) => string;
12
+ export declare const contentCollectionName: (handle: ContentCollectionTarget) => string;
13
+ export declare const resolveLocaleFromRoutePath: (path: string, locales: string[], defaultLocale?: string) => string | undefined;
@@ -0,0 +1,29 @@
1
+ import { toValue } from "vue";
2
+ import { hash } from "ohash";
3
+ const unwrapDeep = (value, seen = /* @__PURE__ */ new WeakSet()) => {
4
+ if (value === null || value === void 0) return value;
5
+ if (typeof value === "function") return unwrapDeep(toValue(value), seen);
6
+ if (typeof value === "object" && "__v_isRef" in value) {
7
+ return unwrapDeep(toValue(value), seen);
8
+ }
9
+ if (Array.isArray(value)) return value.map((item) => unwrapDeep(item, seen));
10
+ if (typeof value === "object" && value.constructor === Object) {
11
+ if (seen.has(value)) return value;
12
+ seen.add(value);
13
+ const out = {};
14
+ for (const [k, v] of Object.entries(value)) {
15
+ out[k] = unwrapDeep(v, seen);
16
+ }
17
+ return out;
18
+ }
19
+ return value;
20
+ };
21
+ export const resolveOptions = (reactive) => {
22
+ return unwrapDeep(reactive);
23
+ };
24
+ export const stableKey = (prefix, name, options) => `${prefix}:${name}:${hash(options)}`;
25
+ export const contentCollectionName = (handle) => typeof handle === "string" ? handle : handle.name;
26
+ export const resolveLocaleFromRoutePath = (path, locales, defaultLocale) => {
27
+ const firstSegment = path.split("/").filter(Boolean)[0];
28
+ return firstSegment && locales.includes(firstSegment) ? firstSegment : defaultLocale;
29
+ };