@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.
- package/compatibility.json +1 -1
- package/dist/cli.mjs +2 -2
- package/dist/cms-contract/build.js +15 -20
- package/dist/cms-contract/types.d.ts +12 -7
- package/dist/core/content/document +1 -0
- package/dist/core/content/document.d.ts +16 -0
- package/dist/core/content/document.js +2 -0
- package/dist/core/content/graph.d.ts +3 -1
- package/dist/core/content/graph.js +21 -20
- package/dist/core/content/locale.d.ts +6 -1
- package/dist/core/content/locale.js +17 -6
- package/dist/core/content/path.d.ts +5 -5
- package/dist/core/content/path.js +8 -8
- package/dist/core/content/snapshot +1 -0
- package/dist/core/content/snapshot.d.ts +25 -0
- package/dist/core/content/snapshot.js +99 -0
- package/dist/core/provider-errors.d.ts +1 -1
- package/dist/core/provider-errors.js +1 -0
- package/dist/core/query/builder.js +3 -3
- package/dist/core/query/execute.js +75 -36
- package/dist/core/query/filter.js +2 -2
- package/dist/core/query/lower.js +79 -3
- package/dist/core/query/params.js +7 -7
- package/dist/core/query/plan.d.ts +18 -0
- package/dist/core/query/plan.js +7 -0
- package/dist/core/references/resolve.js +6 -9
- package/dist/features/agent/agent-markdown +1 -0
- package/dist/features/agent/agent-markdown.d.ts +116 -0
- package/dist/features/agent/agent-markdown.js +138 -0
- package/dist/features/agent/walker +1 -0
- package/dist/features/agent/walker.d.ts +10 -0
- package/dist/features/agent/walker.js +174 -0
- package/dist/features/collections/resolve.d.ts +2 -2
- package/dist/features/collections/resolve.js +19 -16
- package/dist/features/localization/links.js +1 -1
- package/dist/features/localization/results.d.ts +3 -3
- package/dist/features/localization/results.js +46 -41
- package/dist/features/localization/translated-slugs.js +5 -5
- package/dist/features/navigation/build.d.ts +2 -2
- package/dist/features/navigation/build.js +23 -26
- package/dist/features/navigation/canonical.d.ts +10 -11
- package/dist/features/navigation/canonical.js +26 -21
- package/dist/features/navigation/resolve.js +4 -4
- package/dist/features/navigation/tree.js +3 -3
- package/dist/{runtime → features}/query/backlinks.js +3 -5
- package/dist/{runtime → features}/query/documents.js +1 -1
- package/dist/{runtime → features}/query/localized-docs.d.ts +1 -1
- package/dist/{runtime → features}/query/localized-docs.js +3 -6
- package/dist/{runtime → features}/query/navigation.d.ts +1 -1
- package/dist/{runtime → features}/query/navigation.js +8 -8
- package/dist/{runtime → features}/query/populate.js +6 -14
- package/dist/{runtime → features}/query/unified.d.ts +4 -4
- package/dist/{runtime → features}/query/unified.js +4 -2
- package/dist/{runtime → features}/query/variants.js +4 -4
- package/dist/features/search/navigation.js +2 -2
- package/dist/features/search/sections.d.ts +1 -1
- package/dist/features/search/sections.js +1 -1
- package/dist/features/sitemap/query.d.ts +5 -5
- package/dist/features/sitemap/query.js +6 -6
- package/dist/integrations/nitro/ingest.js +9 -20
- package/dist/integrations/nitro/storage.d.ts +1 -0
- package/dist/integrations/nitro/storage.js +22 -8
- package/dist/module.d.mts +135 -812
- package/dist/module.json +1 -1
- package/dist/module.mjs +70 -27
- package/dist/parsers/csv.js +3 -3
- package/dist/parsers/index.js +3 -3
- package/dist/parsers/json.js +8 -6
- package/dist/parsers/markdown-plugins.js +9 -5
- package/dist/parsers/markdown.js +7 -6
- package/dist/parsers/path-meta.js +21 -19
- package/dist/parsers/reserved +1 -0
- package/dist/parsers/reserved.d.ts +15 -0
- package/dist/parsers/reserved.js +35 -0
- package/dist/parsers/yaml.js +6 -4
- package/dist/public/agent +1 -0
- package/dist/public/agent.d.ts +17 -0
- package/dist/public/agent.js +34 -0
- package/dist/public/client.d.ts +2 -3
- package/dist/public/client.js +1 -2
- package/dist/public/provider-query +1 -0
- package/dist/public/provider-query.d.ts +58 -0
- package/dist/public/provider-query.js +5 -0
- package/dist/public/provider.d.ts +8 -3
- package/dist/public/provider.js +2 -0
- package/dist/public/server.d.ts +7 -12
- package/dist/public/server.js +5 -36
- package/dist/runtime/app/components/ContentRenderer.d.vue.ts +3 -3
- package/dist/runtime/app/components/ContentRenderer.vue +2 -2
- package/dist/runtime/app/components/ContentRenderer.vue.d.ts +3 -3
- package/dist/runtime/app/components/ContentRendererInline.d.vue.ts +1 -1
- package/dist/runtime/app/components/ContentRendererInline.vue.d.ts +1 -1
- package/dist/runtime/app/components/Prose/ProseImg.d.vue.ts +4 -4
- package/dist/runtime/app/components/Prose/ProseImg.vue.d.ts +4 -4
- package/dist/runtime/app/components/Prose/ProsePre.d.vue.ts +1 -1
- package/dist/runtime/app/components/Prose/ProsePre.vue.d.ts +1 -1
- package/dist/runtime/app/components/internal/ContentRendererMarkdown.d.vue.ts +1 -1
- package/dist/runtime/app/components/internal/ContentRendererMarkdown.vue +4 -4
- package/dist/runtime/app/components/internal/ContentRendererMarkdown.vue.d.ts +1 -1
- package/dist/runtime/app/components/internal/MarkdownRenderer.d.ts +3 -3
- package/dist/runtime/app/composables/preview.d.ts +1 -1
- package/dist/runtime/app/composables/query-api.d.ts +5 -5
- package/dist/runtime/app/composables/query-api.js +5 -3
- package/dist/runtime/app/composables/route.d.ts +1 -1
- package/dist/runtime/app/composables/route.js +4 -4
- package/dist/runtime/app/composables/search.js +1 -1
- package/dist/runtime/app/composables/use-content-document.js +1 -1
- package/dist/runtime/app/composables/use-content-list.js +1 -1
- package/dist/runtime/app/composables/use-content-navigation.js +2 -2
- package/dist/runtime/app/composables/use-content-page.js +4 -4
- package/dist/runtime/app/pages/content.vue +2 -1
- package/dist/runtime/markdown/plugins.js +6 -8
- package/dist/runtime/server/agent-markdown.d.ts +5 -65
- package/dist/runtime/server/agent-markdown.js +40 -315
- package/dist/runtime/server/agent-site.js +1 -1
- package/dist/runtime/server/api/agent-raw.js +1 -1
- package/dist/runtime/server/api/cache.d.ts +1 -1
- package/dist/runtime/server/api/cache.js +43 -4
- package/dist/runtime/server/api/locales.js +3 -3
- package/dist/runtime/server/api/navigation.d.ts +1 -1
- package/dist/runtime/server/api/navigation.js +3 -1
- package/dist/runtime/server/api/query.js +2 -2
- package/dist/runtime/server/api/search-index.d.ts +1 -1
- package/dist/runtime/server/api/sitemap.d.ts +1 -1
- package/dist/runtime/server/cache-adapters.d.ts +18 -0
- package/dist/runtime/server/cache-adapters.js +12 -0
- package/dist/runtime/server/collection-helpers.d.ts +2 -2
- package/dist/runtime/server/collection-helpers.js +15 -11
- package/dist/runtime/server/index.d.ts +6 -4
- package/dist/runtime/server/index.js +10 -3
- package/dist/runtime/server/middleware/agent-link-headers.js +1 -1
- package/dist/runtime/server/middleware/agent-markdown.js +1 -1
- package/dist/runtime/server/navigation-query.d.ts +2 -2
- package/dist/runtime/server/navigation-query.js +52 -7
- package/dist/runtime/server/navigation.d.ts +1 -1
- package/dist/runtime/server/provider-document +1 -0
- package/dist/runtime/server/provider-document.d.ts +59 -0
- package/dist/runtime/server/provider-document.js +48 -0
- package/dist/runtime/server/provider-query-wire +1 -0
- package/dist/runtime/server/provider-query-wire.d.ts +32 -0
- package/dist/runtime/server/provider-query-wire.js +27 -0
- package/dist/runtime/server/provider-query.d.ts +18 -9
- package/dist/runtime/server/provider-query.js +62 -28
- package/dist/runtime/server/provider-result.d.ts +4 -4
- package/dist/runtime/server/providers/filesystem.js +3 -11
- package/dist/runtime/server/providers/index.d.ts +18 -1
- package/dist/runtime/server/providers/index.js +72 -20
- package/dist/runtime/server/query-api.d.ts +5 -5
- package/dist/runtime/server/query-api.js +9 -6
- package/dist/runtime/server/query-executor.d.ts +3 -3
- package/dist/runtime/server/query-executor.js +43 -59
- package/dist/runtime/server/search.d.ts +1 -1
- package/dist/runtime/server/search.js +4 -4
- package/dist/runtime/server/sitemap.js +5 -5
- package/dist/runtime/server/storage-access.d.ts +4 -1
- package/dist/runtime/server/storage-access.js +27 -2
- package/dist/runtime/transformers/component-resolver.d.ts +1 -1
- package/dist/storage/content.d.ts +2 -6
- package/dist/storage/content.js +19 -8
- package/dist/storage/contents.d.ts +9 -1
- package/dist/storage/contents.js +26 -19
- package/dist/storage/driver.d.ts +2 -2
- package/dist/storage/driver.js +2 -1
- package/dist/storage/graph.js +4 -0
- package/dist/storage/manifest.js +3 -1
- package/dist/storage/references.js +9 -5
- package/dist/storage/snapshot-runtime +1 -0
- package/dist/storage/snapshot-runtime.d.ts +11 -0
- package/dist/storage/snapshot-runtime.js +46 -0
- package/dist/storage/validation.d.ts +1 -1
- package/dist/storage/validation.js +47 -102
- package/dist/testing/provider-contract.d.ts +14 -4
- package/dist/testing/provider-contract.js +121 -68
- package/dist/testing/provider-fixture.d.ts +1 -1
- package/dist/testing/provider-fixture.js +124 -120
- package/dist/types/config.d.ts +8 -4
- package/dist/types/content.d.ts +104 -70
- package/dist/types/fields.js +1 -1
- package/dist/types/module.d.ts +1 -1
- package/dist/types/query-parts/public.d.ts +14 -5
- package/dist/types/query-parts/results.d.ts +6 -2
- package/dist/types/query-parts/transport.d.ts +19 -0
- package/dist/types/virtual.d.ts +6 -0
- package/dist/types.d.mts +2 -2
- package/dist/web-types.json +1 -1
- package/package.json +39 -9
- package/dist/runtime/app/plugins/markdown-components +0 -1
- package/dist/runtime/app/plugins/markdown-components.d.ts +0 -2
- package/dist/runtime/app/plugins/markdown-components.js +0 -19
- /package/dist/{runtime → features/agent}/agent-paths +0 -0
- /package/dist/{runtime → features/agent}/agent-paths.d.ts +0 -0
- /package/dist/{runtime → features/agent}/agent-paths.js +0 -0
- /package/dist/{runtime → features}/query/backlinks +0 -0
- /package/dist/{runtime → features}/query/backlinks.d.ts +0 -0
- /package/dist/{runtime → features}/query/context +0 -0
- /package/dist/{runtime → features}/query/context.d.ts +0 -0
- /package/dist/{runtime → features}/query/context.js +0 -0
- /package/dist/{runtime → features}/query/documents +0 -0
- /package/dist/{runtime → features}/query/documents.d.ts +0 -0
- /package/dist/{runtime → features}/query/errors +0 -0
- /package/dist/{runtime → features}/query/errors.d.ts +0 -0
- /package/dist/{runtime → features}/query/errors.js +0 -0
- /package/dist/{runtime → features}/query/handles +0 -0
- /package/dist/{runtime → features}/query/handles.d.ts +0 -0
- /package/dist/{runtime → features}/query/handles.js +0 -0
- /package/dist/{runtime → features}/query/locale-options +0 -0
- /package/dist/{runtime → features}/query/locale-options.d.ts +0 -0
- /package/dist/{runtime → features}/query/locale-options.js +0 -0
- /package/dist/{runtime → features}/query/localized-docs +0 -0
- /package/dist/{runtime → features}/query/navigation +0 -0
- /package/dist/{runtime → features}/query/pagination +0 -0
- /package/dist/{runtime → features}/query/pagination.d.ts +0 -0
- /package/dist/{runtime → features}/query/pagination.js +0 -0
- /package/dist/{runtime → features}/query/populate +0 -0
- /package/dist/{runtime → features}/query/populate.d.ts +0 -0
- /package/dist/{runtime → features}/query/public-limits +0 -0
- /package/dist/{runtime → features}/query/public-limits.d.ts +0 -0
- /package/dist/{runtime → features}/query/public-limits.js +0 -0
- /package/dist/{runtime → features}/query/resolve +0 -0
- /package/dist/{runtime → features}/query/resolve.d.ts +0 -0
- /package/dist/{runtime → features}/query/resolve.js +0 -0
- /package/dist/{runtime → features}/query/responses +0 -0
- /package/dist/{runtime → features}/query/responses.d.ts +0 -0
- /package/dist/{runtime → features}/query/responses.js +0 -0
- /package/dist/{runtime → features}/query/routes +0 -0
- /package/dist/{runtime → features}/query/routes.d.ts +0 -0
- /package/dist/{runtime → features}/query/routes.js +0 -0
- /package/dist/{runtime → features}/query/unified +0 -0
- /package/dist/{runtime → features}/query/variants +0 -0
- /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
|
|
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) =>
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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
|
-
|
|
33
|
-
|
|
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(
|
|
36
|
-
const regex = matched?.[1] ? new RegExp(matched[1], matched[2] || "") : new RegExp(String(
|
|
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
|
-
|
|
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?.
|
|
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, "
|
|
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, "
|
|
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.
|
|
207
|
+
if (plan.resolveLocale?.exact && item.locale !== requestedLocale) {
|
|
181
208
|
continue;
|
|
182
209
|
}
|
|
183
|
-
const key = item.
|
|
184
|
-
const rank = localeRank.get(item.
|
|
185
|
-
const availableLocales = item.
|
|
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
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
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 :
|
|
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?.
|
|
306
|
+
if (plan.collection && content?.collection !== plan.collection) {
|
|
274
307
|
return { result: void 0 };
|
|
275
308
|
}
|
|
276
|
-
const dirConfig = findDirConfig(graph, content?.
|
|
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
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
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" ? "
|
|
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 = {
|
|
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
|
}
|
package/dist/core/query/lower.js
CHANGED
|
@@ -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) ?
|
|
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({
|
|
52
|
+
where.push({ path: withoutTrailingSlash(options.path) });
|
|
53
53
|
} else {
|
|
54
|
-
where.push({
|
|
54
|
+
where.push({ path: new RegExp(`^${escapeContentPath(options.path)}`) });
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
57
|
if (!normalized.sort?.length) {
|
|
58
|
-
normalized.sort = [{
|
|
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.
|
|
72
|
-
where.push({
|
|
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.
|
|
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({
|
|
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
|
package/dist/core/query/plan.js
CHANGED
|
@@ -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.
|
|
149
|
+
if (!document || document.partial || document.navigationFile) {
|
|
150
150
|
continue;
|
|
151
151
|
}
|
|
152
|
-
const fileParts = (document.
|
|
153
|
-
const canonicalId = typeof document.
|
|
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.
|
|
161
|
+
const normalizedPath = normalizeReferenceValue(String(document.path || ""));
|
|
165
162
|
if (normalizedPath) {
|
|
166
163
|
targets.set(normalizedPath, canonicalId);
|
|
167
164
|
}
|
|
168
|
-
if (document.
|
|
169
|
-
targets.set(`${document.
|
|
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;
|