@roottale/cms-core 0.68.0 → 0.69.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/CHANGELOG.md +10 -0
- package/dist/index.cjs +433 -65
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4121 -104
- package/dist/index.d.ts +4121 -104
- package/dist/index.js +413 -65
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -225,6 +225,9 @@ function validateCollections(collections) {
|
|
|
225
225
|
}
|
|
226
226
|
|
|
227
227
|
// src/category-tree.ts
|
|
228
|
+
function isReservedCategoryTreeSlug(presentation, slug) {
|
|
229
|
+
return slug === presentation.categoryFallbackSlug || presentation.reservedCategorySlugs?.includes(slug) === true;
|
|
230
|
+
}
|
|
228
231
|
function basePathPrefix(basePath) {
|
|
229
232
|
if (!basePath.trim()) return null;
|
|
230
233
|
return basePath.replace(/\/+$/, "");
|
|
@@ -253,6 +256,7 @@ function hasChildCategory(termId, terms) {
|
|
|
253
256
|
function resolveCategoryTreeChain(presentation, termId, terms) {
|
|
254
257
|
const chain = categoryTreeAncestry(presentation.categoryDepth, termId, terms);
|
|
255
258
|
if (!chain) return null;
|
|
259
|
+
if (chain.some((term) => isReservedCategoryTreeSlug(presentation, term.slug))) return null;
|
|
256
260
|
if (presentation.entryCategory === "leaf") return chain;
|
|
257
261
|
if (hasChildCategory(termId, terms)) return null;
|
|
258
262
|
return chain.length === presentation.categoryDepth ? chain : null;
|
|
@@ -261,6 +265,33 @@ function resolveCategoryTreeSelection(presentation, termId, terms) {
|
|
|
261
265
|
if (hasChildCategory(termId, terms)) return null;
|
|
262
266
|
return resolveCategoryTreeChain(presentation, termId, terms);
|
|
263
267
|
}
|
|
268
|
+
function resolveCategoryTreeEntryCategory(presentation, entry, terms) {
|
|
269
|
+
const termsById = new Map(terms.map((term) => [term.id, term]));
|
|
270
|
+
const first = presentation.categorySelection === "first";
|
|
271
|
+
const categoryIds = [...new Set(entry.terms.filter((term) => term.taxonomy === "category" && (first || termsById.has(term.id))).map((term) => term.id))];
|
|
272
|
+
if (first) {
|
|
273
|
+
if (termsById.size !== terms.length) return null;
|
|
274
|
+
if (categoryIds.length === 0) {
|
|
275
|
+
const slug = presentation.categoryFallbackSlug;
|
|
276
|
+
if (presentation.categoryDepth !== 1 || !slug || presentation.reservedCategorySlugs?.includes(slug) || terms.some((term) => term.slug === slug)) return null;
|
|
277
|
+
return { kind: "fallback", slug };
|
|
278
|
+
}
|
|
279
|
+
const candidates = [];
|
|
280
|
+
for (const id of categoryIds) {
|
|
281
|
+
const category2 = termsById.get(id);
|
|
282
|
+
if (!category2 || !Number.isInteger(category2.termOrder ?? 0)) return null;
|
|
283
|
+
const chain2 = resolveCategoryTreeChain(presentation, id, terms);
|
|
284
|
+
if (!chain2) return null;
|
|
285
|
+
candidates.push({ category: category2, chain: chain2 });
|
|
286
|
+
}
|
|
287
|
+
candidates.sort((a, b) => (a.category.termOrder ?? 0) - (b.category.termOrder ?? 0) || (a.category.id < b.category.id ? -1 : a.category.id > b.category.id ? 1 : 0));
|
|
288
|
+
return { kind: "category", ...candidates[0] };
|
|
289
|
+
}
|
|
290
|
+
if (categoryIds.length !== 1) return null;
|
|
291
|
+
const category = termsById.get(categoryIds[0]);
|
|
292
|
+
const chain = resolveCategoryTreeChain(presentation, category.id, terms);
|
|
293
|
+
return chain ? { kind: "category", category, chain } : null;
|
|
294
|
+
}
|
|
264
295
|
function resolveCategoryTreeHubPaths(presentation, termId, terms) {
|
|
265
296
|
const chain = resolveCategoryTreeChain(presentation, termId, terms);
|
|
266
297
|
const prefix = basePathPrefix(presentation.basePath);
|
|
@@ -275,20 +306,11 @@ function resolveCategoryTreeHubPaths(presentation, termId, terms) {
|
|
|
275
306
|
}
|
|
276
307
|
function resolveCategoryTreeEntryPath(presentation, entry, terms) {
|
|
277
308
|
if (!entry.slug) return null;
|
|
278
|
-
const
|
|
279
|
-
const
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
];
|
|
284
|
-
if (categoryIds.length !== 1) return null;
|
|
285
|
-
const hubs = resolveCategoryTreeHubPaths(
|
|
286
|
-
presentation,
|
|
287
|
-
categoryIds[0],
|
|
288
|
-
terms
|
|
289
|
-
);
|
|
290
|
-
const leafPath = hubs?.at(-1);
|
|
291
|
-
return leafPath ? `${leafPath}/${encodeURIComponent(entry.slug)}` : null;
|
|
309
|
+
const selected = resolveCategoryTreeEntryCategory(presentation, entry, terms);
|
|
310
|
+
const prefix = basePathPrefix(presentation.basePath);
|
|
311
|
+
if (!selected || prefix === null) return null;
|
|
312
|
+
const slugs = selected.kind === "fallback" ? [selected.slug] : selected.chain.map((term) => term.slug);
|
|
313
|
+
return `${prefix}/${[...slugs, entry.slug].map(encodeURIComponent).join("/")}`;
|
|
292
314
|
}
|
|
293
315
|
|
|
294
316
|
// src/public-path.ts
|
|
@@ -973,10 +995,10 @@ function scopeImportedCss(css, scope, allowedAssetUrls = []) {
|
|
|
973
995
|
|
|
974
996
|
// src/internal-content-links.ts
|
|
975
997
|
var INTERNAL_CONTENT_LINK_TOKEN_SOURCE = String.raw`\[\[internal:([a-z0-9가-힣]+(?:[.-][a-z0-9가-힣]+)*)\|([^\]\n|]+)\]\]`;
|
|
976
|
-
function findInternalContentLinkTokens(
|
|
998
|
+
function findInternalContentLinkTokens(text3) {
|
|
977
999
|
const pattern = new RegExp(INTERNAL_CONTENT_LINK_TOKEN_SOURCE, "giu");
|
|
978
1000
|
const tokens = [];
|
|
979
|
-
for (const match of
|
|
1001
|
+
for (const match of text3.matchAll(pattern)) {
|
|
980
1002
|
const [raw, key, label] = match;
|
|
981
1003
|
if (!key || !label || match.index === void 0) continue;
|
|
982
1004
|
tokens.push({
|
|
@@ -1035,19 +1057,19 @@ function escapeHtmlText(value) {
|
|
|
1035
1057
|
var INTERNAL_CONTENT_LINK_CLASS = "rt-internal-link";
|
|
1036
1058
|
var INTERNAL_CONTENT_LINK_PENDING_CLASS = "rt-internal-link--pending";
|
|
1037
1059
|
var INTERNAL_CONTENT_LINK_PENDING_ATTR = "data-rt-internal-link-pending";
|
|
1038
|
-
function renderInternalContentLinksInText(
|
|
1039
|
-
const tokens = findInternalContentLinkTokens(
|
|
1040
|
-
if (tokens.length === 0) return { html:
|
|
1060
|
+
function renderInternalContentLinksInText(text3, resolve) {
|
|
1061
|
+
const tokens = findInternalContentLinkTokens(text3);
|
|
1062
|
+
if (tokens.length === 0) return { html: text3, hasTokens: false };
|
|
1041
1063
|
let cursor = 0;
|
|
1042
1064
|
let html = "";
|
|
1043
1065
|
for (const token of tokens) {
|
|
1044
|
-
html +=
|
|
1066
|
+
html += text3.slice(cursor, token.index);
|
|
1045
1067
|
const path = resolve(token.key);
|
|
1046
1068
|
const label = escapeHtmlText(token.label);
|
|
1047
1069
|
html += isSafeInternalPath(path) ? `<a href="${escapeHtmlText(path)}" class="${INTERNAL_CONTENT_LINK_CLASS}">${label}</a>` : `<span class="${INTERNAL_CONTENT_LINK_CLASS} ${INTERNAL_CONTENT_LINK_PENDING_CLASS}" ${INTERNAL_CONTENT_LINK_PENDING_ATTR}="${escapeHtmlText(token.key)}">${label}</span>`;
|
|
1048
1070
|
cursor = token.index + token.raw.length;
|
|
1049
1071
|
}
|
|
1050
|
-
html +=
|
|
1072
|
+
html += text3.slice(cursor);
|
|
1051
1073
|
return { html, hasTokens: true };
|
|
1052
1074
|
}
|
|
1053
1075
|
var INTERNAL_LINK_BLOCKED_HTML_TAGS = /* @__PURE__ */ new Set([
|
|
@@ -1081,12 +1103,12 @@ function renderInternalContentLinksInHtml(html, resolve) {
|
|
|
1081
1103
|
let cursor = 0;
|
|
1082
1104
|
let out = "";
|
|
1083
1105
|
let hasTokens = false;
|
|
1084
|
-
const emitText = (
|
|
1085
|
-
if (blockedDepth > 0 || !
|
|
1086
|
-
out +=
|
|
1106
|
+
const emitText = (text3) => {
|
|
1107
|
+
if (blockedDepth > 0 || !text3) {
|
|
1108
|
+
out += text3;
|
|
1087
1109
|
return;
|
|
1088
1110
|
}
|
|
1089
|
-
const rendered = renderInternalContentLinksInText(
|
|
1111
|
+
const rendered = renderInternalContentLinksInText(text3, resolve);
|
|
1090
1112
|
hasTokens ||= rendered.hasTokens;
|
|
1091
1113
|
out += rendered.html;
|
|
1092
1114
|
};
|
|
@@ -1806,13 +1828,13 @@ function findSectionLockViolations(beforeDoc, afterDoc) {
|
|
|
1806
1828
|
...Object.keys(next.attrs)
|
|
1807
1829
|
]);
|
|
1808
1830
|
fields.delete("props");
|
|
1809
|
-
for (const
|
|
1810
|
-
if (!valueEquals(section.attrs[
|
|
1831
|
+
for (const field3 of fields) {
|
|
1832
|
+
if (!valueEquals(section.attrs[field3], next.attrs[field3])) {
|
|
1811
1833
|
violations.push({
|
|
1812
|
-
code:
|
|
1834
|
+
code: field3 === "lock" ? "locked_section_lock_changed" : "locked_section_structure_changed",
|
|
1813
1835
|
sectionId: section.id,
|
|
1814
1836
|
sectionType: section.sectionType,
|
|
1815
|
-
field
|
|
1837
|
+
field: field3
|
|
1816
1838
|
});
|
|
1817
1839
|
}
|
|
1818
1840
|
}
|
|
@@ -1870,11 +1892,11 @@ function imageUrl(value, base) {
|
|
|
1870
1892
|
function resolveMetadata(input) {
|
|
1871
1893
|
const layers = [input.overrides, input.defaults, input.siteDefaults, input.fallback];
|
|
1872
1894
|
const result = {};
|
|
1873
|
-
for (const
|
|
1895
|
+
for (const field3 of ["title", "description", "ogImageAlt"]) {
|
|
1874
1896
|
for (const layer of layers) {
|
|
1875
|
-
const value = text(layer?.[
|
|
1897
|
+
const value = text(layer?.[field3]);
|
|
1876
1898
|
if (value !== void 0) {
|
|
1877
|
-
result[
|
|
1899
|
+
result[field3] = value;
|
|
1878
1900
|
break;
|
|
1879
1901
|
}
|
|
1880
1902
|
}
|
|
@@ -1886,11 +1908,11 @@ function resolveMetadata(input) {
|
|
|
1886
1908
|
break;
|
|
1887
1909
|
}
|
|
1888
1910
|
}
|
|
1889
|
-
for (const
|
|
1911
|
+
for (const field3 of ["noindex", "nofollow"]) {
|
|
1890
1912
|
for (const layer of layers) {
|
|
1891
|
-
const value = layer?.[
|
|
1913
|
+
const value = layer?.[field3];
|
|
1892
1914
|
if (typeof value === "boolean") {
|
|
1893
|
-
result[
|
|
1915
|
+
result[field3] = value;
|
|
1894
1916
|
break;
|
|
1895
1917
|
}
|
|
1896
1918
|
}
|
|
@@ -1937,24 +1959,26 @@ var MetadataPatchSchema = z2.object({
|
|
|
1937
1959
|
}).strict();
|
|
1938
1960
|
var MetadataRobotsPolicySchema = z2.object({
|
|
1939
1961
|
forceNoindex: z2.boolean().optional(),
|
|
1940
|
-
forceNofollow: z2.boolean().optional()
|
|
1962
|
+
forceNofollow: z2.boolean().optional(),
|
|
1963
|
+
/** Stable policy reason codes. They explain an enforced exclusion, never relax it. */
|
|
1964
|
+
noindexReasons: z2.array(z2.string().trim().min(1).max(200)).max(64).optional()
|
|
1941
1965
|
}).strict();
|
|
1942
1966
|
function readMetadataValues(input) {
|
|
1943
1967
|
if (!input || typeof input !== "object" || Array.isArray(input)) return {};
|
|
1944
|
-
const
|
|
1968
|
+
const record2 = input;
|
|
1945
1969
|
const output = {};
|
|
1946
|
-
for (const
|
|
1947
|
-
const parsed = metadataFields[
|
|
1948
|
-
if (parsed.success) output[
|
|
1970
|
+
for (const field3 of METADATA_FIELDS) {
|
|
1971
|
+
const parsed = metadataFields[field3].safeParse(record2[field3]);
|
|
1972
|
+
if (parsed.success) output[field3] = parsed.data;
|
|
1949
1973
|
}
|
|
1950
1974
|
return MetadataValuesSchema.parse(output);
|
|
1951
1975
|
}
|
|
1952
1976
|
function mergeMetadataPatch(current, input) {
|
|
1953
1977
|
const patch = MetadataPatchSchema.parse(input);
|
|
1954
1978
|
const next = { ...current };
|
|
1955
|
-
for (const
|
|
1956
|
-
if (patch[
|
|
1957
|
-
else if (patch[
|
|
1979
|
+
for (const field3 of METADATA_FIELDS) {
|
|
1980
|
+
if (patch[field3] === null) delete next[field3];
|
|
1981
|
+
else if (patch[field3] !== void 0) next[field3] = patch[field3];
|
|
1958
1982
|
}
|
|
1959
1983
|
return next;
|
|
1960
1984
|
}
|
|
@@ -1991,13 +2015,13 @@ var MetadataTextRuleSchema = z3.object({
|
|
|
1991
2015
|
fallbackBySlug: z3.record(z3.string().max(200), fallbackText).optional()
|
|
1992
2016
|
}).strict().refine((rule) => !rule.ellipsis || rule.maxLength !== void 0 && rule.ellipsis.length < rule.maxLength, "\uB9D0\uC904\uC784\uD45C\uC5D0\uB294 \uB354 \uAE34 \uCD5C\uB300 \uAE38\uC774\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4").refine((rule) => rule.source === "template" ? rule.template !== void 0 : rule.template === void 0, "\uD14D\uC2A4\uD2B8 template\uC740 source: template\uACFC \uD568\uAED8 \uC120\uC5B8\uD569\uB2C8\uB2E4").refine((rule) => !rule.wordBoundaryMinLength || Boolean(rule.maxLength && rule.wordBoundaryMinLength < rule.maxLength), "\uB2E8\uC5B4 \uACBD\uACC4\uC5D0\uB294 \uB354 \uAE34 \uCD5C\uB300 \uAE38\uC774\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4");
|
|
1993
2017
|
function cleanMetadataText(value, rule) {
|
|
1994
|
-
let
|
|
1995
|
-
if (rule?.stripLeadingLabel === "question")
|
|
1996
|
-
if (rule?.stripLeadingLabel === "answer")
|
|
1997
|
-
if (rule?.replaceLineBreakTokens)
|
|
1998
|
-
if (rule?.removeEmDashes)
|
|
1999
|
-
if (rule?.normalizeWhitespace)
|
|
2000
|
-
return
|
|
2018
|
+
let text3 = value.trim();
|
|
2019
|
+
if (rule?.stripLeadingLabel === "question") text3 = text3.replace(/^\s*Q[..:]?\s*/iu, "");
|
|
2020
|
+
if (rule?.stripLeadingLabel === "answer") text3 = text3.replace(/^\s*A[..:]?\s*/iu, "");
|
|
2021
|
+
if (rule?.replaceLineBreakTokens) text3 = text3.replace(/\[\[br(?::[^\]]*)?\]\]/giu, " ");
|
|
2022
|
+
if (rule?.removeEmDashes) text3 = text3.replaceAll("\u2014", "");
|
|
2023
|
+
if (rule?.normalizeWhitespace) text3 = text3.replace(/\s+/gu, " ");
|
|
2024
|
+
return text3.trim();
|
|
2001
2025
|
}
|
|
2002
2026
|
function metadataBodyParagraphs(value) {
|
|
2003
2027
|
const paragraphs = [];
|
|
@@ -2015,37 +2039,37 @@ function metadataBodyParagraphs(value) {
|
|
|
2015
2039
|
if (!node || typeof node !== "object") return;
|
|
2016
2040
|
const item = node;
|
|
2017
2041
|
if (item.type === "paragraph") {
|
|
2018
|
-
const
|
|
2019
|
-
if (
|
|
2042
|
+
const text3 = textParts(item.content).join(" ").replace(/\s+/gu, " ").trim();
|
|
2043
|
+
if (text3) paragraphs.push(text3);
|
|
2020
2044
|
} else walk(item.content);
|
|
2021
2045
|
};
|
|
2022
2046
|
walk(value);
|
|
2023
2047
|
return paragraphs;
|
|
2024
2048
|
}
|
|
2025
2049
|
function formatMetadataText(value, rule, input, name) {
|
|
2026
|
-
let
|
|
2050
|
+
let text3 = rule?.source === "fallback" ? "" : cleanMetadataText(value ?? "", rule);
|
|
2027
2051
|
if (rule?.source === "template") {
|
|
2028
|
-
|
|
2052
|
+
text3 = cleanMetadataText((rule.template ?? "").replace(templateToken, (token) => {
|
|
2029
2053
|
if (token === "{{title}}") return name;
|
|
2030
|
-
const
|
|
2031
|
-
const value2 = Object.hasOwn(input.fields ?? {},
|
|
2054
|
+
const field3 = token.slice(8, -2);
|
|
2055
|
+
const value2 = Object.hasOwn(input.fields ?? {}, field3) ? input.fields?.[field3] : void 0;
|
|
2032
2056
|
return typeof value2 === "string" ? value2.trim() : "";
|
|
2033
2057
|
}), rule);
|
|
2034
2058
|
}
|
|
2035
|
-
if (!
|
|
2059
|
+
if (!text3 && rule?.bodyFallback) {
|
|
2036
2060
|
const paragraphs = input.bodyParagraphs ?? [];
|
|
2037
2061
|
const preferred = rule.bodyFallback.preferContaining;
|
|
2038
|
-
|
|
2039
|
-
if (rule.bodyFallback.stripLeadingLabel === "conclusion")
|
|
2040
|
-
|
|
2062
|
+
text3 = (preferred ? paragraphs.find((paragraph) => paragraph.includes(preferred)) : void 0) ?? paragraphs[0] ?? "";
|
|
2063
|
+
if (rule.bodyFallback.stripLeadingLabel === "conclusion") text3 = text3.replace(/^(먼저\s*)?결론부터\s*(말씀드리면|말씀드릴게요)[.!?]?\s*/u, "");
|
|
2064
|
+
text3 = cleanMetadataText(text3, rule);
|
|
2041
2065
|
}
|
|
2042
|
-
if (!
|
|
2066
|
+
if (!text3) {
|
|
2043
2067
|
const slugFallback = input.slug && Object.hasOwn(rule?.fallbackBySlug ?? {}, input.slug) ? rule?.fallbackBySlug?.[input.slug] : void 0;
|
|
2044
2068
|
const fallback = input.parentId ? rule?.fallbackWithParent ?? rule?.fallback : slugFallback ?? rule?.fallbackWithoutParent ?? rule?.fallback;
|
|
2045
|
-
|
|
2069
|
+
text3 = cleanMetadataText((fallback ?? "").replaceAll("%s", () => name), rule);
|
|
2046
2070
|
}
|
|
2047
|
-
if (!
|
|
2048
|
-
const units = rule?.lengthUnit === "code-points" ? [...
|
|
2071
|
+
if (!text3) return void 0;
|
|
2072
|
+
const units = rule?.lengthUnit === "code-points" ? [...text3] : text3.split("");
|
|
2049
2073
|
if (rule?.maxLength && units.length > rule.maxLength) {
|
|
2050
2074
|
const suffix = rule.ellipsis ?? "";
|
|
2051
2075
|
const suffixLength = rule.lengthUnit === "code-points" ? [...suffix].length : suffix.length;
|
|
@@ -2054,7 +2078,7 @@ function formatMetadataText(value, rule, input, name) {
|
|
|
2054
2078
|
if (rule.wordBoundaryMinLength && boundary >= rule.wordBoundaryMinLength) head = head.slice(0, boundary);
|
|
2055
2079
|
return suffix ? head.trimEnd() + suffix : head;
|
|
2056
2080
|
}
|
|
2057
|
-
return
|
|
2081
|
+
return text3;
|
|
2058
2082
|
}
|
|
2059
2083
|
|
|
2060
2084
|
// src/metadata-defaults.ts
|
|
@@ -2140,6 +2164,310 @@ function formatMetadataDefaults(input, declaration) {
|
|
|
2140
2164
|
};
|
|
2141
2165
|
}
|
|
2142
2166
|
|
|
2167
|
+
// src/post-body-source.ts
|
|
2168
|
+
function resolvePostBodySource(bodyHtml) {
|
|
2169
|
+
if (typeof bodyHtml !== "string") return { kind: "json" };
|
|
2170
|
+
const trimmed = bodyHtml.trim();
|
|
2171
|
+
if (trimmed.length === 0) return { kind: "json" };
|
|
2172
|
+
const sanitized = sanitizeHtml(trimmed);
|
|
2173
|
+
const visible = /<(?:img|hr|br|iframe|video|audio)\b/i.test(sanitized) || sanitized.replace(/<[^>]*>/g, "").replace(/ /gi, " ").trim().length > 0;
|
|
2174
|
+
return visible ? { kind: "html", html: sanitized } : { kind: "json" };
|
|
2175
|
+
}
|
|
2176
|
+
|
|
2177
|
+
// src/content-policy-schema.ts
|
|
2178
|
+
import { z as z5 } from "zod";
|
|
2179
|
+
var ContentPolicyFieldNameSchema = z5.string().min(1).max(64).regex(/^[A-Za-z_][A-Za-z0-9_]*$/).refine((name) => name !== "prototype" && !Object.hasOwn(Object.prototype, name));
|
|
2180
|
+
var ContentPolicyScalarSchema = z5.union([
|
|
2181
|
+
z5.string().max(2048),
|
|
2182
|
+
z5.number().finite(),
|
|
2183
|
+
z5.boolean(),
|
|
2184
|
+
z5.null()
|
|
2185
|
+
]);
|
|
2186
|
+
var ContentPolicyWhenSchema = z5.object({
|
|
2187
|
+
field: ContentPolicyFieldNameSchema,
|
|
2188
|
+
equals: ContentPolicyScalarSchema
|
|
2189
|
+
}).strict();
|
|
2190
|
+
var ContentPolicyInternalPathSchema = z5.string().min(1).max(2048).refine((path) => {
|
|
2191
|
+
if (!path.startsWith("/") || path.startsWith("//") || /[\\?#\u0000-\u0020\u007f]/u.test(path)) return false;
|
|
2192
|
+
try {
|
|
2193
|
+
return path.split("/").every((part, index) => {
|
|
2194
|
+
if (index === 0) return true;
|
|
2195
|
+
const decoded2 = decodeURIComponent(part);
|
|
2196
|
+
return decoded2 !== "." && decoded2 !== ".." && !/[\\/\u0000-\u0020\u007f]/u.test(decoded2);
|
|
2197
|
+
});
|
|
2198
|
+
} catch {
|
|
2199
|
+
return false;
|
|
2200
|
+
}
|
|
2201
|
+
}, "\uCF58\uD150\uCE20 \uACBD\uB85C\uB294 \uC548\uC804\uD55C \uC0AC\uC774\uD2B8 \uB0B4\uBD80 \uACBD\uB85C\uC5EC\uC57C \uD569\uB2C8\uB2E4");
|
|
2202
|
+
var field = { field: ContentPolicyFieldNameSchema };
|
|
2203
|
+
var scalarChecks = [
|
|
2204
|
+
z5.object({ ...field, op: z5.literal("equals"), value: ContentPolicyScalarSchema }).strict(),
|
|
2205
|
+
z5.object({ ...field, op: z5.literal("notEquals"), value: ContentPolicyScalarSchema }).strict(),
|
|
2206
|
+
z5.object({ ...field, op: z5.literal("oneOf"), values: z5.array(ContentPolicyScalarSchema).min(1).max(16), allowEmpty: z5.boolean().optional() }).strict(),
|
|
2207
|
+
z5.object({ ...field, op: z5.literal("text") }).strict(),
|
|
2208
|
+
z5.object({ ...field, op: z5.literal("date") }).strict(),
|
|
2209
|
+
z5.object({ ...field, op: z5.literal("safeHttps") }).strict(),
|
|
2210
|
+
z5.object({ ...field, op: z5.literal("allowedPath"), paths: z5.array(ContentPolicyInternalPathSchema).min(1).max(512) }).strict()
|
|
2211
|
+
];
|
|
2212
|
+
var ContentPolicyScalarCheckSchema = z5.discriminatedUnion("op", scalarChecks);
|
|
2213
|
+
var when = { when: ContentPolicyWhenSchema.optional() };
|
|
2214
|
+
var ContentPolicyCheckSchema = z5.discriminatedUnion("op", [
|
|
2215
|
+
scalarChecks[0].extend(when),
|
|
2216
|
+
scalarChecks[1].extend(when),
|
|
2217
|
+
scalarChecks[2].extend(when),
|
|
2218
|
+
scalarChecks[3].extend(when),
|
|
2219
|
+
scalarChecks[4].extend(when),
|
|
2220
|
+
scalarChecks[5].extend(when),
|
|
2221
|
+
scalarChecks[6].extend(when),
|
|
2222
|
+
z5.object({ ...field, ...when, op: z5.literal("some"), checks: z5.array(ContentPolicyScalarCheckSchema).min(1).max(8) }).strict(),
|
|
2223
|
+
z5.object({
|
|
2224
|
+
...when,
|
|
2225
|
+
op: z5.literal("bodyContent"),
|
|
2226
|
+
allowExcerpt: z5.boolean().optional(),
|
|
2227
|
+
allowFeaturedImage: z5.boolean().optional(),
|
|
2228
|
+
additionalUrl: z5.object({ ...field, ...when }).strict().optional()
|
|
2229
|
+
}).strict(),
|
|
2230
|
+
z5.object({ ...when, op: z5.literal("bodyText") }).strict()
|
|
2231
|
+
]);
|
|
2232
|
+
var contentPolicy = z5.object({
|
|
2233
|
+
visibility: z5.array(ContentPolicyCheckSchema).max(24),
|
|
2234
|
+
indexing: z5.array(ContentPolicyCheckSchema).max(24),
|
|
2235
|
+
pathSegmentFormat: z5.literal("lower-kebab").optional()
|
|
2236
|
+
}).strict();
|
|
2237
|
+
var ContentPolicySchema = contentPolicy.superRefine((policy, ctx) => {
|
|
2238
|
+
const refs = collectContentPolicyFieldRefs(policy);
|
|
2239
|
+
if (refs.length > 64) ctx.addIssue({ code: z5.ZodIssueCode.custom, message: "\uCF58\uD150\uCE20 \uC815\uCC45\uC740 \uACF5\uAC1C \uD544\uB4DC 64\uAC1C\uAE4C\uC9C0 \uCC38\uC870\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4" });
|
|
2240
|
+
for (const ref of refs) {
|
|
2241
|
+
if ((ref.children?.length ?? 0) > 8) ctx.addIssue({ code: z5.ZodIssueCode.custom, message: `\uBC18\uBCF5 \uD544\uB4DC ${ref.name}\uC758 \uC790\uC2DD \uCC38\uC870\uB294 8\uAC1C\uAE4C\uC9C0 \uD5C8\uC6A9\uD569\uB2C8\uB2E4` });
|
|
2242
|
+
}
|
|
2243
|
+
});
|
|
2244
|
+
function collectContentPolicyFieldRefs(policy) {
|
|
2245
|
+
const refs = /* @__PURE__ */ new Map();
|
|
2246
|
+
const add = (name, children = []) => {
|
|
2247
|
+
const names = refs.get(name) ?? /* @__PURE__ */ new Set();
|
|
2248
|
+
for (const child of children) names.add(child);
|
|
2249
|
+
refs.set(name, names);
|
|
2250
|
+
};
|
|
2251
|
+
for (const check of [...policy.visibility, ...policy.indexing]) {
|
|
2252
|
+
if (check.when) add(check.when.field);
|
|
2253
|
+
if ("field" in check) add(check.field, check.op === "some" ? check.checks.map((child) => child.field) : []);
|
|
2254
|
+
if (check.op === "bodyContent" && check.additionalUrl) {
|
|
2255
|
+
add(check.additionalUrl.field);
|
|
2256
|
+
if (check.additionalUrl.when) add(check.additionalUrl.when.field);
|
|
2257
|
+
}
|
|
2258
|
+
}
|
|
2259
|
+
return [...refs].map(([name, children]) => ({ name, ...children.size ? { children: [...children] } : {} }));
|
|
2260
|
+
}
|
|
2261
|
+
|
|
2262
|
+
// src/content-policy.ts
|
|
2263
|
+
var text2 = (value) => typeof value === "string" ? value.trim() : "";
|
|
2264
|
+
var record = (value) => value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
2265
|
+
function fieldValue(fields, field3) {
|
|
2266
|
+
return Object.hasOwn(fields, field3) ? fields[field3] : void 0;
|
|
2267
|
+
}
|
|
2268
|
+
function contentPolicySafeHttpsUrl(value) {
|
|
2269
|
+
if (typeof value !== "string" || /[\\\u0000-\u0020\u007f]/u.test(value)) return null;
|
|
2270
|
+
try {
|
|
2271
|
+
const url = new URL(value);
|
|
2272
|
+
return url.protocol === "https:" && !url.username && !url.password ? url.href : null;
|
|
2273
|
+
} catch {
|
|
2274
|
+
return null;
|
|
2275
|
+
}
|
|
2276
|
+
}
|
|
2277
|
+
function dateValue(value) {
|
|
2278
|
+
if (!value || typeof value !== "string" && typeof value !== "number") return null;
|
|
2279
|
+
if (!Number.isFinite(Date.parse(String(value)))) return null;
|
|
2280
|
+
const date = new Date(value).getTime();
|
|
2281
|
+
return Number.isFinite(date) ? date : null;
|
|
2282
|
+
}
|
|
2283
|
+
function contentPolicyBodyText(bodyJson) {
|
|
2284
|
+
const root = record(bodyJson);
|
|
2285
|
+
if (root?.type !== "doc" || !Array.isArray(root.content)) return "";
|
|
2286
|
+
const pending = [{ node: root, depth: 0 }];
|
|
2287
|
+
const output = [];
|
|
2288
|
+
let count = 0;
|
|
2289
|
+
while (pending.length) {
|
|
2290
|
+
const entry = pending.pop();
|
|
2291
|
+
if (++count > 1e5 || entry.depth > 128) throw new Error("Content policy body exceeds traversal limits");
|
|
2292
|
+
const node = record(entry.node);
|
|
2293
|
+
if (!node) continue;
|
|
2294
|
+
if (node.type === "text") {
|
|
2295
|
+
const value = text2(node.text);
|
|
2296
|
+
if (value) output.push(value);
|
|
2297
|
+
} else if (Array.isArray(node.content)) {
|
|
2298
|
+
for (let index = node.content.length - 1; index >= 0; index--) {
|
|
2299
|
+
pending.push({ node: node.content[index], depth: entry.depth + 1 });
|
|
2300
|
+
}
|
|
2301
|
+
}
|
|
2302
|
+
}
|
|
2303
|
+
return output.join(" ");
|
|
2304
|
+
}
|
|
2305
|
+
function matchesContentScalarCheck(check, fields) {
|
|
2306
|
+
const value = fieldValue(fields, check.field);
|
|
2307
|
+
switch (check.op) {
|
|
2308
|
+
case "equals":
|
|
2309
|
+
return value === check.value;
|
|
2310
|
+
case "notEquals":
|
|
2311
|
+
return value !== check.value;
|
|
2312
|
+
case "oneOf":
|
|
2313
|
+
return check.allowEmpty === true && !value || check.values.some((candidate) => candidate === value);
|
|
2314
|
+
case "text":
|
|
2315
|
+
return text2(value).length > 0;
|
|
2316
|
+
case "date":
|
|
2317
|
+
return dateValue(value) !== null;
|
|
2318
|
+
case "safeHttps":
|
|
2319
|
+
return contentPolicySafeHttpsUrl(value) !== null;
|
|
2320
|
+
case "allowedPath":
|
|
2321
|
+
return check.paths.includes(text2(value));
|
|
2322
|
+
}
|
|
2323
|
+
}
|
|
2324
|
+
function matchesWhen(when2, fields) {
|
|
2325
|
+
return !when2 || fieldValue(fields, when2.field) === when2.equals;
|
|
2326
|
+
}
|
|
2327
|
+
function bodyFacts(post) {
|
|
2328
|
+
const jsonText = contentPolicyBodyText(post.bodyJson);
|
|
2329
|
+
const source = resolvePostBodySource(post.bodyHtml);
|
|
2330
|
+
const html = source.kind === "html" ? source.html : "";
|
|
2331
|
+
return { jsonText, html, text: source.kind === "html" ? html.replace(/<[^>]*>/g, "").replace(/ /gi, " ").trim() : jsonText };
|
|
2332
|
+
}
|
|
2333
|
+
function matchesCheck(check, post, fields, getBody) {
|
|
2334
|
+
if (!matchesWhen(check.when, fields)) return true;
|
|
2335
|
+
if (check.op === "some") {
|
|
2336
|
+
const value = fieldValue(fields, check.field);
|
|
2337
|
+
return Array.isArray(value) && value.some((item) => {
|
|
2338
|
+
const child = record(item);
|
|
2339
|
+
return child !== null && check.checks.every((scalar) => matchesContentScalarCheck(scalar, child));
|
|
2340
|
+
});
|
|
2341
|
+
}
|
|
2342
|
+
if (check.op === "bodyText") return Boolean(getBody().text);
|
|
2343
|
+
if (check.op === "bodyContent") {
|
|
2344
|
+
const body = getBody();
|
|
2345
|
+
return Boolean(body.jsonText || body.html || check.allowExcerpt && text2(post.excerpt) || check.allowFeaturedImage && contentPolicySafeHttpsUrl(post.featuredImageUrl) || check.additionalUrl && matchesWhen(check.additionalUrl.when, fields) && contentPolicySafeHttpsUrl(fieldValue(fields, check.additionalUrl.field)));
|
|
2346
|
+
}
|
|
2347
|
+
return matchesContentScalarCheck(check, fields);
|
|
2348
|
+
}
|
|
2349
|
+
function validPath(policy, post, options) {
|
|
2350
|
+
const path = text2(post.path).replace(/\/+$/, "");
|
|
2351
|
+
if (!path || !ContentPolicyInternalPathSchema.safeParse(path).success) return false;
|
|
2352
|
+
const segments = path.slice(1).split("/");
|
|
2353
|
+
if (segments.some((segment) => !segment)) return false;
|
|
2354
|
+
if (policy.pathSegmentFormat === "lower-kebab" && segments.some((segment) => !/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(segment))) return false;
|
|
2355
|
+
if (decodeURIComponent(segments.at(-1)) !== post.slug) return false;
|
|
2356
|
+
if (options.expectedPath !== void 0) {
|
|
2357
|
+
return options.expectedPath !== null && path === options.expectedPath.replace(/\/+$/, "");
|
|
2358
|
+
}
|
|
2359
|
+
return true;
|
|
2360
|
+
}
|
|
2361
|
+
function evaluateContentPolicy(policy, post, options = {}) {
|
|
2362
|
+
const visibility = [];
|
|
2363
|
+
if (post.status !== "published") visibility.push("content:not-published");
|
|
2364
|
+
if (!text2(post.id) || !text2(post.title) || !text2(post.slug)) visibility.push("content:invalid-identity");
|
|
2365
|
+
const publishedAt = dateValue(post.publishedAt);
|
|
2366
|
+
if (publishedAt === null) visibility.push("content:invalid-published-at");
|
|
2367
|
+
else if (publishedAt > (options.now ?? Date.now())) visibility.push("content:future-publication");
|
|
2368
|
+
if (!validPath(policy, post, options)) visibility.push("content:invalid-path");
|
|
2369
|
+
const fields = post.fields ?? {};
|
|
2370
|
+
let body;
|
|
2371
|
+
const getBody = () => body ??= bodyFacts(post);
|
|
2372
|
+
const failures = (checks, stage) => checks.flatMap((check) => matchesCheck(check, post, fields, getBody) ? [] : [`content:${stage}:${check.op}${"field" in check ? `:${check.field}` : ""}`]);
|
|
2373
|
+
if (visibility.length === 0) visibility.push(...failures(policy.visibility, "visibility"));
|
|
2374
|
+
const visible = visibility.length === 0;
|
|
2375
|
+
const indexing = visible ? failures(policy.indexing, "indexing") : [];
|
|
2376
|
+
const forced = !visible || indexing.length > 0 || options.robots?.forceNoindex === true;
|
|
2377
|
+
const inheritedReasons = options.robots?.forceNoindex ? options.robots.noindexReasons?.length ? options.robots.noindexReasons : ["content:forced-noindex"] : [];
|
|
2378
|
+
const noindexReasons = [.../* @__PURE__ */ new Set([...visibility, ...indexing, ...inheritedReasons])].slice(0, 64);
|
|
2379
|
+
const manualNoindex = readMetadataValues(post.metaJson?.seo).noindex === true;
|
|
2380
|
+
return {
|
|
2381
|
+
visible,
|
|
2382
|
+
indexable: visible && !forced && !manualNoindex,
|
|
2383
|
+
robots: {
|
|
2384
|
+
...options.robots,
|
|
2385
|
+
...forced ? { forceNoindex: true } : {},
|
|
2386
|
+
...noindexReasons.length ? { noindexReasons } : {}
|
|
2387
|
+
},
|
|
2388
|
+
reasons: [...noindexReasons, ...manualNoindex ? ["content:manual-noindex"] : []]
|
|
2389
|
+
};
|
|
2390
|
+
}
|
|
2391
|
+
|
|
2392
|
+
// src/content-query.ts
|
|
2393
|
+
import { z as z6 } from "zod";
|
|
2394
|
+
var field2 = { field: ContentPolicyFieldNameSchema };
|
|
2395
|
+
var ContentQueryFilterSchema = z6.discriminatedUnion("op", [
|
|
2396
|
+
z6.object({ ...field2, op: z6.literal("equals"), value: ContentPolicyScalarSchema }).strict(),
|
|
2397
|
+
z6.object({ ...field2, op: z6.literal("notEquals"), value: ContentPolicyScalarSchema }).strict(),
|
|
2398
|
+
z6.object({ ...field2, op: z6.literal("oneOf"), values: z6.array(ContentPolicyScalarSchema).min(1).max(16), allowEmpty: z6.boolean().optional() }).strict()
|
|
2399
|
+
]);
|
|
2400
|
+
var ContentQuerySchema = z6.object({
|
|
2401
|
+
modelKeys: z6.array(z6.string().min(1).max(100).regex(/^[a-z][a-z0-9_-]*$/)).min(1).max(16),
|
|
2402
|
+
filters: z6.array(ContentQueryFilterSchema).max(8).optional(),
|
|
2403
|
+
scope: z6.enum(["currentTerm", "currentPath"]).optional(),
|
|
2404
|
+
pinnedField: ContentPolicyFieldNameSchema.optional(),
|
|
2405
|
+
pageSize: z6.number().int().min(1).max(100),
|
|
2406
|
+
requireItems: z6.boolean().optional()
|
|
2407
|
+
}).strict();
|
|
2408
|
+
function collectContentQueryFieldRefs(query) {
|
|
2409
|
+
return [.../* @__PURE__ */ new Set([...(query.filters ?? []).map((filter) => filter.field), ...query.pinnedField ? [query.pinnedField] : []])].map((name) => ({ name }));
|
|
2410
|
+
}
|
|
2411
|
+
function matchesScope(query, post, options) {
|
|
2412
|
+
if (query.scope === "currentTerm") return post.terms?.some((term) => term.id === options.termId) === true;
|
|
2413
|
+
if (query.scope === "currentPath") {
|
|
2414
|
+
const path = options.path.replace(/\/+$/, "");
|
|
2415
|
+
return post.path?.trim().startsWith(`${path}/`) === true;
|
|
2416
|
+
}
|
|
2417
|
+
return true;
|
|
2418
|
+
}
|
|
2419
|
+
function evaluateContentQuery(query, candidates, options) {
|
|
2420
|
+
if (!options.complete) throw new Error("Content query input is incomplete");
|
|
2421
|
+
const page = options.page ?? 1;
|
|
2422
|
+
if (!Number.isSafeInteger(page) || page < 1) throw new Error("Content query page must be a positive safe integer");
|
|
2423
|
+
if (query.scope === "currentTerm" && !options.termId) throw new Error("Content query requires the current term id");
|
|
2424
|
+
if (query.scope === "currentPath" && (!options.path || !ContentPolicyInternalPathSchema.safeParse(options.path).success)) throw new Error("Content query requires a safe current path");
|
|
2425
|
+
const visible = candidates.filter(({ policy }) => policy.visible);
|
|
2426
|
+
const pathCounts = /* @__PURE__ */ new Map();
|
|
2427
|
+
const canonical = (post) => post.path?.trim().replace(/\/+$/, "") ?? null;
|
|
2428
|
+
for (const { post } of visible) pathCounts.set(canonical(post), (pathCounts.get(canonical(post)) ?? 0) + 1);
|
|
2429
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2430
|
+
const selected = visible.filter(({ post }) => {
|
|
2431
|
+
if (pathCounts.get(canonical(post)) !== 1 || seen.has(post.id)) return false;
|
|
2432
|
+
seen.add(post.id);
|
|
2433
|
+
return query.modelKeys.includes(post.modelKey ?? post.collectionKey ?? "") && matchesScope(query, post, options) && (query.filters ?? []).every((filter) => matchesContentScalarCheck(filter, post.fields ?? {}));
|
|
2434
|
+
}).sort((left, right) => {
|
|
2435
|
+
if (query.pinnedField) {
|
|
2436
|
+
const delta = Number(right.post.fields?.[query.pinnedField] === true) - Number(left.post.fields?.[query.pinnedField] === true);
|
|
2437
|
+
if (delta) return delta;
|
|
2438
|
+
}
|
|
2439
|
+
return Date.parse(right.post.publishedAt) - Date.parse(left.post.publishedAt) || left.post.id.localeCompare(right.post.id);
|
|
2440
|
+
});
|
|
2441
|
+
const pageCount = Math.max(1, Math.ceil(selected.length / query.pageSize));
|
|
2442
|
+
const current = selected.slice((page - 1) * query.pageSize, page * query.pageSize);
|
|
2443
|
+
const pageVisible = page <= pageCount && (!query.requireItems || selected.length > 0);
|
|
2444
|
+
const hasIndexable = current.some(({ policy }) => policy.indexable);
|
|
2445
|
+
const forced = !hasIndexable || options.robots?.forceNoindex === true;
|
|
2446
|
+
const reasons = [.../* @__PURE__ */ new Set([
|
|
2447
|
+
...page > pageCount ? ["content:invalid-page"] : query.requireItems && !selected.length ? ["content:empty-required-list"] : !current.length ? ["content:empty-page"] : !hasIndexable ? ["content:no-indexable-items"] : [],
|
|
2448
|
+
...options.robots?.forceNoindex ? options.robots.noindexReasons?.length ? options.robots.noindexReasons : ["content:forced-noindex"] : []
|
|
2449
|
+
])].slice(0, 64);
|
|
2450
|
+
return {
|
|
2451
|
+
items: current.map(({ post }) => post),
|
|
2452
|
+
total: selected.length,
|
|
2453
|
+
page,
|
|
2454
|
+
pageCount,
|
|
2455
|
+
visible: pageVisible,
|
|
2456
|
+
indexable: pageVisible && !forced,
|
|
2457
|
+
robots: { ...options.robots, ...forced ? { forceNoindex: true } : {}, ...reasons.length ? { noindexReasons: reasons } : {} },
|
|
2458
|
+
reasons
|
|
2459
|
+
};
|
|
2460
|
+
}
|
|
2461
|
+
function selectContentQuery(query, posts, policies, options) {
|
|
2462
|
+
if (!options.complete) throw new Error("Content query input is incomplete");
|
|
2463
|
+
const candidates = posts.filter((post) => query.modelKeys.includes(post.modelKey ?? post.collectionKey ?? "")).map((post) => {
|
|
2464
|
+
const modelKey = post.modelKey ?? post.collectionKey ?? "";
|
|
2465
|
+
if (!Object.hasOwn(policies, modelKey)) throw new Error(`Content query has no policy for model ${modelKey}`);
|
|
2466
|
+
return { post, policy: evaluateContentPolicy(policies[modelKey], post, { now: options.now, ...options.postOptions(post) }) };
|
|
2467
|
+
});
|
|
2468
|
+
return evaluateContentQuery(query, candidates, options);
|
|
2469
|
+
}
|
|
2470
|
+
|
|
2143
2471
|
// src/redirect-paths.ts
|
|
2144
2472
|
var CONTROL_OR_BACKSLASH = /[\\\u0000-\u001f\u007f]/u;
|
|
2145
2473
|
var PROTECTED_PREFIX = /^\/(?:api|_next|_vercel|auth|admin|login|logout|revalidate|preview|assets|static|images|fonts|\.well-known)(?:\/|$)/i;
|
|
@@ -2206,6 +2534,15 @@ export {
|
|
|
2206
2534
|
CMS_IMAGE_WIDTHS,
|
|
2207
2535
|
CMS_LINE_HEIGHTS,
|
|
2208
2536
|
CMS_PARAGRAPH_SPACINGS,
|
|
2537
|
+
ContentPolicyCheckSchema,
|
|
2538
|
+
ContentPolicyFieldNameSchema,
|
|
2539
|
+
ContentPolicyInternalPathSchema,
|
|
2540
|
+
ContentPolicyScalarCheckSchema,
|
|
2541
|
+
ContentPolicyScalarSchema,
|
|
2542
|
+
ContentPolicySchema,
|
|
2543
|
+
ContentPolicyWhenSchema,
|
|
2544
|
+
ContentQueryFilterSchema,
|
|
2545
|
+
ContentQuerySchema,
|
|
2209
2546
|
DEFAULT_SITE_PATTERN_PRESENTATION,
|
|
2210
2547
|
EMPTY_SITE_PATTERN_PLACEMENTS,
|
|
2211
2548
|
FIELD_BINDING_SOURCE,
|
|
@@ -2243,10 +2580,16 @@ export {
|
|
|
2243
2580
|
cmsImageWidth,
|
|
2244
2581
|
cmsLineHeight,
|
|
2245
2582
|
cmsParagraphSpacing,
|
|
2583
|
+
collectContentPolicyFieldRefs,
|
|
2584
|
+
collectContentQueryFieldRefs,
|
|
2585
|
+
contentPolicyBodyText,
|
|
2586
|
+
contentPolicySafeHttpsUrl,
|
|
2246
2587
|
createFieldBindingResolver,
|
|
2247
2588
|
emptyPatternSlots,
|
|
2248
2589
|
ensureImportedHtmlDocumentRoots,
|
|
2249
2590
|
ensureStableIds,
|
|
2591
|
+
evaluateContentPolicy,
|
|
2592
|
+
evaluateContentQuery,
|
|
2250
2593
|
extractFaqEntries,
|
|
2251
2594
|
faqPageJsonLd,
|
|
2252
2595
|
findInternalContentLinkTokens,
|
|
@@ -2263,11 +2606,13 @@ export {
|
|
|
2263
2606
|
isAllowedMapEmbedSrc,
|
|
2264
2607
|
isInCollectionMode,
|
|
2265
2608
|
isProtectedRedirectPath,
|
|
2609
|
+
isReservedCategoryTreeSlug,
|
|
2266
2610
|
isSectionContentPropPath,
|
|
2267
2611
|
isSitePatternSlotKey,
|
|
2268
2612
|
isValidBlock,
|
|
2269
2613
|
isValidSitePatternKey,
|
|
2270
2614
|
issuePreviewToken,
|
|
2615
|
+
matchesContentScalarCheck,
|
|
2271
2616
|
mergeMetadataPatch,
|
|
2272
2617
|
metadataBodyParagraphs,
|
|
2273
2618
|
metadataContentImageUrl,
|
|
@@ -2289,11 +2634,13 @@ export {
|
|
|
2289
2634
|
renderInternalContentLinksInText,
|
|
2290
2635
|
resolveCategoryPath,
|
|
2291
2636
|
resolveCategoryTreeChain,
|
|
2637
|
+
resolveCategoryTreeEntryCategory,
|
|
2292
2638
|
resolveCategoryTreeEntryPath,
|
|
2293
2639
|
resolveCategoryTreeHubPaths,
|
|
2294
2640
|
resolveCategoryTreeSelection,
|
|
2295
2641
|
resolveMetadata,
|
|
2296
2642
|
resolvePatternSlots,
|
|
2643
|
+
resolvePostBodySource,
|
|
2297
2644
|
resolvePostCategorySlug,
|
|
2298
2645
|
resolvePostCollection,
|
|
2299
2646
|
resolvePostPath,
|
|
@@ -2303,6 +2650,7 @@ export {
|
|
|
2303
2650
|
sanitizeImportedCss,
|
|
2304
2651
|
sanitizeImportedHtml,
|
|
2305
2652
|
scopeImportedCss,
|
|
2653
|
+
selectContentQuery,
|
|
2306
2654
|
serializeBlock,
|
|
2307
2655
|
setSitePatternPlacementRule,
|
|
2308
2656
|
sitePatternButtonColor,
|