@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,16 +1,26 @@
1
+ import { isPlanRegex } from "./plan.js";
2
+ import { sortLocalesCanonically } from "../content/locale.js";
1
3
  import { resolveGraphCanonicalKey, resolveGraphRouteVariant, resolveGraphVariant, resolveLocaleChain, selectGraphDocuments } from "../content/graph.js";
2
4
  import { ensureArray, get, omit, sortList, withKeys, withoutKeys } from "./operators.js";
3
5
  import { normalizeRouteMounts, routeToContentPathCandidates } from "../content/path.js";
4
6
  const escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
5
- const includesEntry = (haystack, entry) => typeof haystack === "string" ? haystack.includes(String(entry)) : haystack.includes(entry);
7
+ const reviveRegex = (value) => isPlanRegex(value) ? new RegExp(value.source, value.flags) : value;
8
+ const comparableValue = (value) => value instanceof Date ? value.toISOString() : value;
9
+ const includesEntry = (haystack, entry) => typeof haystack === "string" ? isPlanRegex(entry) ? reviveRegex(entry).test(haystack) : haystack.includes(String(entry)) : haystack.some((value) => compareOperators.eq(value, entry));
6
10
  const compareOperators = {
7
- eq: (item, value) => value instanceof RegExp ? value.test(String(item)) : item === value,
8
- ne: (item, value) => value instanceof RegExp ? !value.test(String(item)) : item !== value,
9
- gt: (item, value) => item > value,
10
- gte: (item, value) => item >= value,
11
- lt: (item, value) => item < value,
12
- lte: (item, value) => item <= value,
13
- in: (item, value) => ensureArray(value).some((entry) => Array.isArray(item) ? item.includes(entry) : compareOperators.eq(item, entry)),
11
+ eq: (item, value) => {
12
+ const operand = reviveRegex(value);
13
+ return operand instanceof RegExp ? operand.test(String(item)) : comparableValue(item) === comparableValue(operand);
14
+ },
15
+ ne: (item, value) => {
16
+ const operand = reviveRegex(value);
17
+ return operand instanceof RegExp ? !operand.test(String(item)) : comparableValue(item) !== comparableValue(operand);
18
+ },
19
+ gt: (item, value) => comparableValue(item) > comparableValue(value),
20
+ gte: (item, value) => comparableValue(item) >= comparableValue(value),
21
+ lt: (item, value) => comparableValue(item) < comparableValue(value),
22
+ lte: (item, value) => comparableValue(item) <= comparableValue(value),
23
+ in: (item, value) => ensureArray(value).some((entry) => Array.isArray(item) ? item.some((itemEntry) => compareOperators.eq(itemEntry, entry)) : compareOperators.eq(item, entry)),
14
24
  contains: (item, value) => {
15
25
  const haystack = Array.isArray(item) ? item : String(item);
16
26
  return ensureArray(value).every((entry) => includesEntry(haystack, entry));
@@ -29,11 +39,17 @@ const compareOperators = {
29
39
  type: (item, value) => typeof item === String(value),
30
40
  prefix: (item, value) => String(item || "").startsWith(String(value)),
31
41
  regex: (item, value) => {
32
- if (value instanceof RegExp) {
33
- return value.test(String(item || ""));
42
+ const operand = reviveRegex(value);
43
+ if (operand instanceof RegExp) {
44
+ return operand.test(String(item || ""));
45
+ }
46
+ if (typeof operand === "object" && operand !== null) {
47
+ throw new TypeError(
48
+ "$regex operand must be a plain string or a tagged { __ginkoContentQueryValue: 'RegExp', source, flags } wire value produced by the current PROVIDER_QUERY_VERSION lowering; received an untagged object (likely an old-wire { source, flags } regex predating PROVIDER_QUERY_VERSION)"
49
+ );
34
50
  }
35
- const matched = String(value).match(/\/(.*)\/([dgimsuy]*)$/);
36
- const regex = matched?.[1] ? new RegExp(matched[1], matched[2] || "") : new RegExp(String(value));
51
+ const matched = String(operand).match(/\/(.*)\/([dgimsuy]*)$/);
52
+ const regex = matched?.[1] ? new RegExp(matched[1], matched[2] || "") : new RegExp(String(operand));
37
53
  return regex.test(String(item || ""));
38
54
  }
39
55
  };
@@ -49,6 +65,8 @@ export const evaluateQueryPlanFilter = (item, filter) => {
49
65
  return filter.clauses.some((clause) => evaluateQueryPlanFilter(item, clause));
50
66
  case "not":
51
67
  return !evaluateQueryPlanFilter(item, filter.clause);
68
+ default:
69
+ throw new TypeError(`Unknown query filter node: ${filter.type}`);
52
70
  }
53
71
  };
54
72
  const collectFieldComparisons = (filter, field) => {
@@ -62,6 +80,9 @@ const collectFieldComparisons = (filter, field) => {
62
80
  if (filter.operator === "eq" && typeof filter.value === "string") {
63
81
  return [filter.value];
64
82
  }
83
+ if (filter.operator === "regex" && isPlanRegex(filter.value)) {
84
+ return [new RegExp(filter.value.source, filter.value.flags)];
85
+ }
65
86
  if (filter.operator === "regex" && filter.value instanceof RegExp) {
66
87
  return [filter.value];
67
88
  }
@@ -77,6 +98,8 @@ const collectFieldComparisons = (filter, field) => {
77
98
  case "or":
78
99
  case "not":
79
100
  return [];
101
+ default:
102
+ throw new TypeError(`Unknown query filter node: ${filter.type}`);
80
103
  }
81
104
  };
82
105
  const applyQueryPlanSort = (matched, plan) => {
@@ -142,21 +165,21 @@ const withDirConfig = (content, dirConfig) => {
142
165
  }
143
166
  return {
144
167
  ...content,
145
- _dir: {
168
+ dir: {
146
169
  ...dirConfig,
147
170
  ...dirConfig.body
148
171
  }
149
172
  };
150
173
  };
151
174
  export const executeQueryPlanOnDocuments = (documents, plan) => {
152
- const matched = documents.filter((item) => !plan.collection || item?._collection === plan.collection).filter((item) => evaluateQueryPlanFilter(item, plan.filter)).map((item) => ({ ...item }));
175
+ const matched = documents.filter((item) => !plan.collection || item?.collection === plan.collection).filter((item) => evaluateQueryPlanFilter(item, plan.filter)).map((item) => ({ ...item }));
153
176
  applyQueryPlanSort(matched, plan);
154
177
  return finalizeQueryPlanResponse(matched, plan);
155
178
  };
156
179
  const executeStandardPlan = (graph, plan) => {
157
180
  const candidates = selectGraphDocuments(graph, {
158
181
  collection: plan.collection,
159
- paths: collectFieldComparisons(plan.filter, "_path")
182
+ paths: collectFieldComparisons(plan.filter, "path")
160
183
  });
161
184
  const matched = candidates.filter((item) => evaluateQueryPlanFilter(item, plan.filter)).map((item) => ({ ...item }));
162
185
  applyQueryPlanSort(matched, plan);
@@ -164,9 +187,13 @@ const executeStandardPlan = (graph, plan) => {
164
187
  };
165
188
  const executeLocalePlan = (graph, plan, options) => {
166
189
  const requestedLocale = plan.resolveLocale?.locale;
190
+ const collectionConfig = plan.collection ? options.collections?.[plan.collection] : void 0;
191
+ const collectionI18n = collectionConfig?.i18n && typeof collectionConfig.i18n === "object" ? collectionConfig.i18n : void 0;
192
+ const defaultLocale = collectionI18n?.defaultLocale || options.defaultLocale;
193
+ const locales = collectionI18n?.locales?.length ? collectionI18n.locales : [];
167
194
  const candidates = selectGraphDocuments(graph, {
168
195
  collection: plan.collection,
169
- paths: collectFieldComparisons(plan.filter, "_path")
196
+ paths: collectFieldComparisons(plan.filter, "path")
170
197
  });
171
198
  const localeChain = resolveLocaleChain(
172
199
  requestedLocale,
@@ -177,18 +204,21 @@ const executeLocalePlan = (graph, plan, options) => {
177
204
  const merged = [];
178
205
  const indexByCanonical = /* @__PURE__ */ new Map();
179
206
  for (const item of candidates.filter((item2) => evaluateQueryPlanFilter(item2, plan.filter))) {
180
- if (plan.resolveLocale?.exact && item._locale !== requestedLocale) {
207
+ if (plan.resolveLocale?.exact && item.locale !== requestedLocale) {
181
208
  continue;
182
209
  }
183
- const key = item._canonicalKey || item.id || item._id || item._path;
184
- const rank = localeRank.get(item._locale || "") ?? Number.MAX_SAFE_INTEGER;
185
- const availableLocales = item._canonicalKey ? Object.keys(graph.byCanonical[item._canonicalKey] || {}) : [item._locale].filter(Boolean);
210
+ const key = item.canonicalKey || item.id || item.id || item.path;
211
+ const rank = localeRank.get(item.locale || "") ?? Number.MAX_SAFE_INTEGER;
212
+ const availableLocales = item.canonicalKey ? sortLocalesCanonically(Object.keys(graph.byCanonical[item.canonicalKey] || {}), { defaultLocale, locales }) : [item.locale].filter(Boolean);
186
213
  const enriched = {
187
214
  ...item,
188
- _requestedLocale: requestedLocale,
189
- _resolvedLocale: item._locale,
190
- _fallback: item._locale !== requestedLocale,
191
- _availableLocales: availableLocales
215
+ resolved: {
216
+ ...item.resolved || {},
217
+ requestedLocale,
218
+ locale: item.locale,
219
+ fallback: item.locale !== requestedLocale,
220
+ availableLocales
221
+ }
192
222
  };
193
223
  if (typeof key !== "string") {
194
224
  continue;
@@ -221,6 +251,7 @@ const executeVariantPlan = (graph, plan, options) => {
221
251
  if (!canonicalKey) return null;
222
252
  return resolveGraphVariant(graph, canonicalKey, plan.resolveVariant.locale, {
223
253
  defaultLocale,
254
+ locales: collectionI18n?.locales,
224
255
  fallback: plan.resolveVariant.fallback,
225
256
  exact: plan.resolveVariant.exact,
226
257
  localeFallback,
@@ -228,7 +259,7 @@ const executeVariantPlan = (graph, plan, options) => {
228
259
  });
229
260
  })() : plan.resolveVariant.route ? (() => {
230
261
  const localeChain = plan.resolveVariant.exact ? plan.resolveVariant.locale ? [plan.resolveVariant.locale] : [] : plan.resolveVariant.fallback?.length ? Array.from(new Set([plan.resolveVariant.locale, ...plan.resolveVariant.fallback].filter(Boolean))) : resolveLocaleChain(plan.resolveVariant.locale, defaultLocale, localeFallback || {});
231
- const candidateLocales = localeChain.length ? localeChain : Array.from(new Set(Object.values(graph.byCanonical).flatMap((variants) => Object.keys(variants))));
262
+ const candidateLocales = localeChain.length ? localeChain : sortLocalesCanonically(Object.values(graph.byCanonical).flatMap((variants) => Object.keys(variants)), { defaultLocale, locales: collectionI18n?.locales });
232
263
  if (!candidateLocales.length) {
233
264
  candidateLocales.push("");
234
265
  }
@@ -246,6 +277,7 @@ const executeVariantPlan = (graph, plan, options) => {
246
277
  }
247
278
  const resolved = resolveGraphVariant(graph, canonicalKey, plan.resolveVariant.locale, {
248
279
  defaultLocale,
280
+ locales: collectionI18n?.locales,
249
281
  fallback: plan.resolveVariant.fallback,
250
282
  exact: plan.resolveVariant.exact,
251
283
  localeFallback,
@@ -260,6 +292,7 @@ const executeVariantPlan = (graph, plan, options) => {
260
292
  plan.resolveVariant.locale,
261
293
  {
262
294
  defaultLocale,
295
+ locales: collectionI18n?.locales,
263
296
  fallback: plan.resolveVariant.fallback,
264
297
  exact: plan.resolveVariant.exact,
265
298
  localeFallback,
@@ -270,23 +303,26 @@ const executeVariantPlan = (graph, plan, options) => {
270
303
  return { result: void 0 };
271
304
  }
272
305
  const content = graph.byId[variant.contentId];
273
- if (plan.collection && content?._collection !== plan.collection) {
306
+ if (plan.collection && content?.collection !== plan.collection) {
274
307
  return { result: void 0 };
275
308
  }
276
- const dirConfig = findDirConfig(graph, content?._path, variant.resolvedLocale);
309
+ const dirConfig = findDirConfig(graph, content?.path, variant.resolvedLocale);
277
310
  const variantPaths = Object.fromEntries(
278
311
  Object.entries(graph.byCanonical[variant.canonicalKey] || {}).map(([locale, entry]) => [locale, entry.path])
279
312
  );
280
313
  const enriched = {
281
314
  ...withDirConfig(content, dirConfig),
282
- ...plan.resolveVariant.path ? { _requestedPath: plan.resolveVariant.path } : {},
283
- ...plan.resolveVariant.route ? { _requestedRoute: plan.resolveVariant.route } : {},
284
- ...plan.resolveVariant.ref ? { _requestedRef: plan.resolveVariant.ref } : {},
285
- _requestedLocale: variant.requestedLocale,
286
- _resolvedLocale: variant.resolvedLocale,
287
- _fallback: variant.fallback,
288
- _availableLocales: variant.availableLocales,
289
- _variantPaths: variantPaths
315
+ resolved: {
316
+ ...content?.resolved || {},
317
+ ...plan.resolveVariant.path ? { requestedPath: plan.resolveVariant.path } : {},
318
+ ...plan.resolveVariant.route ? { requestedRoute: plan.resolveVariant.route } : {},
319
+ ...plan.resolveVariant.ref ? { requestedRef: plan.resolveVariant.ref } : {},
320
+ requestedLocale: variant.requestedLocale,
321
+ locale: variant.resolvedLocale,
322
+ fallback: variant.fallback,
323
+ availableLocales: variant.availableLocales,
324
+ variantPaths
325
+ }
290
326
  };
291
327
  const matched = evaluateQueryPlanFilter(enriched, plan.filter) ? [enriched] : [];
292
328
  return finalizeQueryPlanResponse(matched, plan);
@@ -302,8 +338,11 @@ export const executeQueryPlan = (graph, plan, options = {}) => {
302
338
  };
303
339
  export const resolveQueryPlanVariant = (graph, plan, options = {}) => {
304
340
  if (plan.resolveVariant) {
341
+ const collectionConfig = plan.collection ? options.collections?.[plan.collection] : void 0;
342
+ const collectionI18n = collectionConfig?.i18n && typeof collectionConfig.i18n === "object" ? collectionConfig.i18n : void 0;
305
343
  return resolveGraphRouteVariant(graph, plan.resolveVariant.path || plan.resolveVariant.route || "", plan.resolveVariant.locale, {
306
- defaultLocale: options.defaultLocale,
344
+ defaultLocale: collectionI18n?.defaultLocale || options.defaultLocale,
345
+ locales: collectionI18n?.locales,
307
346
  fallback: plan.resolveVariant.fallback,
308
347
  exact: plan.resolveVariant.exact,
309
348
  localeFallback: options.localeFallback
@@ -50,7 +50,7 @@ export const compileWhere = (where) => {
50
50
  }
51
51
  continue;
52
52
  }
53
- const targetKey = key === "path" ? "_path" : key;
53
+ const targetKey = key === "path" ? "path" : key;
54
54
  const operand = compileFieldOperand(value);
55
55
  if (operand !== void 0) {
56
56
  ;
@@ -110,7 +110,7 @@ export const compileQueryParams = (input) => {
110
110
  ...params.resolveLocale?.exact ? { exact: true } : {}
111
111
  };
112
112
  } else if (path) {
113
- const pathClause = { _path: path };
113
+ const pathClause = { path };
114
114
  const existing = Array.isArray(params.where) ? params.where : params.where ? [params.where] : [];
115
115
  params.where = [...existing, pathClause];
116
116
  }
@@ -1,7 +1,75 @@
1
1
  import { assertSupportedQueryOperators, SUPPORTED_QUERY_OPERATORS } from "./operators.js";
2
+ const serializeQueryValue = (value) => {
3
+ if (value instanceof RegExp) {
4
+ assertSupportedRegexFlags(value.flags);
5
+ return { __ginkoContentQueryValue: "RegExp", source: value.source, flags: value.flags };
6
+ }
7
+ if (value instanceof Date) {
8
+ return value.toISOString();
9
+ }
10
+ if (Array.isArray(value)) {
11
+ return value.map(serializeQueryValue);
12
+ }
13
+ if (value && typeof value === "object" && Object.getPrototypeOf(value) === Object.prototype) {
14
+ return Object.fromEntries(Object.entries(value).map(([key, child]) => [key, serializeQueryValue(child)]));
15
+ }
16
+ return value;
17
+ };
18
+ const SUPPORTED_REGEX_FLAGS = /^[imsu]*$/;
19
+ const assertSupportedRegexFlags = (flags) => {
20
+ if (!SUPPORTED_REGEX_FLAGS.test(flags)) {
21
+ throw new TypeError(`Unsupported RegExp flags "${flags}". Content queries support only i, m, s, and u.`);
22
+ }
23
+ try {
24
+ new RegExp("", flags);
25
+ } catch {
26
+ throw new TypeError(`Invalid RegExp flags "${flags}".`);
27
+ }
28
+ };
29
+ const STRING_REGEX_WITH_FLAGS = /\/(.*)\/([dgimsuy]+)$/;
30
+ const assertSupportedRegexStringFlags = (value) => {
31
+ const matched = value.match(STRING_REGEX_WITH_FLAGS);
32
+ if (matched) {
33
+ assertSupportedRegexFlags(matched[2]);
34
+ }
35
+ };
2
36
  const ensureQueryWhereArray = (where) => {
3
37
  return Array.isArray(where) ? [...where] : where ? [where] : [];
4
38
  };
39
+ const REMOVED_ENVELOPE_FIELDS = {
40
+ _id: "id",
41
+ _path: "path",
42
+ _locale: "locale",
43
+ _collection: "collection",
44
+ _canonicalKey: "canonicalKey",
45
+ _type: "type",
46
+ _extension: "file.extension",
47
+ _navigation: "navigationFile",
48
+ _partial: "partial",
49
+ _draft: "draft"
50
+ };
51
+ const collectWhereFields = (condition, out) => {
52
+ for (const [key, value] of Object.entries(condition)) {
53
+ if ((key === "$and" || key === "$or") && value) {
54
+ for (const clause of ensureQueryWhereArray(value)) collectWhereFields(clause, out);
55
+ } else if (key === "$not" && value && typeof value === "object" && !Array.isArray(value)) {
56
+ collectWhereFields(value, out);
57
+ } else if (!key.startsWith("$")) {
58
+ out.add(key);
59
+ }
60
+ }
61
+ };
62
+ const warnOnRemovedEnvelopeFields = (params) => {
63
+ if (!import.meta.dev) return;
64
+ const fields = /* @__PURE__ */ new Set();
65
+ for (const condition of ensureQueryWhereArray(params.where)) collectWhereFields(condition, fields);
66
+ for (const option of params.sort || []) for (const key of Object.keys(option)) if (!key.startsWith("$")) fields.add(key);
67
+ for (const field of [...params.only || [], ...params.without || []]) fields.add(field);
68
+ for (const field of fields) {
69
+ const replacement = REMOVED_ENVELOPE_FIELDS[field.split(".")[0]];
70
+ if (replacement) console.warn(`[ginko-content] Query references removed envelope field "${field.split(".")[0]}" \u2014 use "${replacement}" instead; the old field no longer exists, so this clause has no effect.`);
71
+ }
72
+ };
5
73
  const COMPARISON_OPERATORS = new Set(SUPPORTED_QUERY_OPERATORS);
6
74
  const collapse = (type, clauses) => {
7
75
  const filtered = clauses.filter((clause) => clause.type !== "true");
@@ -14,7 +82,7 @@ const collapse = (type, clauses) => {
14
82
  return { type, clauses: filtered };
15
83
  };
16
84
  const lowerFieldCondition = (field, value) => {
17
- if (value && typeof value === "object" && !Array.isArray(value) && !(value instanceof RegExp)) {
85
+ if (value && typeof value === "object" && !Array.isArray(value) && !(value instanceof RegExp) && !(value instanceof Date)) {
18
86
  const objectValue = value;
19
87
  if ("$options" in objectValue && !("$regex" in objectValue)) {
20
88
  throw new TypeError("Query operator $options requires $regex.");
@@ -32,13 +100,16 @@ const lowerFieldCondition = (field, value) => {
32
100
  });
33
101
  continue;
34
102
  }
103
+ if (key === "$regex" && typeof nestedValue === "string" && typeof objectValue.$options !== "string") {
104
+ assertSupportedRegexStringFlags(nestedValue);
105
+ }
35
106
  clauses.push({
36
107
  type: "compare",
37
108
  field,
38
109
  // `key` is gated by the `COMPARISON_OPERATORS` set above; after
39
110
  // stripping the leading `$` it maps 1:1 to a `CompareOperator`.
40
111
  operator: key.slice(1),
41
- value: key === "$regex" && typeof objectValue.$options === "string" && !(nestedValue instanceof RegExp) ? new RegExp(String(nestedValue), objectValue.$options) : nestedValue
112
+ value: key === "$regex" && typeof objectValue.$options === "string" && !(nestedValue instanceof RegExp) ? serializeQueryValue(regexValue(String(nestedValue), objectValue.$options)) : serializeQueryValue(nestedValue)
42
113
  });
43
114
  continue;
44
115
  }
@@ -50,9 +121,13 @@ const lowerFieldCondition = (field, value) => {
50
121
  type: "compare",
51
122
  field,
52
123
  operator: "eq",
53
- value
124
+ value: serializeQueryValue(value)
54
125
  };
55
126
  };
127
+ const regexValue = (source, flags) => {
128
+ assertSupportedRegexFlags(flags);
129
+ return { __ginkoContentQueryValue: "RegExp", source, flags };
130
+ };
56
131
  const lowerWhereCondition = (condition) => {
57
132
  const clauses = [];
58
133
  for (const [key, value] of Object.entries(condition)) {
@@ -93,6 +168,7 @@ const lowerSort = (sort = []) => {
93
168
  };
94
169
  export const lowerQueryPlan = (params) => {
95
170
  assertSupportedQueryOperators(params.where);
171
+ warnOnRemovedEnvelopeFields(params);
96
172
  const resolveVariant = params.resolveVariant;
97
173
  return {
98
174
  collection: params.collection,
@@ -49,13 +49,13 @@ export const normalizeContentQueryParams = (params, options = {}) => {
49
49
  };
50
50
  if (options.path) {
51
51
  if (normalized.first && where.length === 0) {
52
- where.push({ _path: withoutTrailingSlash(options.path) });
52
+ where.push({ path: withoutTrailingSlash(options.path) });
53
53
  } else {
54
- where.push({ _path: new RegExp(`^${escapeContentPath(options.path)}`) });
54
+ where.push({ path: new RegExp(`^${escapeContentPath(options.path)}`) });
55
55
  }
56
56
  }
57
57
  if (!normalized.sort?.length) {
58
- normalized.sort = [{ _stem: 1, $numeric: true }];
58
+ normalized.sort = [{ "file.stem": 1, $numeric: true }];
59
59
  }
60
60
  if (normalized.resolveLocale?.locale && normalized.resolveLocale.fallback === true) {
61
61
  normalized.resolveLocale = {
@@ -68,15 +68,15 @@ export const normalizeContentQueryParams = (params, options = {}) => {
68
68
  };
69
69
  }
70
70
  if (options.includeDraftFilter) {
71
- if (!findQueryWhere(where, (item) => typeof item._draft !== "undefined")) {
72
- where.push({ _draft: { $ne: true } });
71
+ if (!findQueryWhere(where, (item) => typeof item.draft !== "undefined")) {
72
+ where.push({ draft: { $ne: true } });
73
73
  }
74
74
  }
75
75
  if (options.collectionI18n?.locales.length && !normalized.resolveLocale && !normalized.resolveVariant) {
76
- const queryLocale = findQueryWhere(where, (item) => typeof item._locale !== "undefined")?._locale;
76
+ const queryLocale = findQueryWhere(where, (item) => typeof item.locale !== "undefined")?.locale;
77
77
  const defaultLocale = options.activeLocale || options.collectionI18n.defaultLocale;
78
78
  if (!queryLocale && defaultLocale) {
79
- where.push({ _locale: defaultLocale });
79
+ where.push({ locale: defaultLocale });
80
80
  }
81
81
  }
82
82
  return normalized;
@@ -15,6 +15,24 @@
15
15
  */
16
16
  /** Discriminator for a `{ type: 'compare' }` filter node. */
17
17
  export type CompareOperator = 'eq' | 'ne' | 'gt' | 'gte' | 'lt' | 'lte' | 'in' | 'contains' | 'containsAny' | 'icontains' | 'exists' | 'type' | 'regex' | 'prefix';
18
+ /**
19
+ * JSON-pure representation of a regular expression on a compare node's value.
20
+ *
21
+ * The plan is the provider wire contract (CS-5) and must survive
22
+ * `JSON.parse(JSON.stringify(plan))` unchanged — a live `RegExp` instance
23
+ * serializes to `{}` and corrupts the wire. Lowering therefore stores regex
24
+ * operands as a tagged object; the executor reconstructs the `RegExp`
25
+ * immediately before matching (see `reviveRegex` in `./execute.ts`). The tag
26
+ * keeps user data shaped like `{ source, flags }` from being mistaken for a
27
+ * regex.
28
+ */
29
+ export interface PlanRegex {
30
+ __ginkoContentQueryValue: 'RegExp';
31
+ source: string;
32
+ flags: string;
33
+ }
34
+ /** True when `value` is a JSON-pure regex operand. */
35
+ export declare const isPlanRegex: (value: unknown) => value is PlanRegex;
18
36
  /**
19
37
  * Normalized where-clause AST. `{ type: 'true' }` is the identity node used
20
38
  * when the user did not pass any predicate; this keeps downstream code free
@@ -0,0 +1,7 @@
1
+ export const isPlanRegex = (value) => {
2
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
3
+ return false;
4
+ }
5
+ const record = value;
6
+ return record.__ginkoContentQueryValue === "RegExp" && typeof record.source === "string" && typeof record.flags === "string" && Object.keys(record).length === 3;
7
+ };
@@ -146,27 +146,24 @@ export const resolveMarkdownRenderRefs = (node, resolvedRefs, links, resolveRout
146
146
  export const buildReferenceTargets = (contents, locales = []) => {
147
147
  const targets = /* @__PURE__ */ new Map();
148
148
  for (const document of contents) {
149
- if (!document || document._partial || document._navigation) {
149
+ if (!document || document.partial || document.navigationFile) {
150
150
  continue;
151
151
  }
152
- const fileParts = (document._file || "").replace(/^\/+/, "").split("/").filter(Boolean);
153
- const canonicalId = typeof document._canonicalKey === "string" && document._canonicalKey.length ? document._canonicalKey : fileParts.slice(fileParts[0] && locales.includes(fileParts[0]) ? 1 : 0).join("/").replace(/\.[^.]+$/, "");
152
+ const fileParts = (document.file?.path || "").replace(/^\/+/, "").split("/").filter(Boolean);
153
+ const canonicalId = typeof document.canonicalKey === "string" && document.canonicalKey.length ? document.canonicalKey : fileParts.slice(fileParts[0] && locales.includes(fileParts[0]) ? 1 : 0).join("/").replace(/\.[^.]+$/, "");
154
154
  if (!canonicalId) {
155
155
  continue;
156
156
  }
157
157
  targets.set(canonicalId, canonicalId);
158
- if (typeof document.id === "string" && document.id.length) {
159
- targets.set(document.id, canonicalId);
160
- }
161
158
  if (typeof document.ref === "string" && document.ref.length) {
162
159
  targets.set(document.ref, canonicalId);
163
160
  }
164
- const normalizedPath = normalizeReferenceValue(String(document._path || ""));
161
+ const normalizedPath = normalizeReferenceValue(String(document.path || ""));
165
162
  if (normalizedPath) {
166
163
  targets.set(normalizedPath, canonicalId);
167
164
  }
168
- if (document._locale && normalizedPath) {
169
- targets.set(`${document._locale}/${normalizedPath}`, canonicalId);
165
+ if (document.locale && normalizedPath) {
166
+ targets.set(`${document.locale}/${normalizedPath}`, canonicalId);
170
167
  }
171
168
  }
172
169
  return targets;
@@ -0,0 +1 @@
1
+ export * from './agent-markdown.js'
@@ -0,0 +1,116 @@
1
+ import type { MarkdownNode, ParsedContent } from '../../types/content';
2
+ import type { AgentMetadataField, ContentCollectionConfig } from '../../types/config';
3
+ export interface AgentMarkdownPublicSignals {
4
+ search?: 'yes' | 'no';
5
+ aiInput?: 'yes' | 'no';
6
+ aiTrain?: 'yes' | 'no';
7
+ }
8
+ export interface ResolvedAgentMarkdownOptions {
9
+ includeInIndex: boolean;
10
+ includeInFull: boolean;
11
+ metadata: AgentMetadataField[];
12
+ }
13
+ export interface AgentMarkdown {
14
+ path: string;
15
+ markdownPath: string;
16
+ rawPath: string;
17
+ locale?: string;
18
+ collection: string;
19
+ title: string;
20
+ description: string;
21
+ markdown: string;
22
+ sourceFile?: string;
23
+ canonicalUrl: string;
24
+ lastModified?: string;
25
+ publicSignals?: AgentMarkdownPublicSignals;
26
+ metadataFields: string[];
27
+ includeInIndex: boolean;
28
+ includeInFull: boolean;
29
+ }
30
+ export type AgentMarkdownMeta = Omit<AgentMarkdown, 'markdown'>;
31
+ /**
32
+ * The single context the agent-markdown walker is a pure function of.
33
+ *
34
+ * It carries (a) the per-app serializer {@link AgentMarkdownRegistry} and
35
+ * (b) the config-derived data the walker reads (markdown tag aliases, the
36
+ * configured locales, and the default locale used for link prefixing), in
37
+ * addition to the document coordinates and the per-node render helpers passed
38
+ * to serializers. The walker holds no module-global state: everything it reads
39
+ * arrives on this context, built by the runtime handlers from the resolved
40
+ * content config.
41
+ */
42
+ export interface AgentMarkdownContext {
43
+ collection: string;
44
+ page: ParsedContent;
45
+ path: string;
46
+ locale?: string;
47
+ /** Per-app serializer registry the walker resolves component tags against. */
48
+ registry: AgentMarkdownRegistry;
49
+ /** Component tag alias map (the resolved config's `markdown.tags`). */
50
+ tagAliases: Record<string, string>;
51
+ /** Default locale used when prefixing localized links. */
52
+ defaultLocale: string;
53
+ /** Configured locales used when prefixing localized links. */
54
+ locales: string[];
55
+ prop: (name: string) => string;
56
+ props: (node?: MarkdownNode) => Record<string, unknown>;
57
+ cleanProps: (node?: MarkdownNode) => Record<string, unknown>;
58
+ children: (node?: MarkdownNode) => string;
59
+ renderChildren: (node: MarkdownNode) => string;
60
+ renderNode: (node: MarkdownNode) => string;
61
+ blockquote: (value: string) => string;
62
+ jsonFence: (value: unknown) => string;
63
+ link: (label: string, href: string) => string;
64
+ omitted: (name: string, reason?: string) => string;
65
+ xmlComponent: (name: string, props?: Record<string, unknown>, children?: string) => string;
66
+ }
67
+ /**
68
+ * The environment slice the runtime handlers supply to the walker entrypoint;
69
+ * the walker derives the per-node render helpers from it. Equivalent to an
70
+ * {@link AgentMarkdownContext} without the closure-bound render helpers.
71
+ */
72
+ export type AgentMarkdownRenderContext = Pick<AgentMarkdownContext, 'collection' | 'page' | 'path' | 'locale' | 'registry' | 'tagAliases' | 'defaultLocale' | 'locales'>;
73
+ export type AgentMarkdownSerializer = (node: MarkdownNode, ctx: AgentMarkdownContext) => string | null | undefined;
74
+ export type AgentMarkdownSerializerMap = Record<string, AgentMarkdownSerializer>;
75
+ export interface AgentMarkdownSerializerRegistrationOptions {
76
+ override?: boolean;
77
+ }
78
+ export interface AgentMarkdownComponent {
79
+ render: AgentMarkdownSerializer;
80
+ }
81
+ export type AgentMarkdownComponentMap = Record<string, AgentMarkdownComponent>;
82
+ /**
83
+ * A per-app serializer registry. Each call to {@link createAgentMarkdownRegistry}
84
+ * returns an isolated registry with its own backing store — there is no
85
+ * module-global serializer map, so re-running module setup (dev HMR) yields a
86
+ * fresh registry instead of accumulating or colliding with prior registrations.
87
+ */
88
+ export interface AgentMarkdownRegistry {
89
+ register: (name: string, serializer: AgentMarkdownSerializer, options?: AgentMarkdownSerializerRegistrationOptions) => void;
90
+ registerMany: (entries: AgentMarkdownSerializerMap, options?: AgentMarkdownSerializerRegistrationOptions) => void;
91
+ registerComponent: (name: string, component: AgentMarkdownComponent, options?: AgentMarkdownSerializerRegistrationOptions) => void;
92
+ registerComponents: (entries: AgentMarkdownComponentMap, options?: AgentMarkdownSerializerRegistrationOptions) => void;
93
+ clear: () => void;
94
+ get: (name: string) => AgentMarkdownSerializer | undefined;
95
+ }
96
+ export declare const isRecord: (value: unknown) => value is Record<string, unknown>;
97
+ export declare const getMarkdownProp: (node: MarkdownNode, name: string) => string;
98
+ export declare const renderMarkdownChildren: (node: MarkdownNode, ctx: AgentMarkdownContext) => string;
99
+ export declare const blockquoteMarkdown: (value: string) => string;
100
+ export declare const escapeMarkdownLinkLabel: (value: string) => string;
101
+ export declare const linkMarkdown: (label: string, href: string) => string;
102
+ export declare const imageMarkdown: (alt: string, src: string) => string;
103
+ export declare const jsonFenceMarkdown: (value: unknown) => string;
104
+ export declare const cleanPropsObject: (props: unknown) => Record<string, unknown>;
105
+ export declare const xmlComponentMarkdown: (name: string, props?: Record<string, unknown>, children?: string) => string;
106
+ export declare const defineAgentMarkdownComponent: (component: AgentMarkdownComponent) => AgentMarkdownComponent;
107
+ /**
108
+ * Create an isolated per-app serializer registry. The backing store is local to
109
+ * the returned registry — two registries never share serializers — which is what
110
+ * makes agent markdown serialization safe under dev HMR and across concurrent
111
+ * apps. Registration/precedence semantics match the historical global registry:
112
+ * re-registering the same serializer under a name is a no-op, a different one
113
+ * throws unless `{ override: true }`.
114
+ */
115
+ export declare const createAgentMarkdownRegistry: () => AgentMarkdownRegistry;
116
+ export declare const resolveAgentMarkdownOptions: (collection: ContentCollectionConfig | undefined) => ResolvedAgentMarkdownOptions | null;