@lupinum/ginko-content 0.1.6 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (230) hide show
  1. package/compatibility.json +1 -1
  2. package/dist/cli.mjs +2 -2
  3. package/dist/cms-contract/build.js +15 -20
  4. package/dist/cms-contract/types.d.ts +12 -7
  5. package/dist/core/content/document +1 -0
  6. package/dist/core/content/document.d.ts +16 -0
  7. package/dist/core/content/document.js +2 -0
  8. package/dist/core/content/graph.d.ts +3 -1
  9. package/dist/core/content/graph.js +21 -20
  10. package/dist/core/content/locale.d.ts +6 -1
  11. package/dist/core/content/locale.js +17 -6
  12. package/dist/core/content/path.d.ts +5 -5
  13. package/dist/core/content/path.js +8 -8
  14. package/dist/core/content/snapshot +1 -0
  15. package/dist/core/content/snapshot.d.ts +25 -0
  16. package/dist/core/content/snapshot.js +99 -0
  17. package/dist/core/provider-errors.d.ts +1 -1
  18. package/dist/core/provider-errors.js +1 -0
  19. package/dist/core/query/builder.js +3 -3
  20. package/dist/core/query/execute.js +75 -36
  21. package/dist/core/query/filter.js +2 -2
  22. package/dist/core/query/lower.js +79 -3
  23. package/dist/core/query/params.js +7 -7
  24. package/dist/core/query/plan.d.ts +18 -0
  25. package/dist/core/query/plan.js +7 -0
  26. package/dist/core/references/resolve.js +6 -9
  27. package/dist/features/agent/agent-markdown +1 -0
  28. package/dist/features/agent/agent-markdown.d.ts +116 -0
  29. package/dist/features/agent/agent-markdown.js +138 -0
  30. package/dist/features/agent/walker +1 -0
  31. package/dist/features/agent/walker.d.ts +10 -0
  32. package/dist/features/agent/walker.js +174 -0
  33. package/dist/features/collections/resolve.d.ts +2 -2
  34. package/dist/features/collections/resolve.js +19 -16
  35. package/dist/features/localization/links.js +1 -1
  36. package/dist/features/localization/results.d.ts +3 -3
  37. package/dist/features/localization/results.js +46 -41
  38. package/dist/features/localization/translated-slugs.js +5 -5
  39. package/dist/features/navigation/build.d.ts +2 -2
  40. package/dist/features/navigation/build.js +23 -26
  41. package/dist/features/navigation/canonical.d.ts +10 -11
  42. package/dist/features/navigation/canonical.js +26 -21
  43. package/dist/features/navigation/resolve.js +4 -4
  44. package/dist/features/navigation/tree.js +3 -3
  45. package/dist/{runtime → features}/query/backlinks.js +3 -5
  46. package/dist/{runtime → features}/query/documents.js +1 -1
  47. package/dist/{runtime → features}/query/localized-docs.d.ts +1 -1
  48. package/dist/{runtime → features}/query/localized-docs.js +3 -6
  49. package/dist/{runtime → features}/query/navigation.d.ts +1 -1
  50. package/dist/{runtime → features}/query/navigation.js +8 -8
  51. package/dist/{runtime → features}/query/populate.js +6 -14
  52. package/dist/{runtime → features}/query/unified.d.ts +4 -4
  53. package/dist/{runtime → features}/query/unified.js +4 -2
  54. package/dist/{runtime → features}/query/variants.js +4 -4
  55. package/dist/features/search/navigation.js +2 -2
  56. package/dist/features/search/sections.d.ts +1 -1
  57. package/dist/features/search/sections.js +1 -1
  58. package/dist/features/sitemap/query.d.ts +5 -5
  59. package/dist/features/sitemap/query.js +6 -6
  60. package/dist/integrations/nitro/ingest.js +9 -20
  61. package/dist/integrations/nitro/storage.d.ts +1 -0
  62. package/dist/integrations/nitro/storage.js +22 -8
  63. package/dist/module.d.mts +135 -812
  64. package/dist/module.json +1 -1
  65. package/dist/module.mjs +70 -27
  66. package/dist/parsers/csv.js +3 -3
  67. package/dist/parsers/index.js +3 -3
  68. package/dist/parsers/json.js +8 -6
  69. package/dist/parsers/markdown-plugins.js +9 -5
  70. package/dist/parsers/markdown.js +7 -6
  71. package/dist/parsers/path-meta.js +21 -19
  72. package/dist/parsers/reserved +1 -0
  73. package/dist/parsers/reserved.d.ts +15 -0
  74. package/dist/parsers/reserved.js +35 -0
  75. package/dist/parsers/yaml.js +6 -4
  76. package/dist/public/agent +1 -0
  77. package/dist/public/agent.d.ts +17 -0
  78. package/dist/public/agent.js +34 -0
  79. package/dist/public/client.d.ts +2 -3
  80. package/dist/public/client.js +1 -2
  81. package/dist/public/provider-query +1 -0
  82. package/dist/public/provider-query.d.ts +58 -0
  83. package/dist/public/provider-query.js +5 -0
  84. package/dist/public/provider.d.ts +8 -3
  85. package/dist/public/provider.js +2 -0
  86. package/dist/public/server.d.ts +7 -12
  87. package/dist/public/server.js +5 -36
  88. package/dist/runtime/app/components/ContentRenderer.d.vue.ts +3 -3
  89. package/dist/runtime/app/components/ContentRenderer.vue +2 -2
  90. package/dist/runtime/app/components/ContentRenderer.vue.d.ts +3 -3
  91. package/dist/runtime/app/components/ContentRendererInline.d.vue.ts +1 -1
  92. package/dist/runtime/app/components/ContentRendererInline.vue.d.ts +1 -1
  93. package/dist/runtime/app/components/Prose/ProseImg.d.vue.ts +4 -4
  94. package/dist/runtime/app/components/Prose/ProseImg.vue.d.ts +4 -4
  95. package/dist/runtime/app/components/Prose/ProsePre.d.vue.ts +1 -1
  96. package/dist/runtime/app/components/Prose/ProsePre.vue.d.ts +1 -1
  97. package/dist/runtime/app/components/internal/ContentRendererMarkdown.d.vue.ts +1 -1
  98. package/dist/runtime/app/components/internal/ContentRendererMarkdown.vue +4 -4
  99. package/dist/runtime/app/components/internal/ContentRendererMarkdown.vue.d.ts +1 -1
  100. package/dist/runtime/app/components/internal/MarkdownRenderer.d.ts +3 -3
  101. package/dist/runtime/app/composables/preview.d.ts +1 -1
  102. package/dist/runtime/app/composables/query-api.d.ts +5 -5
  103. package/dist/runtime/app/composables/query-api.js +5 -3
  104. package/dist/runtime/app/composables/route.d.ts +1 -1
  105. package/dist/runtime/app/composables/route.js +4 -4
  106. package/dist/runtime/app/composables/search.js +1 -1
  107. package/dist/runtime/app/composables/use-content-document.js +1 -1
  108. package/dist/runtime/app/composables/use-content-list.js +1 -1
  109. package/dist/runtime/app/composables/use-content-navigation.js +2 -2
  110. package/dist/runtime/app/composables/use-content-page.js +4 -4
  111. package/dist/runtime/app/pages/content.vue +2 -1
  112. package/dist/runtime/markdown/plugins.js +6 -8
  113. package/dist/runtime/server/agent-markdown.d.ts +5 -65
  114. package/dist/runtime/server/agent-markdown.js +40 -315
  115. package/dist/runtime/server/agent-site.js +1 -1
  116. package/dist/runtime/server/api/agent-raw.js +1 -1
  117. package/dist/runtime/server/api/cache.d.ts +1 -1
  118. package/dist/runtime/server/api/cache.js +43 -4
  119. package/dist/runtime/server/api/locales.js +3 -3
  120. package/dist/runtime/server/api/navigation.d.ts +1 -1
  121. package/dist/runtime/server/api/navigation.js +3 -1
  122. package/dist/runtime/server/api/query.js +2 -2
  123. package/dist/runtime/server/api/search-index.d.ts +1 -1
  124. package/dist/runtime/server/api/sitemap.d.ts +1 -1
  125. package/dist/runtime/server/cache-adapters.d.ts +18 -0
  126. package/dist/runtime/server/cache-adapters.js +12 -0
  127. package/dist/runtime/server/collection-helpers.d.ts +2 -2
  128. package/dist/runtime/server/collection-helpers.js +15 -11
  129. package/dist/runtime/server/index.d.ts +6 -4
  130. package/dist/runtime/server/index.js +10 -3
  131. package/dist/runtime/server/middleware/agent-link-headers.js +1 -1
  132. package/dist/runtime/server/middleware/agent-markdown.js +1 -1
  133. package/dist/runtime/server/navigation-query.d.ts +2 -2
  134. package/dist/runtime/server/navigation-query.js +52 -7
  135. package/dist/runtime/server/navigation.d.ts +1 -1
  136. package/dist/runtime/server/provider-document +1 -0
  137. package/dist/runtime/server/provider-document.d.ts +59 -0
  138. package/dist/runtime/server/provider-document.js +48 -0
  139. package/dist/runtime/server/provider-query-wire +1 -0
  140. package/dist/runtime/server/provider-query-wire.d.ts +32 -0
  141. package/dist/runtime/server/provider-query-wire.js +27 -0
  142. package/dist/runtime/server/provider-query.d.ts +18 -9
  143. package/dist/runtime/server/provider-query.js +62 -28
  144. package/dist/runtime/server/provider-result.d.ts +4 -4
  145. package/dist/runtime/server/providers/filesystem.js +3 -11
  146. package/dist/runtime/server/providers/index.d.ts +18 -1
  147. package/dist/runtime/server/providers/index.js +72 -20
  148. package/dist/runtime/server/query-api.d.ts +5 -5
  149. package/dist/runtime/server/query-api.js +9 -6
  150. package/dist/runtime/server/query-executor.d.ts +3 -3
  151. package/dist/runtime/server/query-executor.js +43 -59
  152. package/dist/runtime/server/search.d.ts +1 -1
  153. package/dist/runtime/server/search.js +4 -4
  154. package/dist/runtime/server/sitemap.js +5 -5
  155. package/dist/runtime/server/storage-access.d.ts +4 -1
  156. package/dist/runtime/server/storage-access.js +27 -2
  157. package/dist/runtime/transformers/component-resolver.d.ts +1 -1
  158. package/dist/storage/content.d.ts +2 -6
  159. package/dist/storage/content.js +19 -8
  160. package/dist/storage/contents.d.ts +9 -1
  161. package/dist/storage/contents.js +26 -19
  162. package/dist/storage/driver.d.ts +2 -2
  163. package/dist/storage/driver.js +2 -1
  164. package/dist/storage/graph.js +4 -0
  165. package/dist/storage/manifest.js +3 -1
  166. package/dist/storage/references.js +9 -5
  167. package/dist/storage/snapshot-runtime +1 -0
  168. package/dist/storage/snapshot-runtime.d.ts +11 -0
  169. package/dist/storage/snapshot-runtime.js +46 -0
  170. package/dist/storage/validation.d.ts +1 -1
  171. package/dist/storage/validation.js +47 -102
  172. package/dist/testing/provider-contract.d.ts +14 -4
  173. package/dist/testing/provider-contract.js +121 -68
  174. package/dist/testing/provider-fixture.d.ts +1 -1
  175. package/dist/testing/provider-fixture.js +124 -120
  176. package/dist/types/config.d.ts +8 -4
  177. package/dist/types/content.d.ts +104 -70
  178. package/dist/types/fields.js +1 -1
  179. package/dist/types/module.d.ts +1 -1
  180. package/dist/types/query-parts/public.d.ts +14 -5
  181. package/dist/types/query-parts/results.d.ts +6 -2
  182. package/dist/types/query-parts/transport.d.ts +19 -0
  183. package/dist/types/virtual.d.ts +6 -0
  184. package/dist/types.d.mts +2 -2
  185. package/dist/web-types.json +1 -1
  186. package/package.json +39 -9
  187. package/dist/runtime/app/plugins/markdown-components +0 -1
  188. package/dist/runtime/app/plugins/markdown-components.d.ts +0 -2
  189. package/dist/runtime/app/plugins/markdown-components.js +0 -19
  190. /package/dist/{runtime → features/agent}/agent-paths +0 -0
  191. /package/dist/{runtime → features/agent}/agent-paths.d.ts +0 -0
  192. /package/dist/{runtime → features/agent}/agent-paths.js +0 -0
  193. /package/dist/{runtime → features}/query/backlinks +0 -0
  194. /package/dist/{runtime → features}/query/backlinks.d.ts +0 -0
  195. /package/dist/{runtime → features}/query/context +0 -0
  196. /package/dist/{runtime → features}/query/context.d.ts +0 -0
  197. /package/dist/{runtime → features}/query/context.js +0 -0
  198. /package/dist/{runtime → features}/query/documents +0 -0
  199. /package/dist/{runtime → features}/query/documents.d.ts +0 -0
  200. /package/dist/{runtime → features}/query/errors +0 -0
  201. /package/dist/{runtime → features}/query/errors.d.ts +0 -0
  202. /package/dist/{runtime → features}/query/errors.js +0 -0
  203. /package/dist/{runtime → features}/query/handles +0 -0
  204. /package/dist/{runtime → features}/query/handles.d.ts +0 -0
  205. /package/dist/{runtime → features}/query/handles.js +0 -0
  206. /package/dist/{runtime → features}/query/locale-options +0 -0
  207. /package/dist/{runtime → features}/query/locale-options.d.ts +0 -0
  208. /package/dist/{runtime → features}/query/locale-options.js +0 -0
  209. /package/dist/{runtime → features}/query/localized-docs +0 -0
  210. /package/dist/{runtime → features}/query/navigation +0 -0
  211. /package/dist/{runtime → features}/query/pagination +0 -0
  212. /package/dist/{runtime → features}/query/pagination.d.ts +0 -0
  213. /package/dist/{runtime → features}/query/pagination.js +0 -0
  214. /package/dist/{runtime → features}/query/populate +0 -0
  215. /package/dist/{runtime → features}/query/populate.d.ts +0 -0
  216. /package/dist/{runtime → features}/query/public-limits +0 -0
  217. /package/dist/{runtime → features}/query/public-limits.d.ts +0 -0
  218. /package/dist/{runtime → features}/query/public-limits.js +0 -0
  219. /package/dist/{runtime → features}/query/resolve +0 -0
  220. /package/dist/{runtime → features}/query/resolve.d.ts +0 -0
  221. /package/dist/{runtime → features}/query/resolve.js +0 -0
  222. /package/dist/{runtime → features}/query/responses +0 -0
  223. /package/dist/{runtime → features}/query/responses.d.ts +0 -0
  224. /package/dist/{runtime → features}/query/responses.js +0 -0
  225. /package/dist/{runtime → features}/query/routes +0 -0
  226. /package/dist/{runtime → features}/query/routes.d.ts +0 -0
  227. /package/dist/{runtime → features}/query/routes.js +0 -0
  228. /package/dist/{runtime → features}/query/unified +0 -0
  229. /package/dist/{runtime → features}/query/variants +0 -0
  230. /package/dist/{runtime → features}/query/variants.d.ts +0 -0
