@roottale/cms-core 0.55.0 → 0.56.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 +9 -0
- package/dist/index.cjs +25 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +49 -1
- package/dist/index.d.ts +49 -1
- package/dist/index.js +23 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -233,6 +233,54 @@ declare function resolveCategoryTreeHubPaths(presentation: CategoryTreePresentat
|
|
|
233
233
|
/** 계층형 분류 모델 항목의 정규 상세 주소. */
|
|
234
234
|
declare function resolveCategoryTreeEntryPath(presentation: CategoryTreePresentation, entry: CategoryTreeEntry, terms: readonly CategoryTreeTerm[]): string | null;
|
|
235
235
|
|
|
236
|
+
/**
|
|
237
|
+
* 글의 정규 공개 경로 — 플랫폼 전체가 쓰는 **하나의** 계산기 (ADR-0105 개정).
|
|
238
|
+
*
|
|
239
|
+
* 우선순위: 콘텐츠 모델 presentation(fixed_page → detail → category_tree) →
|
|
240
|
+
* 호환 컬렉션(`resolvePostPath`) → 레거시 `/blog/{slug}`. `data_only` 모델은
|
|
241
|
+
* 상세 주소가 없으므로 호환 컬렉션이 있으면 그 규칙을, 없으면 null 을 돌려준다.
|
|
242
|
+
*
|
|
243
|
+
* 이 값은 저장 시 `posts.public_path` 로 물질화되고(경로 이력 포함), 웹훅·사이트맵·
|
|
244
|
+
* 단축링크·내부 링크 키·리다이렉트는 저장된 값을 읽는다. 여기 말고 다른 곳에서
|
|
245
|
+
* 주소를 다시 계산하지 않는다.
|
|
246
|
+
*/
|
|
247
|
+
|
|
248
|
+
/** 콘텐츠 모델 presentation 의 구조적 최소형(cms-models 스키마와 호환). */
|
|
249
|
+
type PublicPathPresentation = {
|
|
250
|
+
readonly kind: "data_only";
|
|
251
|
+
} | {
|
|
252
|
+
readonly kind: "fixed_page";
|
|
253
|
+
readonly path: string;
|
|
254
|
+
} | {
|
|
255
|
+
readonly kind: "detail";
|
|
256
|
+
readonly detailPath: string;
|
|
257
|
+
} | {
|
|
258
|
+
readonly kind: "category_tree";
|
|
259
|
+
readonly basePath: string;
|
|
260
|
+
readonly categoryDepth: number;
|
|
261
|
+
};
|
|
262
|
+
interface PublicPathPost {
|
|
263
|
+
readonly slug: string;
|
|
264
|
+
readonly collectionKey?: string | null;
|
|
265
|
+
readonly modelKey?: string | null;
|
|
266
|
+
readonly terms: ReadonlyArray<{
|
|
267
|
+
readonly id: string;
|
|
268
|
+
readonly taxonomy: string;
|
|
269
|
+
readonly slug: string;
|
|
270
|
+
}>;
|
|
271
|
+
}
|
|
272
|
+
interface PublicPathContext {
|
|
273
|
+
/** 글의 모델 presentation. 모델 글이 아니거나 모델을 못 찾으면 null. */
|
|
274
|
+
readonly presentation: PublicPathPresentation | null;
|
|
275
|
+
/** 모델의 호환 컬렉션 key(`compatibilityCollectionKey`, 미지정이면 모델 key). */
|
|
276
|
+
readonly compatibilityCollectionKey?: string | null;
|
|
277
|
+
readonly collections: readonly RouteCollection[];
|
|
278
|
+
readonly categories: readonly CategoryTreeTerm[];
|
|
279
|
+
}
|
|
280
|
+
declare const LEGACY_BLOG_DETAIL_PATH = "/blog";
|
|
281
|
+
/** 글의 정규 공개 경로(origin 제외). 상세 주소가 없으면 null. */
|
|
282
|
+
declare function resolvePostPublicPath(post: PublicPathPost, context: PublicPathContext): string | null;
|
|
283
|
+
|
|
236
284
|
interface ClientHydrationSpec {
|
|
237
285
|
hydration?: "visible" | "idle" | "load" | "media" | "only";
|
|
238
286
|
module?: string;
|
|
@@ -371,4 +419,4 @@ declare function injectStableIdsTree(blocks: readonly (Omit<Block, "_id"> & {
|
|
|
371
419
|
_id?: string;
|
|
372
420
|
})[]): Block[];
|
|
373
421
|
|
|
374
|
-
export { BLOCK_NAME_PATTERN, type BindingResolver, type Block, type BlockBinding, type BlockDefinition, type BlockRegistry, type CategoryTreeEntry, type CategoryTreePresentation, type CategoryTreeTerm, type ClientHydrationSpec, type EmbedProvider, FIELD_BINDING_SOURCE, FIELD_BINDING_SOURCE_ALIAS, type FaqEntry, IMAGE_ALT_MAX_LENGTH, type NormalizedEmbed, PHASE_1_CORE_BLOCKS, PHASE_1_CSP, type Phase1CoreBlockName, type PreviewTokenPayload, type RoutablePost, type RouteCollection, applyBlockBindings, bindingSrcValue, bindingTextValue, blockSchema, boundFieldValue, chainResolvers, createFieldBindingResolver, ensureImportedHtmlDocumentRoots, ensureStableIds, extractFaqEntries, faqPageJsonLd, htmlToPlainText, injectStableIdsTree, isAllowedEmbedSrc, isAllowedMapEmbedSrc, isInCollectionMode, isValidBlock, issuePreviewToken, normalizeEmbed, normalizeImageAlt, parseBlock, postsForCollection, postsInAnyCollection, postsNotInAnyCollection, resolveCategoryPath, resolveCategoryTreeChain, resolveCategoryTreeEntryPath, resolveCategoryTreeHubPaths, resolvePostCategorySlug, resolvePostCollection, resolvePostPath, resolvePostUrl, sanitizeHtml, sanitizeImportedCss, sanitizeImportedHtml, scopeImportedCss, serializeBlock, validateCollections, verifyPreviewToken };
|
|
422
|
+
export { BLOCK_NAME_PATTERN, type BindingResolver, type Block, type BlockBinding, type BlockDefinition, type BlockRegistry, type CategoryTreeEntry, type CategoryTreePresentation, type CategoryTreeTerm, type ClientHydrationSpec, type EmbedProvider, FIELD_BINDING_SOURCE, FIELD_BINDING_SOURCE_ALIAS, type FaqEntry, IMAGE_ALT_MAX_LENGTH, LEGACY_BLOG_DETAIL_PATH, type NormalizedEmbed, PHASE_1_CORE_BLOCKS, PHASE_1_CSP, type Phase1CoreBlockName, type PreviewTokenPayload, type PublicPathContext, type PublicPathPost, type PublicPathPresentation, type RoutablePost, type RouteCollection, applyBlockBindings, bindingSrcValue, bindingTextValue, blockSchema, boundFieldValue, chainResolvers, createFieldBindingResolver, ensureImportedHtmlDocumentRoots, ensureStableIds, extractFaqEntries, faqPageJsonLd, htmlToPlainText, injectStableIdsTree, isAllowedEmbedSrc, isAllowedMapEmbedSrc, isInCollectionMode, isValidBlock, issuePreviewToken, normalizeEmbed, normalizeImageAlt, parseBlock, postsForCollection, postsInAnyCollection, postsNotInAnyCollection, resolveCategoryPath, resolveCategoryTreeChain, resolveCategoryTreeEntryPath, resolveCategoryTreeHubPaths, resolvePostCategorySlug, resolvePostCollection, resolvePostPath, resolvePostPublicPath, resolvePostUrl, sanitizeHtml, sanitizeImportedCss, sanitizeImportedHtml, scopeImportedCss, serializeBlock, validateCollections, verifyPreviewToken };
|
package/dist/index.d.ts
CHANGED
|
@@ -233,6 +233,54 @@ declare function resolveCategoryTreeHubPaths(presentation: CategoryTreePresentat
|
|
|
233
233
|
/** 계층형 분류 모델 항목의 정규 상세 주소. */
|
|
234
234
|
declare function resolveCategoryTreeEntryPath(presentation: CategoryTreePresentation, entry: CategoryTreeEntry, terms: readonly CategoryTreeTerm[]): string | null;
|
|
235
235
|
|
|
236
|
+
/**
|
|
237
|
+
* 글의 정규 공개 경로 — 플랫폼 전체가 쓰는 **하나의** 계산기 (ADR-0105 개정).
|
|
238
|
+
*
|
|
239
|
+
* 우선순위: 콘텐츠 모델 presentation(fixed_page → detail → category_tree) →
|
|
240
|
+
* 호환 컬렉션(`resolvePostPath`) → 레거시 `/blog/{slug}`. `data_only` 모델은
|
|
241
|
+
* 상세 주소가 없으므로 호환 컬렉션이 있으면 그 규칙을, 없으면 null 을 돌려준다.
|
|
242
|
+
*
|
|
243
|
+
* 이 값은 저장 시 `posts.public_path` 로 물질화되고(경로 이력 포함), 웹훅·사이트맵·
|
|
244
|
+
* 단축링크·내부 링크 키·리다이렉트는 저장된 값을 읽는다. 여기 말고 다른 곳에서
|
|
245
|
+
* 주소를 다시 계산하지 않는다.
|
|
246
|
+
*/
|
|
247
|
+
|
|
248
|
+
/** 콘텐츠 모델 presentation 의 구조적 최소형(cms-models 스키마와 호환). */
|
|
249
|
+
type PublicPathPresentation = {
|
|
250
|
+
readonly kind: "data_only";
|
|
251
|
+
} | {
|
|
252
|
+
readonly kind: "fixed_page";
|
|
253
|
+
readonly path: string;
|
|
254
|
+
} | {
|
|
255
|
+
readonly kind: "detail";
|
|
256
|
+
readonly detailPath: string;
|
|
257
|
+
} | {
|
|
258
|
+
readonly kind: "category_tree";
|
|
259
|
+
readonly basePath: string;
|
|
260
|
+
readonly categoryDepth: number;
|
|
261
|
+
};
|
|
262
|
+
interface PublicPathPost {
|
|
263
|
+
readonly slug: string;
|
|
264
|
+
readonly collectionKey?: string | null;
|
|
265
|
+
readonly modelKey?: string | null;
|
|
266
|
+
readonly terms: ReadonlyArray<{
|
|
267
|
+
readonly id: string;
|
|
268
|
+
readonly taxonomy: string;
|
|
269
|
+
readonly slug: string;
|
|
270
|
+
}>;
|
|
271
|
+
}
|
|
272
|
+
interface PublicPathContext {
|
|
273
|
+
/** 글의 모델 presentation. 모델 글이 아니거나 모델을 못 찾으면 null. */
|
|
274
|
+
readonly presentation: PublicPathPresentation | null;
|
|
275
|
+
/** 모델의 호환 컬렉션 key(`compatibilityCollectionKey`, 미지정이면 모델 key). */
|
|
276
|
+
readonly compatibilityCollectionKey?: string | null;
|
|
277
|
+
readonly collections: readonly RouteCollection[];
|
|
278
|
+
readonly categories: readonly CategoryTreeTerm[];
|
|
279
|
+
}
|
|
280
|
+
declare const LEGACY_BLOG_DETAIL_PATH = "/blog";
|
|
281
|
+
/** 글의 정규 공개 경로(origin 제외). 상세 주소가 없으면 null. */
|
|
282
|
+
declare function resolvePostPublicPath(post: PublicPathPost, context: PublicPathContext): string | null;
|
|
283
|
+
|
|
236
284
|
interface ClientHydrationSpec {
|
|
237
285
|
hydration?: "visible" | "idle" | "load" | "media" | "only";
|
|
238
286
|
module?: string;
|
|
@@ -371,4 +419,4 @@ declare function injectStableIdsTree(blocks: readonly (Omit<Block, "_id"> & {
|
|
|
371
419
|
_id?: string;
|
|
372
420
|
})[]): Block[];
|
|
373
421
|
|
|
374
|
-
export { BLOCK_NAME_PATTERN, type BindingResolver, type Block, type BlockBinding, type BlockDefinition, type BlockRegistry, type CategoryTreeEntry, type CategoryTreePresentation, type CategoryTreeTerm, type ClientHydrationSpec, type EmbedProvider, FIELD_BINDING_SOURCE, FIELD_BINDING_SOURCE_ALIAS, type FaqEntry, IMAGE_ALT_MAX_LENGTH, type NormalizedEmbed, PHASE_1_CORE_BLOCKS, PHASE_1_CSP, type Phase1CoreBlockName, type PreviewTokenPayload, type RoutablePost, type RouteCollection, applyBlockBindings, bindingSrcValue, bindingTextValue, blockSchema, boundFieldValue, chainResolvers, createFieldBindingResolver, ensureImportedHtmlDocumentRoots, ensureStableIds, extractFaqEntries, faqPageJsonLd, htmlToPlainText, injectStableIdsTree, isAllowedEmbedSrc, isAllowedMapEmbedSrc, isInCollectionMode, isValidBlock, issuePreviewToken, normalizeEmbed, normalizeImageAlt, parseBlock, postsForCollection, postsInAnyCollection, postsNotInAnyCollection, resolveCategoryPath, resolveCategoryTreeChain, resolveCategoryTreeEntryPath, resolveCategoryTreeHubPaths, resolvePostCategorySlug, resolvePostCollection, resolvePostPath, resolvePostUrl, sanitizeHtml, sanitizeImportedCss, sanitizeImportedHtml, scopeImportedCss, serializeBlock, validateCollections, verifyPreviewToken };
|
|
422
|
+
export { BLOCK_NAME_PATTERN, type BindingResolver, type Block, type BlockBinding, type BlockDefinition, type BlockRegistry, type CategoryTreeEntry, type CategoryTreePresentation, type CategoryTreeTerm, type ClientHydrationSpec, type EmbedProvider, FIELD_BINDING_SOURCE, FIELD_BINDING_SOURCE_ALIAS, type FaqEntry, IMAGE_ALT_MAX_LENGTH, LEGACY_BLOG_DETAIL_PATH, type NormalizedEmbed, PHASE_1_CORE_BLOCKS, PHASE_1_CSP, type Phase1CoreBlockName, type PreviewTokenPayload, type PublicPathContext, type PublicPathPost, type PublicPathPresentation, type RoutablePost, type RouteCollection, applyBlockBindings, bindingSrcValue, bindingTextValue, blockSchema, boundFieldValue, chainResolvers, createFieldBindingResolver, ensureImportedHtmlDocumentRoots, ensureStableIds, extractFaqEntries, faqPageJsonLd, htmlToPlainText, injectStableIdsTree, isAllowedEmbedSrc, isAllowedMapEmbedSrc, isInCollectionMode, isValidBlock, issuePreviewToken, normalizeEmbed, normalizeImageAlt, parseBlock, postsForCollection, postsInAnyCollection, postsNotInAnyCollection, resolveCategoryPath, resolveCategoryTreeChain, resolveCategoryTreeEntryPath, resolveCategoryTreeHubPaths, resolvePostCategorySlug, resolvePostCollection, resolvePostPath, resolvePostPublicPath, resolvePostUrl, sanitizeHtml, sanitizeImportedCss, sanitizeImportedHtml, scopeImportedCss, serializeBlock, validateCollections, verifyPreviewToken };
|
package/dist/index.js
CHANGED
|
@@ -279,6 +279,27 @@ function resolveCategoryTreeEntryPath(presentation, entry, terms) {
|
|
|
279
279
|
return leafPath ? `${leafPath}/${encodeURIComponent(entry.slug)}` : null;
|
|
280
280
|
}
|
|
281
281
|
|
|
282
|
+
// src/public-path.ts
|
|
283
|
+
var LEGACY_BLOG_DETAIL_PATH = "/blog";
|
|
284
|
+
function resolvePostPublicPath(post, context) {
|
|
285
|
+
const presentation = context.presentation;
|
|
286
|
+
if (presentation && presentation.kind !== "data_only") {
|
|
287
|
+
if (presentation.kind === "fixed_page") return presentation.path;
|
|
288
|
+
if (presentation.kind === "detail") {
|
|
289
|
+
if (!presentation.detailPath.includes(":slug")) return null;
|
|
290
|
+
return presentation.detailPath.replace(":slug", encodeURIComponent(post.slug));
|
|
291
|
+
}
|
|
292
|
+
return resolveCategoryTreeEntryPath(presentation, post, context.categories);
|
|
293
|
+
}
|
|
294
|
+
const collectionKey = post.collectionKey ?? (presentation ? context.compatibilityCollectionKey ?? post.modelKey ?? null : null);
|
|
295
|
+
const routable = { slug: post.slug, terms: post.terms, collectionKey };
|
|
296
|
+
if (!resolvePostCollection(routable, context.collections)) {
|
|
297
|
+
if (presentation) return null;
|
|
298
|
+
return `${LEGACY_BLOG_DETAIL_PATH}/${post.slug}`;
|
|
299
|
+
}
|
|
300
|
+
return resolvePostPath(routable, context.collections);
|
|
301
|
+
}
|
|
302
|
+
|
|
282
303
|
// src/delimiter.ts
|
|
283
304
|
function serializeBlock(block) {
|
|
284
305
|
const exportable = sanitizeAttributesForExport(block);
|
|
@@ -1250,6 +1271,7 @@ export {
|
|
|
1250
1271
|
FIELD_BINDING_SOURCE,
|
|
1251
1272
|
FIELD_BINDING_SOURCE_ALIAS,
|
|
1252
1273
|
IMAGE_ALT_MAX_LENGTH,
|
|
1274
|
+
LEGACY_BLOG_DETAIL_PATH,
|
|
1253
1275
|
PHASE_1_CORE_BLOCKS,
|
|
1254
1276
|
PHASE_1_CSP,
|
|
1255
1277
|
applyBlockBindings,
|
|
@@ -1283,6 +1305,7 @@ export {
|
|
|
1283
1305
|
resolvePostCategorySlug,
|
|
1284
1306
|
resolvePostCollection,
|
|
1285
1307
|
resolvePostPath,
|
|
1308
|
+
resolvePostPublicPath,
|
|
1286
1309
|
resolvePostUrl,
|
|
1287
1310
|
sanitizeHtml,
|
|
1288
1311
|
sanitizeImportedCss,
|