@lupinum/ginko-content 0.1.2 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -4
- package/compatibility.json +1 -1
- package/dist/cli.mjs +317 -294
- package/dist/cms-import/index.js +5 -1
- package/dist/config.d.mts +1 -1
- package/dist/config.mjs +1 -1
- package/dist/core/cache-hints.d.ts +9 -1
- package/dist/core/content/collection.js +1 -1
- package/dist/core/content/path.js +1 -1
- package/dist/core/errors.js +5 -1
- package/dist/core/provider-errors +1 -0
- package/dist/core/provider-errors.d.ts +10 -0
- package/dist/core/provider-errors.js +27 -0
- package/dist/core/query/execute.js +12 -3
- package/dist/core/query/filter.js +2 -2
- package/dist/core/query/lower.js +5 -4
- package/dist/features/navigation/build.js +14 -6
- package/dist/features/navigation/canonical.js +1 -1
- package/dist/features/sitemap/query.js +2 -2
- package/dist/integrations/nitro/ingest.d.ts +3 -3
- package/dist/integrations/nitro/ingest.js +3 -3
- package/dist/integrations/nitro/storage.js +3 -1
- package/dist/module.d.mts +151 -526
- package/dist/module.json +1 -1
- package/dist/module.mjs +1091 -722
- package/dist/parsers/csv.js +4 -3
- package/dist/parsers/index.d.ts +2 -5
- package/dist/parsers/index.js +2 -2
- package/dist/parsers/markdown-plugins.d.ts +1 -0
- package/dist/parsers/markdown-plugins.js +83 -1
- package/dist/parsers/markdown.js +1 -1
- package/dist/public/client.d.ts +3 -2
- package/dist/public/client.js +2 -2
- package/dist/public/provider-errors.d.ts +2 -1
- package/dist/public/provider-errors.js +1 -18
- package/dist/public/provider.d.ts +3 -10
- package/dist/public/server.d.ts +6 -1
- package/dist/public/server.js +33 -0
- package/dist/runtime/agent-paths +1 -0
- package/dist/runtime/agent-paths.d.ts +6 -0
- package/dist/runtime/agent-paths.js +41 -0
- package/dist/runtime/app/components/internal/MarkdownRenderer.d.ts +1 -1
- package/dist/runtime/app/composables/head.d.ts +3 -0
- package/dist/runtime/app/composables/head.js +2 -1
- package/dist/runtime/app/composables/hot-reload +1 -0
- package/dist/runtime/app/composables/hot-reload.d.ts +5 -0
- package/dist/runtime/app/composables/hot-reload.js +15 -0
- package/dist/runtime/app/composables/locale-context.d.ts +1 -1
- package/dist/runtime/app/composables/locale-context.js +2 -1
- package/dist/runtime/app/composables/locale.js +2 -2
- package/dist/runtime/app/composables/search.js +10 -6
- package/dist/runtime/app/composables/use-content-document +1 -0
- package/dist/runtime/app/composables/use-content-document.d.ts +29 -0
- package/dist/runtime/app/composables/use-content-document.js +44 -0
- package/dist/runtime/app/composables/use-content-list +1 -0
- package/dist/runtime/app/composables/use-content-list.d.ts +43 -0
- package/dist/runtime/app/composables/use-content-list.js +98 -0
- package/dist/runtime/app/composables/use-content-navigation +1 -0
- package/dist/runtime/app/composables/use-content-navigation.d.ts +40 -0
- package/dist/runtime/app/composables/use-content-navigation.js +102 -0
- package/dist/runtime/app/composables/use-content-page +1 -0
- package/dist/runtime/app/composables/use-content-page.d.ts +47 -0
- package/dist/runtime/app/composables/use-content-page.js +142 -0
- package/dist/runtime/app/composables/use-content-shared +1 -0
- package/dist/runtime/app/composables/use-content-shared.d.ts +13 -0
- package/dist/runtime/app/composables/use-content-shared.js +29 -0
- package/dist/runtime/app/composables/use-content.d.ts +11 -206
- package/dist/runtime/app/composables/use-content.js +4 -377
- package/dist/runtime/app/plugins/hot-reload +1 -0
- package/dist/runtime/app/plugins/hot-reload.js +6 -0
- package/dist/runtime/query/backlinks +1 -0
- package/dist/runtime/query/backlinks.d.ts +8 -0
- package/dist/runtime/query/backlinks.js +90 -0
- package/dist/runtime/query/context +1 -0
- package/dist/runtime/query/context.d.ts +20 -0
- package/dist/runtime/query/context.js +0 -0
- package/dist/runtime/query/documents +1 -0
- package/dist/runtime/query/documents.d.ts +6 -0
- package/dist/runtime/query/documents.js +98 -0
- package/dist/runtime/query/errors +1 -0
- package/dist/runtime/query/errors.d.ts +1 -0
- package/dist/runtime/query/errors.js +4 -0
- package/dist/runtime/query/handles +1 -0
- package/dist/runtime/query/handles.d.ts +2 -0
- package/dist/runtime/query/handles.js +7 -0
- package/dist/runtime/query/locale-options +1 -0
- package/dist/runtime/query/locale-options.d.ts +5 -0
- package/dist/runtime/query/locale-options.js +14 -0
- package/dist/runtime/query/localized-docs +1 -0
- package/dist/runtime/query/localized-docs.d.ts +17 -0
- package/dist/runtime/query/localized-docs.js +44 -0
- package/dist/runtime/query/navigation +1 -0
- package/dist/runtime/query/navigation.d.ts +22 -0
- package/dist/runtime/query/navigation.js +73 -0
- package/dist/runtime/query/pagination +1 -0
- package/dist/runtime/query/pagination.d.ts +7 -0
- package/dist/runtime/query/pagination.js +78 -0
- package/dist/runtime/query/populate +1 -0
- package/dist/runtime/query/populate.d.ts +9 -0
- package/dist/runtime/query/populate.js +106 -0
- package/dist/runtime/query/responses +1 -0
- package/dist/runtime/query/responses.d.ts +10 -0
- package/dist/runtime/query/responses.js +47 -0
- package/dist/runtime/query/unified.d.ts +4 -20
- package/dist/runtime/query/unified.js +14 -544
- package/dist/runtime/query/variants +1 -0
- package/dist/runtime/query/variants.d.ts +11 -0
- package/dist/runtime/query/variants.js +36 -0
- package/dist/runtime/server/agent-http +1 -0
- package/dist/runtime/server/agent-http.d.ts +7 -0
- package/dist/runtime/server/agent-http.js +36 -0
- package/dist/runtime/server/agent-markdown +1 -0
- package/dist/runtime/server/agent-markdown.d.ts +83 -0
- package/dist/runtime/server/agent-markdown.js +449 -0
- package/dist/runtime/server/agent-site +1 -0
- package/dist/runtime/server/agent-site.d.ts +38 -0
- package/dist/runtime/server/agent-site.js +273 -0
- package/dist/runtime/server/api/agent-llms +1 -0
- package/dist/runtime/server/api/agent-llms-full +1 -0
- package/dist/runtime/server/api/agent-llms-full.d.ts +2 -0
- package/dist/runtime/server/api/agent-llms-full.js +13 -0
- package/dist/runtime/server/api/agent-llms.d.ts +2 -0
- package/dist/runtime/server/api/agent-llms.js +14 -0
- package/dist/runtime/server/api/agent-raw +1 -0
- package/dist/runtime/server/api/agent-raw.d.ts +2 -0
- package/dist/runtime/server/api/agent-raw.js +17 -0
- package/dist/runtime/server/api/locales.d.ts +2 -2
- package/dist/runtime/server/api/locales.js +4 -2
- package/dist/runtime/server/api/navigation.d.ts +1 -1
- package/dist/runtime/server/api/query.d.ts +1 -1
- package/dist/runtime/server/api/query.js +4 -1
- package/dist/runtime/server/api/revalidate.js +2 -2
- package/dist/runtime/server/api/site-data.d.ts +1 -1
- package/dist/runtime/server/collection-helpers.js +2 -1
- package/dist/runtime/server/middleware/agent-link-headers +1 -0
- package/dist/runtime/server/middleware/agent-link-headers.d.ts +2 -0
- package/dist/runtime/server/middleware/agent-link-headers.js +42 -0
- package/dist/runtime/server/middleware/agent-markdown +1 -0
- package/dist/runtime/server/middleware/agent-markdown.d.ts +2 -0
- package/dist/runtime/server/middleware/agent-markdown.js +43 -0
- package/dist/runtime/server/plugins/sitemap.js +1 -0
- package/dist/runtime/server/provider-query.d.ts +8 -5
- package/dist/runtime/server/provider-query.js +50 -8
- package/dist/runtime/server/provider-result.d.ts +19 -2
- package/dist/runtime/server/providers/filesystem.js +4 -1
- package/dist/runtime/server/providers/index.d.ts +3 -1
- package/dist/runtime/server/providers/index.js +2 -2
- package/dist/runtime/server/query-api.js +2 -1
- package/dist/runtime/server/query-executor.d.ts +3 -2
- package/dist/runtime/server/query-executor.js +13 -5
- package/dist/runtime/server/search.js +1 -1
- package/dist/runtime/server/sitemap-provider.js +40 -1
- package/dist/runtime/server/sitemap.js +3 -2
- package/dist/runtime/transformers/component-resolver.js +2 -1
- package/dist/runtime/utils/content-components.d.ts +0 -5
- package/dist/storage/content.d.ts +2 -9
- package/dist/storage/references.d.ts +1 -8
- package/dist/storage/references.js +9 -4
- package/dist/testing/provider-contract.js +3 -4
- package/dist/testing/provider-fixture.d.ts +3 -1
- package/dist/testing/provider-fixture.js +27 -14
- package/dist/types/config.d.ts +77 -0
- package/dist/types/config.js +23 -0
- package/dist/types/module.d.ts +15 -5
- package/dist/types/query-parts/collections +1 -0
- package/dist/types/query-parts/collections.d.ts +15 -0
- package/dist/types/query-parts/collections.js +0 -0
- package/dist/types/query-parts/public +1 -0
- package/dist/types/query-parts/public.d.ts +202 -0
- package/dist/types/query-parts/public.js +0 -0
- package/dist/types/query-parts/results +1 -0
- package/dist/types/query-parts/results.d.ts +95 -0
- package/dist/types/query-parts/results.js +0 -0
- package/dist/types/query-parts/transport +1 -0
- package/dist/types/query-parts/transport.d.ts +103 -0
- package/dist/types/query-parts/transport.js +0 -0
- package/dist/types/query.d.ts +4 -870
- package/dist/types/query.js +4 -0
- package/dist/types/runtime.d.ts +2 -2
- package/dist/types/virtual.d.ts +54 -0
- package/dist/types.d.mts +2 -2
- package/dist/web-types.json +1 -1
- package/package.json +5 -5
- package/dist/runtime/app/composables/web-socket +0 -1
- package/dist/runtime/app/composables/web-socket.d.ts +0 -3
- package/dist/runtime/app/composables/web-socket.js +0 -65
- package/dist/runtime/app/plugins/ws +0 -1
- package/dist/runtime/app/plugins/ws.js +0 -7
- /package/dist/runtime/app/plugins/{ws.d.ts → hot-reload.d.ts} +0 -0
|
@@ -4,14 +4,51 @@ import { normalizeContentQueryParams } from "../../core/query/params";
|
|
|
4
4
|
import { normalizeI18nConfig, resolveRuntimeCollectionI18nConfig } from "../../features/localization/config";
|
|
5
5
|
import { normalizeReferenceValue } from "../../core/references/resolve";
|
|
6
6
|
import { getContentRuntimeConfig } from "./runtime-config.js";
|
|
7
|
+
import { createContentProviderError } from "../../public/provider-errors";
|
|
8
|
+
const isObject = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
9
|
+
const isProviderFindResponse = (response) => isObject(response) && Array.isArray(response.result) && typeof response.skip === "number" && typeof response.limit === "number" && typeof response.total === "number";
|
|
7
10
|
export const normalizeProviderQueryResult = (response) => {
|
|
8
|
-
if (
|
|
11
|
+
if (isProviderFindResponse(response)) {
|
|
12
|
+
return response.result;
|
|
13
|
+
}
|
|
14
|
+
return [];
|
|
15
|
+
};
|
|
16
|
+
const isProviderFindOneResponse = (response) => isObject(response) && "result" in response && Object.keys(response).length === 1;
|
|
17
|
+
const isProviderCountResponse = (response) => isObject(response) && typeof response.result === "number" && Object.keys(response).length === 1;
|
|
18
|
+
const hasListEnvelopeKeys = (response) => ["skip", "limit", "total"].some((key) => key in response);
|
|
19
|
+
const describeProviderResponse = (response) => {
|
|
20
|
+
if (Array.isArray(response)) return "array";
|
|
21
|
+
if (response === null) return "null";
|
|
22
|
+
return typeof response;
|
|
23
|
+
};
|
|
24
|
+
const invalidProviderQueryResult = (params, message, response, providerName) => {
|
|
25
|
+
throw createContentProviderError("provider_result_invalid", message, {
|
|
26
|
+
collection: params.collection,
|
|
27
|
+
provider: providerName,
|
|
28
|
+
mode: params.count ? "count" : params.first ? "first" : "many",
|
|
29
|
+
responseType: describeProviderResponse(response)
|
|
30
|
+
});
|
|
31
|
+
};
|
|
32
|
+
export const normalizeProviderQueryResponse = (params, response, providerName) => {
|
|
33
|
+
if (params.count) {
|
|
34
|
+
if (isProviderCountResponse(response)) {
|
|
35
|
+
return response;
|
|
36
|
+
}
|
|
37
|
+
return invalidProviderQueryResult(params, "Provider count queries must return a count envelope: { result: number }.", response, providerName);
|
|
38
|
+
}
|
|
39
|
+
if (params.first) {
|
|
40
|
+
if (isProviderFindOneResponse(response)) {
|
|
41
|
+
return response;
|
|
42
|
+
}
|
|
43
|
+
return invalidProviderQueryResult(params, "Provider first queries must return a find-one envelope: { result: item | undefined }.", response, providerName);
|
|
44
|
+
}
|
|
45
|
+
if (isProviderFindResponse(response)) {
|
|
9
46
|
return response;
|
|
10
47
|
}
|
|
11
|
-
if (response &&
|
|
12
|
-
return
|
|
48
|
+
if (isObject(response) && hasListEnvelopeKeys(response)) {
|
|
49
|
+
return invalidProviderQueryResult(params, "Provider list query envelopes must include array result, skip, limit, and total.", response, providerName);
|
|
13
50
|
}
|
|
14
|
-
return [];
|
|
51
|
+
return invalidProviderQueryResult(params, "Provider list queries must return a list envelope: { result: item[], skip, limit, total }.", response, providerName);
|
|
15
52
|
};
|
|
16
53
|
const identityMatchesDocument = (document, identity) => {
|
|
17
54
|
const normalizedIdentity = normalizeReferenceValue(identity);
|
|
@@ -45,13 +82,16 @@ export const resolveProviderContentVariants = async (event, identity, options) =
|
|
|
45
82
|
const documents = localesToQuery.length ? (await Promise.all(
|
|
46
83
|
localesToQuery.map(
|
|
47
84
|
async (locale) => normalizeProviderQueryResult(
|
|
48
|
-
|
|
85
|
+
normalizeProviderQueryResponse({
|
|
86
|
+
...baseQuery,
|
|
87
|
+
resolveLocale: { locale, exact: true }
|
|
88
|
+
}, await provider.query(event, {
|
|
49
89
|
...baseQuery,
|
|
50
90
|
resolveLocale: { locale, exact: true }
|
|
51
|
-
})
|
|
91
|
+
}), provider.name)
|
|
52
92
|
)
|
|
53
93
|
)
|
|
54
|
-
)).flat() : normalizeProviderQueryResult(await provider.query(event, baseQuery));
|
|
94
|
+
)).flat() : normalizeProviderQueryResult(normalizeProviderQueryResponse(baseQuery, await provider.query(event, baseQuery), provider.name));
|
|
55
95
|
const matched = documents.find((document) => identityMatchesDocument(document, normalizedReference));
|
|
56
96
|
const canonicalKey = matched?._canonicalKey;
|
|
57
97
|
if (!canonicalKey) {
|
|
@@ -84,7 +124,9 @@ export const resolveProviderContentVariants = async (event, identity, options) =
|
|
|
84
124
|
export const createServerProviderQueryFetch = (event) => {
|
|
85
125
|
return async (query) => {
|
|
86
126
|
const { getContentProvider } = await import("./providers/index.js");
|
|
87
|
-
|
|
127
|
+
const provider = await getContentProvider(event);
|
|
128
|
+
const response = await provider.query(event, query.params());
|
|
129
|
+
return normalizeProviderQueryResponse(query.params(), response, provider.name);
|
|
88
130
|
};
|
|
89
131
|
};
|
|
90
132
|
const getServerCollectionI18n = (collection) => {
|
|
@@ -1,10 +1,27 @@
|
|
|
1
1
|
import type { H3Event } from 'h3';
|
|
2
|
-
import type {
|
|
2
|
+
import type { ContentQueryResponse } from '../../types/api';
|
|
3
|
+
import type { NavItem, ParsedContent } from '../../types/content';
|
|
4
|
+
import type { ContentCollectionItemSurroundingsOptions, ContentCollectionNavigationOptions, ContentCollectionPageOptions, ContentCollectionRouteMetaOptions, ContentCollectionSearchSectionsOptions, ContentPageResult, ContentQueryBuilderParams, ContentRouteMeta, ContentSearchSection, ContentSitemapEntry } from '../../types/query';
|
|
5
|
+
import type { QueryCollectionsSitemapEntriesOptions } from '../../features/sitemap/query';
|
|
6
|
+
import type { ContentProvider, ContentProviderResult, ContentProviderSiteDataRequest, ContentProviderSiteDataResponse, MaybeContentProviderResult } from '../../public/provider';
|
|
7
|
+
import type { ContentProviderSearchRequest, ContentSearchResult } from '../../types/search';
|
|
3
8
|
export declare const isContentProviderResult: <T = unknown>(value: unknown) => value is ContentProviderResult<T>;
|
|
4
9
|
export declare const unwrapContentProviderResult: <T>(event: H3Event, result: MaybeContentProviderResult<T>) => T;
|
|
10
|
+
export interface RuntimeContentProvider extends Omit<ContentProvider, 'query' | 'navigationQuery' | 'navigation' | 'surroundings' | 'searchSections' | 'search' | 'siteData' | 'page' | 'routeMeta' | 'sitemapEntries'> {
|
|
11
|
+
query: <T = ParsedContent>(event: H3Event, query: ContentQueryBuilderParams) => Promise<ContentQueryResponse<T>>;
|
|
12
|
+
navigationQuery?: (event: H3Event, query: ContentQueryBuilderParams) => Promise<NavItem[]>;
|
|
13
|
+
navigation?: (event: H3Event, collection: string, options?: string[] | ContentCollectionNavigationOptions) => Promise<NavItem[]>;
|
|
14
|
+
surroundings?: (event: H3Event, collection: string, path: string, options?: ContentCollectionItemSurroundingsOptions) => Promise<Array<NavItem | null>>;
|
|
15
|
+
searchSections?: (event: H3Event, collection: string, options?: ContentCollectionSearchSectionsOptions) => Promise<ContentSearchSection[]>;
|
|
16
|
+
search?: (event: H3Event, request: ContentProviderSearchRequest) => Promise<ContentSearchResult[]>;
|
|
17
|
+
siteData?: <T = unknown>(event: H3Event, request: ContentProviderSiteDataRequest) => Promise<ContentProviderSiteDataResponse<T>>;
|
|
18
|
+
page?: <T = ParsedContent>(event: H3Event, collection: string, routeOrPath?: string, options?: ContentCollectionPageOptions) => Promise<ContentPageResult<T> | null>;
|
|
19
|
+
routeMeta?: (event: H3Event, collection: string, routeOrPath?: string, options?: ContentCollectionRouteMetaOptions) => Promise<ContentRouteMeta | null>;
|
|
20
|
+
sitemapEntries?: (event: H3Event, options?: QueryCollectionsSitemapEntriesOptions) => Promise<ContentSitemapEntry[]>;
|
|
21
|
+
}
|
|
5
22
|
/**
|
|
6
23
|
* External providers may return `{ data, cache }` from any provider method.
|
|
7
24
|
* Runtime consumers should continue receiving the historical raw data shape,
|
|
8
25
|
* while the request-local cache collector records the cache hint.
|
|
9
26
|
*/
|
|
10
|
-
export declare const wrapContentProviderCacheResults: (event: H3Event, provider: ContentProvider) =>
|
|
27
|
+
export declare const wrapContentProviderCacheResults: (event: H3Event, provider: ContentProvider) => RuntimeContentProvider;
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
queryFilesystemCollectionSearchSections
|
|
10
10
|
} from "../collection-helpers.js";
|
|
11
11
|
import { queryFilesystemCollectionsSitemapEntries } from "../sitemap.js";
|
|
12
|
+
import { createContentProviderError } from "../../../public/provider-errors";
|
|
12
13
|
export const filesystemProvider = {
|
|
13
14
|
name: "filesystem",
|
|
14
15
|
capabilities: {
|
|
@@ -33,7 +34,9 @@ export const filesystemProvider = {
|
|
|
33
34
|
},
|
|
34
35
|
query: (event, query) => {
|
|
35
36
|
if (containsStandaloneRegexOptions(query.where)) {
|
|
36
|
-
throw
|
|
37
|
+
throw createContentProviderError("unsupported_query_shape", "Query operator $options requires $regex.", {
|
|
38
|
+
operator: "$options"
|
|
39
|
+
});
|
|
37
40
|
}
|
|
38
41
|
return executeFilesystemContentQuery(event, query);
|
|
39
42
|
},
|
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
import type { H3Event } from 'h3';
|
|
2
2
|
import type { ContentProvider } from '../../../public/provider';
|
|
3
|
-
|
|
3
|
+
import { type RuntimeContentProvider } from '../provider-result';
|
|
4
|
+
export declare function getContentProvider(): Promise<ContentProvider>;
|
|
5
|
+
export declare function getContentProvider(event: H3Event): Promise<RuntimeContentProvider>;
|
|
@@ -160,7 +160,7 @@ const validateContentProvider = (providerName, provider) => {
|
|
|
160
160
|
}
|
|
161
161
|
return provider;
|
|
162
162
|
};
|
|
163
|
-
export
|
|
163
|
+
export async function getContentProvider(event) {
|
|
164
164
|
const runtime = event ? useRuntimeConfig(event) : useRuntimeConfig();
|
|
165
165
|
const eventContent = event?.context?.contentRuntime;
|
|
166
166
|
const provider = eventContent?.provider || runtime.content?.provider || runtime.public?.content?.provider || "filesystem";
|
|
@@ -187,4 +187,4 @@ export const getContentProvider = async (event) => {
|
|
|
187
187
|
}
|
|
188
188
|
}
|
|
189
189
|
throw createContentProviderError("unknown_provider", `Unknown content provider: ${provider}`, { provider });
|
|
190
|
-
}
|
|
190
|
+
}
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
import { getContentProvider } from "./providers/index.js";
|
|
12
12
|
import { createContentProviderError } from "../../public/provider-errors";
|
|
13
13
|
import { getContentRuntimeConfig } from "./runtime-config.js";
|
|
14
|
+
import { normalizeProviderQueryResponse } from "./provider-query.js";
|
|
14
15
|
export const createServerContentQueryContext = async (event) => {
|
|
15
16
|
const provider = await getContentProvider(event);
|
|
16
17
|
return {
|
|
@@ -24,7 +25,7 @@ export const createServerContentQueryContext = async (event) => {
|
|
|
24
25
|
}
|
|
25
26
|
return await provider.navigationQuery(event, params);
|
|
26
27
|
}
|
|
27
|
-
return await provider.query(event, params);
|
|
28
|
+
return normalizeProviderQueryResponse(params, await provider.query(event, params), provider.name);
|
|
28
29
|
}
|
|
29
30
|
};
|
|
30
31
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type H3Event } from 'h3';
|
|
2
|
+
import type { ContentQueryResponse } from '../../types/api';
|
|
2
3
|
import type { ContentQueryBuilderParams } from '../../types/query';
|
|
3
|
-
export declare const executeFilesystemContentQuery: <T = unknown>(event: H3Event, inputQuery: ContentQueryBuilderParams) => Promise<T
|
|
4
|
-
export declare const executeContentQuery: <T = unknown>(event: H3Event, inputQuery: ContentQueryBuilderParams) => Promise<T
|
|
4
|
+
export declare const executeFilesystemContentQuery: <T = unknown>(event: H3Event, inputQuery: ContentQueryBuilderParams) => Promise<ContentQueryResponse<T>>;
|
|
5
|
+
export declare const executeContentQuery: <T = unknown>(event: H3Event, inputQuery: ContentQueryBuilderParams) => Promise<ContentQueryResponse<T>>;
|
|
@@ -72,13 +72,15 @@ const normalizePublicQuery = (event, query, config) => {
|
|
|
72
72
|
includeDraftFilter: false
|
|
73
73
|
});
|
|
74
74
|
if (enforceProductionVisibility) {
|
|
75
|
+
const where = Array.isArray(normalized.where) ? normalized.where : normalized.where ? [normalized.where] : [];
|
|
75
76
|
normalized.where = [
|
|
76
|
-
...
|
|
77
|
+
...where,
|
|
77
78
|
{ _draft: { $ne: true } },
|
|
78
79
|
{ _partial: { $ne: true } }
|
|
79
80
|
];
|
|
80
81
|
}
|
|
81
|
-
normalized.where
|
|
82
|
+
const normalizedWhere = Array.isArray(normalized.where) ? normalized.where : normalized.where ? [normalized.where] : [];
|
|
83
|
+
normalized.where = normalizedWhere.map((condition) => compileWhere(condition)).filter((condition) => Boolean(condition));
|
|
82
84
|
const unsupported = findUnsupportedQueryOperator(normalized.where);
|
|
83
85
|
if (unsupported) {
|
|
84
86
|
throw createContentProviderError("unsupported_query_operator", `Unsupported query operator: ${unsupported}`, {
|
|
@@ -106,7 +108,7 @@ export const executeFilesystemContentQuery = async (event, inputQuery) => {
|
|
|
106
108
|
collections: config.collections
|
|
107
109
|
});
|
|
108
110
|
if (plan.mode === "count") {
|
|
109
|
-
return response
|
|
111
|
+
return response;
|
|
110
112
|
}
|
|
111
113
|
const requestedLocale = plan.resolveVariant?.locale || plan.resolveLocale?.locale;
|
|
112
114
|
if (plan.mode === "first") {
|
|
@@ -114,8 +116,14 @@ export const executeFilesystemContentQuery = async (event, inputQuery) => {
|
|
|
114
116
|
if (!content) {
|
|
115
117
|
notFound(query, plan.resolveVariant ? "Could not find document for the given route variant." : void 0);
|
|
116
118
|
}
|
|
117
|
-
return
|
|
119
|
+
return {
|
|
120
|
+
result: await withResolvedRefs(event, content, requestedLocale)
|
|
121
|
+
};
|
|
118
122
|
}
|
|
119
|
-
|
|
123
|
+
const listResponse = response;
|
|
124
|
+
return {
|
|
125
|
+
...listResponse,
|
|
126
|
+
result: await withResolvedRefsList(event, Array.isArray(response.result) ? response.result : [], requestedLocale)
|
|
127
|
+
};
|
|
120
128
|
};
|
|
121
129
|
export const executeContentQuery = executeFilesystemContentQuery;
|
|
@@ -14,7 +14,7 @@ const isRouteBackedSearchCollection = (config) => {
|
|
|
14
14
|
return config.type === "page" || hasRouteMount(config.route) || config.sitemap === true;
|
|
15
15
|
};
|
|
16
16
|
export const resolveSearchCollections = (runtimeContent, collectionsOverride) => {
|
|
17
|
-
const configuredCollections = collectionsOverride || (runtimeContent.search
|
|
17
|
+
const configuredCollections = collectionsOverride || (runtimeContent.search ? runtimeContent.search.collections : void 0);
|
|
18
18
|
if (configuredCollections?.length) {
|
|
19
19
|
return unique(configuredCollections);
|
|
20
20
|
}
|
|
@@ -1,4 +1,43 @@
|
|
|
1
1
|
import { createContentProviderError } from "../../public/provider-errors";
|
|
2
|
+
const isObject = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
3
|
+
const describeShape = (value) => {
|
|
4
|
+
if (Array.isArray(value)) return "array";
|
|
5
|
+
if (value === null) return "null";
|
|
6
|
+
return typeof value;
|
|
7
|
+
};
|
|
8
|
+
const assertSitemapEntry = (entry, index, provider) => {
|
|
9
|
+
if (!isObject(entry) || typeof entry.loc !== "string" || !entry.loc.trim()) {
|
|
10
|
+
throw createContentProviderError("provider_result_invalid", `${provider} returned an invalid sitemap entry.`, {
|
|
11
|
+
provider,
|
|
12
|
+
operation: "sitemap entries",
|
|
13
|
+
index,
|
|
14
|
+
field: "loc",
|
|
15
|
+
actual: describeShape(entry)
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
if (entry.alternatives !== void 0 && (!Array.isArray(entry.alternatives) || entry.alternatives.some(
|
|
19
|
+
(alternative) => !isObject(alternative) || typeof alternative.hreflang !== "string" || typeof alternative.href !== "string"
|
|
20
|
+
))) {
|
|
21
|
+
throw createContentProviderError("provider_result_invalid", `${provider} returned invalid sitemap alternatives.`, {
|
|
22
|
+
provider,
|
|
23
|
+
operation: "sitemap entries",
|
|
24
|
+
index,
|
|
25
|
+
field: "alternatives",
|
|
26
|
+
actual: describeShape(entry.alternatives)
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
const assertSitemapEntries = (value, provider) => {
|
|
31
|
+
if (!Array.isArray(value)) {
|
|
32
|
+
throw createContentProviderError("provider_result_invalid", `${provider} returned invalid sitemap entries.`, {
|
|
33
|
+
provider,
|
|
34
|
+
operation: "sitemap entries",
|
|
35
|
+
actual: describeShape(value)
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
value.forEach((entry, index) => assertSitemapEntry(entry, index, provider));
|
|
39
|
+
return value;
|
|
40
|
+
};
|
|
2
41
|
export async function queryCollectionsSitemapEntries(event, options = {}) {
|
|
3
42
|
const { getContentProvider } = await import("./providers/index.js");
|
|
4
43
|
const provider = await getContentProvider(event);
|
|
@@ -7,5 +46,5 @@ export async function queryCollectionsSitemapEntries(event, options = {}) {
|
|
|
7
46
|
provider: provider.name
|
|
8
47
|
});
|
|
9
48
|
}
|
|
10
|
-
return await provider.sitemapEntries(event, options);
|
|
49
|
+
return assertSitemapEntries(await provider.sitemapEntries(event, options), provider.name);
|
|
11
50
|
}
|
|
@@ -18,12 +18,13 @@ const sitemapPageFields = [
|
|
|
18
18
|
];
|
|
19
19
|
export async function queryFilesystemCollectionsSitemapEntries(event, options = {}) {
|
|
20
20
|
const runtimeConfig = useRuntimeConfig(event);
|
|
21
|
+
const sitemapRuntime = runtimeConfig;
|
|
21
22
|
const requestUrl = event?.node?.req ? getRequestURL(event) : null;
|
|
22
23
|
return await queryCollectionsSitemapEntriesData({
|
|
23
24
|
collections: contentConfig().collections,
|
|
24
25
|
defaultLocale: contentConfig().defaultLocale,
|
|
25
|
-
runtimeSiteUrl:
|
|
26
|
-
localeConfigs:
|
|
26
|
+
runtimeSiteUrl: sitemapRuntime.public.content?.siteUrl || sitemapRuntime.public.siteUrl,
|
|
27
|
+
localeConfigs: sitemapRuntime.public.i18n?.locales || [],
|
|
27
28
|
requestSiteUrl: requestUrl ? `${requestUrl.protocol}//${requestUrl.host}` : void 0
|
|
28
29
|
}, {
|
|
29
30
|
loadCollectionPages: async (collection) => {
|
|
@@ -20,8 +20,9 @@ export default defineTransformer({
|
|
|
20
20
|
if (import.meta.server) {
|
|
21
21
|
return content;
|
|
22
22
|
}
|
|
23
|
+
const transformerOptions = typeof options === "object" && options !== null ? options : {};
|
|
23
24
|
const _components = await resolveContentComponents(content.body, {
|
|
24
|
-
...
|
|
25
|
+
...transformerOptions.tags || {},
|
|
25
26
|
...content._components || {}
|
|
26
27
|
});
|
|
27
28
|
content._components = _components;
|
|
@@ -1,9 +1,4 @@
|
|
|
1
1
|
import type { AsyncComponentLoader } from 'vue';
|
|
2
|
-
declare module '#build/content-components' {
|
|
3
|
-
const globalComponents: string[];
|
|
4
|
-
const localComponents: string[];
|
|
5
|
-
const localComponentLoaders: Record<string, AsyncComponentLoader>;
|
|
6
|
-
}
|
|
7
2
|
export declare const globalComponents: string[];
|
|
8
3
|
export declare const localComponents: string[];
|
|
9
4
|
export declare const localComponentLoaders: Record<string, AsyncComponentLoader>;
|
|
@@ -1,14 +1,7 @@
|
|
|
1
1
|
import type { H3Event } from 'h3';
|
|
2
2
|
import type { ParsedContent } from '../types/content';
|
|
3
|
-
import type { ContentCollectionMap, ContentLocaleEntry, ContentQueryBuilderParams,
|
|
4
|
-
export declare const createServerQueryFetch: <T = ParsedContent>(event: H3Event) =>
|
|
5
|
-
result: T | T[] | undefined;
|
|
6
|
-
} | {
|
|
7
|
-
result: T | T[] | undefined;
|
|
8
|
-
skip: number;
|
|
9
|
-
limit: number;
|
|
10
|
-
total: number;
|
|
11
|
-
}>;
|
|
3
|
+
import type { ContentCollectionMap, ContentLocaleEntry, ContentQueryBuilderParams, ContentQueryFetcher, CollectionQueryBuilder, ResolveContentReferenceOptions } from '../types/query';
|
|
4
|
+
export declare const createServerQueryFetch: <T = ParsedContent>(event: H3Event) => ContentQueryFetcher<T>;
|
|
12
5
|
/**
|
|
13
6
|
* Resolve an authored content reference value to a concrete document.
|
|
14
7
|
*
|
|
@@ -3,11 +3,4 @@ import type { ContentQueryBuilderParams } from '../types/query';
|
|
|
3
3
|
import type { ContentQueryResponse } from '../types/api';
|
|
4
4
|
export declare const withResolvedRefs: <T>(event: H3Event, content: T, requestedLocale?: string) => Promise<T>;
|
|
5
5
|
export declare const withResolvedRefsList: <T>(event: H3Event, items: T[], requestedLocale?: string) => Promise<T[]>;
|
|
6
|
-
export declare const withResolvedRefsQueryResponse: <T>(event: H3Event, response: ContentQueryResponse<T>, params: ContentQueryBuilderParams) => Promise<
|
|
7
|
-
result: T | T[] | undefined;
|
|
8
|
-
} | {
|
|
9
|
-
result: T | T[] | undefined;
|
|
10
|
-
skip: number;
|
|
11
|
-
limit: number;
|
|
12
|
-
total: number;
|
|
13
|
-
}>;
|
|
6
|
+
export declare const withResolvedRefsQueryResponse: <T>(event: H3Event, response: ContentQueryResponse<T>, params: ContentQueryBuilderParams) => Promise<ContentQueryResponse<T>>;
|
|
@@ -66,13 +66,18 @@ export const withResolvedRefsQueryResponse = async (event, response, params) =>
|
|
|
66
66
|
}
|
|
67
67
|
const requestedLocale = params.resolveLocale?.locale;
|
|
68
68
|
if (params.first) {
|
|
69
|
+
const firstResponse = response;
|
|
69
70
|
return {
|
|
70
|
-
...
|
|
71
|
-
result: await withResolvedRefs(event,
|
|
71
|
+
...firstResponse,
|
|
72
|
+
result: await withResolvedRefs(event, firstResponse.result, requestedLocale)
|
|
72
73
|
};
|
|
73
74
|
}
|
|
75
|
+
if (!Array.isArray(response.result)) {
|
|
76
|
+
return response;
|
|
77
|
+
}
|
|
78
|
+
const listResponse = response;
|
|
74
79
|
return {
|
|
75
|
-
...
|
|
76
|
-
result:
|
|
80
|
+
...listResponse,
|
|
81
|
+
result: await withResolvedRefsList(event, listResponse.result, requestedLocale)
|
|
77
82
|
};
|
|
78
83
|
};
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { expect, test } from "vitest";
|
|
2
2
|
import { contentProviderResultMarker } from "../public/provider";
|
|
3
|
-
import {
|
|
4
|
-
import { createAuthorDependencyProviderFixture, createFixtureContentProvider, createProviderFixtureEvent } from "./provider-fixture.js";
|
|
3
|
+
import { createAuthorDependencyProviderFixture, createFixtureContentProvider, createProviderFixtureEvent, getProviderFixtureCacheHint } from "./provider-fixture.js";
|
|
5
4
|
const defaultCollectNavPaths = (items) => items.flatMap((item) => [
|
|
6
5
|
item._path,
|
|
7
6
|
...defaultCollectNavPaths(item.children || [])
|
|
@@ -137,7 +136,7 @@ export const runSaasProviderFixtureContractSuite = ({
|
|
|
137
136
|
const provider = await loadProvider();
|
|
138
137
|
const event = createEvent();
|
|
139
138
|
await provider.page?.(event, "posts", "/de/magazin/mehrsprachiges-onboarding");
|
|
140
|
-
expect(
|
|
139
|
+
expect(getProviderFixtureCacheHint(event)).toMatchObject({
|
|
141
140
|
tags: expect.arrayContaining([
|
|
142
141
|
"entry:posts:posts.onboarding",
|
|
143
142
|
"entry:authors:emily",
|
|
@@ -149,7 +148,7 @@ export const runSaasProviderFixtureContractSuite = ({
|
|
|
149
148
|
if (provider.siteData) {
|
|
150
149
|
const siteDataEvent = createEvent();
|
|
151
150
|
await provider.siteData(siteDataEvent, { key: "settings", locale: "de" });
|
|
152
|
-
expect(
|
|
151
|
+
expect(getProviderFixtureCacheHint(siteDataEvent)).toMatchObject({
|
|
153
152
|
tags: ["site-data:settings:de"]
|
|
154
153
|
});
|
|
155
154
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type ContentProvider } from '../public/provider';
|
|
1
|
+
import { type ContentCacheHint, type ContentProvider } from '../public/provider';
|
|
2
2
|
import type { H3Event } from 'h3';
|
|
3
3
|
import type { ParsedContent } from '../types/content';
|
|
4
4
|
import { type ContentGraph } from '../core/content/graph';
|
|
@@ -55,6 +55,8 @@ export interface ProviderFixtureEventOptions {
|
|
|
55
55
|
params?: Record<string, string>;
|
|
56
56
|
context?: Record<string, unknown>;
|
|
57
57
|
}
|
|
58
|
+
export declare const collectProviderFixtureCacheHint: (event: H3Event, hint: ContentCacheHint | false | undefined) => void;
|
|
59
|
+
export declare const getProviderFixtureCacheHint: (event: H3Event) => ContentCacheHint | false | undefined;
|
|
58
60
|
export declare const createProviderFixtureDocument: (input: Partial<ParsedContent> & Record<string, unknown>) => ParsedContent;
|
|
59
61
|
export declare const createProviderFixture: (input: ProviderFixtureInput) => ProviderFixture;
|
|
60
62
|
export declare const createProviderFixtureEvent: (options?: ProviderFixtureEventOptions) => H3Event;
|
|
@@ -3,19 +3,18 @@ import { buildContentGraph } from "../core/content/graph";
|
|
|
3
3
|
import { executeQueryPlan } from "../core/query/execute";
|
|
4
4
|
import { lowerQueryPlan } from "../core/query/lower";
|
|
5
5
|
import { findUnsupportedQueryOperator, SUPPORTED_QUERY_OPERATORS } from "../core/query/operators";
|
|
6
|
+
import { mergeContentCacheHints } from "../core/cache-hints";
|
|
6
7
|
import { normalizeRouteMounts, projectContentPathToLocale } from "../features/localization/path";
|
|
7
8
|
import { createRouteMeta, localizePageResult } from "../features/localization/results";
|
|
8
9
|
import { createContentProviderError } from "../public/provider-errors";
|
|
9
|
-
import { collectContentCacheHint } from "../runtime/server/cache-hints";
|
|
10
10
|
const trimSlashes = (value) => value.replace(/^\/+|\/+$/g, "");
|
|
11
|
+
const isObject = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
11
12
|
const unwrapResponseResult = (response) => {
|
|
12
|
-
if (response &&
|
|
13
|
+
if (isObject(response) && response[contentProviderResultMarker] === true) {
|
|
13
14
|
return unwrapResponseResult(response.data);
|
|
14
15
|
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}
|
|
18
|
-
return response;
|
|
16
|
+
const envelope = response;
|
|
17
|
+
return envelope.result;
|
|
19
18
|
};
|
|
20
19
|
const normalizeQueryResult = (value) => {
|
|
21
20
|
if (Array.isArray(value)) return value;
|
|
@@ -72,6 +71,19 @@ const referenceEntryTags = (fixture, doc) => {
|
|
|
72
71
|
}));
|
|
73
72
|
return Array.from(new Set(collectStringValues(doc).map((value) => knownRefs.get(value)).filter((tag) => typeof tag === "string")));
|
|
74
73
|
};
|
|
74
|
+
const getProviderFixtureRuntimeContext = (event) => {
|
|
75
|
+
const context = event.context ||= {};
|
|
76
|
+
context.__contentRuntime ||= {};
|
|
77
|
+
return context.__contentRuntime;
|
|
78
|
+
};
|
|
79
|
+
export const collectProviderFixtureCacheHint = (event, hint) => {
|
|
80
|
+
if (typeof hint === "undefined") {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
const runtime = getProviderFixtureRuntimeContext(event);
|
|
84
|
+
runtime.cacheHint = mergeContentCacheHints(runtime.cacheHint, hint);
|
|
85
|
+
};
|
|
86
|
+
export const getProviderFixtureCacheHint = (event) => getProviderFixtureRuntimeContext(event).cacheHint;
|
|
75
87
|
const createCacheHintForDocument = (fixture, doc, path) => {
|
|
76
88
|
const collection = doc._collection || "content";
|
|
77
89
|
return {
|
|
@@ -224,7 +236,7 @@ export const createFixtureContentProvider = (fixture, name = fixture.providerNam
|
|
|
224
236
|
operator: unsupported
|
|
225
237
|
});
|
|
226
238
|
}
|
|
227
|
-
|
|
239
|
+
collectProviderFixtureCacheHint(event, {
|
|
228
240
|
tags: params.collection ? [collectionTag(params.collection)] : []
|
|
229
241
|
});
|
|
230
242
|
return executeQueryPlan(
|
|
@@ -258,7 +270,7 @@ export const createFixtureContentProvider = (fixture, name = fixture.providerNam
|
|
|
258
270
|
query,
|
|
259
271
|
navigationQuery: async (event, params) => {
|
|
260
272
|
const docs = await docsForNavigation(event, params);
|
|
261
|
-
|
|
273
|
+
collectProviderFixtureCacheHint(event, {
|
|
262
274
|
tags: [
|
|
263
275
|
...params.collection ? [collectionTag(params.collection)] : [],
|
|
264
276
|
...params.resolveLocale?.locale ? [`nav:${params.collection || "all"}:${params.resolveLocale.locale}`] : []
|
|
@@ -277,7 +289,7 @@ export const createFixtureContentProvider = (fixture, name = fixture.providerNam
|
|
|
277
289
|
assertCollection(collection);
|
|
278
290
|
const fields = Array.isArray(options) ? options : options.fields || [];
|
|
279
291
|
const locale = typeof options === "object" && !Array.isArray(options) ? options.locale : void 0;
|
|
280
|
-
|
|
292
|
+
collectProviderFixtureCacheHint(event, {
|
|
281
293
|
tags: [`nav:${collection}:${locale || fixture.defaultLocale}`, collectionTag(collection)]
|
|
282
294
|
});
|
|
283
295
|
const docs = await docsForNavigation(event, {
|
|
@@ -318,7 +330,7 @@ export const createFixtureContentProvider = (fixture, name = fixture.providerNam
|
|
|
318
330
|
},
|
|
319
331
|
searchSections: async (event, collection, options = {}) => {
|
|
320
332
|
assertCollection(collection);
|
|
321
|
-
|
|
333
|
+
collectProviderFixtureCacheHint(event, {
|
|
322
334
|
tags: [`search:${options.locale || fixture.defaultLocale}`, collectionTag(collection)]
|
|
323
335
|
});
|
|
324
336
|
const docs = await docsForNavigation(event, {
|
|
@@ -335,7 +347,8 @@ export const createFixtureContentProvider = (fixture, name = fixture.providerNam
|
|
|
335
347
|
id: localizePath(fixture, collection, doc._path || "/", options.locale || doc._locale),
|
|
336
348
|
title: doc.title || "",
|
|
337
349
|
titles: [doc.title || ""],
|
|
338
|
-
content: String(doc.description || doc.title || "")
|
|
350
|
+
content: String(doc.description || doc.title || ""),
|
|
351
|
+
level: 1
|
|
339
352
|
}));
|
|
340
353
|
},
|
|
341
354
|
search: async (_event, request) => {
|
|
@@ -350,7 +363,7 @@ export const createFixtureContentProvider = (fixture, name = fixture.providerNam
|
|
|
350
363
|
}));
|
|
351
364
|
},
|
|
352
365
|
siteData: async (event, request) => {
|
|
353
|
-
|
|
366
|
+
collectProviderFixtureCacheHint(event, {
|
|
354
367
|
tags: [`site-data:${request.key}:${request.locale || fixture.defaultLocale}`]
|
|
355
368
|
});
|
|
356
369
|
return {
|
|
@@ -389,7 +402,7 @@ export const createFixtureContentProvider = (fixture, name = fixture.providerNam
|
|
|
389
402
|
routeMountsFor(fixture, collection)
|
|
390
403
|
);
|
|
391
404
|
const hint = createCacheHintForDocument(fixture, doc, page.path);
|
|
392
|
-
|
|
405
|
+
collectProviderFixtureCacheHint(event, hint);
|
|
393
406
|
recordRouteDependencies(cache, page.path, hint.tags || []);
|
|
394
407
|
return page;
|
|
395
408
|
},
|
|
@@ -398,7 +411,7 @@ export const createFixtureContentProvider = (fixture, name = fixture.providerNam
|
|
|
398
411
|
return page ? createRouteMeta(page, options.locale || page.locale, fixture.defaultLocale, routeMountsFor(fixture, collection)) : null;
|
|
399
412
|
},
|
|
400
413
|
sitemapEntries: async (event, options = {}) => {
|
|
401
|
-
|
|
414
|
+
collectProviderFixtureCacheHint(event, { tags: ["sitemap"] });
|
|
402
415
|
const explicitInclude = Boolean(options.include?.length);
|
|
403
416
|
const include = explicitInclude ? options.include : Object.keys(fixture.collections);
|
|
404
417
|
const entries = [];
|