@@ -1,10 +1,35 @@
1
1
  import { joinURL, withLeadingSlash } from "ufo";
2
2
  import { createQuery, wrapQueryBuilder } from "../../core/query/builder";
3
3
  import { normalizeContentQueryParams } from "../../core/query/params";
4
+ import { containsStandaloneRegexOptions, findUnsupportedQueryOperator } from "../../core/query/operators";
4
5
  import { normalizeI18nConfig, resolveRuntimeCollectionI18nConfig } from "../../features/localization/config";
6
+ import { sortLocalesCanonically } from "../../core/content/locale";
5
7
  import { normalizeReferenceValue } from "../../core/references/resolve";
6
8
  import { getContentRuntimeConfig } from "./runtime-config.js";
7
9
  import { createContentProviderError } from "../../public/provider-errors";
10
+ import { toContentProviderQuery } from "../../public/provider-query";
11
+ export { toContentProviderNavigationQuery as createProviderNavigationQuery } from "../../public/provider-query";
12
+ export const createProviderQuery = (params) => {
13
+ const unsupported = findUnsupportedQueryOperator(params.where);
14
+ if (unsupported) {
15
+ throw createContentProviderError("unsupported_query_operator", `Unsupported query operator: ${unsupported}`, {
16
+ operator: unsupported
17
+ });
18
+ }
19
+ if (containsStandaloneRegexOptions(params.where)) {
20
+ throw createContentProviderError("unsupported_query_shape", "Query operator $options requires $regex.", {
21
+ operator: "$options"
22
+ });
23
+ }
24
+ const config = getContentRuntimeConfig().content || {};
25
+ const normalized = normalizeContentQueryParams(params, {
26
+ collectionI18n: params.collection ? config.collections?.[params.collection]?.i18n : void 0,
27
+ defaultLocale: config.defaultLocale,
28
+ localeFallback: config.localeFallback,
29
+ includeDraftFilter: false
30
+ });
31
+ return toContentProviderQuery(normalized);
32
+ };
8
33
  const isObject = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
9
34
  const isProviderFindResponse = (response) => isObject(response) && Array.isArray(response.result) && typeof response.skip === "number" && typeof response.limit === "number" && typeof response.total === "number";
10
35
  export const normalizeProviderQueryResult = (response) => {
@@ -55,7 +80,7 @@ const identityMatchesDocument = (document, identity) => {
55
80
  if (!normalizedIdentity) {
56
81
  return false;
57
82
  }
58
- return [document._canonicalKey, document._path, document.ref].filter((value) => typeof value === "string").some((value) => normalizeReferenceValue(value) === normalizedIdentity);
83
+ return [document.canonicalKey, document.path, document.ref].filter((value) => typeof value === "string").some((value) => normalizeReferenceValue(value) === normalizedIdentity);
59
84
  };
60
85
  export const resolveProviderContentVariants = async (event, identity, options) => {
61
86
  if (!options.collection) {
@@ -77,38 +102,43 @@ export const resolveProviderContentVariants = async (event, identity, options) =
77
102
  const provider = await getContentProvider(event);
78
103
  const baseQuery = {
79
104
  collection: options.collection,
80
- only: ["_path", "_canonicalKey", "_locale", "ref", "title", "description", "body"]
105
+ only: ["path", "canonicalKey", "locale", "ref", "title", "description", "body"]
81
106
  };
82
107
  const documents = localesToQuery.length ? (await Promise.all(
83
- localesToQuery.map(
84
- async (locale) => normalizeProviderQueryResult(
85
- normalizeProviderQueryResponse({
86
- ...baseQuery,
87
- resolveLocale: { locale, exact: true }
88
- }, await provider.query(event, {
89
- ...baseQuery,
90
- resolveLocale: { locale, exact: true }
91
- }), provider.name)
92
- )
93
- )
94
- )).flat() : normalizeProviderQueryResult(normalizeProviderQueryResponse(baseQuery, await provider.query(event, baseQuery), provider.name));
108
+ localesToQuery.map(async (locale) => {
109
+ const localeQuery = { ...baseQuery, resolveLocale: { locale, exact: true } };
110
+ return normalizeProviderQueryResult(
111
+ normalizeProviderQueryResponse(
112
+ localeQuery,
113
+ await provider.query(event, createProviderQuery(localeQuery)),
114
+ provider.name
115
+ )
116
+ );
117
+ })
118
+ )).flat() : normalizeProviderQueryResult(normalizeProviderQueryResponse(baseQuery, await provider.query(event, createProviderQuery(baseQuery)), provider.name));
95
119
  const matched = documents.find((document) => identityMatchesDocument(document, normalizedReference));
96
- const canonicalKey = matched?._canonicalKey;
120
+ const canonicalKey = matched?.canonicalKey;
97
121
  if (!canonicalKey) {
98
122
  return null;
99
123
  }
100
- const variants = documents.filter((document) => document._canonicalKey === canonicalKey);
124
+ const variants = documents.filter((document) => document.canonicalKey === canonicalKey);
101
125
  if (!variants.length) {
102
126
  return null;
103
127
  }
104
- const selectByLocale = (locale) => locale ? variants.find((document) => document._locale === locale) : void 0;
128
+ const selectByLocale = (locale) => locale ? variants.find((document) => document.locale === locale) : void 0;
105
129
  const selected = selectByLocale(options.locale) || (!options.exact ? fallbackLocales.map(selectByLocale).find(Boolean) : void 0) || (!options.exact ? selectByLocale(collectionI18n?.defaultLocale || runtimeContent.defaultLocale) : void 0) || (!options.exact ? variants[0] : void 0);
106
130
  if (!selected) {
107
131
  return null;
108
132
  }
109
- const availableLocales = Array.from(new Set(variants.map((document) => document._locale).filter(Boolean)));
133
+ const availableLocales = sortLocalesCanonically(
134
+ Array.from(new Set(variants.map((document) => document.locale).filter(Boolean))),
135
+ {
136
+ defaultLocale: collectionI18n?.defaultLocale || runtimeContent.defaultLocale,
137
+ locales: collectionI18n?.locales
138
+ }
139
+ );
110
140
  const variantPaths = Object.fromEntries(
111
- variants.filter((document) => document._locale && document._path).map((document) => [document._locale, document._path])
141
+ variants.filter((document) => document.locale && document.path).map((document) => [document.locale, document.path])
112
142
  );
113
143
  return {
114
144
  canonicalKey,
@@ -117,16 +147,17 @@ export const resolveProviderContentVariants = async (event, identity, options) =
117
147
  availableLocales,
118
148
  variantPaths,
119
149
  requestedLocale: options.locale,
120
- resolvedLocale: selected._locale,
121
- fallback: Boolean(options.locale && selected._locale && selected._locale !== options.locale)
150
+ resolvedLocale: selected.locale,
151
+ fallback: Boolean(options.locale && selected.locale && selected.locale !== options.locale)
122
152
  };
123
153
  };
124
154
  export const createServerProviderQueryFetch = (event) => {
125
155
  return async (query) => {
126
156
  const { getContentProvider } = await import("./providers/index.js");
127
157
  const provider = await getContentProvider(event);
128
- const response = await provider.query(event, query.params());
129
- return normalizeProviderQueryResponse(query.params(), response, provider.name);
158
+ const params = query.params();
159
+ const response = await provider.query(event, createProviderQuery(params));
160
+ return normalizeProviderQueryResponse(params, response, provider.name);
130
161
  };
131
162
  };
132
163
  const getServerCollectionI18n = (collection) => {
@@ -164,10 +195,13 @@ export const resolveContentReference = async (event, reference, options = {}) =>
164
195
  }
165
196
  return {
166
197
  ...resolved.selected,
167
- _requestedLocale: resolved.requestedLocale,
168
- _resolvedLocale: resolved.resolvedLocale,
169
- _fallback: resolved.fallback,
170
- _availableLocales: resolved.availableLocales,
171
- _variantPaths: resolved.variantPaths
198
+ resolved: {
199
+ ...resolved.selected.resolved || {},
200
+ requestedLocale: resolved.requestedLocale,
201
+ locale: resolved.resolvedLocale,
202
+ fallback: resolved.fallback,
203
+ availableLocales: resolved.availableLocales,
204
+ variantPaths: resolved.variantPaths
205
+ }
172
206
  };
173
207
  };
@@ -1,15 +1,15 @@
1
1
  import type { H3Event } from 'h3';
2
2
  import type { ContentQueryResponse } from '../../types/api';
3
3
  import type { NavItem, ParsedContent } from '../../types/content';
4
- import type { ContentCollectionItemSurroundingsOptions, ContentCollectionNavigationOptions, ContentCollectionPageOptions, ContentCollectionRouteMetaOptions, ContentCollectionSearchSectionsOptions, ContentPageResult, ContentQueryBuilderParams, ContentRouteMeta, ContentSearchSection, ContentSitemapEntry } from '../../types/query';
4
+ import type { ContentCollectionItemSurroundingsOptions, ContentCollectionNavigationOptions, ContentCollectionPageOptions, ContentCollectionRouteMetaOptions, ContentCollectionSearchSectionsOptions, ContentPageResult, ContentRouteMeta, ContentSearchSection, ContentSitemapEntry } from '../../types/query';
5
5
  import type { QueryCollectionsSitemapEntriesOptions } from '../../features/sitemap/query';
6
- import type { ContentProvider, ContentProviderResult, ContentProviderSiteDataRequest, ContentProviderSiteDataResponse, MaybeContentProviderResult } from '../../public/provider';
6
+ import type { ContentProvider, ContentProviderNavigationOptions, ContentProviderQuery, ContentProviderResult, ContentProviderSiteDataRequest, ContentProviderSiteDataResponse, MaybeContentProviderResult } from '../../public/provider';
7
7
  import type { ContentProviderSearchRequest, ContentSearchResult } from '../../types/search';
8
8
  export declare const isContentProviderResult: <T = unknown>(value: unknown) => value is ContentProviderResult<T>;
9
9
  export declare const unwrapContentProviderResult: <T>(event: H3Event, result: MaybeContentProviderResult<T>) => T;
10
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[]>;
11
+ query: <T = ParsedContent>(event: H3Event, query: ContentProviderQuery) => Promise<ContentQueryResponse<T>>;
12
+ navigationQuery?: (event: H3Event, query: ContentProviderQuery, options?: ContentProviderNavigationOptions) => Promise<NavItem[]>;
13
13
  navigation?: (event: H3Event, collection: string, options?: string[] | ContentCollectionNavigationOptions) => Promise<NavItem[]>;
14
14
  surroundings?: (event: H3Event, collection: string, path: string, options?: ContentCollectionItemSurroundingsOptions) => Promise<Array<NavItem | null>>;
15
15
  searchSections?: (event: H3Event, collection: string, options?: ContentCollectionSearchSectionsOptions) => Promise<ContentSearchSection[]>;
@@ -1,4 +1,4 @@
1
- import { containsStandaloneRegexOptions, SUPPORTED_QUERY_OPERATORS } from "../../../core/query/operators";
1
+ import { SUPPORTED_QUERY_OPERATORS } from "../../../core/query/operators";
2
2
  import { executeFilesystemContentQuery } from "../query-executor.js";
3
3
  import { resolveContentNavigation } from "../navigation-query.js";
4
4
  import {
@@ -9,7 +9,6 @@ import {
9
9
  queryFilesystemCollectionSearchSections
10
10
  } from "../collection-helpers.js";
11
11
  import { queryFilesystemCollectionsSitemapEntries } from "../sitemap.js";
12
- import { createContentProviderError } from "../../../public/provider-errors";
13
12
  export const filesystemProvider = {
14
13
  name: "filesystem",
15
14
  capabilities: {
@@ -32,15 +31,8 @@ export const filesystemProvider = {
32
31
  count: true
33
32
  }
34
33
  },
35
- query: (event, query) => {
36
- if (containsStandaloneRegexOptions(query.where)) {
37
- throw createContentProviderError("unsupported_query_shape", "Query operator $options requires $regex.", {
38
- operator: "$options"
39
- });
40
- }
41
- return executeFilesystemContentQuery(event, query);
42
- },
43
- navigationQuery: (event, query) => resolveContentNavigation(event, query),
34
+ query: (event, query) => executeFilesystemContentQuery(event, query.plan),
35
+ navigationQuery: (event, query, options) => resolveContentNavigation(event, query, options),
44
36
  navigation: (event, collection, options) => queryFilesystemCollectionNavigation(event, collection, options),
45
37
  surroundings: (event, collection, path, options) => queryFilesystemCollectionItemSurroundings(event, collection, path, options),
46
38
  searchSections: (event, collection, options) => queryFilesystemCollectionSearchSections(event, collection, options),
@@ -1,5 +1,22 @@
1
1
  import type { H3Event } from 'h3';
2
- import type { ContentProvider } from '../../../public/provider';
2
+ import { type ContentProvider, type ContentProviderQuery } from '../../../public/provider';
3
+ import type { FilterExpr } from '../../../core/query/plan';
3
4
  import { type RuntimeContentProvider } from '../provider-result';
5
+ /**
6
+ * Derive the operator vocabulary a plan actually exercises by walking its
7
+ * `FilterExpr` tree (CS-5 trap 3). Only `compare` nodes carry operators; we
8
+ * recurse through `and`/`or`/`not` to reach every one. Operators are reported
9
+ * in the builder's `$`-prefixed vocabulary so they compare directly against
10
+ * `capabilities.query.operators`.
11
+ */
12
+ export declare const collectPlanFilterOperators: (filter: FilterExpr, operators?: Set<string>) => Set<string>;
13
+ /**
14
+ * Dev-mode guard: the provider wire is JSON-pure by contract (CS-5). A stray
15
+ * `RegExp`/`Date` in `plan.filter` would corrupt any provider that serializes
16
+ * the query, so we fail fast in dev instead of shipping a silently mangled
17
+ * wire. No-op in production (the walk is not free).
18
+ */
19
+ export declare const assertJsonPureProviderQuery: (provider: ContentProvider, query: ContentProviderQuery) => void;
20
+ export declare const enforceProviderCapabilities: (provider: ContentProvider) => ContentProvider;
4
21
  export declare function getContentProvider(): Promise<ContentProvider>;
5
22
  export declare function getContentProvider(event: H3Event): Promise<RuntimeContentProvider>;
@@ -1,5 +1,6 @@
1
1
  import { useRuntimeConfig } from "nitropack/runtime";
2
2
  import { loadExternalContentProvider } from "#content/virtual/providers";
3
+ import { PROVIDER_QUERY_VERSION } from "../../../public/provider";
3
4
  import { createContentProviderError } from "../../../public/provider-errors";
4
5
  import { wrapContentProviderCacheResults } from "../provider-result.js";
5
6
  const isObject = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
@@ -24,43 +25,91 @@ const assertProviderField = (providerName, condition, field) => {
24
25
  const assertProviderMethod = (providerName, provider, method) => {
25
26
  assertProviderField(providerName, typeof provider[method] === "function", method);
26
27
  };
27
- const findProviderUnsupportedOperator = (value, supportedOperators) => {
28
- if (!value || typeof value !== "object") return void 0;
28
+ export const collectPlanFilterOperators = (filter, operators = /* @__PURE__ */ new Set()) => {
29
+ switch (filter.type) {
30
+ case "true":
31
+ break;
32
+ case "compare":
33
+ operators.add(`$${filter.operator}`);
34
+ break;
35
+ case "and":
36
+ case "or":
37
+ for (const clause of filter.clauses) {
38
+ collectPlanFilterOperators(clause, operators);
39
+ }
40
+ break;
41
+ case "not":
42
+ collectPlanFilterOperators(filter.clause, operators);
43
+ break;
44
+ default:
45
+ throw new TypeError(`Unknown query filter node: ${filter.type}`);
46
+ }
47
+ return operators;
48
+ };
49
+ const findNonJsonValue = (value, path) => {
50
+ if (value === null || value === void 0) return void 0;
51
+ const kind = typeof value;
52
+ if (kind === "string" || kind === "number" || kind === "boolean") return void 0;
53
+ if (kind === "function" || kind === "bigint" || kind === "symbol") return path;
29
54
  if (Array.isArray(value)) {
30
- for (const child of value) {
31
- const unsupported = findProviderUnsupportedOperator(child, supportedOperators);
32
- if (unsupported) return unsupported;
55
+ for (let index = 0; index < value.length; index += 1) {
56
+ const offender = findNonJsonValue(value[index], `${path}[${index}]`);
57
+ if (offender) return offender;
33
58
  }
34
59
  return void 0;
35
60
  }
36
- for (const [key, child] of Object.entries(value)) {
37
- if (key.startsWith("$") && !supportedOperators.has(key)) return key;
38
- const unsupported = findProviderUnsupportedOperator(child, supportedOperators);
39
- if (unsupported) return unsupported;
61
+ if (kind === "object") {
62
+ const prototype = Object.getPrototypeOf(value);
63
+ if (prototype !== Object.prototype && prototype !== null) return path;
64
+ for (const [key, child] of Object.entries(value)) {
65
+ const offender = findNonJsonValue(child, `${path}.${key}`);
66
+ if (offender) return offender;
67
+ }
68
+ return void 0;
69
+ }
70
+ return path;
71
+ };
72
+ export const assertJsonPureProviderQuery = (provider, query) => {
73
+ if (!import.meta.dev) return;
74
+ const offender = findNonJsonValue(query, "query");
75
+ if (offender) {
76
+ throw createContentProviderError("provider_query_not_json_pure", `${provider.name} received a non-JSON-pure query at ${offender}. Provider queries must survive JSON.parse(JSON.stringify(query)).`, {
77
+ provider: provider.name,
78
+ field: offender
79
+ });
40
80
  }
41
81
  };
42
82
  const assertProviderQuerySupported = (provider, query) => {
43
- const capabilities = provider.capabilities.query;
44
- const unsupportedOperator = findProviderUnsupportedOperator(query.where, new Set(capabilities.operators));
45
- if (unsupportedOperator) {
46
- throw createContentProviderError("unsupported_query_operator", `${provider.name} does not support query operator: ${unsupportedOperator}`, {
83
+ if (query.v !== PROVIDER_QUERY_VERSION) {
84
+ throw createContentProviderError("unsupported_query_shape", `${provider.name} received unsupported provider query version: ${String(query.v)}.`, {
47
85
  provider: provider.name,
48
- operator: unsupportedOperator
86
+ field: "v"
49
87
  });
50
88
  }
51
- if (!capabilities.limit && query.limit !== void 0) {
89
+ const capabilities = provider.capabilities.query;
90
+ const supported = new Set(capabilities.operators);
91
+ const usedOperators = collectPlanFilterOperators(query.plan.filter);
92
+ for (const operator of usedOperators) {
93
+ if (!supported.has(operator)) {
94
+ throw createContentProviderError("unsupported_query_operator", `${provider.name} does not support query operator: ${operator}`, {
95
+ provider: provider.name,
96
+ operator
97
+ });
98
+ }
99
+ }
100
+ if (!capabilities.limit && query.plan.limit !== void 0) {
52
101
  throw createContentProviderError("unsupported_query_shape", `${provider.name} does not support query limits.`, {
53
102
  provider: provider.name,
54
103
  field: "limit"
55
104
  });
56
105
  }
57
- if (!capabilities.skip && query.skip !== void 0) {
106
+ if (!capabilities.skip && query.plan.skip > 0) {
58
107
  throw createContentProviderError("unsupported_query_shape", `${provider.name} does not support query offsets.`, {
59
108
  provider: provider.name,
60
109
  field: "skip"
61
110
  });
62
111
  }
63
- if (!capabilities.count && query.count === true) {
112
+ if (!capabilities.count && query.plan.mode === "count") {
64
113
  throw createContentProviderError("unsupported_query_shape", `${provider.name} does not support count queries.`, {
65
114
  provider: provider.name,
66
115
  field: "count"
@@ -75,15 +124,18 @@ const assertProviderOperationSupported = (provider, supported, operation) => {
75
124
  });
76
125
  }
77
126
  };
78
- const enforceProviderCapabilities = (provider) => ({
127
+ export const enforceProviderCapabilities = (provider) => ({
79
128
  ...provider,
80
129
  query: async (event, query) => {
130
+ assertJsonPureProviderQuery(provider, query);
81
131
  assertProviderQuerySupported(provider, query);
82
132
  return await provider.query(event, query);
83
133
  },
84
- navigationQuery: provider.navigationQuery ? async (...args) => {
134
+ navigationQuery: provider.navigationQuery ? async (event, query, options) => {
85
135
  assertProviderOperationSupported(provider, provider.capabilities.navigation, "navigation");
86
- return await provider.navigationQuery(...args);
136
+ assertJsonPureProviderQuery(provider, query);
137
+ assertProviderQuerySupported(provider, query);
138
+ return await provider.navigationQuery(event, query, options);
87
139
  } : void 0,
88
140
  navigation: provider.navigation ? async (...args) => {
89
141
  assertProviderOperationSupported(provider, provider.capabilities.navigation, "navigation");
@@ -1,12 +1,12 @@
1
1
  import type { H3Event } from 'h3';
2
2
  import type { ParsedContent } from '../../types/content';
3
3
  import type { ContentCollectionHandle } from '../../types/config';
4
- import type { BacklinksOptions, BacklinksResult, BacklinkSource, ContentVariant, ContentTreeItem, DocumentFromHandle, ManyOptions, LocalizedDoc, NeighborsOptions, NeighborsResult, OneOptions, PopulateSpec, PopulateFromOptions, PopulatedDocument, PaginationOptions, PaginationResult, ResolveOneOptions, ResolveOneResult, TreeOptions, VariantsOptions } from '../../types/query';
5
- import { type ContentQueryContext } from '../query/unified';
4
+ import type { BacklinksOptions, BacklinksResult, BacklinkSource, ContentVariant, ContentTreeItem, DocumentFromHandle, ManyOptions, LocalizedDoc, NeighborsOptions, NeighborsResult, OneOptions, OptionsArg, PopulateSpec, PopulateFromOptions, PopulatedDocument, PaginationOptions, PaginationResult, ResolveOneOptions, ResolveOneResult, TreeOptions, VariantsOptions } from '../../types/query';
5
+ import { type ContentQueryContext } from '../../features/query/unified';
6
6
  export declare const createServerContentQueryContext: (event: H3Event) => Promise<ContentQueryContext>;
7
7
  export declare function resolveOne<const H extends ContentCollectionHandle | string, O extends ResolveOneOptions<H, PopulateSpec | undefined>>(event: H3Event, handle: H, options: O): Promise<ResolveOneResult<PopulatedDocument<DocumentFromHandle<H>, PopulateFromOptions<O>>>>;
8
8
  export declare function one<const H extends ContentCollectionHandle | string, O extends OneOptions<H, PopulateSpec | undefined>>(event: H3Event, handle: H, options: O): Promise<LocalizedDoc<PopulatedDocument<DocumentFromHandle<H>, PopulateFromOptions<O>>> | null>;
9
- export declare function many<const H extends ContentCollectionHandle | string, O extends ManyOptions<H, PopulateSpec | undefined>>(event: H3Event, handle: H, options?: O): Promise<Array<LocalizedDoc<PopulatedDocument<DocumentFromHandle<H>, PopulateFromOptions<O>>>>>;
9
+ export declare function many<const H extends ContentCollectionHandle | string, O extends ManyOptions<H, PopulateSpec | undefined>>(event: H3Event, handle: H, ...args: OptionsArg<H, O>): Promise<Array<LocalizedDoc<PopulatedDocument<DocumentFromHandle<H>, PopulateFromOptions<O>>>>>;
10
10
  export declare function paginate<const H extends ContentCollectionHandle | string, O extends PaginationOptions<H, PopulateSpec | undefined>>(event: H3Event, handle: H, options: O): Promise<PaginationResult<PopulatedDocument<DocumentFromHandle<H>, PopulateFromOptions<O>>>>;
11
11
  export declare function backlinks<const Target extends ContentCollectionHandle | string, const Source extends BacklinkSource | ReadonlyArray<BacklinkSource>, P extends PopulateSpec | undefined = undefined>(event: H3Event, handle: Target, options: BacklinksOptions<Target, Source, P>): Promise<BacklinksResult<Source, P>>;
12
12
  export declare function variants<H extends ContentCollectionHandle | string>(event: H3Event, handle: H, options: VariantsOptions<H>): Promise<Array<ContentVariant<H extends {
@@ -14,9 +14,9 @@ export declare function variants<H extends ContentCollectionHandle | string>(eve
14
14
  _output: infer O;
15
15
  };
16
16
  } ? O & ParsedContent : ParsedContent>>>;
17
- export declare function tree<H extends ContentCollectionHandle | string, Fields extends ReadonlyArray<string> | undefined = undefined>(event: H3Event, handle: H, options?: Omit<TreeOptions<H>, 'fields'> & {
17
+ export declare function tree<H extends ContentCollectionHandle | string, Fields extends ReadonlyArray<string> | undefined = undefined>(event: H3Event, handle: H, ...args: OptionsArg<H, Omit<TreeOptions<H>, 'fields'> & {
18
18
  fields?: Fields;
19
- }): Promise<ContentTreeItem<H extends {
19
+ }>): Promise<ContentTreeItem<H extends {
20
20
  __schema: {
21
21
  _output: infer O;
22
22
  };
@@ -7,11 +7,11 @@ import {
7
7
  resolveOne as resolveOneWithContext,
8
8
  tree as treeWithContext,
9
9
  variants as variantsWithContext
10
- } from "../query/unified.js";
10
+ } from "../../features/query/unified";
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
+ import { createProviderNavigationQuery, createProviderQuery, normalizeProviderQueryResponse } from "./provider-query.js";
15
15
  export const createServerContentQueryContext = async (event) => {
16
16
  const provider = await getContentProvider(event);
17
17
  return {
@@ -23,9 +23,10 @@ export const createServerContentQueryContext = async (event) => {
23
23
  provider: provider.name
24
24
  });
25
25
  }
26
- return await provider.navigationQuery(event, params);
26
+ const { query, options } = createProviderNavigationQuery(params);
27
+ return await provider.navigationQuery(event, query, options);
27
28
  }
28
- return normalizeProviderQueryResponse(params, await provider.query(event, params), provider.name);
29
+ return normalizeProviderQueryResponse(params, await provider.query(event, createProviderQuery(params)), provider.name);
29
30
  }
30
31
  };
31
32
  };
@@ -35,7 +36,8 @@ export async function resolveOne(event, handle, options) {
35
36
  export async function one(event, handle, options) {
36
37
  return await oneWithContext(await createServerContentQueryContext(event), handle, options);
37
38
  }
38
- export async function many(event, handle, options = {}) {
39
+ export async function many(event, handle, ...args) {
40
+ const options = args[0] ?? {};
39
41
  return await manyWithContext(await createServerContentQueryContext(event), handle, options);
40
42
  }
41
43
  export async function paginate(event, handle, options) {
@@ -47,7 +49,8 @@ export async function backlinks(event, handle, options) {
47
49
  export async function variants(event, handle, options) {
48
50
  return await variantsWithContext(await createServerContentQueryContext(event), handle, options);
49
51
  }
50
- export async function tree(event, handle, options = {}) {
52
+ export async function tree(event, handle, ...args) {
53
+ const options = args[0] ?? {};
51
54
  return await treeWithContext(await createServerContentQueryContext(event), handle, options);
52
55
  }
53
56
  export async function neighbors(event, handle, options) {
@@ -1,5 +1,5 @@
1
1
  import { type H3Event } from 'h3';
2
2
  import type { ContentQueryResponse } from '../../types/api';
3
- import type { ContentQueryBuilderParams } from '../../types/query';
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>>;
3
+ import type { ContentQueryPlan } from '../../core/query/plan';
4
+ export declare const executeFilesystemContentQuery: <T = unknown>(event: H3Event, inputPlan: ContentQueryPlan) => Promise<ContentQueryResponse<T>>;
5
+ export declare const executeContentQuery: <T = unknown>(event: H3Event, inputPlan: ContentQueryPlan) => Promise<ContentQueryResponse<T>>;
@@ -1,34 +1,30 @@
1
1
  import { createError } from "h3";
2
- import { lowerQueryPlan } from "../../core/query/lower";
2
+ import { isPlanRegex } from "../../core/query/plan";
3
3
  import { executeQueryPlan } from "../../core/query/execute";
4
4
  import { ContentError } from "../../core/errors";
5
- import { normalizeContentQueryParams } from "../../core/query/params";
6
- import { compileWhere } from "../../core/query/filter";
7
- import { containsStandaloneRegexOptions, findUnsupportedQueryOperator } from "../../core/query/operators";
8
5
  import { withResolvedRefs, withResolvedRefsList } from "../../storage/references";
9
6
  import { getContentGraph } from "../../storage/graph";
10
7
  import { getContentRuntimeConfig } from "./runtime-config.js";
11
8
  import { isPreview } from "../../integrations/nitro/preview";
12
- import { createContentProviderError } from "../../public/provider-errors";
13
- import { MAX_PUBLIC_QUERY_LIMIT, MAX_PUBLIC_QUERY_SKIP } from "../query/public-limits.js";
14
- const notFound = (query, description = "Could not find document for the given query.") => {
9
+ import { MAX_PUBLIC_QUERY_LIMIT, MAX_PUBLIC_QUERY_SKIP } from "../../features/query/public-limits";
10
+ const notFound = (plan, description = "Could not find document for the given query.") => {
15
11
  throw createError({
16
12
  statusMessage: "Document not found!",
17
13
  statusCode: 404,
18
14
  data: {
19
15
  description,
20
- query
16
+ query: plan
21
17
  }
22
18
  });
23
19
  };
24
20
  const statusForContentError = {
25
21
  INVALID_REF_VALUE: 422
26
22
  };
27
- const toHttpError = (error, query) => createError({
23
+ const toHttpError = (error, plan) => createError({
28
24
  statusCode: statusForContentError[error.code] ?? 500,
29
25
  statusMessage: error.code,
30
26
  message: error.message,
31
- data: { code: error.code, context: error.context, query }
27
+ data: { code: error.code, context: error.context, query: plan }
32
28
  });
33
29
  const badQuery = (message) => {
34
30
  throw createError({
@@ -37,71 +33,59 @@ const badQuery = (message) => {
37
33
  message
38
34
  });
39
35
  };
40
- const containsPublicRegex = (value) => {
41
- if (value instanceof RegExp) {
42
- return true;
36
+ const planFilterContainsRegex = (filter) => {
37
+ switch (filter.type) {
38
+ case "true":
39
+ return false;
40
+ case "compare":
41
+ return filter.operator === "regex" || isPlanRegex(filter.value);
42
+ case "and":
43
+ case "or":
44
+ return filter.clauses.some(planFilterContainsRegex);
45
+ case "not":
46
+ return planFilterContainsRegex(filter.clause);
47
+ default:
48
+ throw new TypeError(`Unknown query filter node: ${filter.type}`);
43
49
  }
44
- if (!value || typeof value !== "object") {
45
- return false;
46
- }
47
- if (Array.isArray(value)) {
48
- return value.some(containsPublicRegex);
49
- }
50
- const record = value;
51
- return Object.entries(record).some(([key, child]) => key === "$regex" || containsPublicRegex(child));
52
50
  };
53
- const normalizePublicQuery = (event, query, config) => {
54
- if (!query.collection) {
51
+ const andPlanFilters = (base, ...extra) => {
52
+ const clauses = [base, ...extra].filter((clause) => clause.type !== "true");
53
+ if (!clauses.length) return { type: "true" };
54
+ if (clauses.length === 1) return clauses[0];
55
+ return { type: "and", clauses };
56
+ };
57
+ const applyFilesystemQueryPolicy = (event, plan) => {
58
+ if (!plan.collection) {
55
59
  badQuery("Public content queries must target a collection.");
56
60
  }
57
- if (containsPublicRegex(query.where)) {
61
+ if (planFilterContainsRegex(plan.filter)) {
58
62
  badQuery("Public content queries do not accept RegExp filters.");
59
63
  }
60
- if (containsStandaloneRegexOptions(query.where)) {
61
- badQuery("Query operator $options requires $regex.");
62
- }
63
64
  const enforceProductionVisibility = !import.meta.dev && !isPreview(event);
64
- const normalized = normalizeContentQueryParams({
65
- ...query,
66
- limit: typeof query.limit === "number" ? Math.max(0, Math.min(query.limit, MAX_PUBLIC_QUERY_LIMIT)) : query.limit,
67
- skip: typeof query.skip === "number" ? Math.max(0, Math.min(query.skip, MAX_PUBLIC_QUERY_SKIP)) : query.skip
68
- }, {
69
- collectionI18n: query.collection ? config.collections?.[query.collection]?.i18n : void 0,
70
- defaultLocale: config.defaultLocale,
71
- localeFallback: config.localeFallback,
72
- includeDraftFilter: false
73
- });
74
- if (enforceProductionVisibility) {
75
- const where = Array.isArray(normalized.where) ? normalized.where : normalized.where ? [normalized.where] : [];
76
- normalized.where = [
77
- ...where,
78
- { _draft: { $ne: true } },
79
- { _partial: { $ne: true } }
80
- ];
81
- }
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));
84
- const unsupported = findUnsupportedQueryOperator(normalized.where);
85
- if (unsupported) {
86
- throw createContentProviderError("unsupported_query_operator", `Unsupported query operator: ${unsupported}`, {
87
- operator: unsupported
88
- });
89
- }
90
- return normalized;
65
+ const filter = enforceProductionVisibility ? andPlanFilters(
66
+ plan.filter,
67
+ { type: "compare", field: "draft", operator: "ne", value: true },
68
+ { type: "compare", field: "partial", operator: "ne", value: true }
69
+ ) : plan.filter;
70
+ return {
71
+ ...plan,
72
+ filter,
73
+ limit: typeof plan.limit === "number" ? Math.max(0, Math.min(plan.limit, MAX_PUBLIC_QUERY_LIMIT)) : plan.limit,
74
+ skip: Math.max(0, Math.min(plan.skip, MAX_PUBLIC_QUERY_SKIP))
75
+ };
91
76
  };
92
- export const executeFilesystemContentQuery = async (event, inputQuery) => {
77
+ export const executeFilesystemContentQuery = async (event, inputPlan) => {
93
78
  const config = getContentRuntimeConfig().content || {};
94
- const query = normalizePublicQuery(event, inputQuery, config);
79
+ const plan = applyFilesystemQueryPolicy(event, inputPlan);
95
80
  let graph;
96
81
  try {
97
82
  graph = await getContentGraph(event);
98
83
  } catch (cause) {
99
84
  if (cause instanceof ContentError) {
100
- throw toHttpError(cause, query);
85
+ throw toHttpError(cause, plan);
101
86
  }
102
87
  throw cause;
103
88
  }
104
- const plan = lowerQueryPlan(query);
105
89
  const response = executeQueryPlan(graph, plan, {
106
90
  defaultLocale: config.defaultLocale,
107
91
  localeFallback: config.localeFallback,
@@ -114,7 +98,7 @@ export const executeFilesystemContentQuery = async (event, inputQuery) => {
114
98
  if (plan.mode === "first") {
115
99
  const content = response.result;
116
100
  if (!content) {
117
- notFound(query, plan.resolveVariant ? "Could not find document for the given route variant." : void 0);
101
+ notFound(plan, plan.resolveVariant ? "Could not find document for the given route variant." : void 0);
118
102
  }
119
103
  return {
120
104
  result: await withResolvedRefs(event, content, requestedLocale)
@@ -3,7 +3,7 @@ import type { ParsedContent } from '../../types/content';
3
3
  import type { ContentQueryBuilderWhere } from '../../types/query';
4
4
  import type { ContentSearchIndexRecord } from '../../types/search';
5
5
  export { searchRecords } from '../shared/search';
6
- type SearchablePage = Pick<ParsedContent, '_path' | '_locale' | 'title' | 'description' | 'body'> & Record<string, unknown>;
6
+ type SearchablePage = Pick<ParsedContent, 'path' | 'locale' | 'title' | 'description' | 'body'> & Record<string, unknown>;
7
7
  type RuntimeSearchConfig = {
8
8
  search?: {
9
9
  collections?: string[];