@roottale/cms-core 0.71.0 → 0.72.1

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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/block.ts","../src/bindings.ts","../src/collections.ts","../src/category-tree.ts","../src/public-path.ts","../src/delimiter.ts","../src/embed.ts","../src/faq.ts","../src/image-alt.ts","../src/imported-html-sanitize.ts","../src/internal-content-links.ts","../src/preview-token.ts","../src/sanitize.ts","../src/stable-id.ts","../src/site-patterns.ts","../src/editor-formatting.ts","../src/section-locks.ts","../src/metadata-resolver.ts","../src/metadata-contract.ts","../src/metadata-defaults.ts","../src/metadata-text.ts","../src/content-policy-schema.ts","../src/content-policy-condition.ts","../src/post-body-source.ts","../src/content-policy.ts","../src/metadata-video-thumbnail.ts","../src/metadata-default-fields.ts","../src/content-query.ts","../src/redirect-paths.ts"],"sourcesContent":["// @roottale/cms-core — ADR-0034 §3 §5 (Phase 1 Block JSON foundation)\nexport * from \"./block\";\nexport * from \"./bindings\";\nexport * from \"./collections\";\nexport * from \"./category-tree\";\nexport * from \"./public-path\";\nexport * from \"./block-definition\";\nexport * from \"./delimiter\";\nexport * from \"./embed\";\nexport * from \"./faq\";\nexport * from \"./image-alt\";\nexport * from \"./imported-html-sanitize\";\nexport * from \"./internal-content-links\";\nexport * from \"./preview-token\";\nexport * from \"./sanitize\";\nexport * from \"./stable-id\";\nexport * from \"./site-patterns\";\nexport * from \"./editor-formatting\";\n\nexport * from \"./section-locks\";\nexport * from \"./metadata-resolver\";\nexport * from \"./metadata-contract\";\nexport * from \"./metadata-defaults\";\nexport * from \"./post-body-source\";\nexport * from \"./content-policy-schema\";\nexport * from \"./content-policy\";\nexport * from \"./content-query\";\n\nexport { normalizeRedirectPath, normalizeRedirectSource, normalizeRedirectTarget, isProtectedRedirectPath, type NormalizedRedirectSourceResult, type NormalizedRedirectTargetResult } from \"./redirect-paths\";\n","// ADR-0034 §5 — Block JSON 스펙 (WP 호환)\n// Gutenberg block shape + 우리 확장:\n// - `_id` (uuidv7 stable across revisions, ADR §2 P5)\n// - `rawHtml` (WP delimiter innerHTML 보존, codex v2 verdict #7)\n// - `metadata.reusableRef` (Phase 2 = flat expand)\n\nimport { z } from \"zod\";\n\n/** WP-compatible name pattern: `core/<x>` | `roottale/<x>` */\nexport const BLOCK_NAME_PATTERN = /^(core|roottale)\\/[a-z0-9-]+$/;\n\nconst blockMetadataSchema = z\n .object({\n bindings: z\n .record(\n z.object({\n source: z.string(),\n args: z.record(z.unknown()).optional(),\n }),\n )\n .optional(),\n name: z.string().optional(),\n reusableRef: z.string().optional(),\n })\n .strict()\n .optional();\n\n/**\n * Block schema (recursive — innerBlocks).\n * `_id` 는 internal-only (export 시 strip, ADR §5).\n */\nexport const blockSchema: z.ZodType<Block> = z.lazy(() =>\n z\n .object({\n _id: z.string().uuid(),\n name: z.string().regex(BLOCK_NAME_PATTERN),\n attributes: z.record(z.unknown()),\n innerBlocks: z.array(blockSchema),\n rawHtml: z.string().optional(),\n metadata: blockMetadataSchema,\n })\n .strict(),\n);\n\nexport interface Block {\n /** uuidv7. Internal-only — never exported as WP delimiter attribute. */\n _id: string;\n /** `core/<x>` or `roottale/<x>` */\n name: string;\n attributes: Record<string, unknown>;\n innerBlocks: Block[];\n /** WP delimiter innerHTML (preserved for round-trip — codex v2 verdict #7). */\n rawHtml?: string;\n metadata?: {\n bindings?: Record<string, { source: string; args?: Record<string, unknown> }>;\n name?: string;\n reusableRef?: string;\n };\n}\n\nexport function isValidBlock(value: unknown): value is Block {\n return blockSchema.safeParse(value).success;\n}\n\nexport function parseBlock(value: unknown): Block {\n return blockSchema.parse(value);\n}\n\n/**\n * Phase 1 core block names (10) — ADR-0034 §5.\n * Phase 2+ 에 `core/embed`, `core/gallery`, `core/cover` 등 추가.\n */\nexport const PHASE_1_CORE_BLOCKS = [\n \"core/paragraph\",\n \"core/heading\",\n \"core/image\",\n \"core/list\",\n \"core/quote\",\n \"core/code\",\n \"core/columns\",\n \"core/group\",\n \"core/separator\",\n \"core/spacer\",\n] as const;\nexport type Phase1CoreBlockName = (typeof PHASE_1_CORE_BLOCKS)[number];\n","// ADR-0041 §7 — Block Bindings: ACF 필드 → 블록 attribute.\n//\n// 블록의 `metadata.bindings[attr] = { source, args }` 를 만나면 attribute 를\n// 바인딩 값으로 치환한다. 렌더 직전 pure transform — renderBlock 시그니처 불변.\n//\n// canonical source id = `roottale/field`. WP round-trip 위해 `acf/field` alias\n// 인식. resolver 는 주입(렌더러는 cms-fields 비종속) — 값 record 만 흐른다.\n\nimport type { Block } from \"./block\";\n\nexport interface BlockBinding {\n source: string;\n args?: Record<string, unknown>;\n}\n\n/**\n * 바인딩 해석기. 주어진 binding(+attribute/block)에 대한 값을 반환.\n * `undefined` 반환 = 미해석(원래 attribute 유지).\n */\nexport type BindingResolver = (\n binding: BlockBinding,\n attribute: string,\n block: Block,\n) => unknown;\n\n/** ACF 필드 바인딩 source id. */\nexport const FIELD_BINDING_SOURCE = \"roottale/field\";\n/** WP ACF 호환 alias (import round-trip). */\nexport const FIELD_BINDING_SOURCE_ALIAS = \"acf/field\";\n\n/**\n * acf 값 record 로부터 필드 바인딩 resolver 생성.\n * `roottale/field` / `acf/field` source 의 `args.name` 을 record 에서 조회.\n */\nexport function createFieldBindingResolver(\n fields: Record<string, unknown> | undefined | null,\n): BindingResolver {\n const values = fields ?? {};\n return (binding) => {\n if (\n binding.source !== FIELD_BINDING_SOURCE &&\n binding.source !== FIELD_BINDING_SOURCE_ALIAS\n ) {\n return undefined;\n }\n const name = binding.args?.name;\n if (typeof name !== \"string\") return undefined;\n const v = values[name];\n return v === undefined ? undefined : v;\n };\n}\n\n/** 여러 resolver 를 순서대로 시도(첫 non-undefined 채택). */\nexport function chainResolvers(\n ...resolvers: BindingResolver[]\n): BindingResolver {\n return (binding, attribute, block) => {\n for (const r of resolvers) {\n const v = r(binding, attribute, block);\n if (v !== undefined) return v;\n }\n return undefined;\n };\n}\n\nfunction applyToBlock(block: Block, resolver: BindingResolver): Block {\n const bindings = block.metadata?.bindings;\n let attributes = block.attributes;\n if (bindings) {\n let mutated = false;\n const next = { ...block.attributes };\n for (const [attr, binding] of Object.entries(bindings)) {\n const value = resolver(binding, attr, block);\n if (value !== undefined) {\n next[attr] = value;\n mutated = true;\n }\n }\n if (mutated) attributes = next;\n }\n const innerBlocks = block.innerBlocks.length\n ? block.innerBlocks.map((b) => applyToBlock(b, resolver))\n : block.innerBlocks;\n if (attributes === block.attributes && innerBlocks === block.innerBlocks) {\n return block;\n }\n return { ...block, attributes, innerBlocks };\n}\n\n/**\n * 블록 트리에 바인딩을 적용해 새 트리 반환(immutable). 바인딩 없으면 원본 그대로.\n */\nexport function applyBlockBindings(\n blocks: readonly Block[],\n resolver: BindingResolver,\n): Block[] {\n return blocks.map((b) => applyToBlock(b, resolver));\n}\n\n// ── 라이브 Tiptap 렌더 경로용 헬퍼 (cms-field-binding-editor.md D3·D4) ─────────\n//\n// Tiptap doc 의 node.attrs.bindings (BlockBindings extension 이 기록, Block JSON\n// metadata.bindings 와 동일 형태) 를 렌더 시점에 필드 값으로 치환할 때 사용.\n\n/**\n * bindings 에서 `attribute` 의 필드 값을 조회. `roottale/field`/`acf/field`\n * source 만 해석. 빈 값(undefined/null/\"\") = undefined → caller 가 fallback 렌더.\n */\nexport function boundFieldValue(\n bindings: unknown,\n attribute: string,\n fields: Record<string, unknown> | null | undefined,\n): unknown {\n if (!bindings || typeof bindings !== \"object\" || !fields) return undefined;\n const binding = (bindings as Record<string, BlockBinding | undefined>)[\n attribute\n ];\n if (\n !binding ||\n (binding.source !== FIELD_BINDING_SOURCE &&\n binding.source !== FIELD_BINDING_SOURCE_ALIAS)\n ) {\n return undefined;\n }\n const name = binding.args?.name;\n if (typeof name !== \"string\") return undefined;\n const value = fields[name];\n if (value === undefined || value === null || value === \"\") return undefined;\n return value;\n}\n\n/**\n * content 바인딩 값의 텍스트화. **항상 plain text** — caller 가 escape 해\n * 삽입한다 (바인딩 경로로 raw HTML 진입 금지, D4). string/number 외 = undefined.\n */\nexport function bindingTextValue(value: unknown): string | undefined {\n if (typeof value === \"string\") return value;\n if (typeof value === \"number\" && Number.isFinite(value)) return String(value);\n return undefined;\n}\n\n/**\n * image `src` 바인딩 값 → URL. 포맷된 MediaRef(`{url}`) 또는 http(s) 문자열만\n * 인정 — raw media id(uuid) 는 URL 이 아니므로 fallback.\n */\nexport function bindingSrcValue(value: unknown): string | undefined {\n if (value && typeof value === \"object\") {\n const url = (value as { url?: unknown }).url;\n if (typeof url === \"string\" && /^https?:\\/\\//i.test(url)) return url;\n return undefined;\n }\n if (typeof value === \"string\" && /^https?:\\/\\//i.test(value)) return value;\n return undefined;\n}\n","/**\n * 다중 스트림 콘텐츠 라우팅 프리미티브 (ADR-0060).\n *\n * 같은 글 풀(post)을 둘 이상의 스트림으로 분리(공지=게시판 /notice, 블로그=그리드\n * /blog 등). `cms-renderer-next` / `cms-renderer-astro` 가 동일 로직을 공유하도록\n * 여기(cms-core)에 둔다 — cms-client·프레임워크 비의존(구조적 타입만).\n *\n * 설계(codex consult `019ef577` 반영):\n * - **활성화 = `collections.length > 0`.** 빈 배열·미전달 = 레거시 단일 /blog.\n * - `RouteCollection` 은 **라우팅 최소셋**만(key/basePath/categories/feed/archives와\n * 경로 정책) 가진다.\n * label/layout/icon 등 표시·작성 메타는 templates `CollectionSchema` 에 둔다.\n */\n\n/** 콘텐츠 스트림 1개 — sitemap/feed/아카이브 파생의 라우팅 단위. */\nexport interface RouteCollection {\n /** 안정 식별자 (예: \"notice\" | \"blog\"). */\n readonly key: string;\n /**\n * 라우트 base path (예: \"/notice\" | \"/blog\"). 앞 슬래시 포함, 뒤 슬래시 없이.\n * **`\"\"`(빈 문자열) = 주소 없는 유형** — 페이지 안에서 불러와 쓰는 콘텐츠(강사·리뷰\n * 등)로, 라우트/사이트맵/피드/아카이브를 일절 파생하지 않는다. 외부 사이트는\n * posts API 의 `collectionKey` 필터로 내용만 가져다 렌더한다. `resolvePostUrl`/\n * `resolvePostPath` 는 이 값이 빈 문자열이면 항상 null. 소속 판정\n * (`resolvePostCollection`)에는 영향 없음 — key 만으로 소속이 성립한다.\n */\n readonly basePath: string;\n /**\n * 이 섹션이 제공하는 **주제(category) allowlist** — `${basePath}/categories/{slug}`\n * 아카이브 라우트 범위. ADR-0060 Amendment 1 (M4): 섹션 소속은 글의 `collectionKey`\n * 로 정해지므로 이 배열은 더 이상 라우팅 소유권이 아니다(catch-all 개념 폐기). 빈\n * 배열 = 이 섹션은 주제 없음.\n */\n readonly categories: readonly string[];\n /** RSS 피드 포함 여부 (기본 false). */\n readonly feed?: boolean;\n /** `${basePath}/categories/{slug}` 아카이브 emit 여부 (기본 false). */\n readonly archives?: boolean;\n /**\n * 글별 상세 라우트 존재 여부 (기본 true — 하위 호환). false 면 이 스트림은\n * \"목록 전용\" 유형(예: 후기 — 목록에서 전문 노출, 글마다 별도 상세 URL 없음).\n * `resolvePostUrl`/`resolvePostPath` 가 null 을 반환해 sitemap/feed 에서 글\n * URL 을 발행하지 않는다. 소속 판정(`resolvePostCollection`)에는 영향 없음\n * — 소속과 상세 URL 존재 여부는 별개(M4 원칙 연장).\n */\n readonly detail?: boolean;\n /**\n * 글 상세 주소 형식. `flat`(기본) = `${basePath}/{postSlug}`,\n * `category` = `${basePath}/{categorySlug}/{postSlug}`.\n * category 모드는 URL을 하나로 결정해야 하므로 `categoryCardinality: \"exactly-one\"`\n * 과 함께 쓴다.\n */\n readonly detailPath?: \"flat\" | \"category\";\n /**\n * 카테고리 허브 주소 형식. `namespaced`(기본) =\n * `${basePath}/categories/{slug}`, `direct` = `${basePath}/{slug}`.\n */\n readonly categoryPath?: \"namespaced\" | \"direct\";\n /** 글에 허용하는 category term 개수. 기본 `multiple`(기존 동작). */\n readonly categoryCardinality?: \"multiple\" | \"exactly-one\";\n}\n\n/** resolve 입력 — 글의 구조적 최소 형태(cms-client `CmsPostContent` 와 구조 호환). */\nexport interface RoutablePost {\n readonly slug: string;\n /** 글의 주제(category) terms — 아카이브 라우트 필터용. 섹션 판정엔 쓰지 않는다(M4). */\n readonly terms: ReadonlyArray<{ readonly taxonomy: string; readonly slug: string }>;\n /**\n * ADR-0060 Amendment 1 (Option A, M4) — 글에 명시된 섹션 key. 섹션 판정의 유일한\n * 근거. 미설정/미존재면 어느 스트림에도 안 속함(null). 카테고리 파생 fallback 은 제거됨.\n */\n readonly collectionKey?: string | null;\n}\n\nfunction trimTrailingSlash(value: string): string {\n return value.replace(/\\/+$/, \"\");\n}\n\n/** collection 모드인지 — 길이 0(또는 미전달)이면 레거시 단일 /blog 동작. */\nexport function isInCollectionMode(\n collections: readonly RouteCollection[] | undefined,\n): boolean {\n return !!collections && collections.length > 0;\n}\n\n/**\n * 글이 속한 collection 1개를 해석 — ADR-0060 Amendment 1 (Option A, M4): **글의\n * 명시 `collectionKey` 만** 본다. 미설정/미존재 key 면 null(상세 라우트 없는 글 →\n * sitemap 제외 신호). 카테고리 slug 파생·catch-all·first-wins 는 M4 에서 제거됨 —\n * 섹션은 카테고리가 아니라 글에 명시한다. (`categories` 는 이제 주제 allowlist 로만,\n * 아카이브 라우트에서 쓰임.)\n */\nexport function resolvePostCollection(\n post: RoutablePost,\n collections: readonly RouteCollection[],\n): RouteCollection | null {\n if (!post.collectionKey) return null;\n return collections.find((c) => c.key === post.collectionKey) ?? null;\n}\n\n/**\n * 글의 정규 절대 URL — 소속 스트림 basePath 기준. 어느 스트림에도 안 속하거나,\n * 소속 스트림이 `detail: false`(목록 전용 유형) 이거나, `basePath` 가 빈 문자열\n * (주소 없는 유형)이면 null. slug 는 percent-encoding (한글 slug 안전).\n */\nexport function resolvePostUrl(\n siteUrl: string,\n post: RoutablePost,\n collections: readonly RouteCollection[],\n): string | null {\n const path = resolvePostPath(post, collections);\n return path ? `${trimTrailingSlash(siteUrl)}${path}` : null;\n}\n\n/**\n * category 기반 상세 주소에 사용할 단일 category slug. collection 에 연결된\n * category만 세고 정확히 1개일 때만 반환한다. 0개·2개 이상이면 정규 URL을\n * 임의 선택하지 않고 null 로 닫는다.\n */\nexport function resolvePostCategorySlug(\n post: RoutablePost,\n collection: RouteCollection,\n): string | null {\n const allowed = new Set(collection.categories);\n const slugs = [\n ...new Set(\n post.terms\n .filter(\n (term) => term.taxonomy === \"category\" && allowed.has(term.slug),\n )\n .map((term) => term.slug),\n ),\n ];\n return slugs.length === 1 ? slugs[0]! : null;\n}\n\n/** 카테고리 허브 경로. archives 가 꺼졌거나 주소가 없으면 null. */\nexport function resolveCategoryPath(\n collection: RouteCollection,\n categorySlug: string,\n): string | null {\n if (!collection.archives || !collection.basePath || !categorySlug) return null;\n const basePath = trimTrailingSlash(collection.basePath);\n const encodedSlug = encodeURIComponent(categorySlug);\n return collection.categoryPath === \"direct\"\n ? `${basePath}/${encodedSlug}`\n : `${basePath}/categories/${encodedSlug}`;\n}\n\n/**\n * 글의 *경로*(siteUrl 없는 basePath-상대) — 예: \"/blog/hello\". 소속 스트림이\n * 없거나, `detail: false`(목록 전용 유형) 이거나, `basePath` 가 빈 문자열\n * (주소 없는 유형)이면 null. astro 처럼 상대 href 가 필요한 소비자용\n * (`resolvePostUrl` 은 절대 URL).\n */\nexport function resolvePostPath(\n post: RoutablePost,\n collections: readonly RouteCollection[],\n): string | null {\n const collection = resolvePostCollection(post, collections);\n if (!collection) return null;\n if (collection.detail === false) return null;\n if (!collection.basePath) return null;\n const basePath = trimTrailingSlash(collection.basePath);\n const encodedPostSlug = encodeURIComponent(post.slug);\n if (collection.detailPath !== \"category\") {\n return `${basePath}/${encodedPostSlug}`;\n }\n const categorySlug = resolvePostCategorySlug(post, collection);\n return categorySlug\n ? `${basePath}/${encodeURIComponent(categorySlug)}/${encodedPostSlug}`\n : null;\n}\n\n/**\n * 특정 collection에 명시적으로 귀속된 글만 남긴다.\n */\nexport function postsForCollection<T extends RoutablePost>(\n posts: readonly T[],\n collections: readonly RouteCollection[],\n key: string,\n): T[] {\n return posts.filter((p) => resolvePostCollection(p, collections)?.key === key);\n}\n\n/** feed/sitemap 등 파생에서 제외할, 어느 스트림에도 안 속한 글 제거. */\nexport function postsInAnyCollection<T extends RoutablePost>(\n posts: readonly T[],\n collections: readonly RouteCollection[],\n): T[] {\n return posts.filter((p) => resolvePostCollection(p, collections) !== null);\n}\n\n/**\n * `postsInAnyCollection` 의 대칭 — **어느 스트림에도 안 속한(\"미소속\") 글만** 남긴다.\n * 판정 시맨틱 = `resolvePostCollection(post, collections) === null`(collectionKey 가\n * NULL 이거나 사이트 선언 컬렉션 밖). `/blog` 처럼 \"선언 스트림에 안 잡힌 글 = 블로그\"로\n * 취급하는 표면에서 스트림 글 누출을 막는 데 쓴다(`blog-list-collection-scope.md` §2①).\n */\nexport function postsNotInAnyCollection<T extends RoutablePost>(\n posts: readonly T[],\n collections: readonly RouteCollection[],\n): T[] {\n return posts.filter((p) => resolvePostCollection(p, collections) === null);\n}\n\n/**\n * config 검증 — 중복 key/basePath, 그리고 **두 스트림에 동시 등장하는 category slug**\n * (라우팅 모호성)를 탐지. `basePath === \"\"`(주소 없는 유형)는 basePath 형식·중복\n * 검사를 건너뛴다 — 라우트가 없으므로 여러 개 공존해도 모호성이 없다. key 중복·\n * category 중복 검사는 주소 유무와 무관하게 전 항목에 적용된다. 문제 메시지 배열\n * 반환(빈 = 정상). 빌드 타임 fail-fast 용.\n */\nexport function validateCollections(collections: readonly RouteCollection[]): string[] {\n const problems: string[] = [];\n const seenKey = new Set<string>();\n const seenBase = new Set<string>();\n const categoryOwner = new Map<string, string>();\n\n for (const c of collections) {\n if (seenKey.has(c.key)) problems.push(`중복 collection key: \"${c.key}\"`);\n seenKey.add(c.key);\n\n if (c.basePath !== \"\") {\n const base = trimTrailingSlash(c.basePath);\n if (seenBase.has(base)) problems.push(`중복 basePath: \"${c.basePath}\"`);\n seenBase.add(base);\n\n if (!c.basePath.startsWith(\"/\")) {\n problems.push(`basePath 는 \"/\" 로 시작해야 함: \"${c.basePath}\" (${c.key})`);\n } else if (\n base.length < 2 ||\n base.startsWith(\"//\") ||\n /[\\\\?#\\s]/.test(base) ||\n base.split(\"/\").some((segment) => segment === \".\" || segment === \"..\")\n ) {\n problems.push(\n `basePath 는 사이트 내부 절대 경로여야 함: \"${c.basePath}\" (${c.key})`,\n );\n }\n }\n\n for (const slug of c.categories) {\n const owner = categoryOwner.get(slug);\n if (owner && owner !== c.key) {\n problems.push(\n `category \"${slug}\" 가 collection \"${owner}\" 와 \"${c.key}\" 에 중복 — 한 카테고리는 한 스트림에만.`,\n );\n }\n categoryOwner.set(slug, c.key);\n }\n\n if (c.detailPath === \"category\") {\n if (!c.basePath) {\n problems.push(\n `category 상세 경로는 basePath 가 필요함: \"${c.key}\"`,\n );\n }\n if (c.detail === false) {\n problems.push(\n `category 상세 경로는 글별 상세 페이지가 필요함: \"${c.key}\"`,\n );\n }\n if (!c.archives) {\n problems.push(\n `category 상세 경로는 카테고리 아카이브가 필요함: \"${c.key}\"`,\n );\n }\n if (c.categoryCardinality !== \"exactly-one\") {\n problems.push(\n `category 상세 경로는 categoryCardinality \"exactly-one\" 이 필요함: \"${c.key}\"`,\n );\n }\n }\n }\n return problems;\n}\n","/**\n * 콘텐츠 모델의 계층형 분류 주소 계산기.\n *\n * FAQ·도움말·문서처럼 하나의 모델이 여러 단계 분류 허브를 가지는 경우에 쓴다.\n * 저장소나 프레임워크를 모르며, 공개 API와 ROOT-ADMIN이 같은 계산을 재사용한다.\n */\n\n/**\n * 글을 붙일 수 있는 분류.\n * - `deepest`(기본): `categoryDepth` 단계의 말단 분류에만 붙는다.\n * - `leaf`: 하위 분류가 없는 분류면 단계와 무관하게 붙는다(`categoryDepth`는 최대 단계).\n * 하위가 필요한 영역만 나누고 나머지는 상위 분류에 바로 글을 쓸 때 쓴다.\n */\nexport type CategoryTreeEntryCategory = \"deepest\" | \"leaf\";\n\nexport interface CategoryTreePresentation {\n readonly basePath: string;\n readonly categoryDepth: number;\n readonly entryCategory?: CategoryTreeEntryCategory;\n /** 생략하면 정확히 한 분류. first는 termOrder, id 순서의 첫 분류를 쓴다. */\n readonly categorySelection?: \"first\";\n /** first + 1단계 모델에서 실제 미분류 글에만 쓰는 가상 분류 slug. */\n readonly categoryFallbackSlug?: string;\n readonly reservedCategorySlugs?: readonly string[];\n}\n\nexport interface CategoryTreeTerm {\n readonly id: string;\n readonly slug: string;\n readonly parentId?: string | null;\n readonly termOrder?: number;\n}\n\nexport type CategoryTreeEntryResolution<T extends CategoryTreeTerm = CategoryTreeTerm> =\n | { readonly kind: \"category\"; readonly category: T; readonly chain: readonly T[] }\n | { readonly kind: \"fallback\"; readonly slug: string };\n\n/** 실제 분류가 사용할 수 없는 예약 주소. fallback은 분류 원장에 만들지 않는다. */\nexport function isReservedCategoryTreeSlug(\n presentation: CategoryTreePresentation,\n slug: string,\n): boolean {\n return slug === presentation.categoryFallbackSlug ||\n presentation.reservedCategorySlugs?.includes(slug) === true;\n}\n\nexport interface CategoryTreeEntry {\n readonly slug: string;\n readonly terms: ReadonlyArray<{\n readonly id: string;\n readonly taxonomy: string;\n }>;\n}\n\n/**\n * 하위 주소를 붙일 접두사. 루트(`/`)는 빈 접두사(`\"\"`)이고, 빈 basePath 는 주소가\n * 없는 모델이라 null 이다 — 둘을 같게 보면 `/`에 올린 모델의 주소가 사라진다.\n */\nfunction basePathPrefix(basePath: string): string | null {\n if (!basePath.trim()) return null;\n return basePath.replace(/\\/+$/, \"\");\n}\n\n/** 루트→분류 사슬. 끊긴 부모·순환·중복 id·slug 없음·최대 단계 초과는 null. */\nfunction categoryTreeAncestry<T extends CategoryTreeTerm>(\n categoryDepth: number,\n termId: string,\n terms: readonly T[],\n): T[] | null {\n if (!Number.isInteger(categoryDepth) || categoryDepth < 1) return null;\n const termsById = new Map(terms.map((term) => [term.id, term]));\n if (termsById.size !== terms.length) return null;\n\n const reversed: T[] = [];\n const visited = new Set<string>();\n let currentId: string | null = termId;\n while (currentId) {\n if (visited.has(currentId)) return null;\n visited.add(currentId);\n const current = termsById.get(currentId);\n if (!current || !current.slug) return null;\n reversed.push(current);\n if (reversed.length > categoryDepth) return null;\n currentId = current.parentId ?? null;\n }\n return reversed.reverse();\n}\n\nfunction hasChildCategory(termId: string, terms: readonly CategoryTreeTerm[]): boolean {\n return terms.some((term) => term.parentId === termId);\n}\n\n/**\n * 글 주소에 쓰는 분류의 루트→분류 사슬. 주소를 만들 수 없으면 null.\n *\n * - `deepest`: 정확히 `categoryDepth` 단계의 말단이어야 한다.\n * - `leaf`: `categoryDepth` 이하면 된다. 하위가 없는지는 *새로 고를 때*만 본다\n * (`resolveCategoryTreeSelection`) — 글이 붙은 뒤 하위 분류가 생겨도 이미 발행한\n * 글의 주소는 그대로 살아 있어야 한다.\n */\nexport function resolveCategoryTreeChain<T extends CategoryTreeTerm>(\n presentation: CategoryTreePresentation,\n termId: string,\n terms: readonly T[],\n): T[] | null {\n const chain = categoryTreeAncestry(presentation.categoryDepth, termId, terms);\n if (!chain) return null;\n if (chain.some((term) => isReservedCategoryTreeSlug(presentation, term.slug))) return null;\n if (presentation.entryCategory === \"leaf\") return chain;\n if (hasChildCategory(termId, terms)) return null;\n return chain.length === presentation.categoryDepth ? chain : null;\n}\n\n/**\n * 글에 새로 고를 수 있는 분류의 사슬 — 주소를 만들 수 있고 지금 하위 분류가 없어야\n * 한다. 편집기 선택지와 발행 검사가 같은 규칙을 쓴다.\n */\nexport function resolveCategoryTreeSelection<T extends CategoryTreeTerm>(\n presentation: CategoryTreePresentation,\n termId: string,\n terms: readonly T[],\n): T[] | null {\n if (hasChildCategory(termId, terms)) return null;\n return resolveCategoryTreeChain(presentation, termId, terms);\n}\n\n/**\n * 상세 경로·분류 표시가 함께 쓰는 선택기. terms에는 해당 모델의 전체 분류를 준다.\n * first의 미지정/외부 분류나 잘못된 계층은 fallback으로 숨기지 않는다.\n */\nexport function resolveCategoryTreeEntryCategory<T extends CategoryTreeTerm>(\n presentation: CategoryTreePresentation,\n entry: Pick<CategoryTreeEntry, \"terms\">,\n terms: readonly T[],\n): CategoryTreeEntryResolution<T> | null {\n const termsById = new Map(terms.map((term) => [term.id, term]));\n const first = presentation.categorySelection === \"first\";\n const categoryIds = [...new Set(entry.terms\n .filter((term) => term.taxonomy === \"category\" && (first || termsById.has(term.id)))\n .map((term) => term.id))];\n\n if (first) {\n if (termsById.size !== terms.length) return null;\n if (categoryIds.length === 0) {\n const slug = presentation.categoryFallbackSlug;\n if (presentation.categoryDepth !== 1 || !slug ||\n presentation.reservedCategorySlugs?.includes(slug) ||\n terms.some((term) => term.slug === slug)) return null;\n return { kind: \"fallback\", slug };\n }\n const candidates: Array<{ category: T; chain: T[] }> = [];\n for (const id of categoryIds) {\n const category = termsById.get(id);\n if (!category || !Number.isInteger(category.termOrder ?? 0)) return null;\n const chain = resolveCategoryTreeChain(presentation, id, terms);\n if (!chain) return null;\n candidates.push({ category, chain });\n }\n candidates.sort((a, b) => (a.category.termOrder ?? 0) - (b.category.termOrder ?? 0) ||\n (a.category.id < b.category.id ? -1 : a.category.id > b.category.id ? 1 : 0));\n return { kind: \"category\", ...candidates[0]! };\n }\n\n if (categoryIds.length !== 1) return null;\n const category = termsById.get(categoryIds[0]!)!;\n const chain = resolveCategoryTreeChain(presentation, category.id, terms);\n return chain ? { kind: \"category\", category, chain } : null;\n}\n\n/** 루트부터 선택 분류까지 모든 허브 주소. */\nexport function resolveCategoryTreeHubPaths(\n presentation: CategoryTreePresentation,\n termId: string,\n terms: readonly CategoryTreeTerm[],\n): string[] | null {\n const chain = resolveCategoryTreeChain(presentation, termId, terms);\n const prefix = basePathPrefix(presentation.basePath);\n if (!chain || prefix === null) return null;\n const paths = [prefix || \"/\"];\n const segments: string[] = [];\n for (const term of chain) {\n segments.push(encodeURIComponent(term.slug));\n paths.push(`${prefix}/${segments.join(\"/\")}`);\n }\n return paths;\n}\n\n/** 계층형 분류 모델 항목의 정규 상세 주소. */\nexport function resolveCategoryTreeEntryPath(\n presentation: CategoryTreePresentation,\n entry: CategoryTreeEntry,\n terms: readonly CategoryTreeTerm[],\n): string | null {\n if (!entry.slug) return null;\n const selected = resolveCategoryTreeEntryCategory(presentation, entry, terms);\n const prefix = basePathPrefix(presentation.basePath);\n if (!selected || prefix === null) return null;\n const slugs = selected.kind === \"fallback\"\n ? [selected.slug]\n : selected.chain.map((term) => term.slug);\n return `${prefix}/${[...slugs, entry.slug].map(encodeURIComponent).join(\"/\")}`;\n}\n","/**\n * 글의 정규 공개 경로 — 플랫폼 전체가 쓰는 **하나의** 계산기 (ADR-0105 개정).\n *\n * 우선순위: 콘텐츠 모델 presentation(fixed_page → detail → category_tree) →\n * 호환 컬렉션(`resolvePostPath`) → 레거시 `/blog/{slug}`. `data_only` 모델은\n * 상세 주소가 없으므로 호환 컬렉션이 있으면 그 규칙을, 없으면 null 을 돌려준다.\n *\n * 이 값은 저장 시 `posts.public_path` 로 물질화되고(경로 이력 포함), 웹훅·사이트맵·\n * 단축링크·내부 링크 키·리다이렉트는 저장된 값을 읽는다. 여기 말고 다른 곳에서\n * 주소를 다시 계산하지 않는다.\n */\nimport {\n resolveCategoryTreeEntryPath,\n type CategoryTreePresentation,\n type CategoryTreeTerm,\n} from \"./category-tree\";\nimport {\n resolvePostCollection,\n resolvePostPath,\n type RouteCollection,\n} from \"./collections\";\n\n/** 콘텐츠 모델 presentation 의 구조적 최소형(cms-models 스키마와 호환). */\nexport type PublicPathPresentation =\n | { readonly kind: \"data_only\" }\n | { readonly kind: \"fixed_page\"; readonly path: string }\n | { readonly kind: \"detail\"; readonly detailPath: string }\n | (CategoryTreePresentation & {\n readonly kind: \"category_tree\";\n });\n\nexport interface PublicPathPost {\n readonly slug: string;\n readonly collectionKey?: string | null;\n readonly modelKey?: string | null;\n readonly terms: ReadonlyArray<{\n readonly id: string;\n readonly taxonomy: string;\n readonly slug: string;\n }>;\n}\n\nexport interface PublicPathContext {\n /** 글의 모델 presentation. 모델 글이 아니거나 모델을 못 찾으면 null. */\n readonly presentation: PublicPathPresentation | null;\n /** 모델의 호환 컬렉션 key(`compatibilityCollectionKey`, 미지정이면 모델 key). */\n readonly compatibilityCollectionKey?: string | null;\n readonly collections: readonly RouteCollection[];\n readonly categories: readonly CategoryTreeTerm[];\n}\n\nexport const LEGACY_BLOG_DETAIL_PATH = \"/blog\";\n\n/** 글의 정규 공개 경로(origin 제외). 상세 주소가 없으면 null. */\nexport function resolvePostPublicPath(\n post: PublicPathPost,\n context: PublicPathContext,\n): string | null {\n const presentation = context.presentation;\n if (presentation && presentation.kind !== \"data_only\") {\n if (presentation.kind === \"fixed_page\") return presentation.path;\n if (presentation.kind === \"detail\") {\n if (!presentation.detailPath.includes(\":slug\")) return null;\n return presentation.detailPath.replace(\":slug\", encodeURIComponent(post.slug));\n }\n return resolveCategoryTreeEntryPath(presentation, post, context.categories);\n }\n\n const collectionKey =\n post.collectionKey ??\n (presentation ? context.compatibilityCollectionKey ?? post.modelKey ?? null : null);\n const routable = { slug: post.slug, terms: post.terms, collectionKey };\n if (!resolvePostCollection(routable, context.collections)) {\n // 모델 글인데 규칙도 컬렉션도 없으면 주소가 없다. 레거시 단일 블로그만 /blog 폴백.\n if (presentation) return null;\n return `${LEGACY_BLOG_DETAIL_PATH}/${post.slug}`;\n }\n return resolvePostPath(routable, context.collections);\n}\n","// ADR-0034 §5 + §6 — WP delimiter parse/serialize\n//\n// WP 의 block serialization 은 HTML 주석 형태:\n// <!-- wp:paragraph {\"attr\":1} -->\n// <p>본문</p>\n// <!-- /wp:paragraph -->\n//\n// Round-trip 룰 (codex v2 verdict #7 + v3 verdict #4):\n// - `_id` 는 export 시 strip (WP-side import 가 unknown attribute 로 인식해 깨짐 차단)\n// - `rawHtml` ↔ innerHTML\n// - 우리가 schema 정의 안 한 block = Tiptap opaque atom node (편집 불가, 보존)\n\nimport type { Block } from \"./block\";\n\n/**\n * Block → WP delimiter 형식 직렬화 (export).\n * `_id` 와 `metadata.reusableRef` 는 strip — WP 호환.\n */\nexport function serializeBlock(block: Block): string {\n const exportable = sanitizeAttributesForExport(block);\n const attrsJson = serializeAttributes(exportable.attributes);\n const innerHtml = block.rawHtml ?? \"\";\n const innerSerialized = (block.innerBlocks ?? [])\n .map((b) => serializeBlock(b))\n .join(\"\\n\");\n\n // self-closing 형태 (innerHTML + innerBlocks 모두 없으면)\n if (!innerHtml && innerSerialized.length === 0) {\n return attrsJson\n ? `<!-- wp:${normalizeName(block.name)} ${attrsJson} /-->`\n : `<!-- wp:${normalizeName(block.name)} /-->`;\n }\n\n const open = attrsJson\n ? `<!-- wp:${normalizeName(block.name)} ${attrsJson} -->`\n : `<!-- wp:${normalizeName(block.name)} -->`;\n const close = `<!-- /wp:${normalizeName(block.name)} -->`;\n\n const body = [innerHtml, innerSerialized].filter(Boolean).join(\"\\n\");\n return `${open}\\n${body}\\n${close}`;\n}\n\n/** WP-side 에서 `core/` 는 보통 namespace 생략 (e.g. `<!-- wp:paragraph -->`) */\nfunction normalizeName(name: string): string {\n return name.startsWith(\"core/\") ? name.slice(\"core/\".length) : name;\n}\n\nfunction serializeAttributes(attrs: Record<string, unknown>): string {\n const keys = Object.keys(attrs);\n if (keys.length === 0) return \"\";\n return JSON.stringify(attrs);\n}\n\nfunction sanitizeAttributesForExport(block: Block): {\n attributes: Record<string, unknown>;\n} {\n // `_id` 와 `metadata.reusableRef` 제거. attributes 안의 임의 키는 그대로 export.\n return { attributes: { ...block.attributes } };\n}\n","// ADR-0034 §5 — `core/embed` 영상 임베드 헬퍼 (provider allowlist)\n//\n// 본 모듈은 editor (URL → embed 노드 정규화) 와 renderer (저장된 embed src 의\n// 출력 시 재검증, defense-in-depth) 양쪽에서 공유. 순수 모듈 — `URL` 만 사용해\n// 브라우저/서버 모두 안전.\n//\n// 보안 원칙: 임의 iframe src 를 신뢰하지 않는다. 신규 삽입은 `normalizeEmbed`\n// 가 allowlist provider 의 canonical embed URL 로만 변환하고, 출력은\n// `isAllowedEmbedSrc` 로 host 를 한 번 더 검증한다 (legacy/import/외부 테넌트가\n// 임의 src 를 DB 에 남겨도 렌더 단계에서 차단).\n\nexport type EmbedProvider = \"youtube\" | \"vimeo\";\n\nexport interface NormalizedEmbed {\n provider: EmbedProvider;\n /** allowlist host 의 canonical iframe src (가능 시 privacy-enhanced). */\n embedSrc: string;\n /** 사용자가 입력한 원본 URL — fallback 링크 / round-trip 보존용. */\n originalUrl: string;\n}\n\nconst YOUTUBE_HOSTS = new Set([\n \"youtube.com\",\n \"www.youtube.com\",\n \"m.youtube.com\",\n \"youtu.be\",\n \"www.youtu.be\",\n]);\n\nconst VIMEO_HOSTS = new Set([\"vimeo.com\", \"www.vimeo.com\", \"player.vimeo.com\"]);\n\n/** 출력(iframe) 시 허용되는 embed src host — render 단계 defense-in-depth. */\nconst ALLOWED_EMBED_HOSTS = new Set([\n \"www.youtube-nocookie.com\",\n \"youtube-nocookie.com\",\n \"www.youtube.com\",\n \"youtube.com\",\n \"player.vimeo.com\",\n]);\n\nconst YOUTUBE_ID = /^[A-Za-z0-9_-]{11}$/;\nconst VIMEO_ID = /^\\d+$/;\n\n/**\n * 사용자 입력 URL → allowlist provider 의 canonical embed. 지원하지 않는\n * provider / 파싱 실패 시 `null`.\n */\nexport function normalizeEmbed(input: unknown): NormalizedEmbed | null {\n if (typeof input !== \"string\") return null;\n const raw = input.trim();\n if (raw.length === 0) return null;\n\n let url: URL;\n try {\n url = new URL(raw);\n } catch {\n return null;\n }\n if (url.protocol !== \"https:\" && url.protocol !== \"http:\") return null;\n\n const host = url.hostname.toLowerCase();\n\n if (YOUTUBE_HOSTS.has(host)) {\n const id = extractYouTubeId(url, host);\n if (id) {\n return {\n provider: \"youtube\",\n embedSrc: `https://www.youtube-nocookie.com/embed/${id}`,\n originalUrl: raw,\n };\n }\n }\n\n if (VIMEO_HOSTS.has(host)) {\n const id = extractVimeoId(url, host);\n if (id) {\n return {\n provider: \"vimeo\",\n embedSrc: `https://player.vimeo.com/video/${id}`,\n originalUrl: raw,\n };\n }\n }\n\n return null;\n}\n\n/**\n * 이미 저장된 embed src 가 allowlist host(https) 인지 검증 — 렌더러가 iframe\n * 출력 전 호출. 임의 host / 비 https 는 거부.\n */\nexport function isAllowedEmbedSrc(value: unknown): value is string {\n if (typeof value !== \"string\") return false;\n let url: URL;\n try {\n url = new URL(value);\n } catch {\n return false;\n }\n if (url.protocol !== \"https:\") return false;\n const host = url.hostname.toLowerCase();\n if (!ALLOWED_EMBED_HOSTS.has(host)) return false;\n // host 뿐 아니라 canonical embed path 형태까지 검증 — 허용 host 의 임의 경로\n // (예: 스크립트·홈) 가 iframe 으로 로드되는 것을 차단.\n if (host === \"player.vimeo.com\") {\n return /^\\/video\\/\\d+$/.test(url.pathname);\n }\n // youtube / youtube-nocookie → /embed/<11-char id>\n return new RegExp(\"^/embed/[A-Za-z0-9_-]{11}$\").test(url.pathname);\n}\n\n/** 지도 embed 허용 host — keyless embed 만 (API 키 의존 provider 금지). */\nconst GOOGLE_MAP_HOSTS = new Set([\"www.google.com\", \"google.com\", \"maps.google.com\"]);\nconst OSM_HOSTS = new Set([\"www.openstreetmap.org\", \"openstreetmap.org\"]);\n\n/**\n * 지도 embed src allowlist — 렌더러가 contact 지도 iframe 출력 전 호출\n * (W8-7, `isAllowedEmbedSrc` 의 영상 allowlist 와 별도).\n *\n * 별도 함수인 이유: 지도는 \"외부 API 키가 필요 없는 keyless embed 만\"이라는\n * 추가 제약이 있다. 허용 형태:\n * - Google Maps 공유 embed: https://www.google.com/maps/embed?pb=…\n * - Google Maps keyless 쿼리 embed: https://maps.google.com/maps?q=…&output=embed\n * - OpenStreetMap export embed: https://www.openstreetmap.org/export/embed.html?…\n * `/maps/embed/v1/*`(Embed API — 키 필요) 와 그 밖의 host/경로는 거부 —\n * 거부 시 렌더러는 기존 facade 이미지/길찾기 링크 폴백을 유지한다(fail-soft).\n */\nexport function isAllowedMapEmbedSrc(value: unknown): value is string {\n if (typeof value !== \"string\") return false;\n let url: URL;\n try {\n url = new URL(value);\n } catch {\n return false;\n }\n if (url.protocol !== \"https:\") return false;\n const host = url.hostname.toLowerCase();\n if (GOOGLE_MAP_HOSTS.has(host)) {\n if (url.pathname === \"/maps/embed\") return true;\n if (url.pathname === \"/maps\" || url.pathname === \"/maps/\") {\n return url.searchParams.get(\"output\") === \"embed\";\n }\n return false;\n }\n if (OSM_HOSTS.has(host)) {\n return url.pathname === \"/export/embed.html\";\n }\n return false;\n}\n\nfunction extractYouTubeId(url: URL, host: string): string | null {\n if (host === \"youtu.be\" || host === \"www.youtu.be\") {\n const id = url.pathname.split(\"/\").filter(Boolean)[0] ?? \"\";\n return YOUTUBE_ID.test(id) ? id : null;\n }\n const v = url.searchParams.get(\"v\");\n if (v && YOUTUBE_ID.test(v)) return v;\n const parts = url.pathname.split(\"/\").filter(Boolean);\n if (\n parts.length >= 2 &&\n (parts[0] === \"embed\" || parts[0] === \"shorts\" || parts[0] === \"v\")\n ) {\n return YOUTUBE_ID.test(parts[1]) ? parts[1] : null;\n }\n return null;\n}\n\nfunction extractVimeoId(url: URL, host: string): string | null {\n const parts = url.pathname.split(\"/\").filter(Boolean);\n let candidate: string | undefined;\n if (host === \"player.vimeo.com\") {\n const vi = parts.indexOf(\"video\");\n candidate = vi >= 0 ? parts[vi + 1] : parts[parts.length - 1];\n } else {\n candidate = parts[0];\n }\n return candidate && VIMEO_ID.test(candidate) ? candidate : null;\n}\n","// FAQ 추출 — Tiptap doc 에서 faq 블록의 Q/A 를 평문으로 수집.\n//\n// 두 렌더러(cms-renderer-astro / cms-renderer-next)가 공유하여 FAQPage\n// 구조화데이터(JSON-LD)를 생성한다. 직렬화(HTML <details>)는 각 렌더러가\n// 자체 처리(마크업 패리티), 본 모듈은 schema.org 용 평문 추출만 담당.\n\ninterface DocNode {\n type?: string;\n text?: string;\n content?: DocNode[];\n}\n\nexport interface FaqEntry {\n question: string;\n answer: string;\n}\n\n/** 노드 트리의 모든 text 를 이어붙여 평문화 (공백 정규화). */\nfunction plainText(node: DocNode | undefined): string {\n if (!node) return \"\";\n if (typeof node.text === \"string\") return node.text;\n if (!Array.isArray(node.content)) return \"\";\n // 블록 경계는 공백으로 — 문단 사이가 붙지 않도록.\n return node.content.map(plainText).join(\" \");\n}\n\nfunction collapse(s: string): string {\n return s.replace(/\\s+/g, \" \").trim();\n}\n\n/** doc 을 순회하며 faqItem(질문/답변)을 순서대로 수집. */\nexport function extractFaqEntries(doc: unknown): FaqEntry[] {\n const out: FaqEntry[] = [];\n const walk = (node: DocNode | undefined): void => {\n if (!node || typeof node !== \"object\") return;\n if (node.type === \"faqItem\" && Array.isArray(node.content)) {\n const q = node.content.find((c) => c.type === \"faqQuestion\");\n const a = node.content.find((c) => c.type === \"faqAnswer\");\n const question = collapse(plainText(q));\n const answer = collapse(plainText(a));\n if (question && answer) out.push({ question, answer });\n return; // faqItem 내부는 더 내려가지 않음\n }\n if (Array.isArray(node.content)) node.content.forEach(walk);\n };\n if (doc && typeof doc === \"object\") {\n walk(doc as DocNode);\n }\n return out;\n}\n\n/** FAQPage JSON-LD 객체 (script 삽입은 렌더러 책임). null = 항목 없음. */\nexport function faqPageJsonLd(\n entries: FaqEntry[],\n): Record<string, unknown> | null {\n if (entries.length === 0) return null;\n return {\n \"@context\": \"https://schema.org\",\n \"@type\": \"FAQPage\",\n mainEntity: entries.map((e) => ({\n \"@type\": \"Question\",\n name: e.question,\n acceptedAnswer: { \"@type\": \"Answer\", text: e.answer },\n })),\n };\n}\n","/**\n * 본문 이미지 대체텍스트의 제품 안전 상한.\n *\n * HTML 표준의 길이 제한이 아니라, 본문·캡션 전체가 alt 에 잘못 저장돼\n * 스크린리더와 검색 결과를 오염시키는 사고를 막기 위한 RootTale 계약이다.\n */\nexport const IMAGE_ALT_MAX_LENGTH = 160;\n\n/**\n * 이미지 alt 를 한 줄 설명으로 정규화한다.\n *\n * - 문자열이 아니면 빈 alt\n * - 개행·연속 공백을 한 칸으로 축약\n * - 160자를 넘으면 말줄임표를 포함해 상한 안에서 자름\n */\nexport function normalizeImageAlt(value: unknown): string {\n if (typeof value !== \"string\") return \"\";\n const normalized = value.replace(/\\s+/g, \" \").trim();\n const characters = Array.from(normalized);\n if (characters.length <= IMAGE_ALT_MAX_LENGTH) return normalized;\n\n const visible = characters\n .slice(0, IMAGE_ALT_MAX_LENGTH - 1)\n .join(\"\")\n .trimEnd();\n return `${visible}…`;\n}\n","import postcss, { type Container, type Rule } from \"postcss\";\nimport sanitizeHtmlLibrary from \"sanitize-html\";\nimport type sanitizeHtmlTypes from \"sanitize-html\";\n\nconst IMPORT_SCOPE_RE = /^rt-import-[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;\nconst ALLOWED_AT_RULES = new Set([\n \"container\",\n \"font-face\",\n \"keyframes\",\n \"-webkit-keyframes\",\n \"layer\",\n \"media\",\n \"supports\",\n]);\nconst URL_ATTRIBUTES = new Set([\"href\", \"src\"]);\nconst ALLOWED_URL_SCHEMES = new Set([\"http\", \"https\", \"mailto\", \"tel\"]);\nconst UNSAFE_CSS_VALUE_RE =\n /(?:expression\\s*\\(|javascript\\s*:|vbscript\\s*:|@import|<\\/?(?:style|script)|\\\\0|\\\\0000)/i;\nconst UNSAFE_CSS_PROPERTY_RE = /^(?:behavior|-moz-binding)$/i;\nconst FIXED_POSITION_RE = /^(?:fixed|sticky)$/i;\nconst ORIGINAL_H1_ATTRIBUTE = \"data-rt-original-heading\";\nconst SCOPE_ROOT_BOUNDARY_PROPERTIES = new Set([\n \"bottom\",\n \"inset\",\n \"inset-block\",\n \"inset-inline\",\n \"left\",\n \"position\",\n \"right\",\n \"rotate\",\n \"scale\",\n \"top\",\n \"transform\",\n \"translate\",\n \"z-index\",\n]);\n\nfunction fromCodePointSafe(value: string, radix: number): string {\n const codePoint = Number.parseInt(value, radix);\n if (!Number.isFinite(codePoint) || codePoint < 0 || codePoint > 0x10ffff) {\n return \"\";\n }\n try {\n return String.fromCodePoint(codePoint);\n } catch {\n return \"\";\n }\n}\n\nfunction isSafeUrl(tagName: string, attributeName: string, value: string): boolean {\n const normalized = value\n .replace(/&#x([0-9a-f]+);?/gi, (_, hex: string) => fromCodePointSafe(hex, 16))\n .replace(/&#(\\d+);?/g, (_, decimal: string) => fromCodePointSafe(decimal, 10))\n .replace(/[\\u0000-\\u0020\\u007f]/g, \"\")\n .toLowerCase();\n const scheme = /^([a-z][a-z0-9+.-]*):/.exec(normalized)?.[1];\n if (!scheme) return true;\n if (ALLOWED_URL_SCHEMES.has(scheme)) return true;\n return (\n tagName === \"img\" &&\n attributeName === \"src\" &&\n scheme === \"data\" &&\n normalized.startsWith(\"data:image/\") &&\n !normalized.startsWith(\"data:image/svg\")\n );\n}\n\nfunction cssUrlsAreSafe(\n value: string,\n allowedAssetUrls: ReadonlySet<string>,\n): boolean {\n const urlRe = /url\\(\\s*([\"']?)([^)'\"\\s]+)\\1\\s*\\)/gi;\n let matched = false;\n for (const match of value.matchAll(urlRe)) {\n matched = true;\n const url = match[2]?.trim() ?? \"\";\n if (!url.startsWith(\"#\") && !allowedAssetUrls.has(url)) return false;\n }\n return !/url\\s*\\(/i.test(value) || matched;\n}\n\nfunction declarationIsSafe(\n property: string,\n value: string,\n allowedAssetUrls: ReadonlySet<string>,\n): boolean {\n if (UNSAFE_CSS_PROPERTY_RE.test(property) || UNSAFE_CSS_VALUE_RE.test(value)) {\n return false;\n }\n if (!cssUrlsAreSafe(value, allowedAssetUrls)) return false;\n if (property === \"position\" && /\\b(?:fixed|sticky)\\b/i.test(value)) {\n return false;\n }\n if (property.startsWith(\"--\") && FIXED_POSITION_RE.test(value.trim())) {\n return false;\n }\n if (property === \"z-index\") {\n const normalized = value.trim().toLowerCase();\n if (normalized !== \"auto\") {\n const zIndex = Number(normalized);\n if (!Number.isInteger(zIndex) || Math.abs(zIndex) > 100) return false;\n }\n }\n for (const match of value.matchAll(/(-?\\d+(?:\\.\\d+)?)(px|vh|vw)/gi)) {\n const magnitude = Math.abs(Number(match[1]));\n const limit = match[2].toLowerCase() === \"px\" ? 4_000 : 200;\n if (magnitude > limit) return false;\n }\n return true;\n}\n\nfunction cleanCssContainer(\n container: Container,\n allowedAssetUrls: ReadonlySet<string>,\n): void {\n container.walkComments((comment) => {\n comment.remove();\n });\n container.walkAtRules((atRule) => {\n const name = atRule.name.toLowerCase();\n const isKeyframes = name === \"keyframes\" || name === \"-webkit-keyframes\";\n if (\n !ALLOWED_AT_RULES.has(name) ||\n !atRule.nodes?.length ||\n (name === \"font-face\" && atRule.parent?.type !== \"root\") ||\n (isKeyframes && !/^[a-z_][\\w-]*$/i.test(atRule.params.trim())) ||\n /<\\/?(?:style|script)/i.test(atRule.params)\n ) {\n atRule.remove();\n }\n });\n container.walkDecls((declaration) => {\n const property = declaration.prop.trim().toLowerCase();\n if (\n !property ||\n !declarationIsSafe(property, declaration.value, allowedAssetUrls)\n ) {\n declaration.remove();\n return;\n }\n });\n container.walkRules((rule) => {\n if (\n !rule.selector.trim() ||\n !rule.nodes?.length ||\n /<\\/?(?:style|script)/i.test(rule.selector)\n ) {\n rule.remove();\n }\n });\n container.walkAtRules(\"font-face\", (fontFace) => {\n const declarations = fontFace.nodes ?? [];\n const hasFamily = declarations.some(\n (node) => node.type === \"decl\" && node.prop.toLowerCase() === \"font-family\",\n );\n const hasSource = declarations.some(\n (node) => node.type === \"decl\" && node.prop.toLowerCase() === \"src\",\n );\n if (!hasFamily || !hasSource) fontFace.remove();\n });\n}\n\n/** ChatGPT HTML의 style 블록에서 실행·외부 로드 위험만 제거한다. */\nexport function sanitizeImportedCss(\n css: string,\n allowedAssetUrls: readonly string[] = [],\n): string {\n if (!css.trim()) return \"\";\n try {\n const root = postcss.parse(css);\n cleanCssContainer(root, new Set(allowedAssetUrls));\n return root.toString().trim();\n } catch {\n return \"\";\n }\n}\n\nfunction sanitizeInlineStyle(\n style: string,\n allowedAssetUrls: ReadonlySet<string>,\n): string {\n try {\n const root = postcss.parse(`x{${style}}`);\n cleanCssContainer(root, allowedAssetUrls);\n const rule = root.first;\n if (!rule || rule.type !== \"rule\") return \"\";\n return rule.nodes.map((node) => node.toString()).join(\";\");\n } catch {\n return \"\";\n }\n}\n\nfunction sanitizeImportedAttributes(\n tagName: string,\n attributes: sanitizeHtmlTypes.Attributes,\n allowedAssetUrls: ReadonlySet<string>,\n): sanitizeHtmlTypes.Tag {\n const next: sanitizeHtmlTypes.Attributes = {};\n for (const [name, value] of Object.entries(attributes)) {\n const attributeName = name.toLowerCase();\n if (attributeName === \"style\") {\n const style = sanitizeInlineStyle(value, allowedAssetUrls);\n if (style) next[name] = style;\n continue;\n }\n if (attributeName === \"srcset\") {\n const srcset = sanitizeSrcset(value);\n if (srcset) next[name] = srcset;\n continue;\n }\n if (\n (attributeName === \"fill\" || attributeName === \"stroke\") &&\n /url\\s*\\(/i.test(value) &&\n !/^url\\(#[a-z_][\\w:.-]*\\)$/i.test(value.trim())\n ) {\n continue;\n }\n if (\n (tagName === \"use\" || tagName === \"image\") &&\n (attributeName === \"href\" || attributeName === \"xlink:href\") &&\n !value.trim().startsWith(\"#\")\n ) {\n continue;\n }\n next[name] =\n URL_ATTRIBUTES.has(attributeName) &&\n !isSafeUrl(tagName, attributeName, value)\n ? \"#\"\n : value;\n }\n if (tagName === \"a\" && next.target === \"_blank\") {\n next.rel = \"noopener noreferrer\";\n }\n if (tagName === \"h1\") {\n next[ORIGINAL_H1_ATTRIBUTE] = \"h1\";\n return { tagName: \"h2\", attribs: next };\n }\n return { tagName, attribs: next };\n}\n\nfunction sanitizeSrcset(value: string): string {\n const candidates: string[] = [];\n for (const rawCandidate of value.split(\",\")) {\n const candidate = rawCandidate.trim();\n if (!candidate) continue;\n const [url, ...descriptorParts] = candidate.split(/\\s+/);\n if (!url || !isSafeUrl(\"img\", \"src\", url)) continue;\n const descriptor = descriptorParts.join(\" \");\n if (descriptor && !/^(?:\\d+(?:\\.\\d+)?x|\\d+w)$/.test(descriptor)) continue;\n candidates.push(descriptor ? `${url} ${descriptor}` : url);\n }\n return candidates.join(\", \");\n}\n\nconst IMPORTED_HTML_OPTIONS: sanitizeHtmlTypes.IOptions = {\n allowedTags: [\n \"a\", \"abbr\", \"address\", \"article\", \"aside\", \"b\", \"bdi\", \"bdo\",\n \"blockquote\", \"br\", \"caption\", \"cite\", \"code\", \"col\", \"colgroup\",\n \"dd\", \"del\", \"details\", \"dfn\", \"div\", \"dl\", \"dt\", \"em\",\n \"figcaption\", \"figure\", \"footer\", \"h1\", \"h2\", \"h3\", \"h4\", \"h5\", \"h6\",\n \"header\", \"hr\", \"i\", \"img\", \"kbd\", \"li\", \"main\", \"mark\", \"nav\",\n \"ol\", \"p\", \"picture\", \"pre\", \"q\", \"s\", \"samp\", \"section\", \"small\",\n \"source\", \"svg\", \"g\", \"path\", \"circle\", \"ellipse\", \"line\", \"polyline\",\n \"polygon\", \"rect\", \"defs\", \"lineargradient\", \"radialgradient\", \"stop\",\n \"use\", \"clippath\", \"mask\", \"pattern\", \"title\", \"desc\",\n \"span\", \"strong\", \"sub\", \"summary\", \"sup\", \"table\", \"tbody\", \"td\",\n \"tfoot\", \"th\", \"thead\", \"time\", \"tr\", \"u\", \"ul\", \"var\", \"wbr\",\n ],\n allowedAttributes: {\n \"*\": [\"aria-*\", \"class\", \"data-*\", \"dir\", \"id\", \"lang\", \"role\", \"style\", \"title\"],\n a: [\n \"href\",\n { name: \"rel\", multiple: true, values: [\"noopener\", \"noreferrer\"] },\n { name: \"target\", values: [\"_blank\"] },\n ],\n col: [\"span\", \"style\"],\n img: [\"alt\", \"height\", \"loading\", \"sizes\", \"src\", \"srcset\", \"style\", \"width\"],\n source: [\"height\", \"media\", \"sizes\", \"src\", \"srcset\", \"type\", \"width\"],\n svg: [\"aria-*\", \"class\", \"fill\", \"height\", \"preserveAspectRatio\", \"role\", \"stroke\", \"style\", \"viewBox\", \"viewbox\", \"width\", \"xmlns\"],\n g: [\"class\", \"fill\", \"stroke\", \"stroke-width\", \"style\", \"transform\"],\n path: [\"class\", \"d\", \"fill\", \"fill-rule\", \"stroke\", \"stroke-linecap\", \"stroke-linejoin\", \"stroke-width\", \"style\", \"transform\"],\n circle: [\"class\", \"cx\", \"cy\", \"fill\", \"r\", \"stroke\", \"stroke-width\", \"style\", \"transform\"],\n ellipse: [\"class\", \"cx\", \"cy\", \"fill\", \"rx\", \"ry\", \"stroke\", \"stroke-width\", \"style\", \"transform\"],\n line: [\"class\", \"stroke\", \"stroke-linecap\", \"stroke-width\", \"style\", \"transform\", \"x1\", \"x2\", \"y1\", \"y2\"],\n polyline: [\"class\", \"fill\", \"points\", \"stroke\", \"stroke-linecap\", \"stroke-linejoin\", \"stroke-width\", \"style\", \"transform\"],\n polygon: [\"class\", \"fill\", \"points\", \"stroke\", \"stroke-width\", \"style\", \"transform\"],\n rect: [\"class\", \"fill\", \"height\", \"rx\", \"ry\", \"stroke\", \"stroke-width\", \"style\", \"transform\", \"width\", \"x\", \"y\"],\n lineargradient: [\"gradientTransform\", \"gradientUnits\", \"id\", \"spreadMethod\", \"x1\", \"x2\", \"y1\", \"y2\"],\n radialgradient: [\"cx\", \"cy\", \"fx\", \"fy\", \"gradientTransform\", \"gradientUnits\", \"id\", \"r\", \"spreadMethod\"],\n stop: [\"offset\", \"stop-color\", \"stop-opacity\", \"style\"],\n use: [\"fill\", \"height\", \"href\", \"stroke\", \"style\", \"width\", \"x\", \"xlink:href\", \"y\"],\n clippath: [\"clipPathUnits\", \"id\", \"transform\"],\n mask: [\"height\", \"id\", \"maskContentUnits\", \"maskUnits\", \"width\", \"x\", \"y\"],\n pattern: [\"height\", \"id\", \"patternContentUnits\", \"patternTransform\", \"patternUnits\", \"preserveAspectRatio\", \"viewBox\", \"width\", \"x\", \"y\"],\n td: [\"colspan\", \"rowspan\", \"style\"],\n th: [\n \"colspan\",\n \"rowspan\",\n \"style\",\n { name: \"scope\", values: [\"col\", \"row\", \"colgroup\", \"rowgroup\"] },\n ],\n time: [\"datetime\"],\n },\n allowedSchemes: [\"http\", \"https\", \"mailto\", \"tel\"],\n allowedSchemesByTag: { img: [\"http\", \"https\", \"data\"] },\n allowedSchemesAppliedToAttributes: [\"href\", \"src\"],\n allowProtocolRelative: false,\n nonTextTags: [\"script\", \"style\", \"textarea\", \"option\", \"iframe\", \"object\", \"embed\", \"foreignobject\", \"math\", \"template\"],\n};\n\n/** class·id·안전한 inline style을 보존하는 원문 디자인 전용 HTML 경계. */\nexport function sanitizeImportedHtml(\n html: string,\n allowedAssetUrls: readonly string[] = [],\n): string {\n if (!html) return html;\n const allowedSet = new Set(allowedAssetUrls);\n return sanitizeHtmlLibrary(html, {\n ...IMPORTED_HTML_OPTIONS,\n transformTags: {\n \"*\": (tagName, attributes) =>\n sanitizeImportedAttributes(tagName, attributes, allowedSet),\n },\n });\n}\n\n/** 구 버전 저장값도 현재의 html/body 가상 루트 계약으로 올린다. */\nexport function ensureImportedHtmlDocumentRoots(\n html: string,\n allowedAssetUrls: readonly string[] = [],\n): string {\n const safeHtml = sanitizeImportedHtml(html, allowedAssetUrls).trim();\n if (!safeHtml) return safeHtml;\n if (\n /^<div\\b[^>]*\\bdata-rt-import-document(?:=(?:\"\"|'')|(?=[\\s>]))[^>]*>\\s*<div\\b[^>]*\\bdata-rt-import-body(?:=(?:\"\"|'')|(?=[\\s>]))/i.test(\n safeHtml,\n )\n ) {\n return safeHtml;\n }\n return `<div data-rt-import-document><div data-rt-import-body>${safeHtml}</div></div>`;\n}\n\nfunction normalizeDocumentSelector(selector: string): string {\n const typeBoundary = \"(?=$|[\\\\s>+~,.#:\\\\[\\\\)])\";\n return selector\n .replace(/html\\s*>\\s*body/gi, \"[data-rt-import-document] > [data-rt-import-body]\")\n .replace(/html\\s+body/gi, \"[data-rt-import-document] [data-rt-import-body]\")\n .replace(new RegExp(`(^|[\\\\s>+~,\\\\(])html${typeBoundary}`, \"gi\"), \"$1[data-rt-import-document]\")\n .replace(new RegExp(`(^|[\\\\s>+~,\\\\(])body${typeBoundary}`, \"gi\"), \"$1[data-rt-import-body]\")\n .replace(\n new RegExp(`(^|[\\\\s>+~,\\\\(])h1${typeBoundary}`, \"gi\"),\n `$1[${ORIGINAL_H1_ATTRIBUTE}=\"h1\"]`,\n )\n .replace(/:root/g, \"[data-rt-import-document]\");\n}\n\nfunction selectorTargetsDocumentBoundary(selector: string): boolean {\n return selector.split(\",\").some((part) => {\n const normalized = part.trim();\n const targetsDocumentRoot =\n normalized.startsWith(\"[data-rt-import-document]\") &&\n !/[\\s>+~]/.test(normalized.slice(\"[data-rt-import-document]\".length));\n const targetsBodyRoot =\n /\\[data-rt-import-body\\](?:[.#:][\\w-]+|\\[[^\\]]+\\])*\\s*$/.test(\n normalized,\n );\n return targetsDocumentRoot || targetsBodyRoot;\n });\n}\n\nfunction replaceAnimationName(value: string, names: ReadonlyMap<string, string>): string {\n let next = value;\n for (const [name, scopedName] of names) {\n const escaped = name.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n next = next.replace(new RegExp(`(^|[^\\\\w-])${escaped}(?=$|[^\\\\w-])`, \"g\"), `$1${scopedName}`);\n }\n return next;\n}\n\nfunction unquoteCssIdentifier(value: string): string | null {\n const normalized = value.trim();\n const match = /^(?:\"([^\"]+)\"|'([^']+)'|([a-z_][\\w -]*))$/i.exec(normalized);\n return (match?.[1] ?? match?.[2] ?? match?.[3] ?? \"\").trim() || null;\n}\n\nfunction replaceFontFamilyName(\n value: string,\n names: ReadonlyMap<string, string>,\n): string {\n let next = value;\n for (const [name, scopedName] of names) {\n const escaped = name.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n next = next.replace(\n new RegExp(`([\"'])${escaped}\\\\1|(^|[,\\\\s])${escaped}(?=$|[,\\\\s/])`, \"gi\"),\n (matched, quote: string | undefined, prefix: string | undefined) =>\n quote ? `${quote}${scopedName}${quote}` : `${prefix ?? \"\"}${scopedName}`,\n );\n }\n return next;\n}\n\n/** 저장된 안전 CSS가 같은 importedHtml 블록 밖으로 새지 않도록 범위를 고정한다. */\nexport function scopeImportedCss(\n css: string,\n scope: string,\n allowedAssetUrls: readonly string[] = [],\n): string {\n if (!IMPORT_SCOPE_RE.test(scope)) return \"\";\n const safeCss = sanitizeImportedCss(css, allowedAssetUrls);\n if (!safeCss) return \"\";\n try {\n const root = postcss.parse(safeCss);\n const keyframeNames = new Map<string, string>();\n const fontFamilyNames = new Map<string, string>();\n const fontFaces = root.nodes.filter(\n (node) => node.type === \"atrule\" && node.name.toLowerCase() === \"font-face\",\n );\n for (const fontFace of fontFaces) {\n if (fontFace.type !== \"atrule\") continue;\n const family = fontFace.nodes?.find(\n (node) => node.type === \"decl\" && node.prop.toLowerCase() === \"font-family\",\n );\n if (!family || family.type !== \"decl\") {\n fontFace.remove();\n continue;\n }\n const originalName = unquoteCssIdentifier(family.value);\n const hasSupportedSource = fontFace.nodes?.some(\n (node) =>\n node.type === \"decl\" &&\n node.prop.toLowerCase() === \"src\" &&\n /url\\(\\s*[\"']?https:\\/\\/[^)'\"\\s]+\\.woff2?(?:[?#][^)'\"\\s]*)?[\"']?\\s*\\)/i.test(\n node.value,\n ),\n );\n if (!originalName || !hasSupportedSource) {\n fontFace.remove();\n continue;\n }\n const scopedName = `${scope}-${originalName}`;\n fontFamilyNames.set(originalName, scopedName);\n family.value = family.value.replace(originalName, scopedName);\n }\n root.walkAtRules((atRule) => {\n const name = atRule.name.toLowerCase();\n if (name !== \"keyframes\" && name !== \"-webkit-keyframes\") return;\n const originalName = atRule.params.trim();\n const scopedName = `${scope}-${originalName}`;\n keyframeNames.set(originalName, scopedName);\n atRule.params = scopedName;\n });\n root.walkDecls((declaration) => {\n const property = declaration.prop.trim().toLowerCase();\n if (property === \"animation\" || property === \"animation-name\") {\n declaration.value = replaceAnimationName(declaration.value, keyframeNames);\n }\n if (property === \"font-family\" || property === \"font\") {\n declaration.value = replaceFontFamilyName(\n declaration.value,\n fontFamilyNames,\n );\n }\n });\n root.walkRules((rule: Rule) => {\n const parent = rule.parent;\n if (\n parent?.type === \"atrule\" &&\n (parent.name.toLowerCase() === \"keyframes\" ||\n parent.name.toLowerCase() === \"-webkit-keyframes\")\n ) {\n return;\n }\n rule.selector = normalizeDocumentSelector(rule.selector);\n if (selectorTargetsDocumentBoundary(rule.selector)) {\n rule.walkDecls((declaration) => {\n if (\n SCOPE_ROOT_BOUNDARY_PROPERTIES.has(\n declaration.prop.trim().toLowerCase(),\n )\n ) {\n declaration.remove();\n }\n });\n }\n });\n const scoped = postcss.atRule({\n name: \"scope\",\n params: `([data-rt-import-scope=\"${scope}\"])`,\n });\n const preservedFontFaces = fontFaces.filter((fontFace) => fontFace.parent);\n for (const fontFace of preservedFontFaces) fontFace.remove();\n scoped.append(root.nodes);\n return [...preservedFontFaces.map((fontFace) => fontFace.toString()), scoped.toString()]\n .filter(Boolean)\n .join(\"\\n\");\n } catch {\n return \"\";\n }\n}\n","// 본문 예약 내부 링크 토큰 — `[[internal:{키}|표시 문구]]`.\n//\n// 키는 대상 글의 정규 공개 경로 조각을 점으로 이은 값(`faq.두통.편두통.slug`,\n// `blog.my-post`). 토큰은 body_json 의 일반 텍스트로 저장되고, 편집기는 칩으로\n// 보여 주며, 공개 렌더러는 발행 원장의 경로와 대조해 링크로 바꾼다. 여기 정규식\n// 하나가 편집기·렌더러·FRONT 예제의 SSoT 다(어드민 `buildInternalContentLinkToken`\n// 이 만드는 형식과 같다).\n\nexport const INTERNAL_CONTENT_LINK_TOKEN_SOURCE = String.raw`\\[\\[internal:([a-z0-9가-힣]+(?:[.-][a-z0-9가-힣]+)*)\\|([^\\]\\n|]+)\\]\\]`;\n\nexport interface InternalContentLinkToken {\n /** 토큰이 시작하는 문자 인덱스(텍스트 기준). */\n readonly index: number;\n /** 원문 토큰 전체(`[[internal:…|…]]`). */\n readonly raw: string;\n /** 소문자로 정규화한 키. */\n readonly key: string;\n /** 표시 문구(앞뒤 공백 제거). */\n readonly label: string;\n}\n\n/** 텍스트 안의 토큰을 앞에서부터 모두 찾는다. 겹치지 않는다. */\nexport function findInternalContentLinkTokens(\n text: string,\n): InternalContentLinkToken[] {\n const pattern = new RegExp(INTERNAL_CONTENT_LINK_TOKEN_SOURCE, \"giu\");\n const tokens: InternalContentLinkToken[] = [];\n for (const match of text.matchAll(pattern)) {\n const [raw, key, label] = match;\n if (!key || !label || match.index === undefined) continue;\n tokens.push({\n index: match.index,\n raw,\n key: key.toLowerCase(),\n label: label.trim(),\n });\n }\n return tokens;\n}\n\n/** 공개 경로(`/faq/a/b/slug`) → 키(`faq.a.b.slug`). 조각이 없으면 null. */\nexport function internalContentKeyFromPath(\n path: string | null | undefined,\n): string | null {\n if (!path) return null;\n const segments = path\n .split(\"/\")\n .filter(Boolean)\n .map((segment) => {\n try {\n return decodeURIComponent(segment).trim().toLowerCase();\n } catch {\n return \"\";\n }\n });\n if (\n segments.length === 0 ||\n segments.some((segment) => segment === \"\" || segment.includes(\".\"))\n ) {\n return null;\n }\n return segments.join(\".\");\n}\n\n/** 키 → 공개 경로. 없으면 null(문구만 표시). */\nexport type InternalContentLinkResolver = (key: string) => string | null;\n\ninterface InternalContentPathEntry {\n /** 저장된 정규 공개 경로(`/faq/a/b/slug`). 없으면 대상이 아니다. */\n readonly path?: string | null;\n /** 옛 slug 목록 — 옛 키로 남은 본문도 현재 주소로 이어 준다. */\n readonly previousSlugs?: readonly string[] | null;\n}\n\n/**\n * 발행 글 목록에서 \"키 → 현재 경로\" 색인을 만든다. 옛 slug 도 같은 글로 등록한다\n * (경로 마지막 조각을 옛 slug 로 바꾼 키). 현재 경로가 옛 키와 겹치면 현재가 이긴다.\n */\nexport function internalContentPathIndex(\n entries: Iterable<InternalContentPathEntry>,\n): Map<string, string> {\n const index = new Map<string, string>();\n const previous = new Map<string, string>();\n for (const entry of entries) {\n const path = entry.path;\n const key = internalContentKeyFromPath(path);\n if (!path || !key) continue;\n index.set(key, path);\n const segments = key.split(\".\");\n for (const oldSlug of entry.previousSlugs ?? []) {\n const oldKey = [...segments.slice(0, -1), oldSlug.trim().toLowerCase()].join(\".\");\n if (oldKey && !oldKey.includes(\"..\")) previous.set(oldKey, path);\n }\n }\n for (const [key, path] of previous) {\n if (!index.has(key)) index.set(key, path);\n }\n return index;\n}\n\n/** Map 이나 함수 어느 쪽이든 resolver 로. */\nexport function internalContentLinkResolverFrom(\n source: InternalContentLinkResolver | ReadonlyMap<string, string> | null | undefined,\n): InternalContentLinkResolver {\n if (!source) return () => null;\n if (typeof source === \"function\") return source;\n return (key) => source.get(key.toLowerCase()) ?? null;\n}\n\nfunction isSafeInternalPath(path: string | null): path is string {\n return typeof path === \"string\" && path.startsWith(\"/\") && !path.startsWith(\"//\");\n}\n\nfunction escapeHtmlText(value: string): string {\n return value\n .replaceAll(\"&\", \"&amp;\")\n .replaceAll(\"<\", \"&lt;\")\n .replaceAll(\">\", \"&gt;\")\n .replaceAll('\"', \"&quot;\")\n .replaceAll(\"'\", \"&#39;\");\n}\n\n/** 링크로 렌더된 토큰의 class — 사이트 CSS 가 잡을 고리. */\nexport const INTERNAL_CONTENT_LINK_CLASS = \"rt-internal-link\";\n/** 대상이 아직 없어 문구만 남긴 토큰의 class·data 속성. */\nexport const INTERNAL_CONTENT_LINK_PENDING_CLASS = \"rt-internal-link--pending\";\nexport const INTERNAL_CONTENT_LINK_PENDING_ATTR = \"data-rt-internal-link-pending\";\n\n/**\n * 이미 이스케이프된 텍스트 한 구간의 토큰을 HTML 로 바꾼다. 텍스트 부분은 다시\n * 이스케이프하지 않는다(정화기가 이미 처리한 값). 토큰이 없으면 원문 그대로.\n */\nexport function renderInternalContentLinksInText(\n text: string,\n resolve: InternalContentLinkResolver,\n): { readonly html: string; readonly hasTokens: boolean } {\n const tokens = findInternalContentLinkTokens(text);\n if (tokens.length === 0) return { html: text, hasTokens: false };\n let cursor = 0;\n let html = \"\";\n for (const token of tokens) {\n html += text.slice(cursor, token.index);\n const path = resolve(token.key);\n const label = escapeHtmlText(token.label);\n html += isSafeInternalPath(path)\n ? `<a href=\"${escapeHtmlText(path)}\" class=\"${INTERNAL_CONTENT_LINK_CLASS}\">${label}</a>`\n : `<span class=\"${INTERNAL_CONTENT_LINK_CLASS} ${INTERNAL_CONTENT_LINK_PENDING_CLASS}\" ${INTERNAL_CONTENT_LINK_PENDING_ATTR}=\"${escapeHtmlText(token.key)}\">${label}</span>`;\n cursor = token.index + token.raw.length;\n }\n html += text.slice(cursor);\n return { html, hasTokens: true };\n}\n\nconst INTERNAL_LINK_BLOCKED_HTML_TAGS = new Set([\n \"a\",\n \"code\",\n \"pre\",\n \"script\",\n \"style\",\n \"textarea\",\n]);\nconst VOID_HTML_TAGS = new Set([\"br\", \"hr\", \"img\", \"input\", \"source\", \"wbr\"]);\n\nfunction htmlTagEnd(html: string, start: number): number {\n let quote: '\"' | \"'\" | null = null;\n for (let index = start + 1; index < html.length; index += 1) {\n const character = html[index];\n if (quote) {\n if (character === quote) quote = null;\n continue;\n }\n if (character === '\"' || character === \"'\") {\n quote = character;\n continue;\n }\n if (character === \">\") return index;\n }\n return -1;\n}\n\n/**\n * 정화된 HTML(원문 보존 블록 등)의 텍스트 구간에만 토큰을 적용한다. `<a>`·`<code>`·\n * `<pre>` 안과 태그 속성값은 건드리지 않는다.\n */\nexport function renderInternalContentLinksInHtml(\n html: string,\n resolve: InternalContentLinkResolver,\n): { readonly html: string; readonly hasTokens: boolean } {\n if (!html.includes(\"[[internal:\")) return { html, hasTokens: false };\n let blockedDepth = 0;\n let cursor = 0;\n let out = \"\";\n let hasTokens = false;\n const emitText = (text: string) => {\n if (blockedDepth > 0 || !text) {\n out += text;\n return;\n }\n const rendered = renderInternalContentLinksInText(text, resolve);\n hasTokens ||= rendered.hasTokens;\n out += rendered.html;\n };\n while (cursor < html.length) {\n const tagStart = html.indexOf(\"<\", cursor);\n if (tagStart < 0) {\n emitText(html.slice(cursor));\n break;\n }\n emitText(html.slice(cursor, tagStart));\n const tagEnd = htmlTagEnd(html, tagStart);\n if (tagEnd < 0) {\n out += html.slice(tagStart);\n break;\n }\n const tag = html.slice(tagStart, tagEnd + 1);\n out += tag;\n const parsed = /^<\\s*(\\/?)\\s*([a-z0-9-]+)/i.exec(tag);\n const tagName = parsed?.[2]?.toLowerCase();\n if (tagName && INTERNAL_LINK_BLOCKED_HTML_TAGS.has(tagName)) {\n const isClosing = parsed?.[1] === \"/\";\n const isVoid = VOID_HTML_TAGS.has(tagName) || /\\/\\s*>$/.test(tag);\n if (isClosing) blockedDepth = Math.max(0, blockedDepth - 1);\n else if (!isVoid) blockedDepth += 1;\n }\n cursor = tagEnd + 1;\n }\n return { html: out, hasTokens };\n}\n","// ADR-0034 §7.1.1 test #10 — Draft preview token sign/verify.\n//\n// HMAC-SHA256 signed token + DB row (post_preview_tokens_v2) lookup.\n// Atomic consume = consumedAt update at verify time.\n//\n// Token format: `<postId>.<expiresAt-ms>.<hmac-base64url>`\n\nimport { createHmac, randomBytes, timingSafeEqual } from \"node:crypto\";\n\nexport interface PreviewTokenPayload {\n postId: string;\n expiresAt: Date;\n}\n\nconst DELIMITER = \".\";\n\nfunction base64url(buf: Buffer): string {\n return buf.toString(\"base64url\");\n}\n\nfunction fromBase64url(s: string): Buffer {\n return Buffer.from(s, \"base64url\");\n}\n\nfunction sign(secret: string, message: string): string {\n return base64url(createHmac(\"sha256\", secret).update(message).digest());\n}\n\n/** 새 preview token 생성. DB insert 책임은 caller. */\nexport function issuePreviewToken(\n secret: string,\n payload: PreviewTokenPayload,\n): string {\n const exp = payload.expiresAt.getTime();\n const nonce = base64url(randomBytes(12));\n const message = `${payload.postId}${DELIMITER}${exp}${DELIMITER}${nonce}`;\n const sig = sign(secret, message);\n return `${message}${DELIMITER}${sig}`;\n}\n\n/** Token 검증 (서명 + 만료). DB row consumed_at update 는 caller (atomic). */\nexport function verifyPreviewToken(\n secret: string,\n token: string,\n): PreviewTokenPayload | null {\n const parts = token.split(DELIMITER);\n if (parts.length !== 4) return null;\n const [postId, expRaw, nonce, sig] = parts as [string, string, string, string];\n const exp = Number(expRaw);\n if (!Number.isFinite(exp)) return null;\n if (Date.now() > exp) return null;\n\n const expectedSig = sign(secret, `${postId}${DELIMITER}${exp}${DELIMITER}${nonce}`);\n const a = fromBase64url(sig);\n const b = fromBase64url(expectedSig);\n if (a.length !== b.length) return null;\n if (!timingSafeEqual(a, b)) return null;\n\n return { postId, expiresAt: new Date(exp) };\n}\n","import sanitizeHtmlLibrary from \"sanitize-html\";\nimport type sanitizeHtmlTypes from \"sanitize-html\";\n\n// ADR-0034 §7.1.1 test #7 — XSS sanitize helper (allow-list, 2차 방어).\n//\n// Phase 1 = strict CSP (`script-src 'self'`) 가 1차 방어. 본 helper 는 렌더 직전\n// 2차 방어로 위험 태그/속성/URL scheme 을 strip 한다.\n//\n// Tiptap 에디터/renderer 실측 allowlist:\n// - StarterKit: p/h2-h4/ul/ol/li/blockquote/pre/code/hr/br + strong/em/s\n// - editor 추가 mark: u/mark/span(style: color/background) + TextAlign(style)\n// - media/table/custom nodes: img/figure/figcaption/table/thead/tbody/tfoot/tr/th/td/div\n// (thead/tfoot 는 이관된 원문 HTML 표에 실제로 쓰인다. allowlist 에서 빠져 있으면\n// sanitize-html 이 그 태그만 벗겨내 `<table><tr>…` 처럼 머리행이 섹션 밖으로\n// 빠져나온 깨진 표가 되므로 안전한 구조 태그로 명시 허용한다.)\n// - FAQ renderer: details/summary + rt-cms-* classes\n// Embed iframe 은 sanitize 경로가 아니라 Tiptap JSON `renderEmbed()` 에서\n// `isAllowedEmbedSrc` 로 검증 후 렌더된다. raw HTML sanitizer 에서는 iframe 전면 strip.\n\n// URL 속성(href/src/…) — deny-list 가 아니라 **scheme allow-list** 로 판정한다.\n// HTML entity / 제어문자 인코딩(`&#106;avascript:`, `java&#9;script:`,\n// `jav&#x0d;ascript:`)은 regex 리터럴 매칭을 우회하므로, 값에서 entity 를 decode\n// 하고 ASCII 공백/제어문자를 제거(브라우저 URL 파서와 동일 정규화)한 뒤 scheme 을\n// 검사한다. sanitize-html parser 가 처리한 속성에도 동일 probe 를 적용한다.\nconst URL_ATTRS = new Set([\"href\", \"src\", \"xlink:href\"]);\n\nconst ALLOWED_URL_SCHEMES = new Set([\"http\", \"https\", \"mailto\", \"tel\"]);\nconst MALFORMED_DANGEROUS_TOKEN_RE =\n /<\\/?[^>\\s/]*(?:script|style|iframe|object|embed|svg|math|template)[^>\\s/]*(?:\\s[^>]*)?>[\\s\\S]*?<\\/?[^>\\s/]*(?:script|style|iframe|object|embed|svg|math|template)[^>\\s/]*(?:\\s[^>]*)?>/gi;\nconst SLASH_EVENT_ATTR_IN_URL_RE = /\\/on[a-z]+=/i;\nconst ROOTTALE_CLASS_RE = /^rt-[a-z0-9_-]+$/;\nconst HEX_COLOR_RE = /^#[0-9a-f]{3}(?:[0-9a-f]{3})?$/i;\nconst TEXT_ALIGN_RE = /^(left|right|center|justify)$/;\nconst ASPECT_RATIO_RE = /^\\d+(?:\\.\\d+)?\\s*\\/\\s*\\d+(?:\\.\\d+)?$/;\nconst CSS_LENGTH_RE = /^(?:0|\\d+(?:\\.\\d+)?(?:px|em|rem|%))$/i;\nconst CSS_LENGTH_LIST_RE =\n /^(?:0|\\d+(?:\\.\\d+)?(?:px|em|rem|%))(?:\\s+(?:0|\\d+(?:\\.\\d+)?(?:px|em|rem|%))){0,3}$/i;\nconst CSS_LINE_HEIGHT_RE = /^(?:normal|\\d+(?:\\.\\d+)?(?:px|em|rem|%)?)$/i;\nconst CSS_FONT_WEIGHT_RE = /^(?:normal|bold|[1-9]00)$/i;\nconst CSS_BORDER_RE =\n /^(?:none|0|(?:\\d(?:\\.\\d+)?|[12]\\d(?:\\.\\d+)?|3[0-2](?:\\.0+)?)px\\s+(?:solid|dashed|dotted)\\s+#[0-9a-f]{3}(?:[0-9a-f]{3})?)$/i;\nconst CSS_TEXT_DECORATION_RE = /^(?:none|underline|line-through)$/i;\n\nconst CSS_MAGNITUDE_LIMITS: Record<string, Record<string, number>> = {\n \"font-size\": { \"\": 0, px: 96, em: 5, rem: 5, \"%\": 200 },\n \"line-height\": { \"\": 5, px: 96, em: 5, rem: 5, \"%\": 500 },\n margin: { \"\": 0, px: 2000, em: 20, rem: 20, \"%\": 200 },\n \"margin-bottom\": { \"\": 0, px: 2000, em: 20, rem: 20, \"%\": 200 },\n \"margin-left\": { \"\": 0, px: 2000, em: 20, rem: 20, \"%\": 200 },\n \"margin-right\": { \"\": 0, px: 2000, em: 20, rem: 20, \"%\": 200 },\n \"margin-top\": { \"\": 0, px: 2000, em: 20, rem: 20, \"%\": 200 },\n padding: { \"\": 0, px: 2000, em: 20, rem: 20, \"%\": 200 },\n \"padding-bottom\": { \"\": 0, px: 2000, em: 20, rem: 20, \"%\": 200 },\n \"padding-left\": { \"\": 0, px: 2000, em: 20, rem: 20, \"%\": 200 },\n \"padding-right\": { \"\": 0, px: 2000, em: 20, rem: 20, \"%\": 200 },\n \"padding-top\": { \"\": 0, px: 2000, em: 20, rem: 20, \"%\": 200 },\n width: { \"\": 0, px: 4000, em: 100, rem: 100, \"%\": 100 },\n \"max-width\": { \"\": 0, px: 4000, em: 100, rem: 100, \"%\": 100 },\n height: { \"\": 0, px: 4000, em: 100, rem: 100, \"%\": 100 },\n};\n\nfunction hasSafeCssMagnitude(property: string, value: string): boolean {\n const limits = CSS_MAGNITUDE_LIMITS[property];\n if (!limits) return true;\n const numericTokens = value.matchAll(/(\\d+(?:\\.\\d+)?)(px|em|rem|%)?/gi);\n for (const match of numericTokens) {\n const unit = (match[2] ?? \"\").toLowerCase();\n const limit = limits[unit];\n if (limit === undefined || Number(match[1]) > limit) return false;\n }\n return true;\n}\n\nfunction filterUnsafeCssMagnitudes(style: string): string {\n return style\n .split(\";\")\n .map((declaration) => declaration.trim())\n .filter(Boolean)\n .filter((declaration) => {\n const colon = declaration.indexOf(\":\");\n if (colon < 1) return false;\n const property = declaration.slice(0, colon).trim().toLowerCase();\n const value = declaration.slice(colon + 1).trim();\n return hasSafeCssMagnitude(property, value);\n })\n .join(\";\");\n}\n\n// scheme 구성에 악용되는 named entity 만 decode (`&colon;`/`&Tab;` 등).\nconst NAMED_ENTITY: Record<string, string> = {\n colon: \":\",\n tab: \"\\t\",\n newline: \"\\n\",\n sol: \"/\",\n semi: \";\",\n num: \"#\",\n period: \".\",\n comma: \",\",\n lpar: \"(\",\n rpar: \")\",\n};\n\nfunction fromCodePointSafe(n: number): string {\n if (!Number.isFinite(n) || n < 0 || n > 0x10ffff) return \"\";\n try {\n return String.fromCodePoint(n);\n } catch {\n return \"\";\n }\n}\n\n/** URL 값 정규화 — entity decode + 공백/제어문자 제거 + 소문자 (scheme 판정용). */\nfunction decodeUrlProbe(value: string): string {\n const decoded = value\n .replace(/&#x([0-9a-f]+);?/gi, (_, h) => fromCodePointSafe(parseInt(h, 16)))\n .replace(/&#(\\d+);?/g, (_, d) => fromCodePointSafe(parseInt(d, 10)))\n .replace(/&([a-z]+);/gi, (m, name) => NAMED_ENTITY[name.toLowerCase()] ?? m);\n // 브라우저는 scheme 판정 시 ASCII 공백/제어문자(탭/개행/CR/NUL 포함)를 제거한다.\n return [...decoded].filter((c) => { const n = c.codePointAt(0) ?? 0; return n > 0x20 && n !== 0x7f; }).join(\"\").toLowerCase();\n}\n\nfunction stripMalformedDangerousTokens(html: string): string {\n let out = html;\n for (let i = 0; i < 3; i++) {\n const next = out.replace(MALFORMED_DANGEROUS_TOKEN_RE, \"\");\n if (next === out) return out;\n out = next;\n }\n return out;\n}\n\n/**\n * 안전한 URL 값인지 — 상대경로/허용 scheme, data:image/* 는 img[src] 에서만(SVG 제외).\n */\nfunction isSafeUrlValue(tag: string, attr: string, value: string): boolean {\n const probe = decodeUrlProbe(value);\n if (SLASH_EVENT_ATTR_IN_URL_RE.test(probe)) return false;\n const scheme = /^([a-z][a-z0-9+.-]*):/.exec(probe)?.[1];\n if (!scheme) return true; // 상대경로 / fragment / query\n if (ALLOWED_URL_SCHEMES.has(scheme)) return true;\n if (\n scheme === \"data\" &&\n tag === \"img\" &&\n attr === \"src\" &&\n probe.startsWith(\"data:image/\")\n ) {\n // data:image/svg+xml 은 스크립트 실행 가능 → 차단.\n return !probe.startsWith(\"data:image/svg\");\n }\n return false;\n}\n\nfunction sanitizeUrlAttributes(\n tagName: string,\n attribs: sanitizeHtmlTypes.Attributes,\n): sanitizeHtmlTypes.Tag {\n const next: sanitizeHtmlTypes.Attributes = {};\n for (const [name, value] of Object.entries(attribs)) {\n const attr = name.toLowerCase();\n if (attr === \"style\") {\n const style = filterUnsafeCssMagnitudes(value);\n if (style) next[name] = style;\n continue;\n }\n next[name] = URL_ATTRS.has(attr) && !isSafeUrlValue(tagName, attr, value) ? \"#\" : value;\n }\n if (tagName === \"a\" && next.target === \"_blank\") {\n next.rel = \"noopener noreferrer\";\n }\n return { tagName, attribs: next };\n}\n\nconst SANITIZE_OPTIONS: sanitizeHtmlTypes.IOptions = {\n allowedTags: [\n \"a\",\n \"blockquote\",\n \"br\",\n \"cite\",\n \"code\",\n \"details\",\n \"div\",\n \"em\",\n \"figcaption\",\n \"figure\",\n \"h2\",\n \"h3\",\n \"h4\",\n \"hr\",\n \"img\",\n \"li\",\n \"mark\",\n \"ol\",\n \"p\",\n \"pre\",\n \"s\",\n \"section\",\n \"small\",\n \"span\",\n \"strong\",\n \"sub\",\n \"summary\",\n \"sup\",\n \"table\",\n \"tbody\",\n \"td\",\n \"tfoot\",\n \"th\",\n \"thead\",\n \"tr\",\n \"u\",\n \"ul\",\n ],\n allowedAttributes: {\n \"*\": [\"class\", \"data-*\", \"style\"],\n a: [\n \"href\",\n \"title\",\n { name: \"rel\", multiple: true, values: [\"noopener\", \"noreferrer\"] },\n { name: \"target\", values: [\"_blank\"] },\n ],\n details: [\"open\", \"class\", \"data-*\"],\n div: [\"class\", \"data-*\", \"style\"],\n h2: [\"id\", \"class\", \"data-*\", \"style\"],\n h3: [\"id\", \"class\", \"data-*\", \"style\"],\n h4: [\"id\", \"class\", \"data-*\", \"style\"],\n img: [\"src\", \"alt\", \"title\", \"width\", \"height\", \"loading\", \"class\", \"data-*\"],\n mark: [\"class\", \"data-*\", \"style\"],\n p: [\"class\", \"data-*\", \"style\"],\n span: [\"class\", \"data-*\", \"style\"],\n td: [\"colspan\", \"rowspan\", \"class\", \"data-*\"],\n // scope 는 표 머리행의 접근성 의미(스크린리더 열/행 연결) — 값 집합을 고정해 허용.\n th: [\n \"colspan\",\n \"rowspan\",\n \"class\",\n \"data-*\",\n { name: \"scope\", values: [\"col\", \"row\", \"colgroup\", \"rowgroup\"] },\n ],\n },\n allowedClasses: {\n \"*\": [ROOTTALE_CLASS_RE],\n },\n allowedStyles: {\n img: {\n display: [/^block$/i],\n \"margin-left\": [CSS_LENGTH_RE, /^auto$/i],\n \"margin-right\": [CSS_LENGTH_RE, /^auto$/i],\n },\n figure: {\n display: [/^block$/i],\n \"margin-left\": [CSS_LENGTH_RE, /^auto$/i],\n \"margin-right\": [CSS_LENGTH_RE, /^auto$/i],\n },\n \"*\": {\n \"aspect-ratio\": [ASPECT_RATIO_RE],\n background: [HEX_COLOR_RE],\n \"background-color\": [HEX_COLOR_RE],\n border: [CSS_BORDER_RE],\n \"border-bottom\": [CSS_BORDER_RE],\n \"border-left\": [CSS_BORDER_RE],\n \"border-right\": [CSS_BORDER_RE],\n \"border-top\": [CSS_BORDER_RE],\n \"border-collapse\": [/^(?:collapse|separate)$/i],\n color: [HEX_COLOR_RE],\n \"font-size\": [CSS_LENGTH_RE],\n \"font-weight\": [CSS_FONT_WEIGHT_RE],\n height: [CSS_LENGTH_RE, /^auto$/i],\n \"line-height\": [CSS_LINE_HEIGHT_RE],\n margin: [CSS_LENGTH_LIST_RE],\n \"margin-bottom\": [CSS_LENGTH_RE],\n \"margin-left\": [CSS_LENGTH_RE],\n \"margin-right\": [CSS_LENGTH_RE],\n \"margin-top\": [CSS_LENGTH_RE],\n \"max-width\": [CSS_LENGTH_RE, /^none$/i],\n padding: [CSS_LENGTH_LIST_RE],\n \"padding-bottom\": [CSS_LENGTH_RE],\n \"padding-left\": [CSS_LENGTH_RE],\n \"padding-right\": [CSS_LENGTH_RE],\n \"padding-top\": [CSS_LENGTH_RE],\n \"text-align\": [TEXT_ALIGN_RE],\n \"text-decoration\": [CSS_TEXT_DECORATION_RE],\n width: [CSS_LENGTH_RE, /^auto$/i],\n },\n },\n allowedSchemes: [\"http\", \"https\", \"mailto\", \"tel\"],\n allowedSchemesByTag: {\n // tag별 목록은 전역 allowedSchemes를 덮어쓴다. data만 쓰면 정상적인\n // http/https 이미지 src까지 제거되므로 이미지가 사용할 전체 scheme을 명시한다.\n img: [\"http\", \"https\", \"data\"],\n },\n allowedSchemesAppliedToAttributes: [\"href\", \"src\", \"xlink:href\"],\n allowProtocolRelative: false,\n nonTextTags: [\n \"script\",\n \"style\",\n \"textarea\",\n \"option\",\n \"iframe\",\n \"object\",\n \"embed\",\n \"svg\",\n \"math\",\n \"template\",\n ],\n transformTags: {\n \"*\": sanitizeUrlAttributes,\n },\n};\n\n/**\n * XSS sanitize — Tiptap HTML output 또는 rawHtml 의 안전 정리 (렌더 직전 적용).\n * Phase 1 acceptance: `<script>alert(1)</script>` 등 인라인 벡터가 inert.\n */\nexport function sanitizeHtml(html: string): string {\n if (!html) return html;\n return sanitizeHtmlLibrary(stripMalformedDangerousTokens(html), SANITIZE_OPTIONS);\n}\n\n/** 공개 렌더링 원장인 HTML에서 검사·검색용 텍스트를 서버에서도 동일하게 파생한다. */\nexport function htmlToPlainText(html: string): string {\n const safeHtml = sanitizeHtml(html);\n const withBlockBoundaries = safeHtml.replace(\n /<(?:br|hr)\\b[^>]*>|<\\/(?:blockquote|details|div|figcaption|figure|h[2-4]|li|p|pre|section|summary|table|tbody|td|th|tr)>/gi,\n \" \",\n );\n return sanitizeHtmlLibrary(withBlockBoundaries, {\n allowedTags: [],\n allowedAttributes: {},\n })\n .replace(/\\u00a0/g, \" \")\n .replace(/\\s+/g, \" \")\n .trim();\n}\n\n/**\n * 표준 CSP 헤더 값 (Phase 1 acceptance test #7 정합).\n * Astro middleware / Next.js header config 에서 사용.\n */\nexport const PHASE_1_CSP =\n \"default-src 'self'; \" +\n \"script-src 'self'; \" +\n \"style-src 'self' 'unsafe-inline'; \" +\n \"img-src 'self' data: https://imagedelivery.net https://*.r2.cloudflarestorage.com; \" +\n \"font-src 'self' data:; \" +\n \"connect-src 'self'; \" +\n // core/embed allowlist (embed.ts) 의 영상 iframe — youtube-nocookie / vimeo.\n \"frame-src https://www.youtube-nocookie.com https://www.youtube.com https://player.vimeo.com; \" +\n \"frame-ancestors 'none'; \" +\n \"base-uri 'self'; \" +\n \"form-action 'self'\";\n","// ADR-0034 §2 P5 — Stable block id (uuidv7 inject)\n//\n// Tiptap 의 BlockId extension 이 본 helper 를 사용해 새 block 에 uuidv7 inject.\n// 기존 block 의 `_id` 는 보존 (revision 간 정합).\n\nimport { uuidv7 } from \"uuidv7\";\n\nimport type { Block } from \"./block\";\n\n/**\n * Block 에 stable id 주입.\n * 이미 `_id` 있는 block 은 그대로, 없으면 uuidv7 생성. recursive (innerBlocks).\n */\nexport function ensureStableIds(block: Omit<Block, \"_id\"> & { _id?: string }): Block {\n return {\n ...block,\n _id: block._id ?? uuidv7(),\n innerBlocks: (block.innerBlocks ?? []).map((inner) =>\n ensureStableIds(inner as Omit<Block, \"_id\"> & { _id?: string }),\n ),\n };\n}\n\n/**\n * 한 번에 block tree 전체에 stable id 주입.\n * import 또는 새 post 생성 시 entrypoint.\n */\nexport function injectStableIdsTree(blocks: readonly (Omit<Block, \"_id\"> & { _id?: string })[]): Block[] {\n return blocks.map((b) => ensureStableIds(b));\n}\n","/**\n * 공통 블록(Site Patterns) — 여러 글에 같은 자리로 붙는 재사용 블록의 순수 규칙.\n *\n * 블록 본문은 ROOT-ADMIN 이 `site_patterns` 에 저장하고, \"어느 자리(slot)에 어느\n * 콘텐츠 모델(`model_key`)의 글이면 어떤 블록\"인지는 배치 규칙\n * (`settings.site_pattern_placements`)으로 정한다. 유형의 단위는 콘텐츠 모델이다 —\n * 컬렉션 설정(ADR-0060)은 ADR-0105 로 폐기 중이라 규칙 키로 쓰지 않는다. 공개 API 는 글마다 `pattern_slots`(자리 → 블록 key | null) 를 계산해\n * 내려주므로 FRONT 는 규칙을 몰라도 된다. 이 파일은 api-core(계산)·tenant-admin(편집)·\n * cms-renderer-next(렌더)가 같은 판정을 공유하기 위한 프레임워크 비의존 모듈이다.\n *\n * 규칙 판정:\n * - 자리는 `SITE_PATTERN_SLOTS` 에 선언된 것만 계산한다(미선언 자리는 무시).\n * - 글의 `modelKey` 와 같은 규칙이 있으면 그 규칙(블록 없음 = null 도 명시적 결정).\n * - 없으면 `modelKey: null`(모든 글 기본) 규칙.\n * - 가리키는 블록이 발행 상태가 아니면 null(FRONT 가 미발행 블록을 그리지 않게).\n */\n\n/** 블록을 놓을 수 있는 자리. key 는 공개 API `pattern_slots` 의 키와 같다. */\nexport const SITE_PATTERN_SLOTS = [\n { key: \"post_footer\", label: \"글 하단\", description: \"글 본문 바로 아래\" },\n] as const;\n\nexport type SitePatternSlotKey = (typeof SITE_PATTERN_SLOTS)[number][\"key\"];\n\nexport const POST_FOOTER_SLOT: SitePatternSlotKey = \"post_footer\";\n\n/** 배치 규칙을 담는 `settings` 키. */\nexport const SITE_PATTERN_PLACEMENTS_KEY = \"site_pattern_placements\";\n\n/** 블록 key 형식 — 소문자·숫자·`-`·`_`, 64자 이하, 영숫자로 시작. */\nexport const SITE_PATTERN_KEY_PATTERN = /^[a-z0-9][a-z0-9_-]{0,63}$/;\n\nexport function isValidSitePatternKey(key: string): boolean {\n return SITE_PATTERN_KEY_PATTERN.test(key);\n}\n\nexport function isSitePatternSlotKey(value: string): value is SitePatternSlotKey {\n return SITE_PATTERN_SLOTS.some((slot) => slot.key === value);\n}\n\n/**\n * 배치 규칙 1건. `modelKey: null` = 모든 글의 기본값, 문자열 = 그 콘텐츠 모델의 글만.\n * `patternKey: null` = 이 자리에 블록을 놓지 않는다(유형 규칙이면 기본값 덮어쓰기).\n */\nexport interface SitePatternPlacementRule {\n readonly slot: SitePatternSlotKey;\n readonly modelKey: string | null;\n readonly patternKey: string | null;\n}\n\nexport interface SitePatternPlacements {\n readonly rules: readonly SitePatternPlacementRule[];\n}\n\nexport const EMPTY_SITE_PATTERN_PLACEMENTS: SitePatternPlacements = { rules: [] };\n\n/** 글 1건의 자리별 블록 key. 선언된 모든 자리를 키로 가진다(없으면 null). */\nexport type PatternSlots = Record<string, string | null>;\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction readNullableKey(value: unknown): string | null | undefined {\n if (value === null) return null;\n if (typeof value === \"string\") {\n const trimmed = value.trim();\n return trimmed.length > 0 ? trimmed : null;\n }\n return undefined;\n}\n\n/**\n * 저장된 값(unknown) → 정규화된 배치 규칙. 형식이 틀린 항목·미선언 자리는 버리고,\n * 같은 (slot, modelKey) 는 마지막 항목이 이긴다. 이중 인코딩된 JSON 문자열도 받는다.\n * 2026-08-19 이전에 저장된 규칙은 `collectionKey` 필드를 썼다 — `modelKey` 가 없으면 그\n * 값을 읽는다(실측상 남은 규칙은 기본값 `null` 뿐이라 의미가 같다). 다시 저장하면\n * `modelKey` 로 쓰인다.\n */\nexport function readSitePatternPlacements(raw: unknown): SitePatternPlacements {\n let value = raw;\n if (typeof value === \"string\") {\n try {\n value = JSON.parse(value) as unknown;\n } catch {\n return EMPTY_SITE_PATTERN_PLACEMENTS;\n }\n }\n if (!isRecord(value) || !Array.isArray(value.rules)) {\n return EMPTY_SITE_PATTERN_PLACEMENTS;\n }\n const byTarget = new Map<string, SitePatternPlacementRule>();\n for (const item of value.rules) {\n if (!isRecord(item)) continue;\n const slot = typeof item.slot === \"string\" ? item.slot : \"\";\n if (!isSitePatternSlotKey(slot)) continue;\n const modelKey = readNullableKey(\n item.modelKey === undefined ? item.collectionKey : item.modelKey,\n );\n if (modelKey === undefined) continue;\n const patternKey = readNullableKey(item.patternKey);\n if (patternKey === undefined) continue;\n if (patternKey !== null && !isValidSitePatternKey(patternKey)) continue;\n byTarget.set(`${slot}\u0000${modelKey ?? \"\"}`, {\n slot,\n modelKey,\n patternKey,\n });\n }\n return { rules: [...byTarget.values()] };\n}\n\n/** (slot, modelKey) 에 해당하는 규칙. 없으면 undefined(= 기본값으로 위임). */\nexport function findSitePatternPlacementRule(\n placements: SitePatternPlacements,\n slot: SitePatternSlotKey,\n modelKey: string | null,\n): SitePatternPlacementRule | undefined {\n return placements.rules.find((rule) => rule.slot === slot && rule.modelKey === modelKey);\n}\n\n/**\n * 규칙 1건을 갈아 끼운 새 배치 규칙을 돌려준다(불변). `patternKey: undefined` 는\n * \"규칙 삭제 = 기본값으로 위임\"이다 — 유형 규칙에서 \"표시 안 함\"(null)과 구분된다.\n */\nexport function setSitePatternPlacementRule(\n placements: SitePatternPlacements,\n target: { slot: SitePatternSlotKey; modelKey: string | null },\n patternKey: string | null | undefined,\n): SitePatternPlacements {\n const rest = placements.rules.filter(\n (rule) => !(rule.slot === target.slot && rule.modelKey === target.modelKey),\n );\n if (patternKey === undefined) return { rules: rest };\n return {\n rules: [...rest, { slot: target.slot, modelKey: target.modelKey, patternKey }],\n };\n}\n\n/** 선언된 자리마다 null 인 빈 결과 — 규칙이나 발행 블록이 없을 때의 기본 응답. */\nexport function emptyPatternSlots(): PatternSlots {\n return Object.fromEntries(SITE_PATTERN_SLOTS.map((slot) => [slot.key, null]));\n}\n\n/**\n * 글 1건의 자리별 블록 key 를 계산한다. 글의 콘텐츠 모델(`modelKey`) 규칙 > 모든 글 기본\n * 규칙. `publishedKeys` 에 없는 블록(미발행·삭제)은 null.\n */\nexport function resolvePatternSlots(\n post: { readonly modelKey: string | null },\n placements: SitePatternPlacements,\n publishedKeys: ReadonlySet<string>,\n): PatternSlots {\n const slots = emptyPatternSlots();\n for (const slot of SITE_PATTERN_SLOTS) {\n const specific =\n post.modelKey !== null\n ? findSitePatternPlacementRule(placements, slot.key, post.modelKey)\n : undefined;\n const rule =\n specific ?? findSitePatternPlacementRule(placements, slot.key, null);\n const key = rule?.patternKey ?? null;\n slots[slot.key] = key !== null && publishedKeys.has(key) ? key : null;\n }\n return slots;\n}\n\n/**\n * 규칙에서 참조하는 블록 key 집합 — 어드민이 \"이 블록은 어디에 쓰이는가\"를 표시하거나\n * 삭제 전 경고할 때 쓴다.\n */\nexport function referencedSitePatternKeys(\n placements: SitePatternPlacements,\n): ReadonlySet<string> {\n return new Set(\n placements.rules\n .map((rule) => rule.patternKey)\n .filter((key): key is string => key !== null),\n );\n}\n\n// ── 표시 형태(presentation) ──────────────────────────────────────────────────\n// 블록의 \"어떻게 보일지\"는 어드민이 정하고 FRONT 는 값을 받아 그리기만 한다 — 사이트 CSS 에\n// 색·모양을 박지 않는다. 값은 작고 범용적인 것만: 카드/본문, 배경색, 링크를 버튼으로, 버튼 색.\n\nexport type SitePatternLayout = \"plain\" | \"card\";\nexport type SitePatternLinkStyle = \"text\" | \"buttons\";\n\nexport interface SitePatternPresentation {\n /** `plain` = 본문처럼 흐름에 놓임, `card` = 배경색이 있는 카드. */\n readonly layout: SitePatternLayout;\n /** 카드 배경색(hex). layout 이 card 일 때만 의미. */\n readonly background: string;\n /** 마지막 문단의 링크를 버튼으로 그릴지. */\n readonly linkStyle: SitePatternLinkStyle;\n /** 버튼 배경색(hex, 순서대로 1·2·3…; 링크가 더 많으면 마지막 색을 반복). 글자는 흰색. */\n readonly buttonColors: readonly string[];\n}\n\nexport const DEFAULT_SITE_PATTERN_PRESENTATION: SitePatternPresentation = {\n layout: \"plain\",\n background: \"#f8f9fa\",\n linkStyle: \"text\",\n buttonColors: [\"#03c75a\", \"#1a1a1a\", \"#666666\"],\n};\n\nexport const SITE_PATTERN_BUTTON_COLORS_MAX = 4;\n\nconst HEX_COLOR_RE = /^#[0-9a-f]{6}$/;\n\nexport function normalizeHexColor(value: unknown): string | null {\n if (typeof value !== \"string\") return null;\n let hex = value.trim().toLowerCase();\n if (/^#[0-9a-f]{3}$/.test(hex)) hex = `#${hex[1]}${hex[1]}${hex[2]}${hex[2]}${hex[3]}${hex[3]}`;\n return HEX_COLOR_RE.test(hex) ? hex : null;\n}\n\n/** 저장된 값(unknown) → 표시 형태. 틀린 값은 기본값으로 채운다(절대 throw 하지 않음). */\nexport function readSitePatternPresentation(raw: unknown): SitePatternPresentation {\n let value = raw;\n if (typeof value === \"string\") {\n try {\n value = JSON.parse(value) as unknown;\n } catch {\n value = null;\n }\n }\n const base = DEFAULT_SITE_PATTERN_PRESENTATION;\n if (!isRecord(value)) return base;\n const layout: SitePatternLayout = value.layout === \"card\" ? \"card\" : \"plain\";\n const linkStyle: SitePatternLinkStyle = value.linkStyle === \"buttons\" ? \"buttons\" : \"text\";\n const background = normalizeHexColor(value.background) ?? base.background;\n const colors = Array.isArray(value.buttonColors)\n ? value.buttonColors\n .map(normalizeHexColor)\n .filter((c): c is string => c !== null)\n .slice(0, SITE_PATTERN_BUTTON_COLORS_MAX)\n : [];\n return {\n layout,\n background,\n linkStyle,\n buttonColors: colors.length > 0 ? colors : base.buttonColors,\n };\n}\n\n/** 버튼 n번째(0부터)의 색 — 링크가 색 개수보다 많으면 마지막 색을 반복. */\nexport function sitePatternButtonColor(presentation: SitePatternPresentation, index: number): string {\n const colors = presentation.buttonColors;\n return colors[Math.min(index, colors.length - 1)] ?? DEFAULT_SITE_PATTERN_PRESENTATION.buttonColors[0]!;\n}\n\n/**\n * FRONT 가 래퍼 요소에 얹을 data 속성과 CSS 변수. 사이트 CSS 는 이 변수만 읽는다 —\n * `data-pattern-layout`·`data-pattern-link-style`, `--rt-pattern-bg`, `--rt-pattern-btn-1..n`.\n */\nexport function sitePatternPresentationAttributes(presentation: SitePatternPresentation): {\n readonly dataset: Readonly<Record<string, string>>;\n readonly cssVariables: Readonly<Record<string, string>>;\n} {\n const cssVariables: Record<string, string> = { \"--rt-pattern-bg\": presentation.background };\n presentation.buttonColors.forEach((color, index) => {\n cssVariables[`--rt-pattern-btn-${index + 1}`] = color;\n });\n return {\n dataset: {\n \"data-pattern-layout\": presentation.layout,\n \"data-pattern-link-style\": presentation.linkStyle,\n },\n cssVariables,\n };\n}\n","/** 편집기와 공개 렌더러가 공유하는 허용 서식. 기본값은 사이트 CSS를 따른다. */\nexport const CMS_FONT_SIZES = [14, 16, 18, 20, 24, 28, 32] as const;\nexport const CMS_LINE_HEIGHTS = [1.2, 1.5, 1.8, 2, 2.4] as const;\nexport const CMS_PARAGRAPH_SPACINGS = [0, 8, 16, 24, 32] as const;\nexport const CMS_IMAGE_WIDTHS = [25, 50, 75, 100] as const;\n\nexport function cmsFontSize(value: unknown): string | null {\n if (typeof value !== \"string\") return null;\n return CMS_FONT_SIZES.some((size) => value === `${size}px`) ? value : null;\n}\nexport function cmsLineHeight(value: unknown): number | null {\n if (typeof value !== \"number\" && typeof value !== \"string\") return null;\n return CMS_LINE_HEIGHTS.find((size) => String(size) === String(value)) ?? null;\n}\nexport function cmsParagraphSpacing(value: unknown): string | null {\n if (typeof value !== \"string\") return null;\n return CMS_PARAGRAPH_SPACINGS.some((size) => value === `${size}px`) ? value : null;\n}\nexport function cmsImageWidth(value: unknown): string | null {\n if (typeof value !== \"string\") return null;\n return CMS_IMAGE_WIDTHS.some((size) => value === `${size}%`) ? value : null;\n}\nexport function cmsImageAlign(value: unknown): \"left\" | \"center\" | \"right\" | null {\n return value === \"left\" || value === \"center\" || value === \"right\" ? value : null;\n}\nexport function cmsImageStyle(attrs: Record<string, unknown>) {\n const width = cmsImageWidth(attrs.displayWidth);\n const align = cmsImageAlign(attrs.imageAlign);\n return {\n ...(width ? { width, maxWidth: \"100%\", height: \"auto\" } : {}),\n ...(align ? {\n display: \"block\",\n marginLeft: align === \"left\" ? \"0\" : \"auto\",\n marginRight: align === \"right\" ? \"0\" : \"auto\",\n } : {}),\n };\n}\n","export interface SectionLock {\n move?: boolean;\n remove?: boolean;\n structure?: boolean;\n}\n\ninterface SectionNodeSnapshot {\n id: string;\n path: string;\n sectionType: string;\n attrs: Record<string, unknown>;\n props: unknown;\n}\n\nexport type SectionLockViolationCode =\n | \"locked_section_inserted\"\n | \"locked_section_removed\"\n | \"locked_section_moved\"\n | \"locked_section_lock_changed\"\n | \"locked_section_structure_changed\";\n\nexport interface SectionLockViolation {\n code: SectionLockViolationCode;\n sectionId: string;\n sectionType: string;\n field: string;\n}\n\nconst CONTENT_PROP_PATHS: Readonly<\n Record<string, readonly (readonly (string | number)[])[]>\n> = {\n hero: [\n [\"eyebrow\"],\n [\"title\"],\n [\"description\"],\n [\"imageSrc\"],\n [\"imageAlt\"],\n [\"cta\", \"label\"],\n [\"cta\", \"href\"],\n ],\n \"rich-text\": [[\"heading\"], [\"body\"]],\n services: [\n [\"heading\"],\n [\"description\"],\n [\"items\", Number.NaN, \"title\"],\n [\"items\", Number.NaN, \"description\"],\n [\"items\", Number.NaN, \"href\"],\n ],\n faq: [\n [\"heading\"],\n [\"items\", Number.NaN, \"question\"],\n [\"items\", Number.NaN, \"answer\"],\n ],\n \"cta-banner\": [[\"title\"], [\"description\"], [\"cta\", \"label\"], [\"cta\", \"href\"]],\n \"blog-list\": [[\"heading\"], [\"emptyMessage\"]],\n \"lead-form\": [[\"heading\"], [\"description\"], [\"submitLabel\"]],\n trust: [\n [\"heading\"],\n [\"metrics\", Number.NaN, \"value\"],\n [\"metrics\", Number.NaN, \"unit\"],\n [\"metrics\", Number.NaN, \"label\"],\n [\"metrics\", Number.NaN, \"source\"],\n [\"credentials\", Number.NaN, \"name\"],\n [\"credentials\", Number.NaN, \"issuer\"],\n [\"credentials\", Number.NaN, \"kind\"],\n [\"credentials\", Number.NaN, \"image\"],\n [\"logos\", Number.NaN, \"src\"],\n [\"logos\", Number.NaN, \"alt\"],\n [\"logos\", Number.NaN, \"href\"],\n [\"disclaimer\"],\n ],\n contact: [\n [\"heading\"],\n [\"form\", \"enabled\"],\n [\"phone\"],\n [\"address\"],\n [\"hours\", Number.NaN, \"day\"],\n [\"hours\", Number.NaN, \"open\"],\n [\"hours\", Number.NaN, \"close\"],\n [\"hours\", Number.NaN, \"note\"],\n [\"mapEmbedUrl\"],\n ],\n expertise: [\n [\"heading\"],\n [\"description\"],\n [\"items\", Number.NaN, \"label\"],\n [\"items\", Number.NaN, \"kicker\"],\n [\"items\", Number.NaN, \"title\"],\n [\"items\", Number.NaN, \"body\"],\n [\"items\", Number.NaN, \"imageSrc\"],\n [\"items\", Number.NaN, \"imageAlt\"],\n [\"items\", Number.NaN, \"tags\", Number.NaN],\n [\"items\", Number.NaN, \"href\"],\n [\"items\", Number.NaN, \"ctaLabel\"],\n ],\n spaces: [\n [\"heading\"],\n [\"description\"],\n [\"spaces\", Number.NaN, \"label\"],\n [\"spaces\", Number.NaN, \"kicker\"],\n [\"spaces\", Number.NaN, \"title\"],\n [\"spaces\", Number.NaN, \"description\"],\n [\"spaces\", Number.NaN, \"imageSrc\"],\n [\"spaces\", Number.NaN, \"imageAlt\"],\n [\"spaces\", Number.NaN, \"tags\", Number.NaN],\n [\"disclaimer\"],\n ],\n concerns: [\n [\"heading\"],\n [\"description\"],\n [\"figureImage\"],\n [\"figureAlt\"],\n [\"regions\", Number.NaN, \"label\"],\n [\"regions\", Number.NaN, \"title\"],\n [\"regions\", Number.NaN, \"description\"],\n [\"regions\", Number.NaN, \"items\", Number.NaN],\n [\"regions\", Number.NaN, \"href\"],\n [\"regions\", Number.NaN, \"ctaLabel\"],\n [\"disclaimer\"],\n ],\n};\n\nfunction valueEquals(left: unknown, right: unknown): boolean {\n return JSON.stringify(left) === JSON.stringify(right);\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return Boolean(value) && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction collectChangedPaths(\n before: unknown,\n after: unknown,\n prefix: readonly (string | number)[] = [],\n): Array<readonly (string | number)[]> {\n if (valueEquals(before, after)) return [];\n if (Array.isArray(before) && Array.isArray(after)) {\n if (before.length !== after.length) return [prefix];\n return before.flatMap((item, index) =>\n collectChangedPaths(item, after[index], [...prefix, index]),\n );\n }\n if ((isRecord(before) || before == null) && (isRecord(after) || after == null)) {\n const beforeRecord = isRecord(before) ? before : {};\n const afterRecord = isRecord(after) ? after : {};\n const keys = new Set([...Object.keys(beforeRecord), ...Object.keys(afterRecord)]);\n if (keys.size === 0) return [prefix];\n return [...keys].flatMap((key) =>\n collectChangedPaths(beforeRecord[key], afterRecord[key], [...prefix, key]),\n );\n }\n return [prefix];\n}\n\nfunction contentPathMatches(\n pattern: readonly (string | number)[],\n path: readonly (string | number)[],\n): boolean {\n if (pattern.length !== path.length) return false;\n return pattern.every((segment, index) => {\n const actual = path[index];\n return Number.isNaN(segment)\n ? typeof actual === \"number\"\n : segment === actual;\n });\n}\n\nexport function isSectionContentPropPath(\n sectionType: string,\n path: readonly (string | number)[],\n): boolean {\n const patterns = CONTENT_PROP_PATHS[sectionType] ?? [];\n return patterns.some((pattern) => contentPathMatches(pattern, path));\n}\n\nfunction collectSectionSnapshots(doc: unknown): SectionNodeSnapshot[] {\n const sections: SectionNodeSnapshot[] = [];\n function visit(node: unknown, path: string): void {\n if (!isRecord(node)) return;\n if (node.type === \"section\") {\n const attrs = isRecord(node.attrs) ? node.attrs : {};\n sections.push({\n id: typeof attrs._id === \"string\" ? attrs._id : \"\",\n path,\n sectionType: typeof attrs.sectionType === \"string\" ? attrs.sectionType : \"\",\n attrs,\n props: attrs.props ?? {},\n });\n }\n if (Array.isArray(node.content)) {\n node.content.forEach((child, index) => visit(child, `${path}.${index}`));\n }\n }\n visit(doc, \"doc\");\n return sections;\n}\n\n/** 고객 콘텐츠 저장 경계. 기존 lock 플래그와 무관하게 제작 구조를 보호한다. */\nexport function findSectionLockViolations(\n beforeDoc: unknown,\n afterDoc: unknown,\n): SectionLockViolation[] {\n const before = collectSectionSnapshots(beforeDoc);\n const after = collectSectionSnapshots(afterDoc);\n const afterById = new Map(after.map((section) => [section.id, section]));\n const beforeById = new Map(before.map((section) => [section.id, section]));\n const violations: SectionLockViolation[] = [];\n\n // ID 누락·중복은 동일 섹션 비교를 우회할 수 있으므로 실패로 처리한다.\n for (const sections of [before, after]) {\n const ids = new Set<string>();\n for (const section of sections) {\n if (!section.id || ids.has(section.id)) {\n violations.push({\n code: \"locked_section_structure_changed\",\n sectionId: section.id,\n sectionType: section.sectionType,\n field: \"_id\",\n });\n }\n ids.add(section.id);\n }\n }\n\n for (const section of before) {\n const next = afterById.get(section.id);\n if (!next) {\n violations.push({\n code: \"locked_section_removed\",\n sectionId: section.id,\n sectionType: section.sectionType,\n field: \"(node)\",\n });\n continue;\n }\n\n if (section.path !== next.path) {\n violations.push({\n code: \"locked_section_moved\",\n sectionId: section.id,\n sectionType: section.sectionType,\n field: \"(index)\",\n });\n }\n\n const fields = new Set([\n ...Object.keys(section.attrs),\n ...Object.keys(next.attrs),\n ]);\n fields.delete(\"props\");\n for (const field of fields) {\n if (!valueEquals(section.attrs[field], next.attrs[field])) {\n violations.push({\n code: field === \"lock\"\n ? \"locked_section_lock_changed\"\n : \"locked_section_structure_changed\",\n sectionId: section.id,\n sectionType: section.sectionType,\n field,\n });\n }\n }\n\n for (const path of collectChangedPaths(section.props, next.props)) {\n if (!isSectionContentPropPath(section.sectionType, path)) {\n violations.push({\n code: \"locked_section_structure_changed\",\n sectionId: section.id,\n sectionType: section.sectionType,\n field: `props.${path.join(\".\") || \"(root)\"}`,\n });\n }\n }\n }\n\n for (const section of after) {\n if (beforeById.has(section.id)) continue;\n violations.push({\n code: \"locked_section_inserted\",\n sectionId: section.id,\n sectionType: section.sectionType,\n field: \"(node)\",\n });\n }\n\n return violations;\n}\n\nexport function formatSectionLockViolations(\n violations: readonly SectionLockViolation[],\n): string {\n return violations\n .map((violation) => {\n const label = `${violation.sectionType || \"section\"} 섹션(${violation.sectionId})`;\n if (violation.code === \"locked_section_inserted\") return `${label} 추가`;\n if (violation.code === \"locked_section_removed\") return `${label} 삭제`;\n if (violation.code === \"locked_section_moved\") return `${label} 이동`;\n if (violation.code === \"locked_section_lock_changed\") {\n return `${label} 잠금 변경`;\n }\n return `${label} 구조 변경(${violation.field})`;\n })\n .join(\" / \");\n}\n","import type { MetadataRobotsPolicy, MetadataValues } from \"./metadata-contract\";\n\n/** 같은 필드의 우선순위: 직접 설정 → 콘텐츠/코드 기본값 → 사이트 기본값 → 코드 fallback. */\nexport interface MetadataResolutionInput {\n overrides?: MetadataValues | null;\n defaults?: MetadataValues | null;\n siteDefaults?: MetadataValues | null;\n fallback?: MetadataValues | null;\n robots?: MetadataRobotsPolicy;\n /** 상대 이미지 경로를 절대 주소로 바꿀 기준 URL. canonical 정책에는 관여하지 않는다. */\n imageBaseUrl?: string;\n}\n\nfunction text(value: unknown): string | undefined {\n return typeof value === \"string\" && value.trim() ? value.trim() : undefined;\n}\n\nfunction imageUrl(value: unknown, base?: string): string | undefined {\n const candidate = text(value);\n if (!candidate || candidate.startsWith(\"//\") || /[\\\\\\u0000-\\u0020\\u007f]/u.test(candidate)) return undefined;\n if (candidate.startsWith(\"/\") && !base) return candidate;\n try {\n const url = new URL(candidate, base);\n if (![\"http:\", \"https:\"].includes(url.protocol) || url.username || url.password) return undefined;\n return url.href;\n } catch {\n return undefined;\n }\n}\n\n/** 글·분류·코드 경로가 공유하는 순수 해석기. false는 상속을 덮는 명시값이다. */\nexport function resolveMetadata(input: MetadataResolutionInput): MetadataValues {\n const layers = [input.overrides, input.defaults, input.siteDefaults, input.fallback];\n const result: MetadataValues = {};\n for (const field of [\"title\", \"description\", \"ogImageAlt\"] as const) {\n for (const layer of layers) {\n const value = text(layer?.[field]);\n if (value !== undefined) { result[field] = value; break; }\n }\n }\n for (const layer of layers) {\n const value = imageUrl(layer?.ogImage, input.imageBaseUrl);\n if (value !== undefined) { result.ogImage = value; break; }\n }\n for (const field of [\"noindex\", \"nofollow\"] as const) {\n for (const layer of layers) {\n const value = layer?.[field];\n if (typeof value === \"boolean\") { result[field] = value; break; }\n }\n }\n if (input.robots?.forceNoindex) result.noindex = true;\n if (input.robots?.forceNofollow) result.nofollow = true;\n return result;\n}\n","import { z } from \"zod\";\n\nexport const METADATA_FIELDS = [\"title\", \"description\", \"ogImage\", \"ogImageAlt\", \"noindex\", \"nofollow\"] as const;\nexport const MetadataFieldSchema = z.enum(METADATA_FIELDS);\nexport type MetadataField = z.infer<typeof MetadataFieldSchema>;\nexport const MetadataTargetKindSchema = z.enum([\"route\", \"post\", \"term\"]);\nexport type MetadataTargetKind = z.infer<typeof MetadataTargetKindSchema>;\n\nconst textValue = (max: number) => z.string().trim().min(1).max(max);\nconst imageValue = textValue(2048).refine((value) => {\n if (/[\\\\\\u0000-\\u0020\\u007f]/u.test(value) || value.startsWith(\"//\")) return false;\n if (value.startsWith(\"/\")) return true;\n try {\n const url = new URL(value);\n return [\"http:\", \"https:\"].includes(url.protocol) && !url.username && !url.password;\n } catch { return false; }\n}, \"공유 이미지에는 사이트 내부 경로나 HTTP/HTTPS 주소를 입력해 주세요\");\n\nconst metadataFields = {\n // Existing content titles (500) plus a site title template (400) remain valid automatic values.\n title: textValue(1000),\n description: textValue(2000),\n ogImage: imageValue,\n // Automatic alt text may use the complete automatic title and its site suffix.\n ogImageAlt: textValue(1000),\n noindex: z.boolean(),\n nofollow: z.boolean(),\n};\n\n/** Empty/omitted values inherit defaults. Canonical remains owned by site routing. */\nexport const MetadataValuesSchema = z.object(metadataFields).partial().strict();\nexport type MetadataValues = z.infer<typeof MetadataValuesSchema>;\nexport const MetadataPatchSchema = z.object({\n title: textValue(300).nullable().optional(),\n description: metadataFields.description.nullable().optional(),\n ogImage: metadataFields.ogImage.nullable().optional(),\n ogImageAlt: textValue(500).nullable().optional(),\n noindex: metadataFields.noindex.nullable().optional(),\n nofollow: metadataFields.nofollow.nullable().optional(),\n}).strict();\nexport type MetadataPatch = z.infer<typeof MetadataPatchSchema>;\n\nexport const MetadataRobotsPolicySchema = z.object({\n forceNoindex: z.boolean().optional(),\n forceNofollow: z.boolean().optional(),\n /** Stable policy reason codes. They explain an enforced exclusion, never relax it. */\n noindexReasons: z.array(z.string().trim().min(1).max(200)).max(64).optional(),\n}).strict();\nexport type MetadataRobotsPolicy = z.infer<typeof MetadataRobotsPolicySchema>;\n\nexport interface MetadataTarget {\n readonly kind: MetadataTargetKind;\n readonly key: string;\n readonly locale: string;\n readonly label: string;\n readonly path: string | null;\n /** Opaque CAS token. Consumers must not parse or increment it. */\n readonly version: string;\n readonly overrides: MetadataValues;\n readonly defaults: MetadataValues;\n readonly fallback?: MetadataValues;\n readonly effective: MetadataValues;\n readonly supportedFields: MetadataField[];\n readonly robots?: MetadataRobotsPolicy;\n readonly connection: \"connected\" | \"unconnected\" | \"removed\";\n}\n\nexport interface PublicRouteMetadata {\n readonly routeKey: string;\n readonly locale: string;\n readonly path: string;\n readonly version: string;\n readonly overrides: MetadataValues;\n readonly defaults: MetadataValues;\n readonly fallback?: MetadataValues;\n readonly effective: MetadataValues;\n readonly supportedFields: MetadataField[];\n readonly robots?: MetadataRobotsPolicy;\n}\n\n/** Read recognized, valid legacy fields without losing false or accepting arbitrary head HTML. */\nexport function readMetadataValues(input: unknown): MetadataValues {\n if (!input || typeof input !== \"object\" || Array.isArray(input)) return {};\n const record = input as Record<string, unknown>;\n const output: Record<string, unknown> = {};\n for (const field of METADATA_FIELDS) {\n const parsed = metadataFields[field].safeParse(record[field]);\n if (parsed.success) output[field] = parsed.data;\n }\n return MetadataValuesSchema.parse(output);\n}\n\n/** Preserve legacy namespaces (including canonical) while changing only the requested fields. */\nexport function mergeMetadataPatch(current: Record<string, unknown>, input: MetadataPatch): Record<string, unknown> {\n const patch = MetadataPatchSchema.parse(input);\n const next = { ...current };\n for (const field of METADATA_FIELDS) {\n if (patch[field] === null) delete next[field];\n else if (patch[field] !== undefined) next[field] = patch[field];\n }\n return next;\n}\n","import { z } from \"zod\";\nimport { MetadataRobotsPolicySchema, MetadataValuesSchema, readMetadataValues, type MetadataValues } from \"./metadata-contract\";\nimport { cleanMetadataText, formatMetadataText, MetadataTextRuleSchema, type MetadataTextInput } from \"./metadata-text\";\nimport { contentPolicySafeHttpsUrl } from \"./content-policy\";\nimport { MetadataVideoThumbnailRuleSchema, metadataVideoThumbnail } from \"./metadata-video-thumbnail\";\nimport { ContentPolicyFieldNameSchema } from \"./content-policy-schema\";\nimport { collectMetadataDefaultFieldRefs } from \"./metadata-default-fields\";\nexport { MetadataTextRuleSchema, metadataBodyParagraphs } from \"./metadata-text\";\nexport type { MetadataTextRule } from \"./metadata-text\";\nexport { collectMetadataDefaultFieldRefs } from \"./metadata-default-fields\";\nexport { metadataVideoThumbnail } from \"./metadata-video-thumbnail\";\n\nconst imageOrigin = z.string().max(2048).refine((value) => {\n try { const url = new URL(value); return url.protocol === \"https:\" && url.origin === value; }\n catch { return false; }\n}, \"이미지 허용 목록에는 경로 없는 HTTPS origin만 선언합니다\");\n\nexport const MetadataDefaultRulesSchema = z.object({\n title: MetadataTextRuleSchema.optional(),\n titleTemplateMaxLength: z.number().int().min(1).max(1000).optional(),\n description: MetadataTextRuleSchema.optional(),\n /** Code-owned defaults precede site defaults but always follow manual overrides. */\n defaults: MetadataValuesSchema.optional(),\n robots: MetadataRobotsPolicySchema.optional(),\n useFeaturedImage: z.boolean().optional(),\n videoThumbnail: MetadataVideoThumbnailRuleSchema.optional(),\n useFirstBodyImage: z.boolean().optional(),\n useTermImage: z.boolean().optional(),\n imageAltSource: z.enum([\"content\", \"title\"]).optional(),\n legacyImageSource: z.literal(\"legacy-thumbnail\").optional(),\n imageAllowedOrigins: z.array(imageOrigin).min(1).max(32).optional(),\n legacyImageAllowedOrigins: z.array(imageOrigin).min(1).max(32).optional(),\n normalizeCloudflareImages: z.boolean().optional(),\n fallback: MetadataValuesSchema.optional(),\n}).strict().refine((rule) => !rule.legacyImageSource || Boolean(rule.legacyImageAllowedOrigins?.length), \"이전 이미지 원본에는 허용 origin 선언이 필요합니다\")\n .superRefine((rule, context) => {\n if (!rule.videoThumbnail) return;\n const refs = collectMetadataDefaultFieldRefs(rule);\n if (refs.length > 64) context.addIssue({ code: z.ZodIssueCode.custom, message: \"영상 자동값은 공개 필드 64개까지 참조할 수 있습니다\" });\n for (const { name } of refs) {\n if (!ContentPolicyFieldNameSchema.safeParse(name).success) context.addIssue({ code: z.ZodIssueCode.custom,\n message: \"영상 자동값의 텍스트·조건은 안전한 공개 필드 이름을 사용합니다\" });\n }\n });\nexport type MetadataDefaultRules = z.infer<typeof MetadataDefaultRulesSchema>;\n\nexport interface MetadataDefaultsDeclaration {\n posts?: { modelKeys?: string[]; titleTemplates?: Record<string, string>; defaultRules?: Record<string, MetadataDefaultRules> };\n terms?: { modelKeys?: string[]; titleTemplates?: Record<string, string>; defaultRules?: Record<string, MetadataDefaultRules> };\n}\nexport interface MetadataDefaultsInput extends MetadataTextInput {\n kind: \"posts\" | \"terms\";\n modelKey: string;\n description?: string | null;\n featuredImage?: string | null;\n firstBodyImage?: string | null;\n legacyImage?: string | null;\n /** Current public site origin, used by the historical image adapter's origin check. */\n imageBaseUrl?: string;\n}\n\n/** Keep the customer's existing CF Images /lg behavior without rewriting unrelated origins. */\nexport function metadataContentImageUrl(value: string, normalizeCloudflareImages = false): string {\n if (!normalizeCloudflareImages) return value;\n try {\n const url = new URL(value);\n const [account, id, variant] = url.pathname.split(\"/\").filter(Boolean);\n if (url.protocol === \"https:\" && url.hostname === \"imagedelivery.net\" && account && id && variant) {\n url.pathname = `/${account}/${id}/lg`;\n return url.href;\n }\n } catch { /* Relative URLs are resolved later using the verified site origin. */ }\n return value;\n}\n\n/** Historical thumbnails have their own narrow, explicitly declared origin list. */\nfunction legacyImageUrl(value: string | null | undefined, rule?: MetadataDefaultRules): string | undefined {\n if (!value || rule?.legacyImageSource !== \"legacy-thumbnail\") return undefined;\n try {\n const url = new URL(value);\n if (!rule.legacyImageAllowedOrigins?.includes(url.origin)) return undefined;\n return readMetadataValues({ ogImage: url.href }).ogImage;\n } catch { return undefined; }\n}\n\n/** Preserve the original importer: reject its first untrusted candidate without trying the next. */\nfunction contentCandidateAllowed(value: string, input: MetadataDefaultsInput, rule?: MetadataDefaultRules): boolean {\n if (!rule?.imageAllowedOrigins) return true;\n if (value.startsWith(\"/\") && !value.startsWith(\"//\")) return true;\n try {\n const url = new URL(value);\n if (url.protocol !== \"https:\") return false;\n const origins = [...rule.imageAllowedOrigins];\n if (input.imageBaseUrl) origins.push(new URL(input.imageBaseUrl).origin);\n return origins.includes(url.origin);\n } catch { return false; }\n}\n\n/** Receives automatic source values only. User-entered SEO never passes through cleanup/truncation. */\nexport function formatMetadataDefaults(input: MetadataDefaultsInput, declaration: MetadataDefaultsDeclaration): MetadataValues {\n const dynamic = declaration[input.kind];\n const rule = dynamic?.defaultRules?.[input.modelKey];\n const name = cleanMetadataText(input.title, rule?.title);\n const title = formatMetadataText(input.title, rule?.title, input, name);\n const template = dynamic?.titleTemplates?.[input.modelKey];\n const templatedTitle = title && template ? template.replace(\"%s\", () => title) : title;\n const finalTitle = templatedTitle && rule?.titleTemplateMaxLength && templatedTitle.length > rule.titleTemplateMaxLength ? title : templatedTitle;\n const description = formatMetadataText(input.description, rule?.description, input, name);\n // The existing public body helper accepts HTTPS or a root-relative source, never HTTP.\n const bodyImage = input.firstBodyImage && (input.firstBodyImage.startsWith(\"https://\") || /^\\/(?!\\/)/u.test(input.firstBodyImage)) ? input.firstBodyImage : undefined;\n const rawFeaturedImage = (input.kind === \"terms\" ? rule?.useTermImage : rule?.useFeaturedImage) === false ? undefined : input.featuredImage;\n // This opt-in preserves the source site's safe featured -> video fallback behavior.\n const featuredImage = rule?.videoThumbnail ? contentPolicySafeHttpsUrl(rawFeaturedImage) : rawFeaturedImage;\n const candidateImage = featuredImage\n || (input.kind === \"posts\" ? metadataVideoThumbnail(rule?.videoThumbnail, input.fields) : undefined)\n || (rule?.useFirstBodyImage ? bodyImage : undefined) || legacyImageUrl(input.legacyImage, rule);\n const ogImage = candidateImage && contentCandidateAllowed(candidateImage, input, rule)\n ? readMetadataValues({ ogImage: metadataContentImageUrl(candidateImage, rule?.normalizeCloudflareImages) }).ogImage : undefined;\n return {\n ...readMetadataValues(rule?.defaults),\n ...(finalTitle ? { title: finalTitle } : {}),\n ...(description ? { description } : {}),\n ...(ogImage ? { ogImage } : {}),\n };\n}\n","import { z } from \"zod\";\n\nconst fallbackText = z.string().max(2000);\nconst templateToken = /\\{\\{(?:title|field:[a-zA-Z][a-zA-Z0-9_-]{0,159})\\}\\}/gu;\nconst template = fallbackText.refine((value) => !/[{}]/u.test(value.replace(templateToken, \"\")),\n \"자동 텍스트에는 {{title}} 또는 {{field:필드이름}} 토큰만 사용합니다\");\n\n/** A fixed set of content rules; never accept executable expressions or caller regexes. */\nexport const MetadataTextRuleSchema = z.object({\n source: z.enum([\"content\", \"fallback\", \"template\"]).optional(),\n template: template.optional(),\n stripLeadingLabel: z.enum([\"question\", \"answer\"]).optional(),\n replaceLineBreakTokens: z.boolean().optional(),\n removeEmDashes: z.boolean().optional(),\n normalizeWhitespace: z.boolean().optional(),\n maxLength: z.number().int().min(1).max(2000).optional(),\n lengthUnit: z.enum([\"code-units\", \"code-points\"]).optional(),\n wordBoundaryMinLength: z.number().int().min(1).max(2000).optional(),\n ellipsis: z.string().max(10).optional(),\n bodyFallback: z.object({\n preferContaining: z.string().trim().min(1).max(100).optional(),\n stripLeadingLabel: z.literal(\"conclusion\").optional(),\n }).strict().optional(),\n fallback: fallbackText.optional(),\n fallbackWithParent: fallbackText.optional(),\n fallbackWithoutParent: fallbackText.optional(),\n fallbackBySlug: z.record(z.string().max(200), fallbackText).optional(),\n}).strict()\n .refine((rule) => !rule.ellipsis || (rule.maxLength !== undefined && rule.ellipsis.length < rule.maxLength), \"말줄임표에는 더 긴 최대 길이가 필요합니다\")\n .refine((rule) => rule.source === \"template\" ? rule.template !== undefined : rule.template === undefined, \"텍스트 template은 source: template과 함께 선언합니다\")\n .refine((rule) => !rule.wordBoundaryMinLength || Boolean(rule.maxLength && rule.wordBoundaryMinLength < rule.maxLength), \"단어 경계에는 더 긴 최대 길이가 필요합니다\");\nexport type MetadataTextRule = z.infer<typeof MetadataTextRuleSchema>;\n\nexport interface MetadataTextInput {\n title: string;\n slug?: string;\n parentId?: string | null;\n /** Plain scalar text fields, matching public post.fields / stored metaJson.acf. */\n fields?: Record<string, unknown> | null;\n bodyParagraphs?: readonly string[] | null;\n}\n\nexport function cleanMetadataText(value: string, rule?: MetadataTextRule): string {\n let text = value.trim();\n if (rule?.stripLeadingLabel === \"question\") text = text.replace(/^\\s*Q[..:]?\\s*/iu, \"\");\n if (rule?.stripLeadingLabel === \"answer\") text = text.replace(/^\\s*A[..:]?\\s*/iu, \"\");\n if (rule?.replaceLineBreakTokens) text = text.replace(/\\[\\[br(?::[^\\]]*)?\\]\\]/giu, \" \");\n if (rule?.removeEmDashes) text = text.replaceAll(\"—\", \"\");\n if (rule?.normalizeWhitespace) text = text.replace(/\\s+/gu, \" \");\n return text.trim();\n}\n\n/** Walk only content nodes. Attributes, HTML, image alt and unrelated objects are not prose. */\nexport function metadataBodyParagraphs(value: unknown): string[] {\n const paragraphs: string[] = [];\n const textParts = (node: unknown): string[] => {\n if (Array.isArray(node)) return node.flatMap(textParts);\n if (!node || typeof node !== \"object\") return [];\n const item = node as Record<string, unknown>;\n return [...(typeof item.text === \"string\" ? [item.text] : []), ...textParts(item.content)];\n };\n const walk = (node: unknown): void => {\n if (Array.isArray(node)) { node.forEach(walk); return; }\n if (!node || typeof node !== \"object\") return;\n const item = node as Record<string, unknown>;\n if (item.type === \"paragraph\") {\n const text = textParts(item.content).join(\" \").replace(/\\s+/gu, \" \").trim();\n if (text) paragraphs.push(text);\n } else walk(item.content);\n };\n walk(value);\n return paragraphs;\n}\n\nexport function formatMetadataText(value: string | null | undefined, rule: MetadataTextRule | undefined, input: MetadataTextInput, name: string): string | undefined {\n let text = rule?.source === \"fallback\" ? \"\" : cleanMetadataText(value ?? \"\", rule);\n if (rule?.source === \"template\") {\n text = cleanMetadataText((rule.template ?? \"\").replace(templateToken, (token) => {\n if (token === \"{{title}}\") return name;\n const field = token.slice(8, -2);\n const value = Object.hasOwn(input.fields ?? {}, field) ? input.fields?.[field] : undefined;\n return typeof value === \"string\" ? value.trim() : \"\";\n }), rule);\n }\n if (!text && rule?.bodyFallback) {\n const paragraphs = input.bodyParagraphs ?? [];\n const preferred = rule.bodyFallback.preferContaining;\n text = (preferred ? paragraphs.find((paragraph) => paragraph.includes(preferred)) : undefined) ?? paragraphs[0] ?? \"\";\n if (rule.bodyFallback.stripLeadingLabel === \"conclusion\") text = text.replace(/^(먼저\\s*)?결론부터\\s*(말씀드리면|말씀드릴게요)[.!?]?\\s*/u, \"\");\n text = cleanMetadataText(text, rule);\n }\n if (!text) {\n const slugFallback = input.slug && Object.hasOwn(rule?.fallbackBySlug ?? {}, input.slug) ? rule?.fallbackBySlug?.[input.slug] : undefined;\n const fallback = input.parentId\n ? rule?.fallbackWithParent ?? rule?.fallback\n : slugFallback ?? rule?.fallbackWithoutParent ?? rule?.fallback;\n text = cleanMetadataText((fallback ?? \"\").replaceAll(\"%s\", () => name), rule);\n }\n if (!text) return undefined;\n const units = rule?.lengthUnit === \"code-points\" ? [...text] : text.split(\"\");\n if (rule?.maxLength && units.length > rule.maxLength) {\n const suffix = rule.ellipsis ?? \"\";\n const suffixLength = rule.lengthUnit === \"code-points\" ? [...suffix].length : suffix.length;\n let head = units.slice(0, rule.maxLength - suffixLength).join(\"\");\n const boundary = head.lastIndexOf(\" \");\n if (rule.wordBoundaryMinLength && boundary >= rule.wordBoundaryMinLength) head = head.slice(0, boundary);\n return suffix ? head.trimEnd() + suffix : head;\n }\n return text;\n}\n","import { z } from \"zod\";\n\n/** Public field names only: no property paths, prototype keys, or executable expressions. */\nexport const ContentPolicyFieldNameSchema = z.string().min(1).max(64)\n .regex(/^[A-Za-z_][A-Za-z0-9_]*$/)\n .refine((name) => name !== \"prototype\" && !Object.hasOwn(Object.prototype, name));\n\nexport const ContentPolicyScalarSchema = z.union([\n z.string().max(2048), z.number().finite(), z.boolean(), z.null(),\n]);\nconst contentPolicyEquality = z.object({\n field: ContentPolicyFieldNameSchema,\n equals: ContentPolicyScalarSchema,\n}).strict();\n/** Exact scalar equality, or a flat AND with an optional NOT-AND exception. */\nexport const ContentPolicyWhenSchema = z.union([\n contentPolicyEquality,\n z.object({\n all: z.array(contentPolicyEquality).min(1).max(4).optional(),\n unlessAll: z.array(contentPolicyEquality).min(1).max(4).optional(),\n }).strict().refine((when) => when.all !== undefined || when.unlessAll !== undefined,\n \"콘텐츠 조건에는 all 또는 unlessAll 그룹이 필요합니다\"),\n]);\n\nexport const ContentPolicyInternalPathSchema = z.string().min(1).max(2048).refine((path) => {\n if (!path.startsWith(\"/\") || path.startsWith(\"//\") || /[\\\\?#\\u0000-\\u0020\\u007f]/u.test(path)) return false;\n try {\n return path.split(\"/\").every((part, index) => {\n if (index === 0) return true;\n const decoded = decodeURIComponent(part);\n return decoded !== \".\" && decoded !== \"..\" && !/[\\\\/\\u0000-\\u0020\\u007f]/u.test(decoded);\n });\n } catch { return false; }\n}, \"콘텐츠 경로는 안전한 사이트 내부 경로여야 합니다\");\n\nconst field = { field: ContentPolicyFieldNameSchema };\nconst scalarChecks = [\n z.object({ ...field, op: z.literal(\"equals\"), value: ContentPolicyScalarSchema }).strict(),\n z.object({ ...field, op: z.literal(\"notEquals\"), value: ContentPolicyScalarSchema }).strict(),\n z.object({ ...field, op: z.literal(\"oneOf\"), values: z.array(ContentPolicyScalarSchema).min(1).max(16), allowEmpty: z.boolean().optional() }).strict(),\n z.object({ ...field, op: z.literal(\"text\") }).strict(),\n z.object({ ...field, op: z.literal(\"date\") }).strict(),\n z.object({ ...field, op: z.literal(\"safeHttps\") }).strict(),\n z.object({ ...field, op: z.literal(\"allowedPath\"), paths: z.array(ContentPolicyInternalPathSchema).min(1).max(512) }).strict(),\n] as const;\n\n/** Repeater children cannot contain another repeater, body check, or conditional expression. */\nexport const ContentPolicyScalarCheckSchema = z.discriminatedUnion(\"op\", scalarChecks);\nexport type ContentPolicyScalarCheck = z.infer<typeof ContentPolicyScalarCheckSchema>;\n\nconst when = { when: ContentPolicyWhenSchema.optional() };\nexport const ContentPolicyCheckSchema = z.discriminatedUnion(\"op\", [\n scalarChecks[0].extend(when), scalarChecks[1].extend(when), scalarChecks[2].extend(when),\n scalarChecks[3].extend(when), scalarChecks[4].extend(when), scalarChecks[5].extend(when), scalarChecks[6].extend(when),\n z.object({ ...field, ...when, op: z.literal(\"some\"), checks: z.array(ContentPolicyScalarCheckSchema).min(1).max(8) }).strict(),\n z.object({\n ...when, op: z.literal(\"bodyContent\"),\n allowExcerpt: z.boolean().optional(),\n allowFeaturedImage: z.boolean().optional(),\n additionalUrl: z.object({ ...field, ...when }).strict().optional(),\n }).strict(),\n z.object({ ...when, op: z.literal(\"bodyText\") }).strict(),\n]);\nexport type ContentPolicyCheck = z.infer<typeof ContentPolicyCheckSchema>;\n\n/** Flat requirements with bounded scalar conditions and at most one repeater level. */\nconst contentPolicy = z.object({\n visibility: z.array(ContentPolicyCheckSchema).max(24),\n indexing: z.array(ContentPolicyCheckSchema).max(24),\n pathSegmentFormat: z.literal(\"lower-kebab\").optional(),\n}).strict();\nexport type ContentPolicy = z.infer<typeof contentPolicy>;\nexport const ContentPolicySchema = contentPolicy.superRefine((policy, ctx) => {\n const refs = collectContentPolicyFieldRefs(policy);\n if (refs.length > 64) ctx.addIssue({ code: z.ZodIssueCode.custom, message: \"콘텐츠 정책은 공개 필드 64개까지 참조할 수 있습니다\" });\n for (const ref of refs) {\n if ((ref.children?.length ?? 0) > 8) ctx.addIssue({ code: z.ZodIssueCode.custom, message: `반복 필드 ${ref.name}의 자식 참조는 8개까지 허용합니다` });\n }\n});\n\nexport interface ContentPolicyFieldRef {\n readonly name: string;\n readonly children?: string[];\n}\n\nexport function collectContentPolicyFieldRefs(policy: ContentPolicy): ContentPolicyFieldRef[] {\n const refs = new Map<string, Set<string>>();\n const add = (name: string, children: string[] = []) => {\n const names = refs.get(name) ?? new Set<string>();\n for (const child of children) names.add(child);\n refs.set(name, names);\n };\n const addWhen = (when: ContentPolicyCheck[\"when\"]) => {\n if (!when) return;\n if (\"field\" in when) add(when.field);\n else for (const condition of [...(when.all ?? []), ...(when.unlessAll ?? [])]) add(condition.field);\n };\n for (const check of [...policy.visibility, ...policy.indexing]) {\n addWhen(check.when);\n if (\"field\" in check) add(check.field, check.op === \"some\" ? check.checks.map((child) => child.field) : []);\n if (check.op === \"bodyContent\" && check.additionalUrl) {\n add(check.additionalUrl.field);\n addWhen(check.additionalUrl.when);\n }\n }\n return [...refs].map(([name, children]) => ({ name, ...(children.size ? { children: [...children] } : {}) }));\n}\n","import type { ContentPolicyCheck } from \"./content-policy-schema\";\n\n/** Shared internal condition gate for public content and metadata defaults. */\nexport function matchesContentPolicyWhen(when: ContentPolicyCheck[\"when\"], fields: Readonly<Record<string, unknown>>): boolean {\n if (!when) return true;\n const matches = ({ field, equals }: { field: string; equals: unknown }) => {\n const value = Object.hasOwn(fields, field) ? fields[field] : undefined;\n return value === equals;\n };\n if (\"field\" in when) return matches(when);\n return (when.all?.every(matches) ?? true) && !(when.unlessAll?.every(matches) ?? false);\n}\n","import { sanitizeHtml } from \"./sanitize\";\n\nexport type PostBodySource =\n | { readonly kind: \"html\"; readonly html: string }\n | { readonly kind: \"json\" };\n\n/** Imported HTML is authoritative when sanitization leaves visible content. */\nexport function resolvePostBodySource(bodyHtml: string | null | undefined): PostBodySource {\n if (typeof bodyHtml !== \"string\") return { kind: \"json\" };\n const trimmed = bodyHtml.trim();\n if (trimmed.length === 0) return { kind: \"json\" };\n const sanitized = sanitizeHtml(trimmed);\n const visible = /<(?:img|hr|br|iframe|video|audio)\\b/i.test(sanitized)\n || sanitized.replace(/<[^>]*>/g, \"\").replace(/&nbsp;/gi, \" \").trim().length > 0;\n return visible ? { kind: \"html\", html: sanitized } : { kind: \"json\" };\n}\n","import type { ContentPolicy, ContentPolicyCheck, ContentPolicyScalarCheck } from \"./content-policy-schema\";\nimport { ContentPolicyInternalPathSchema } from \"./content-policy-schema\";\nimport { matchesContentPolicyWhen } from \"./content-policy-condition\";\nimport { readMetadataValues, type MetadataRobotsPolicy } from \"./metadata-contract\";\nimport { resolvePostBodySource } from \"./post-body-source\";\n\n/** Only the SDK's public projection belongs here; never pass private ACF as fields. */\nexport interface ContentPolicyPost {\n readonly id: string;\n readonly title: string;\n readonly slug: string;\n readonly path: string | null;\n readonly status?: string;\n readonly publishedAt?: string | null;\n readonly modelKey?: string | null;\n readonly collectionKey?: string | null;\n readonly fields?: Readonly<Record<string, unknown>> | null;\n readonly bodyJson?: unknown;\n readonly bodyHtml?: string | null;\n readonly excerpt?: string | null;\n readonly featuredImageUrl?: string | null;\n readonly metaJson?: { readonly seo?: unknown } | null;\n readonly terms?: ReadonlyArray<{ readonly id: string; readonly taxonomy: string }>;\n}\n\nexport interface ContentPolicyOptions {\n readonly now?: number;\n /** Supply the shared model resolver result, including null. The policy does not own route templates. */\n readonly expectedPath?: string | null;\n readonly robots?: MetadataRobotsPolicy;\n}\n\nexport interface ContentPolicyResult {\n readonly visible: boolean;\n readonly indexable: boolean;\n readonly robots: MetadataRobotsPolicy & { readonly noindexReasons?: string[] };\n readonly reasons: string[];\n}\n\nconst text = (value: unknown): string => typeof value === \"string\" ? value.trim() : \"\";\nconst record = (value: unknown): Record<string, unknown> | null =>\n value !== null && typeof value === \"object\" && !Array.isArray(value) ? value as Record<string, unknown> : null;\n\nfunction fieldValue(fields: Readonly<Record<string, unknown>>, field: string): unknown {\n return Object.hasOwn(fields, field) ? fields[field] : undefined;\n}\n\n/** Same strict HTTPS boundary used for public images and source links. */\nexport function contentPolicySafeHttpsUrl(value: unknown): string | null {\n if (typeof value !== \"string\" || /[\\\\\\u0000-\\u0020\\u007f]/u.test(value)) return null;\n try {\n const url = new URL(value);\n return url.protocol === \"https:\" && !url.username && !url.password ? url.href : null;\n } catch { return null; }\n}\n\nfunction dateValue(value: unknown): number | null {\n if (!value || (typeof value !== \"string\" && typeof value !== \"number\")) return null;\n if (!Number.isFinite(Date.parse(String(value)))) return null;\n const date = new Date(value).getTime();\n return Number.isFinite(date) ? date : null;\n}\n\n/** Text nodes only: an image-only JSON document is not textual content. */\nexport function contentPolicyBodyText(bodyJson: unknown): string {\n const root = record(bodyJson);\n if (root?.type !== \"doc\" || !Array.isArray(root.content)) return \"\";\n const pending: Array<{ node: unknown; depth: number }> = [{ node: root, depth: 0 }];\n const output: string[] = [];\n let count = 0;\n while (pending.length) {\n const entry = pending.pop()!;\n if (++count > 100_000 || entry.depth > 128) throw new Error(\"Content policy body exceeds traversal limits\");\n const node = record(entry.node);\n if (!node) continue;\n if (node.type === \"text\") {\n const value = text(node.text);\n if (value) output.push(value);\n } else if (Array.isArray(node.content)) {\n for (let index = node.content.length - 1; index >= 0; index--) {\n pending.push({ node: node.content[index], depth: entry.depth + 1 });\n }\n }\n }\n return output.join(\" \");\n}\n\nexport function matchesContentScalarCheck(check: ContentPolicyScalarCheck, fields: Readonly<Record<string, unknown>>): boolean {\n const value = fieldValue(fields, check.field);\n switch (check.op) {\n case \"equals\": return value === check.value;\n case \"notEquals\": return value !== check.value;\n case \"oneOf\": return (check.allowEmpty === true && !value) || check.values.some((candidate) => candidate === value);\n case \"text\": return text(value).length > 0;\n case \"date\": return dateValue(value) !== null;\n case \"safeHttps\": return contentPolicySafeHttpsUrl(value) !== null;\n case \"allowedPath\": return check.paths.includes(text(value));\n }\n}\n\ninterface BodyFacts { readonly jsonText: string; readonly html: string; readonly text: string }\nfunction bodyFacts(post: ContentPolicyPost): BodyFacts {\n const jsonText = contentPolicyBodyText(post.bodyJson);\n const source = resolvePostBodySource(post.bodyHtml);\n const html = source.kind === \"html\" ? source.html : \"\";\n return { jsonText, html, text: source.kind === \"html\" ? html.replace(/<[^>]*>/g, \"\").replace(/&nbsp;/gi, \" \").trim() : jsonText };\n}\n\nfunction matchesCheck(check: ContentPolicyCheck, post: ContentPolicyPost, fields: Readonly<Record<string, unknown>>, getBody: () => BodyFacts): boolean {\n if (!matchesContentPolicyWhen(check.when, fields)) return true;\n if (check.op === \"some\") {\n const value = fieldValue(fields, check.field);\n return Array.isArray(value) && value.some((item) => {\n const child = record(item);\n return child !== null && check.checks.every((scalar) => matchesContentScalarCheck(scalar, child));\n });\n }\n if (check.op === \"bodyText\") return Boolean(getBody().text);\n if (check.op === \"bodyContent\") {\n const body = getBody();\n return Boolean(body.jsonText || body.html\n || (check.allowExcerpt && text(post.excerpt))\n || (check.allowFeaturedImage && contentPolicySafeHttpsUrl(post.featuredImageUrl))\n || (check.additionalUrl && matchesContentPolicyWhen(check.additionalUrl.when, fields)\n && contentPolicySafeHttpsUrl(fieldValue(fields, check.additionalUrl.field))));\n }\n return matchesContentScalarCheck(check, fields);\n}\n\nfunction validPath(policy: ContentPolicy, post: ContentPolicyPost, options: ContentPolicyOptions): boolean {\n const path = text(post.path).replace(/\\/+$/, \"\");\n if (!path || !ContentPolicyInternalPathSchema.safeParse(path).success) return false;\n const segments = path.slice(1).split(\"/\");\n if (segments.some((segment) => !segment)) return false;\n if (policy.pathSegmentFormat === \"lower-kebab\" && segments.some((segment) => !/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(segment))) return false;\n if (decodeURIComponent(segments.at(-1)!) !== post.slug) return false;\n if (options.expectedPath !== undefined) {\n return options.expectedPath !== null && path === options.expectedPath.replace(/\\/+$/, \"\");\n }\n return true;\n}\n\n/**\n * Evaluate opt-in policy only. Callers supply the model's expectedPath to enforce prefixes,\n * category selection and reserved slugs; without it only the common path boundary is checked.\n */\nexport function evaluateContentPolicy(policy: ContentPolicy, post: ContentPolicyPost, options: ContentPolicyOptions = {}): ContentPolicyResult {\n const visibility: string[] = [];\n if (post.status !== \"published\") visibility.push(\"content:not-published\");\n if (!text(post.id) || !text(post.title) || !text(post.slug)) visibility.push(\"content:invalid-identity\");\n const publishedAt = dateValue(post.publishedAt);\n if (publishedAt === null) visibility.push(\"content:invalid-published-at\");\n else if (publishedAt > (options.now ?? Date.now())) visibility.push(\"content:future-publication\");\n if (!validPath(policy, post, options)) visibility.push(\"content:invalid-path\");\n\n const fields = post.fields ?? {};\n let body: BodyFacts | undefined;\n const getBody = () => body ??= bodyFacts(post);\n const failures = (checks: ContentPolicyCheck[], stage: string) => checks.flatMap((check) =>\n matchesCheck(check, post, fields, getBody) ? [] : [`content:${stage}:${check.op}${\"field\" in check ? `:${check.field}` : \"\"}`]);\n if (visibility.length === 0) visibility.push(...failures(policy.visibility, \"visibility\"));\n const visible = visibility.length === 0;\n const indexing = visible ? failures(policy.indexing, \"indexing\") : [];\n const forced = !visible || indexing.length > 0 || options.robots?.forceNoindex === true;\n const inheritedReasons = options.robots?.forceNoindex\n ? options.robots.noindexReasons?.length ? options.robots.noindexReasons : [\"content:forced-noindex\"] : [];\n const noindexReasons = [...new Set([...visibility, ...indexing, ...inheritedReasons])].slice(0, 64);\n const manualNoindex = readMetadataValues(post.metaJson?.seo).noindex === true;\n return {\n visible,\n indexable: visible && !forced && !manualNoindex,\n robots: {\n ...options.robots,\n ...(forced ? { forceNoindex: true } : {}),\n ...(noindexReasons.length ? { noindexReasons } : {}),\n },\n reasons: [...noindexReasons, ...(manualNoindex ? [\"content:manual-noindex\"] : [])],\n };\n}\n","import { z } from \"zod\";\nimport { ContentPolicyFieldNameSchema, ContentPolicyWhenSchema } from \"./content-policy-schema\";\nimport { matchesContentPolicyWhen } from \"./content-policy-condition\";\nimport { contentPolicySafeHttpsUrl } from \"./content-policy\";\n\n/** A declared public URL field; never accept a caller-provided URL template or host. */\nexport const MetadataVideoThumbnailRuleSchema = z.object({\n source: z.literal(\"youtube\"),\n field: ContentPolicyFieldNameSchema,\n when: ContentPolicyWhenSchema.optional(),\n}).strict();\n\n/** Preserve the existing YouTube parser, including its strict ID and HTTPS boundaries. */\nexport function metadataVideoThumbnail(\n rule: z.infer<typeof MetadataVideoThumbnailRuleSchema> | undefined,\n fields: Readonly<Record<string, unknown>> | null | undefined,\n): string | undefined {\n if (!rule || !matchesContentPolicyWhen(rule.when, fields ?? {})) return undefined;\n const value = fields && Object.hasOwn(fields, rule.field) ? fields[rule.field] : undefined;\n const safe = contentPolicySafeHttpsUrl(value);\n if (!safe) return undefined;\n const url = new URL(safe);\n const id = url.hostname === \"youtu.be\" ? url.pathname.slice(1)\n : [\"www.youtube.com\", \"youtube.com\", \"m.youtube.com\"].includes(url.hostname)\n ? (url.pathname === \"/watch\" ? url.searchParams.get(\"v\") : url.pathname.match(/^\\/(?:shorts|embed)\\/([^/]+)$/)?.[1]) : null;\n return /^[a-zA-Z0-9_-]{11}$/.test(id ?? \"\") ? `https://i.ytimg.com/vi/${id}/hqdefault.jpg` : undefined;\n}\n","import type { z } from \"zod\";\nimport type { ContentPolicyFieldRef } from \"./content-policy-schema\";\nimport type { MetadataVideoThumbnailRuleSchema } from \"./metadata-video-thumbnail\";\n\ninterface FieldSources {\n title?: { source?: string; template?: string };\n description?: { source?: string; template?: string };\n videoThumbnail?: z.infer<typeof MetadataVideoThumbnailRuleSchema>;\n}\n\n/** Public source fields needed by one model's automatic values, without duplicates. */\nexport function collectMetadataDefaultFieldRefs(rule: FieldSources | undefined): ContentPolicyFieldRef[] {\n const fields = new Set<string>();\n const video = rule?.videoThumbnail;\n if (video) {\n fields.add(video.field);\n if (video.when) {\n if (\"field\" in video.when) fields.add(video.when.field);\n else for (const condition of [...(video.when.all ?? []), ...(video.when.unlessAll ?? [])]) fields.add(condition.field);\n }\n }\n for (const text of [rule?.title, rule?.description]) {\n if (text?.source !== \"template\") continue;\n for (const match of (text.template ?? \"\").matchAll(/\\{\\{field:([a-zA-Z][a-zA-Z0-9_-]{0,159})\\}\\}/gu)) fields.add(match[1]!);\n }\n return [...fields].map((name) => ({ name }));\n}\n","import { z } from \"zod\";\nimport { ContentPolicyFieldNameSchema, ContentPolicyInternalPathSchema, ContentPolicyScalarSchema, type ContentPolicy, type ContentPolicyFieldRef } from \"./content-policy-schema\";\nimport { evaluateContentPolicy, matchesContentScalarCheck, type ContentPolicyOptions, type ContentPolicyPost, type ContentPolicyResult } from \"./content-policy\";\nimport type { MetadataRobotsPolicy } from \"./metadata-contract\";\n\nconst field = { field: ContentPolicyFieldNameSchema };\nexport const ContentQueryFilterSchema = z.discriminatedUnion(\"op\", [\n z.object({ ...field, op: z.literal(\"equals\"), value: ContentPolicyScalarSchema }).strict(),\n z.object({ ...field, op: z.literal(\"notEquals\"), value: ContentPolicyScalarSchema }).strict(),\n z.object({ ...field, op: z.literal(\"oneOf\"), values: z.array(ContentPolicyScalarSchema).min(1).max(16), allowEmpty: z.boolean().optional() }).strict(),\n]);\n\n/** Ordering is fixed: optional pinned=true first, publication newest first, then id ascending. */\nexport const ContentQuerySchema = z.object({\n modelKeys: z.array(z.string().min(1).max(100).regex(/^[a-z][a-z0-9_-]*$/)).min(1).max(16),\n filters: z.array(ContentQueryFilterSchema).max(8).optional(),\n scope: z.enum([\"currentTerm\", \"currentPath\"]).optional(),\n pinnedField: ContentPolicyFieldNameSchema.optional(),\n pageSize: z.number().int().min(1).max(100),\n requireItems: z.boolean().optional(),\n}).strict();\nexport type ContentQuery = z.infer<typeof ContentQuerySchema>;\n\nexport function collectContentQueryFieldRefs(query: ContentQuery): ContentPolicyFieldRef[] {\n return [...new Set([...(query.filters ?? []).map((filter) => filter.field), ...(query.pinnedField ? [query.pinnedField] : [])])]\n .map((name) => ({ name }));\n}\n\nexport interface ContentQueryCandidate<T extends ContentPolicyPost = ContentPolicyPost> {\n readonly post: T;\n readonly policy: ContentPolicyResult;\n}\n\nexport interface ContentQueryOptions {\n /** A missing or failed cursor page must throw instead of becoming an empty noindex page. */\n readonly complete: boolean;\n readonly page?: number;\n readonly path?: string | null;\n readonly termId?: string | null;\n readonly robots?: MetadataRobotsPolicy;\n}\n\nexport interface ContentQueryResult<T extends ContentPolicyPost = ContentPolicyPost> {\n readonly items: T[];\n readonly total: number;\n readonly page: number;\n readonly pageCount: number;\n readonly visible: boolean;\n readonly indexable: boolean;\n readonly robots: MetadataRobotsPolicy & { readonly noindexReasons?: string[] };\n readonly reasons: string[];\n}\n\nfunction matchesScope(query: ContentQuery, post: ContentPolicyPost, options: ContentQueryOptions): boolean {\n if (query.scope === \"currentTerm\") return post.terms?.some((term) => term.id === options.termId) === true;\n if (query.scope === \"currentPath\") {\n const path = options.path!.replace(/\\/+$/, \"\");\n return post.path?.trim().startsWith(`${path}/`) === true;\n }\n return true;\n}\n\n/** Reuse already evaluated posts across routes and terms within a request. */\nexport function evaluateContentQuery<T extends ContentPolicyPost>(query: ContentQuery, candidates: ReadonlyArray<ContentQueryCandidate<T>>, options: ContentQueryOptions): ContentQueryResult<T> {\n if (!options.complete) throw new Error(\"Content query input is incomplete\");\n const page = options.page ?? 1;\n if (!Number.isSafeInteger(page) || page < 1) throw new Error(\"Content query page must be a positive safe integer\");\n if (query.scope === \"currentTerm\" && !options.termId) throw new Error(\"Content query requires the current term id\");\n if (query.scope === \"currentPath\" && (!options.path || !ContentPolicyInternalPathSchema.safeParse(options.path).success)) throw new Error(\"Content query requires a safe current path\");\n const visible = candidates.filter(({ policy }) => policy.visible);\n // Preserve the public list boundary: ambiguous canonical paths are never arbitrarily chosen.\n const pathCounts = new Map<string | null, number>();\n const canonical = (post: ContentPolicyPost) => post.path?.trim().replace(/\\/+$/, \"\") ?? null;\n for (const { post } of visible) pathCounts.set(canonical(post), (pathCounts.get(canonical(post)) ?? 0) + 1);\n const seen = new Set<string>();\n const selected = visible.filter(({ post }) => {\n if (pathCounts.get(canonical(post)) !== 1 || seen.has(post.id)) return false;\n seen.add(post.id);\n return query.modelKeys.includes(post.modelKey ?? post.collectionKey ?? \"\") && matchesScope(query, post, options)\n && (query.filters ?? []).every((filter) => matchesContentScalarCheck(filter, post.fields ?? {}));\n }).sort((left, right) => {\n if (query.pinnedField) {\n const delta = Number(right.post.fields?.[query.pinnedField] === true) - Number(left.post.fields?.[query.pinnedField] === true);\n if (delta) return delta;\n }\n return Date.parse(right.post.publishedAt!) - Date.parse(left.post.publishedAt!) || left.post.id.localeCompare(right.post.id);\n });\n const pageCount = Math.max(1, Math.ceil(selected.length / query.pageSize));\n const current = selected.slice((page - 1) * query.pageSize, page * query.pageSize);\n const pageVisible = page <= pageCount && (!query.requireItems || selected.length > 0);\n const hasIndexable = current.some(({ policy }) => policy.indexable);\n const forced = !hasIndexable || options.robots?.forceNoindex === true;\n const reasons = [...new Set([\n ...(page > pageCount ? [\"content:invalid-page\"] : query.requireItems && !selected.length ? [\"content:empty-required-list\"] : !current.length ? [\"content:empty-page\"] : !hasIndexable ? [\"content:no-indexable-items\"] : []),\n ...(options.robots?.forceNoindex ? options.robots.noindexReasons?.length ? options.robots.noindexReasons : [\"content:forced-noindex\"] : []),\n ])].slice(0, 64);\n return {\n items: current.map(({ post }) => post), total: selected.length, page, pageCount,\n visible: pageVisible, indexable: pageVisible && !forced,\n robots: { ...options.robots, ...(forced ? { forceNoindex: true } : {}), ...(reasons.length ? { noindexReasons: reasons } : {}) },\n reasons,\n };\n}\n\n/** Convenience entry point for FRONT consumers; DB readers may reuse evaluated candidates. */\nexport function selectContentQuery<T extends ContentPolicyPost>(query: ContentQuery, posts: ReadonlyArray<T>, policies: Readonly<Record<string, ContentPolicy>>, options: ContentQueryOptions & {\n readonly now?: number;\n readonly postOptions: (post: T) => ContentPolicyOptions & { expectedPath: string | null };\n}): ContentQueryResult<T> {\n if (!options.complete) throw new Error(\"Content query input is incomplete\");\n const candidates = posts.filter((post) => query.modelKeys.includes(post.modelKey ?? post.collectionKey ?? \"\")).map((post) => {\n const modelKey = post.modelKey ?? post.collectionKey ?? \"\";\n if (!Object.hasOwn(policies, modelKey)) throw new Error(`Content query has no policy for model ${modelKey}`);\n return { post, policy: evaluateContentPolicy(policies[modelKey], post, { now: options.now, ...options.postOptions(post) }) };\n });\n return evaluateContentQuery(query, candidates, options);\n}\n","/** 검색 관리의 등록·검토·공개 Proxy가 공유하는 정확 경로 계약. */\nexport type NormalizedRedirectSourceResult =\n | { readonly ok: true; readonly value: string }\n | { readonly ok: false; readonly code: \"invalid_source_path\" };\n\nexport type NormalizedRedirectTargetResult =\n | { readonly ok: true; readonly value: string; readonly kind: \"internal\" | \"external\" }\n | { readonly ok: false; readonly code: \"invalid_target\" };\n\nconst CONTROL_OR_BACKSLASH = /[\\\\\\u0000-\\u001f\\u007f]/u;\nconst PROTECTED_PREFIX = /^\\/(?:api|_next|_vercel|auth|admin|login|logout|revalidate|preview|assets|static|images|fonts|\\.well-known)(?:\\/|$)/i;\nconst STATIC_EXTENSION = /\\.(?:ico|png|jpe?g|gif|svg|webp|avif|css|js|txt|xml|json|woff2?|map)$/i;\n\nfunction decoded(value: string): string | null {\n try { return decodeURIComponent(value); } catch { return null; }\n}\n\n/** 쿼리·해시를 제외하고 한글·후행 슬래시를 같은 경로로 비교한다. */\nfunction trimInternalPath(path: string): string {\n if (!path.startsWith(\"/\")) path = `/${path}`;\n if (path.length > 1) path = path.replace(/\\/+$/, \"\");\n return path || \"/\";\n}\n\nexport function normalizeRedirectPath(raw: string): string {\n return trimInternalPath((decoded(raw) ?? raw).split(/[?#]/, 1)[0] ?? \"\");\n}\n\n/** 등록 제한과 Proxy의 자체 차단은 이 함수를 공유한다. */\nexport function isProtectedRedirectPath(path: string): boolean {\n const normalized = normalizeRedirectPath(path);\n return PROTECTED_PREFIX.test(normalized) || STATIC_EXTENSION.test(normalized);\n}\n\nfunction unsafePath(path: string): boolean {\n return CONTROL_OR_BACKSLASH.test(path) || path.startsWith(\"//\") ||\n /(?:^|\\/)\\.{1,2}(?:\\/|$)/.test(path) || /[%](?:2f|5c|0[0-9a-f]|1[0-9a-f]|7f)/i.test(path);\n}\n\nexport function normalizeRedirectSource(raw: string): NormalizedRedirectSourceResult {\n const invalid = { ok: false, code: \"invalid_source_path\" } as const;\n if (CONTROL_OR_BACKSLASH.test(raw)) return invalid;\n const trimmed = raw.trim();\n if (!trimmed || /^[a-z][a-z\\d+.-]*:/i.test(trimmed) || trimmed.startsWith(\"//\")) return invalid;\n const rawPath = trimmed.split(/[?#]/, 1)[0] ?? \"\";\n const path = decoded(rawPath);\n if (!path || unsafePath(path) || /[?#*]/.test(path)) return invalid;\n const normalized = trimInternalPath(path);\n if (isProtectedRedirectPath(normalized)) return invalid;\n return { ok: true, value: normalized };\n}\n\nexport function normalizeRedirectTarget(raw: string): NormalizedRedirectTargetResult {\n const invalid = { ok: false, code: \"invalid_target\" } as const;\n const decodedRaw = decoded(raw);\n if (decodedRaw === null || CONTROL_OR_BACKSLASH.test(raw) || CONTROL_OR_BACKSLASH.test(decodedRaw)) return invalid;\n const trimmed = raw.trim();\n if (!trimmed || trimmed.startsWith(\"//\")) return invalid;\n if (trimmed.startsWith(\"/\")) {\n const suffixIndex = trimmed.search(/[?#]/);\n const rawPath = suffixIndex === -1 ? trimmed : trimmed.slice(0, suffixIndex);\n const path = decoded(rawPath);\n if (!path || unsafePath(path) || /[?#]/.test(path)) return invalid;\n const suffix = suffixIndex === -1 ? \"\" : trimmed.slice(suffixIndex);\n return { ok: true, value: `${trimInternalPath(path)}${suffix}`, kind: \"internal\" };\n }\n try {\n const url = new URL(trimmed);\n if ((url.protocol !== \"http:\" && url.protocol !== \"https:\") || url.username || url.password) return invalid;\n return { ok: true, value: url.toString(), kind: \"external\" };\n } catch { return invalid; }\n}\n"],"mappings":"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","names":["category","chain","postcss","sanitizeHtmlLibrary","text","import_sanitize_html","ALLOWED_URL_SCHEMES","fromCodePointSafe","decoded","sanitizeHtmlLibrary","HEX_COLOR_RE","isRecord","field","field","import_zod","record","field","import_zod","import_zod","text","field","value","import_zod","when","decoded","when","field","text","field","import_zod","text","template","import_zod","field"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/block.ts","../src/bindings.ts","../src/collections.ts","../src/category-tree.ts","../src/public-path.ts","../src/delimiter.ts","../src/embed.ts","../src/faq.ts","../src/image-alt.ts","../src/imported-html-sanitize.ts","../src/internal-content-links.ts","../src/preview-token.ts","../src/visual-edit-contract.ts","../src/sanitize.ts","../src/stable-id.ts","../src/site-patterns.ts","../src/editor-formatting.ts","../src/section-locks.ts","../src/metadata-resolver.ts","../src/metadata-contract.ts","../src/metadata-defaults.ts","../src/metadata-text.ts","../src/content-policy-schema.ts","../src/content-policy-condition.ts","../src/post-body-source.ts","../src/content-policy.ts","../src/metadata-video-thumbnail.ts","../src/metadata-default-fields.ts","../src/content-query.ts","../src/content-relations.ts","../src/content-values.ts","../src/content-dependencies.ts","../src/redirect-paths.ts"],"sourcesContent":["// @roottale/cms-core — ADR-0034 §3 §5 (Phase 1 Block JSON foundation)\nexport * from \"./block\";\nexport * from \"./bindings\";\nexport * from \"./collections\";\nexport * from \"./category-tree\";\nexport * from \"./public-path\";\nexport * from \"./block-definition\";\nexport * from \"./delimiter\";\nexport * from \"./embed\";\nexport * from \"./faq\";\nexport * from \"./image-alt\";\nexport * from \"./imported-html-sanitize\";\nexport * from \"./internal-content-links\";\nexport * from \"./preview-token\";\nexport * from \"./visual-edit-contract\";\nexport * from \"./sanitize\";\nexport * from \"./stable-id\";\nexport * from \"./site-patterns\";\nexport * from \"./editor-formatting\";\n\nexport * from \"./section-locks\";\nexport * from \"./metadata-resolver\";\nexport * from \"./metadata-contract\";\nexport * from \"./metadata-defaults\";\nexport * from \"./post-body-source\";\nexport * from \"./content-policy-schema\";\nexport * from \"./content-policy\";\nexport * from \"./content-query\";\nexport * from \"./content-relations\";\nexport * from \"./content-values\";\nexport * from \"./content-dependencies\";\n\nexport { normalizeRedirectPath, normalizeRedirectSource, normalizeRedirectTarget, isProtectedRedirectPath, type NormalizedRedirectSourceResult, type NormalizedRedirectTargetResult } from \"./redirect-paths\";\n","// ADR-0034 §5 — Block JSON 스펙 (WP 호환)\n// Gutenberg block shape + 우리 확장:\n// - `_id` (uuidv7 stable across revisions, ADR §2 P5)\n// - `rawHtml` (WP delimiter innerHTML 보존, codex v2 verdict #7)\n// - `metadata.reusableRef` (Phase 2 = flat expand)\n\nimport { z } from \"zod\";\n\n/** WP-compatible name pattern: `core/<x>` | `roottale/<x>` */\nexport const BLOCK_NAME_PATTERN = /^(core|roottale)\\/[a-z0-9-]+$/;\n\nconst blockMetadataSchema = z\n .object({\n bindings: z\n .record(\n z.object({\n source: z.string(),\n args: z.record(z.unknown()).optional(),\n }),\n )\n .optional(),\n name: z.string().optional(),\n reusableRef: z.string().optional(),\n })\n .strict()\n .optional();\n\n/**\n * Block schema (recursive — innerBlocks).\n * `_id` 는 internal-only (export 시 strip, ADR §5).\n */\nexport const blockSchema: z.ZodType<Block> = z.lazy(() =>\n z\n .object({\n _id: z.string().uuid(),\n name: z.string().regex(BLOCK_NAME_PATTERN),\n attributes: z.record(z.unknown()),\n innerBlocks: z.array(blockSchema),\n rawHtml: z.string().optional(),\n metadata: blockMetadataSchema,\n })\n .strict(),\n);\n\nexport interface Block {\n /** uuidv7. Internal-only — never exported as WP delimiter attribute. */\n _id: string;\n /** `core/<x>` or `roottale/<x>` */\n name: string;\n attributes: Record<string, unknown>;\n innerBlocks: Block[];\n /** WP delimiter innerHTML (preserved for round-trip — codex v2 verdict #7). */\n rawHtml?: string;\n metadata?: {\n bindings?: Record<string, { source: string; args?: Record<string, unknown> }>;\n name?: string;\n reusableRef?: string;\n };\n}\n\nexport function isValidBlock(value: unknown): value is Block {\n return blockSchema.safeParse(value).success;\n}\n\nexport function parseBlock(value: unknown): Block {\n return blockSchema.parse(value);\n}\n\n/**\n * Phase 1 core block names (10) — ADR-0034 §5.\n * Phase 2+ 에 `core/embed`, `core/gallery`, `core/cover` 등 추가.\n */\nexport const PHASE_1_CORE_BLOCKS = [\n \"core/paragraph\",\n \"core/heading\",\n \"core/image\",\n \"core/list\",\n \"core/quote\",\n \"core/code\",\n \"core/columns\",\n \"core/group\",\n \"core/separator\",\n \"core/spacer\",\n] as const;\nexport type Phase1CoreBlockName = (typeof PHASE_1_CORE_BLOCKS)[number];\n","// ADR-0041 §7 — Block Bindings: ACF 필드 → 블록 attribute.\n//\n// 블록의 `metadata.bindings[attr] = { source, args }` 를 만나면 attribute 를\n// 바인딩 값으로 치환한다. 렌더 직전 pure transform — renderBlock 시그니처 불변.\n//\n// canonical source id = `roottale/field`. WP round-trip 위해 `acf/field` alias\n// 인식. resolver 는 주입(렌더러는 cms-fields 비종속) — 값 record 만 흐른다.\n\nimport type { Block } from \"./block\";\n\nexport interface BlockBinding {\n source: string;\n args?: Record<string, unknown>;\n}\n\n/**\n * 바인딩 해석기. 주어진 binding(+attribute/block)에 대한 값을 반환.\n * `undefined` 반환 = 미해석(원래 attribute 유지).\n */\nexport type BindingResolver = (\n binding: BlockBinding,\n attribute: string,\n block: Block,\n) => unknown;\n\n/** ACF 필드 바인딩 source id. */\nexport const FIELD_BINDING_SOURCE = \"roottale/field\";\n/** WP ACF 호환 alias (import round-trip). */\nexport const FIELD_BINDING_SOURCE_ALIAS = \"acf/field\";\n\n/**\n * acf 값 record 로부터 필드 바인딩 resolver 생성.\n * `roottale/field` / `acf/field` source 의 `args.name` 을 record 에서 조회.\n */\nexport function createFieldBindingResolver(\n fields: Record<string, unknown> | undefined | null,\n): BindingResolver {\n const values = fields ?? {};\n return (binding) => {\n if (\n binding.source !== FIELD_BINDING_SOURCE &&\n binding.source !== FIELD_BINDING_SOURCE_ALIAS\n ) {\n return undefined;\n }\n const name = binding.args?.name;\n if (typeof name !== \"string\") return undefined;\n const v = values[name];\n return v === undefined ? undefined : v;\n };\n}\n\n/** 여러 resolver 를 순서대로 시도(첫 non-undefined 채택). */\nexport function chainResolvers(\n ...resolvers: BindingResolver[]\n): BindingResolver {\n return (binding, attribute, block) => {\n for (const r of resolvers) {\n const v = r(binding, attribute, block);\n if (v !== undefined) return v;\n }\n return undefined;\n };\n}\n\nfunction applyToBlock(block: Block, resolver: BindingResolver): Block {\n const bindings = block.metadata?.bindings;\n let attributes = block.attributes;\n if (bindings) {\n let mutated = false;\n const next = { ...block.attributes };\n for (const [attr, binding] of Object.entries(bindings)) {\n const value = resolver(binding, attr, block);\n if (value !== undefined) {\n next[attr] = value;\n mutated = true;\n }\n }\n if (mutated) attributes = next;\n }\n const innerBlocks = block.innerBlocks.length\n ? block.innerBlocks.map((b) => applyToBlock(b, resolver))\n : block.innerBlocks;\n if (attributes === block.attributes && innerBlocks === block.innerBlocks) {\n return block;\n }\n return { ...block, attributes, innerBlocks };\n}\n\n/**\n * 블록 트리에 바인딩을 적용해 새 트리 반환(immutable). 바인딩 없으면 원본 그대로.\n */\nexport function applyBlockBindings(\n blocks: readonly Block[],\n resolver: BindingResolver,\n): Block[] {\n return blocks.map((b) => applyToBlock(b, resolver));\n}\n\n// ── 라이브 Tiptap 렌더 경로용 헬퍼 (cms-field-binding-editor.md D3·D4) ─────────\n//\n// Tiptap doc 의 node.attrs.bindings (BlockBindings extension 이 기록, Block JSON\n// metadata.bindings 와 동일 형태) 를 렌더 시점에 필드 값으로 치환할 때 사용.\n\n/**\n * bindings 에서 `attribute` 의 필드 값을 조회. `roottale/field`/`acf/field`\n * source 만 해석. 빈 값(undefined/null/\"\") = undefined → caller 가 fallback 렌더.\n */\nexport function boundFieldValue(\n bindings: unknown,\n attribute: string,\n fields: Record<string, unknown> | null | undefined,\n): unknown {\n if (!bindings || typeof bindings !== \"object\" || !fields) return undefined;\n const binding = (bindings as Record<string, BlockBinding | undefined>)[\n attribute\n ];\n if (\n !binding ||\n (binding.source !== FIELD_BINDING_SOURCE &&\n binding.source !== FIELD_BINDING_SOURCE_ALIAS)\n ) {\n return undefined;\n }\n const name = binding.args?.name;\n if (typeof name !== \"string\") return undefined;\n const value = fields[name];\n if (value === undefined || value === null || value === \"\") return undefined;\n return value;\n}\n\n/**\n * content 바인딩 값의 텍스트화. **항상 plain text** — caller 가 escape 해\n * 삽입한다 (바인딩 경로로 raw HTML 진입 금지, D4). string/number 외 = undefined.\n */\nexport function bindingTextValue(value: unknown): string | undefined {\n if (typeof value === \"string\") return value;\n if (typeof value === \"number\" && Number.isFinite(value)) return String(value);\n return undefined;\n}\n\n/**\n * image `src` 바인딩 값 → URL. 포맷된 MediaRef(`{url}`) 또는 http(s) 문자열만\n * 인정 — raw media id(uuid) 는 URL 이 아니므로 fallback.\n */\nexport function bindingSrcValue(value: unknown): string | undefined {\n if (value && typeof value === \"object\") {\n const url = (value as { url?: unknown }).url;\n if (typeof url === \"string\" && /^https?:\\/\\//i.test(url)) return url;\n return undefined;\n }\n if (typeof value === \"string\" && /^https?:\\/\\//i.test(value)) return value;\n return undefined;\n}\n","/**\n * 다중 스트림 콘텐츠 라우팅 프리미티브 (ADR-0060).\n *\n * 같은 글 풀(post)을 둘 이상의 스트림으로 분리(공지=게시판 /notice, 블로그=그리드\n * /blog 등). `cms-renderer-next` / `cms-renderer-astro` 가 동일 로직을 공유하도록\n * 여기(cms-core)에 둔다 — cms-client·프레임워크 비의존(구조적 타입만).\n *\n * 설계(codex consult `019ef577` 반영):\n * - **활성화 = `collections.length > 0`.** 빈 배열·미전달 = 레거시 단일 /blog.\n * - `RouteCollection` 은 **라우팅 최소셋**만(key/basePath/categories/feed/archives와\n * 경로 정책) 가진다.\n * label/layout/icon 등 표시·작성 메타는 templates `CollectionSchema` 에 둔다.\n */\n\n/** 콘텐츠 스트림 1개 — sitemap/feed/아카이브 파생의 라우팅 단위. */\nexport interface RouteCollection {\n /** 안정 식별자 (예: \"notice\" | \"blog\"). */\n readonly key: string;\n /**\n * 라우트 base path (예: \"/notice\" | \"/blog\"). 앞 슬래시 포함, 뒤 슬래시 없이.\n * **`\"\"`(빈 문자열) = 주소 없는 유형** — 페이지 안에서 불러와 쓰는 콘텐츠(강사·리뷰\n * 등)로, 라우트/사이트맵/피드/아카이브를 일절 파생하지 않는다. 외부 사이트는\n * posts API 의 `collectionKey` 필터로 내용만 가져다 렌더한다. `resolvePostUrl`/\n * `resolvePostPath` 는 이 값이 빈 문자열이면 항상 null. 소속 판정\n * (`resolvePostCollection`)에는 영향 없음 — key 만으로 소속이 성립한다.\n */\n readonly basePath: string;\n /**\n * 이 섹션이 제공하는 **주제(category) allowlist** — `${basePath}/categories/{slug}`\n * 아카이브 라우트 범위. ADR-0060 Amendment 1 (M4): 섹션 소속은 글의 `collectionKey`\n * 로 정해지므로 이 배열은 더 이상 라우팅 소유권이 아니다(catch-all 개념 폐기). 빈\n * 배열 = 이 섹션은 주제 없음.\n */\n readonly categories: readonly string[];\n /** RSS 피드 포함 여부 (기본 false). */\n readonly feed?: boolean;\n /** `${basePath}/categories/{slug}` 아카이브 emit 여부 (기본 false). */\n readonly archives?: boolean;\n /**\n * 글별 상세 라우트 존재 여부 (기본 true — 하위 호환). false 면 이 스트림은\n * \"목록 전용\" 유형(예: 후기 — 목록에서 전문 노출, 글마다 별도 상세 URL 없음).\n * `resolvePostUrl`/`resolvePostPath` 가 null 을 반환해 sitemap/feed 에서 글\n * URL 을 발행하지 않는다. 소속 판정(`resolvePostCollection`)에는 영향 없음\n * — 소속과 상세 URL 존재 여부는 별개(M4 원칙 연장).\n */\n readonly detail?: boolean;\n /**\n * 글 상세 주소 형식. `flat`(기본) = `${basePath}/{postSlug}`,\n * `category` = `${basePath}/{categorySlug}/{postSlug}`.\n * category 모드는 URL을 하나로 결정해야 하므로 `categoryCardinality: \"exactly-one\"`\n * 과 함께 쓴다.\n */\n readonly detailPath?: \"flat\" | \"category\";\n /**\n * 카테고리 허브 주소 형식. `namespaced`(기본) =\n * `${basePath}/categories/{slug}`, `direct` = `${basePath}/{slug}`.\n */\n readonly categoryPath?: \"namespaced\" | \"direct\";\n /** 글에 허용하는 category term 개수. 기본 `multiple`(기존 동작). */\n readonly categoryCardinality?: \"multiple\" | \"exactly-one\";\n}\n\n/** resolve 입력 — 글의 구조적 최소 형태(cms-client `CmsPostContent` 와 구조 호환). */\nexport interface RoutablePost {\n readonly slug: string;\n /** 글의 주제(category) terms — 아카이브 라우트 필터용. 섹션 판정엔 쓰지 않는다(M4). */\n readonly terms: ReadonlyArray<{ readonly taxonomy: string; readonly slug: string }>;\n /**\n * ADR-0060 Amendment 1 (Option A, M4) — 글에 명시된 섹션 key. 섹션 판정의 유일한\n * 근거. 미설정/미존재면 어느 스트림에도 안 속함(null). 카테고리 파생 fallback 은 제거됨.\n */\n readonly collectionKey?: string | null;\n}\n\nfunction trimTrailingSlash(value: string): string {\n return value.replace(/\\/+$/, \"\");\n}\n\n/** collection 모드인지 — 길이 0(또는 미전달)이면 레거시 단일 /blog 동작. */\nexport function isInCollectionMode(\n collections: readonly RouteCollection[] | undefined,\n): boolean {\n return !!collections && collections.length > 0;\n}\n\n/**\n * 글이 속한 collection 1개를 해석 — ADR-0060 Amendment 1 (Option A, M4): **글의\n * 명시 `collectionKey` 만** 본다. 미설정/미존재 key 면 null(상세 라우트 없는 글 →\n * sitemap 제외 신호). 카테고리 slug 파생·catch-all·first-wins 는 M4 에서 제거됨 —\n * 섹션은 카테고리가 아니라 글에 명시한다. (`categories` 는 이제 주제 allowlist 로만,\n * 아카이브 라우트에서 쓰임.)\n */\nexport function resolvePostCollection(\n post: RoutablePost,\n collections: readonly RouteCollection[],\n): RouteCollection | null {\n if (!post.collectionKey) return null;\n return collections.find((c) => c.key === post.collectionKey) ?? null;\n}\n\n/**\n * 글의 정규 절대 URL — 소속 스트림 basePath 기준. 어느 스트림에도 안 속하거나,\n * 소속 스트림이 `detail: false`(목록 전용 유형) 이거나, `basePath` 가 빈 문자열\n * (주소 없는 유형)이면 null. slug 는 percent-encoding (한글 slug 안전).\n */\nexport function resolvePostUrl(\n siteUrl: string,\n post: RoutablePost,\n collections: readonly RouteCollection[],\n): string | null {\n const path = resolvePostPath(post, collections);\n return path ? `${trimTrailingSlash(siteUrl)}${path}` : null;\n}\n\n/**\n * category 기반 상세 주소에 사용할 단일 category slug. collection 에 연결된\n * category만 세고 정확히 1개일 때만 반환한다. 0개·2개 이상이면 정규 URL을\n * 임의 선택하지 않고 null 로 닫는다.\n */\nexport function resolvePostCategorySlug(\n post: RoutablePost,\n collection: RouteCollection,\n): string | null {\n const allowed = new Set(collection.categories);\n const slugs = [\n ...new Set(\n post.terms\n .filter(\n (term) => term.taxonomy === \"category\" && allowed.has(term.slug),\n )\n .map((term) => term.slug),\n ),\n ];\n return slugs.length === 1 ? slugs[0]! : null;\n}\n\n/** 카테고리 허브 경로. archives 가 꺼졌거나 주소가 없으면 null. */\nexport function resolveCategoryPath(\n collection: RouteCollection,\n categorySlug: string,\n): string | null {\n if (!collection.archives || !collection.basePath || !categorySlug) return null;\n const basePath = trimTrailingSlash(collection.basePath);\n const encodedSlug = encodeURIComponent(categorySlug);\n return collection.categoryPath === \"direct\"\n ? `${basePath}/${encodedSlug}`\n : `${basePath}/categories/${encodedSlug}`;\n}\n\n/**\n * 글의 *경로*(siteUrl 없는 basePath-상대) — 예: \"/blog/hello\". 소속 스트림이\n * 없거나, `detail: false`(목록 전용 유형) 이거나, `basePath` 가 빈 문자열\n * (주소 없는 유형)이면 null. astro 처럼 상대 href 가 필요한 소비자용\n * (`resolvePostUrl` 은 절대 URL).\n */\nexport function resolvePostPath(\n post: RoutablePost,\n collections: readonly RouteCollection[],\n): string | null {\n const collection = resolvePostCollection(post, collections);\n if (!collection) return null;\n if (collection.detail === false) return null;\n if (!collection.basePath) return null;\n const basePath = trimTrailingSlash(collection.basePath);\n const encodedPostSlug = encodeURIComponent(post.slug);\n if (collection.detailPath !== \"category\") {\n return `${basePath}/${encodedPostSlug}`;\n }\n const categorySlug = resolvePostCategorySlug(post, collection);\n return categorySlug\n ? `${basePath}/${encodeURIComponent(categorySlug)}/${encodedPostSlug}`\n : null;\n}\n\n/**\n * 특정 collection에 명시적으로 귀속된 글만 남긴다.\n */\nexport function postsForCollection<T extends RoutablePost>(\n posts: readonly T[],\n collections: readonly RouteCollection[],\n key: string,\n): T[] {\n return posts.filter((p) => resolvePostCollection(p, collections)?.key === key);\n}\n\n/** feed/sitemap 등 파생에서 제외할, 어느 스트림에도 안 속한 글 제거. */\nexport function postsInAnyCollection<T extends RoutablePost>(\n posts: readonly T[],\n collections: readonly RouteCollection[],\n): T[] {\n return posts.filter((p) => resolvePostCollection(p, collections) !== null);\n}\n\n/**\n * `postsInAnyCollection` 의 대칭 — **어느 스트림에도 안 속한(\"미소속\") 글만** 남긴다.\n * 판정 시맨틱 = `resolvePostCollection(post, collections) === null`(collectionKey 가\n * NULL 이거나 사이트 선언 컬렉션 밖). `/blog` 처럼 \"선언 스트림에 안 잡힌 글 = 블로그\"로\n * 취급하는 표면에서 스트림 글 누출을 막는 데 쓴다(`blog-list-collection-scope.md` §2①).\n */\nexport function postsNotInAnyCollection<T extends RoutablePost>(\n posts: readonly T[],\n collections: readonly RouteCollection[],\n): T[] {\n return posts.filter((p) => resolvePostCollection(p, collections) === null);\n}\n\n/**\n * config 검증 — 중복 key/basePath, 그리고 **두 스트림에 동시 등장하는 category slug**\n * (라우팅 모호성)를 탐지. `basePath === \"\"`(주소 없는 유형)는 basePath 형식·중복\n * 검사를 건너뛴다 — 라우트가 없으므로 여러 개 공존해도 모호성이 없다. key 중복·\n * category 중복 검사는 주소 유무와 무관하게 전 항목에 적용된다. 문제 메시지 배열\n * 반환(빈 = 정상). 빌드 타임 fail-fast 용.\n */\nexport function validateCollections(collections: readonly RouteCollection[]): string[] {\n const problems: string[] = [];\n const seenKey = new Set<string>();\n const seenBase = new Set<string>();\n const categoryOwner = new Map<string, string>();\n\n for (const c of collections) {\n if (seenKey.has(c.key)) problems.push(`중복 collection key: \"${c.key}\"`);\n seenKey.add(c.key);\n\n if (c.basePath !== \"\") {\n const base = trimTrailingSlash(c.basePath);\n if (seenBase.has(base)) problems.push(`중복 basePath: \"${c.basePath}\"`);\n seenBase.add(base);\n\n if (!c.basePath.startsWith(\"/\")) {\n problems.push(`basePath 는 \"/\" 로 시작해야 함: \"${c.basePath}\" (${c.key})`);\n } else if (\n base.length < 2 ||\n base.startsWith(\"//\") ||\n /[\\\\?#\\s]/.test(base) ||\n base.split(\"/\").some((segment) => segment === \".\" || segment === \"..\")\n ) {\n problems.push(\n `basePath 는 사이트 내부 절대 경로여야 함: \"${c.basePath}\" (${c.key})`,\n );\n }\n }\n\n for (const slug of c.categories) {\n const owner = categoryOwner.get(slug);\n if (owner && owner !== c.key) {\n problems.push(\n `category \"${slug}\" 가 collection \"${owner}\" 와 \"${c.key}\" 에 중복 — 한 카테고리는 한 스트림에만.`,\n );\n }\n categoryOwner.set(slug, c.key);\n }\n\n if (c.detailPath === \"category\") {\n if (!c.basePath) {\n problems.push(\n `category 상세 경로는 basePath 가 필요함: \"${c.key}\"`,\n );\n }\n if (c.detail === false) {\n problems.push(\n `category 상세 경로는 글별 상세 페이지가 필요함: \"${c.key}\"`,\n );\n }\n if (!c.archives) {\n problems.push(\n `category 상세 경로는 카테고리 아카이브가 필요함: \"${c.key}\"`,\n );\n }\n if (c.categoryCardinality !== \"exactly-one\") {\n problems.push(\n `category 상세 경로는 categoryCardinality \"exactly-one\" 이 필요함: \"${c.key}\"`,\n );\n }\n }\n }\n return problems;\n}\n","/**\n * 콘텐츠 모델의 계층형 분류 주소 계산기.\n *\n * FAQ·도움말·문서처럼 하나의 모델이 여러 단계 분류 허브를 가지는 경우에 쓴다.\n * 저장소나 프레임워크를 모르며, 공개 API와 ROOT-ADMIN이 같은 계산을 재사용한다.\n */\n\n/**\n * 글을 붙일 수 있는 분류.\n * - `deepest`(기본): `categoryDepth` 단계의 말단 분류에만 붙는다.\n * - `leaf`: 하위 분류가 없는 분류면 단계와 무관하게 붙는다(`categoryDepth`는 최대 단계).\n * 하위가 필요한 영역만 나누고 나머지는 상위 분류에 바로 글을 쓸 때 쓴다.\n */\nexport type CategoryTreeEntryCategory = \"deepest\" | \"leaf\";\n\nexport interface CategoryTreePresentation {\n readonly basePath: string;\n readonly categoryDepth: number;\n readonly entryCategory?: CategoryTreeEntryCategory;\n /** 생략하면 정확히 한 분류. first는 termOrder, id 순서의 첫 분류를 쓴다. */\n readonly categorySelection?: \"first\";\n /** first + 1단계 모델에서 실제 미분류 글에만 쓰는 가상 분류 slug. */\n readonly categoryFallbackSlug?: string;\n readonly reservedCategorySlugs?: readonly string[];\n}\n\nexport interface CategoryTreeTerm {\n readonly id: string;\n readonly slug: string;\n readonly parentId?: string | null;\n readonly termOrder?: number;\n}\n\nexport type CategoryTreeEntryResolution<T extends CategoryTreeTerm = CategoryTreeTerm> =\n | { readonly kind: \"category\"; readonly category: T; readonly chain: readonly T[] }\n | { readonly kind: \"fallback\"; readonly slug: string };\n\n/** 실제 분류가 사용할 수 없는 예약 주소. fallback은 분류 원장에 만들지 않는다. */\nexport function isReservedCategoryTreeSlug(\n presentation: CategoryTreePresentation,\n slug: string,\n): boolean {\n return slug === presentation.categoryFallbackSlug ||\n presentation.reservedCategorySlugs?.includes(slug) === true;\n}\n\nexport interface CategoryTreeEntry {\n readonly slug: string;\n readonly terms: ReadonlyArray<{\n readonly id: string;\n readonly taxonomy: string;\n }>;\n}\n\n/**\n * 하위 주소를 붙일 접두사. 루트(`/`)는 빈 접두사(`\"\"`)이고, 빈 basePath 는 주소가\n * 없는 모델이라 null 이다 — 둘을 같게 보면 `/`에 올린 모델의 주소가 사라진다.\n */\nfunction basePathPrefix(basePath: string): string | null {\n if (!basePath.trim()) return null;\n return basePath.replace(/\\/+$/, \"\");\n}\n\n/** 루트→분류 사슬. 끊긴 부모·순환·중복 id·slug 없음·최대 단계 초과는 null. */\nfunction categoryTreeAncestry<T extends CategoryTreeTerm>(\n categoryDepth: number,\n termId: string,\n terms: readonly T[],\n): T[] | null {\n if (!Number.isInteger(categoryDepth) || categoryDepth < 1) return null;\n const termsById = new Map(terms.map((term) => [term.id, term]));\n if (termsById.size !== terms.length) return null;\n\n const reversed: T[] = [];\n const visited = new Set<string>();\n let currentId: string | null = termId;\n while (currentId) {\n if (visited.has(currentId)) return null;\n visited.add(currentId);\n const current = termsById.get(currentId);\n if (!current || !current.slug) return null;\n reversed.push(current);\n if (reversed.length > categoryDepth) return null;\n currentId = current.parentId ?? null;\n }\n return reversed.reverse();\n}\n\nfunction hasChildCategory(termId: string, terms: readonly CategoryTreeTerm[]): boolean {\n return terms.some((term) => term.parentId === termId);\n}\n\n/**\n * 글 주소에 쓰는 분류의 루트→분류 사슬. 주소를 만들 수 없으면 null.\n *\n * - `deepest`: 정확히 `categoryDepth` 단계의 말단이어야 한다.\n * - `leaf`: `categoryDepth` 이하면 된다. 하위가 없는지는 *새로 고를 때*만 본다\n * (`resolveCategoryTreeSelection`) — 글이 붙은 뒤 하위 분류가 생겨도 이미 발행한\n * 글의 주소는 그대로 살아 있어야 한다.\n */\nexport function resolveCategoryTreeChain<T extends CategoryTreeTerm>(\n presentation: CategoryTreePresentation,\n termId: string,\n terms: readonly T[],\n): T[] | null {\n const chain = categoryTreeAncestry(presentation.categoryDepth, termId, terms);\n if (!chain) return null;\n if (chain.some((term) => isReservedCategoryTreeSlug(presentation, term.slug))) return null;\n if (presentation.entryCategory === \"leaf\") return chain;\n if (hasChildCategory(termId, terms)) return null;\n return chain.length === presentation.categoryDepth ? chain : null;\n}\n\n/**\n * 글에 새로 고를 수 있는 분류의 사슬 — 주소를 만들 수 있고 지금 하위 분류가 없어야\n * 한다. 편집기 선택지와 발행 검사가 같은 규칙을 쓴다.\n */\nexport function resolveCategoryTreeSelection<T extends CategoryTreeTerm>(\n presentation: CategoryTreePresentation,\n termId: string,\n terms: readonly T[],\n): T[] | null {\n if (hasChildCategory(termId, terms)) return null;\n return resolveCategoryTreeChain(presentation, termId, terms);\n}\n\n/**\n * 상세 경로·분류 표시가 함께 쓰는 선택기. terms에는 해당 모델의 전체 분류를 준다.\n * first의 미지정/외부 분류나 잘못된 계층은 fallback으로 숨기지 않는다.\n */\nexport function resolveCategoryTreeEntryCategory<T extends CategoryTreeTerm>(\n presentation: CategoryTreePresentation,\n entry: Pick<CategoryTreeEntry, \"terms\">,\n terms: readonly T[],\n): CategoryTreeEntryResolution<T> | null {\n const termsById = new Map(terms.map((term) => [term.id, term]));\n const first = presentation.categorySelection === \"first\";\n const categoryIds = [...new Set(entry.terms\n .filter((term) => term.taxonomy === \"category\" && (first || termsById.has(term.id)))\n .map((term) => term.id))];\n\n if (first) {\n if (termsById.size !== terms.length) return null;\n if (categoryIds.length === 0) {\n const slug = presentation.categoryFallbackSlug;\n if (presentation.categoryDepth !== 1 || !slug ||\n presentation.reservedCategorySlugs?.includes(slug) ||\n terms.some((term) => term.slug === slug)) return null;\n return { kind: \"fallback\", slug };\n }\n const candidates: Array<{ category: T; chain: T[] }> = [];\n for (const id of categoryIds) {\n const category = termsById.get(id);\n if (!category || !Number.isInteger(category.termOrder ?? 0)) return null;\n const chain = resolveCategoryTreeChain(presentation, id, terms);\n if (!chain) return null;\n candidates.push({ category, chain });\n }\n candidates.sort((a, b) => (a.category.termOrder ?? 0) - (b.category.termOrder ?? 0) ||\n (a.category.id < b.category.id ? -1 : a.category.id > b.category.id ? 1 : 0));\n return { kind: \"category\", ...candidates[0]! };\n }\n\n if (categoryIds.length !== 1) return null;\n const category = termsById.get(categoryIds[0]!)!;\n const chain = resolveCategoryTreeChain(presentation, category.id, terms);\n return chain ? { kind: \"category\", category, chain } : null;\n}\n\n/** 루트부터 선택 분류까지 모든 허브 주소. */\nexport function resolveCategoryTreeHubPaths(\n presentation: CategoryTreePresentation,\n termId: string,\n terms: readonly CategoryTreeTerm[],\n): string[] | null {\n const chain = resolveCategoryTreeChain(presentation, termId, terms);\n const prefix = basePathPrefix(presentation.basePath);\n if (!chain || prefix === null) return null;\n const paths = [prefix || \"/\"];\n const segments: string[] = [];\n for (const term of chain) {\n segments.push(encodeURIComponent(term.slug));\n paths.push(`${prefix}/${segments.join(\"/\")}`);\n }\n return paths;\n}\n\n/** 계층형 분류 모델 항목의 정규 상세 주소. */\nexport function resolveCategoryTreeEntryPath(\n presentation: CategoryTreePresentation,\n entry: CategoryTreeEntry,\n terms: readonly CategoryTreeTerm[],\n): string | null {\n if (!entry.slug) return null;\n const selected = resolveCategoryTreeEntryCategory(presentation, entry, terms);\n const prefix = basePathPrefix(presentation.basePath);\n if (!selected || prefix === null) return null;\n const slugs = selected.kind === \"fallback\"\n ? [selected.slug]\n : selected.chain.map((term) => term.slug);\n return `${prefix}/${[...slugs, entry.slug].map(encodeURIComponent).join(\"/\")}`;\n}\n","/**\n * 글의 정규 공개 경로 — 플랫폼 전체가 쓰는 **하나의** 계산기 (ADR-0105 개정).\n *\n * 우선순위: 콘텐츠 모델 presentation(fixed_page → detail → category_tree) →\n * 호환 컬렉션(`resolvePostPath`) → 레거시 `/blog/{slug}`. `data_only` 모델은\n * 상세 주소가 없으므로 호환 컬렉션이 있으면 그 규칙을, 없으면 null 을 돌려준다.\n *\n * 이 값은 저장 시 `posts.public_path` 로 물질화되고(경로 이력 포함), 웹훅·사이트맵·\n * 단축링크·내부 링크 키·리다이렉트는 저장된 값을 읽는다. 여기 말고 다른 곳에서\n * 주소를 다시 계산하지 않는다.\n */\nimport {\n resolveCategoryTreeEntryPath,\n type CategoryTreePresentation,\n type CategoryTreeTerm,\n} from \"./category-tree\";\nimport {\n resolvePostCollection,\n resolvePostPath,\n type RouteCollection,\n} from \"./collections\";\n\n/** 콘텐츠 모델 presentation 의 구조적 최소형(cms-models 스키마와 호환). */\nexport type PublicPathPresentation =\n | { readonly kind: \"data_only\" }\n | { readonly kind: \"fixed_page\"; readonly path: string }\n | { readonly kind: \"detail\"; readonly detailPath: string }\n | (CategoryTreePresentation & {\n readonly kind: \"category_tree\";\n });\n\nexport interface PublicPathPost {\n readonly slug: string;\n readonly collectionKey?: string | null;\n readonly modelKey?: string | null;\n readonly terms: ReadonlyArray<{\n readonly id: string;\n readonly taxonomy: string;\n readonly slug: string;\n }>;\n}\n\nexport interface PublicPathContext {\n /** 글의 모델 presentation. 모델 글이 아니거나 모델을 못 찾으면 null. */\n readonly presentation: PublicPathPresentation | null;\n /** 모델의 호환 컬렉션 key(`compatibilityCollectionKey`, 미지정이면 모델 key). */\n readonly compatibilityCollectionKey?: string | null;\n readonly collections: readonly RouteCollection[];\n readonly categories: readonly CategoryTreeTerm[];\n}\n\nexport const LEGACY_BLOG_DETAIL_PATH = \"/blog\";\n\n/** 글의 정규 공개 경로(origin 제외). 상세 주소가 없으면 null. */\nexport function resolvePostPublicPath(\n post: PublicPathPost,\n context: PublicPathContext,\n): string | null {\n const presentation = context.presentation;\n if (presentation && presentation.kind !== \"data_only\") {\n if (presentation.kind === \"fixed_page\") return presentation.path;\n if (presentation.kind === \"detail\") {\n if (!presentation.detailPath.includes(\":slug\")) return null;\n return presentation.detailPath.replace(\":slug\", encodeURIComponent(post.slug));\n }\n return resolveCategoryTreeEntryPath(presentation, post, context.categories);\n }\n\n const collectionKey =\n post.collectionKey ??\n (presentation ? context.compatibilityCollectionKey ?? post.modelKey ?? null : null);\n const routable = { slug: post.slug, terms: post.terms, collectionKey };\n if (!resolvePostCollection(routable, context.collections)) {\n // 모델 글인데 규칙도 컬렉션도 없으면 주소가 없다. 레거시 단일 블로그만 /blog 폴백.\n if (presentation) return null;\n return `${LEGACY_BLOG_DETAIL_PATH}/${post.slug}`;\n }\n return resolvePostPath(routable, context.collections);\n}\n","// ADR-0034 §5 + §6 — WP delimiter parse/serialize\n//\n// WP 의 block serialization 은 HTML 주석 형태:\n// <!-- wp:paragraph {\"attr\":1} -->\n// <p>본문</p>\n// <!-- /wp:paragraph -->\n//\n// Round-trip 룰 (codex v2 verdict #7 + v3 verdict #4):\n// - `_id` 는 export 시 strip (WP-side import 가 unknown attribute 로 인식해 깨짐 차단)\n// - `rawHtml` ↔ innerHTML\n// - 우리가 schema 정의 안 한 block = Tiptap opaque atom node (편집 불가, 보존)\n\nimport type { Block } from \"./block\";\n\n/**\n * Block → WP delimiter 형식 직렬화 (export).\n * `_id` 와 `metadata.reusableRef` 는 strip — WP 호환.\n */\nexport function serializeBlock(block: Block): string {\n const exportable = sanitizeAttributesForExport(block);\n const attrsJson = serializeAttributes(exportable.attributes);\n const innerHtml = block.rawHtml ?? \"\";\n const innerSerialized = (block.innerBlocks ?? [])\n .map((b) => serializeBlock(b))\n .join(\"\\n\");\n\n // self-closing 형태 (innerHTML + innerBlocks 모두 없으면)\n if (!innerHtml && innerSerialized.length === 0) {\n return attrsJson\n ? `<!-- wp:${normalizeName(block.name)} ${attrsJson} /-->`\n : `<!-- wp:${normalizeName(block.name)} /-->`;\n }\n\n const open = attrsJson\n ? `<!-- wp:${normalizeName(block.name)} ${attrsJson} -->`\n : `<!-- wp:${normalizeName(block.name)} -->`;\n const close = `<!-- /wp:${normalizeName(block.name)} -->`;\n\n const body = [innerHtml, innerSerialized].filter(Boolean).join(\"\\n\");\n return `${open}\\n${body}\\n${close}`;\n}\n\n/** WP-side 에서 `core/` 는 보통 namespace 생략 (e.g. `<!-- wp:paragraph -->`) */\nfunction normalizeName(name: string): string {\n return name.startsWith(\"core/\") ? name.slice(\"core/\".length) : name;\n}\n\nfunction serializeAttributes(attrs: Record<string, unknown>): string {\n const keys = Object.keys(attrs);\n if (keys.length === 0) return \"\";\n return JSON.stringify(attrs);\n}\n\nfunction sanitizeAttributesForExport(block: Block): {\n attributes: Record<string, unknown>;\n} {\n // `_id` 와 `metadata.reusableRef` 제거. attributes 안의 임의 키는 그대로 export.\n return { attributes: { ...block.attributes } };\n}\n","// ADR-0034 §5 — `core/embed` 영상 임베드 헬퍼 (provider allowlist)\n//\n// 본 모듈은 editor (URL → embed 노드 정규화) 와 renderer (저장된 embed src 의\n// 출력 시 재검증, defense-in-depth) 양쪽에서 공유. 순수 모듈 — `URL` 만 사용해\n// 브라우저/서버 모두 안전.\n//\n// 보안 원칙: 임의 iframe src 를 신뢰하지 않는다. 신규 삽입은 `normalizeEmbed`\n// 가 allowlist provider 의 canonical embed URL 로만 변환하고, 출력은\n// `isAllowedEmbedSrc` 로 host 를 한 번 더 검증한다 (legacy/import/외부 테넌트가\n// 임의 src 를 DB 에 남겨도 렌더 단계에서 차단).\n\nexport type EmbedProvider = \"youtube\" | \"vimeo\";\n\nexport interface NormalizedEmbed {\n provider: EmbedProvider;\n /** allowlist host 의 canonical iframe src (가능 시 privacy-enhanced). */\n embedSrc: string;\n /** 사용자가 입력한 원본 URL — fallback 링크 / round-trip 보존용. */\n originalUrl: string;\n}\n\nconst YOUTUBE_HOSTS = new Set([\n \"youtube.com\",\n \"www.youtube.com\",\n \"m.youtube.com\",\n \"youtu.be\",\n \"www.youtu.be\",\n]);\n\nconst VIMEO_HOSTS = new Set([\"vimeo.com\", \"www.vimeo.com\", \"player.vimeo.com\"]);\n\n/** 출력(iframe) 시 허용되는 embed src host — render 단계 defense-in-depth. */\nconst ALLOWED_EMBED_HOSTS = new Set([\n \"www.youtube-nocookie.com\",\n \"youtube-nocookie.com\",\n \"www.youtube.com\",\n \"youtube.com\",\n \"player.vimeo.com\",\n]);\n\nconst YOUTUBE_ID = /^[A-Za-z0-9_-]{11}$/;\nconst VIMEO_ID = /^\\d+$/;\n\n/**\n * 사용자 입력 URL → allowlist provider 의 canonical embed. 지원하지 않는\n * provider / 파싱 실패 시 `null`.\n */\nexport function normalizeEmbed(input: unknown): NormalizedEmbed | null {\n if (typeof input !== \"string\") return null;\n const raw = input.trim();\n if (raw.length === 0) return null;\n\n let url: URL;\n try {\n url = new URL(raw);\n } catch {\n return null;\n }\n if (url.protocol !== \"https:\" && url.protocol !== \"http:\") return null;\n\n const host = url.hostname.toLowerCase();\n\n if (YOUTUBE_HOSTS.has(host)) {\n const id = extractYouTubeId(url, host);\n if (id) {\n return {\n provider: \"youtube\",\n embedSrc: `https://www.youtube-nocookie.com/embed/${id}`,\n originalUrl: raw,\n };\n }\n }\n\n if (VIMEO_HOSTS.has(host)) {\n const id = extractVimeoId(url, host);\n if (id) {\n return {\n provider: \"vimeo\",\n embedSrc: `https://player.vimeo.com/video/${id}`,\n originalUrl: raw,\n };\n }\n }\n\n return null;\n}\n\n/**\n * 이미 저장된 embed src 가 allowlist host(https) 인지 검증 — 렌더러가 iframe\n * 출력 전 호출. 임의 host / 비 https 는 거부.\n */\nexport function isAllowedEmbedSrc(value: unknown): value is string {\n if (typeof value !== \"string\") return false;\n let url: URL;\n try {\n url = new URL(value);\n } catch {\n return false;\n }\n if (url.protocol !== \"https:\") return false;\n const host = url.hostname.toLowerCase();\n if (!ALLOWED_EMBED_HOSTS.has(host)) return false;\n // host 뿐 아니라 canonical embed path 형태까지 검증 — 허용 host 의 임의 경로\n // (예: 스크립트·홈) 가 iframe 으로 로드되는 것을 차단.\n if (host === \"player.vimeo.com\") {\n return /^\\/video\\/\\d+$/.test(url.pathname);\n }\n // youtube / youtube-nocookie → /embed/<11-char id>\n return new RegExp(\"^/embed/[A-Za-z0-9_-]{11}$\").test(url.pathname);\n}\n\n/** 지도 embed 허용 host — keyless embed 만 (API 키 의존 provider 금지). */\nconst GOOGLE_MAP_HOSTS = new Set([\"www.google.com\", \"google.com\", \"maps.google.com\"]);\nconst OSM_HOSTS = new Set([\"www.openstreetmap.org\", \"openstreetmap.org\"]);\n\n/**\n * 지도 embed src allowlist — 렌더러가 contact 지도 iframe 출력 전 호출\n * (W8-7, `isAllowedEmbedSrc` 의 영상 allowlist 와 별도).\n *\n * 별도 함수인 이유: 지도는 \"외부 API 키가 필요 없는 keyless embed 만\"이라는\n * 추가 제약이 있다. 허용 형태:\n * - Google Maps 공유 embed: https://www.google.com/maps/embed?pb=…\n * - Google Maps keyless 쿼리 embed: https://maps.google.com/maps?q=…&output=embed\n * - OpenStreetMap export embed: https://www.openstreetmap.org/export/embed.html?…\n * `/maps/embed/v1/*`(Embed API — 키 필요) 와 그 밖의 host/경로는 거부 —\n * 거부 시 렌더러는 기존 facade 이미지/길찾기 링크 폴백을 유지한다(fail-soft).\n */\nexport function isAllowedMapEmbedSrc(value: unknown): value is string {\n if (typeof value !== \"string\") return false;\n let url: URL;\n try {\n url = new URL(value);\n } catch {\n return false;\n }\n if (url.protocol !== \"https:\") return false;\n const host = url.hostname.toLowerCase();\n if (GOOGLE_MAP_HOSTS.has(host)) {\n if (url.pathname === \"/maps/embed\") return true;\n if (url.pathname === \"/maps\" || url.pathname === \"/maps/\") {\n return url.searchParams.get(\"output\") === \"embed\";\n }\n return false;\n }\n if (OSM_HOSTS.has(host)) {\n return url.pathname === \"/export/embed.html\";\n }\n return false;\n}\n\nfunction extractYouTubeId(url: URL, host: string): string | null {\n if (host === \"youtu.be\" || host === \"www.youtu.be\") {\n const id = url.pathname.split(\"/\").filter(Boolean)[0] ?? \"\";\n return YOUTUBE_ID.test(id) ? id : null;\n }\n const v = url.searchParams.get(\"v\");\n if (v && YOUTUBE_ID.test(v)) return v;\n const parts = url.pathname.split(\"/\").filter(Boolean);\n if (\n parts.length >= 2 &&\n (parts[0] === \"embed\" || parts[0] === \"shorts\" || parts[0] === \"v\")\n ) {\n return YOUTUBE_ID.test(parts[1]) ? parts[1] : null;\n }\n return null;\n}\n\nfunction extractVimeoId(url: URL, host: string): string | null {\n const parts = url.pathname.split(\"/\").filter(Boolean);\n let candidate: string | undefined;\n if (host === \"player.vimeo.com\") {\n const vi = parts.indexOf(\"video\");\n candidate = vi >= 0 ? parts[vi + 1] : parts[parts.length - 1];\n } else {\n candidate = parts[0];\n }\n return candidate && VIMEO_ID.test(candidate) ? candidate : null;\n}\n","// FAQ 추출 — Tiptap doc 에서 faq 블록의 Q/A 를 평문으로 수집.\n//\n// 두 렌더러(cms-renderer-astro / cms-renderer-next)가 공유하여 FAQPage\n// 구조화데이터(JSON-LD)를 생성한다. 직렬화(HTML <details>)는 각 렌더러가\n// 자체 처리(마크업 패리티), 본 모듈은 schema.org 용 평문 추출만 담당.\n\ninterface DocNode {\n type?: string;\n text?: string;\n content?: DocNode[];\n}\n\nexport interface FaqEntry {\n question: string;\n answer: string;\n}\n\n/** 노드 트리의 모든 text 를 이어붙여 평문화 (공백 정규화). */\nfunction plainText(node: DocNode | undefined): string {\n if (!node) return \"\";\n if (typeof node.text === \"string\") return node.text;\n if (!Array.isArray(node.content)) return \"\";\n // 블록 경계는 공백으로 — 문단 사이가 붙지 않도록.\n return node.content.map(plainText).join(\" \");\n}\n\nfunction collapse(s: string): string {\n return s.replace(/\\s+/g, \" \").trim();\n}\n\n/** doc 을 순회하며 faqItem(질문/답변)을 순서대로 수집. */\nexport function extractFaqEntries(doc: unknown): FaqEntry[] {\n const out: FaqEntry[] = [];\n const walk = (node: DocNode | undefined): void => {\n if (!node || typeof node !== \"object\") return;\n if (node.type === \"faqItem\" && Array.isArray(node.content)) {\n const q = node.content.find((c) => c.type === \"faqQuestion\");\n const a = node.content.find((c) => c.type === \"faqAnswer\");\n const question = collapse(plainText(q));\n const answer = collapse(plainText(a));\n if (question && answer) out.push({ question, answer });\n return; // faqItem 내부는 더 내려가지 않음\n }\n if (Array.isArray(node.content)) node.content.forEach(walk);\n };\n if (doc && typeof doc === \"object\") {\n walk(doc as DocNode);\n }\n return out;\n}\n\n/** FAQPage JSON-LD 객체 (script 삽입은 렌더러 책임). null = 항목 없음. */\nexport function faqPageJsonLd(\n entries: FaqEntry[],\n): Record<string, unknown> | null {\n if (entries.length === 0) return null;\n return {\n \"@context\": \"https://schema.org\",\n \"@type\": \"FAQPage\",\n mainEntity: entries.map((e) => ({\n \"@type\": \"Question\",\n name: e.question,\n acceptedAnswer: { \"@type\": \"Answer\", text: e.answer },\n })),\n };\n}\n","/**\n * 본문 이미지 대체텍스트의 제품 안전 상한.\n *\n * HTML 표준의 길이 제한이 아니라, 본문·캡션 전체가 alt 에 잘못 저장돼\n * 스크린리더와 검색 결과를 오염시키는 사고를 막기 위한 RootTale 계약이다.\n */\nexport const IMAGE_ALT_MAX_LENGTH = 160;\n\n/**\n * 이미지 alt 를 한 줄 설명으로 정규화한다.\n *\n * - 문자열이 아니면 빈 alt\n * - 개행·연속 공백을 한 칸으로 축약\n * - 160자를 넘으면 말줄임표를 포함해 상한 안에서 자름\n */\nexport function normalizeImageAlt(value: unknown): string {\n if (typeof value !== \"string\") return \"\";\n const normalized = value.replace(/\\s+/g, \" \").trim();\n const characters = Array.from(normalized);\n if (characters.length <= IMAGE_ALT_MAX_LENGTH) return normalized;\n\n const visible = characters\n .slice(0, IMAGE_ALT_MAX_LENGTH - 1)\n .join(\"\")\n .trimEnd();\n return `${visible}…`;\n}\n","import postcss, { type Container, type Rule } from \"postcss\";\nimport sanitizeHtmlLibrary from \"sanitize-html\";\nimport type sanitizeHtmlTypes from \"sanitize-html\";\n\nconst IMPORT_SCOPE_RE = /^rt-import-[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;\nconst ALLOWED_AT_RULES = new Set([\n \"container\",\n \"font-face\",\n \"keyframes\",\n \"-webkit-keyframes\",\n \"layer\",\n \"media\",\n \"supports\",\n]);\nconst URL_ATTRIBUTES = new Set([\"href\", \"src\"]);\nconst ALLOWED_URL_SCHEMES = new Set([\"http\", \"https\", \"mailto\", \"tel\"]);\nconst UNSAFE_CSS_VALUE_RE =\n /(?:expression\\s*\\(|javascript\\s*:|vbscript\\s*:|@import|<\\/?(?:style|script)|\\\\0|\\\\0000)/i;\nconst UNSAFE_CSS_PROPERTY_RE = /^(?:behavior|-moz-binding)$/i;\nconst FIXED_POSITION_RE = /^(?:fixed|sticky)$/i;\nconst ORIGINAL_H1_ATTRIBUTE = \"data-rt-original-heading\";\nconst SCOPE_ROOT_BOUNDARY_PROPERTIES = new Set([\n \"bottom\",\n \"inset\",\n \"inset-block\",\n \"inset-inline\",\n \"left\",\n \"position\",\n \"right\",\n \"rotate\",\n \"scale\",\n \"top\",\n \"transform\",\n \"translate\",\n \"z-index\",\n]);\n\nfunction fromCodePointSafe(value: string, radix: number): string {\n const codePoint = Number.parseInt(value, radix);\n if (!Number.isFinite(codePoint) || codePoint < 0 || codePoint > 0x10ffff) {\n return \"\";\n }\n try {\n return String.fromCodePoint(codePoint);\n } catch {\n return \"\";\n }\n}\n\nfunction isSafeUrl(tagName: string, attributeName: string, value: string): boolean {\n const normalized = value\n .replace(/&#x([0-9a-f]+);?/gi, (_, hex: string) => fromCodePointSafe(hex, 16))\n .replace(/&#(\\d+);?/g, (_, decimal: string) => fromCodePointSafe(decimal, 10))\n .replace(/[\\u0000-\\u0020\\u007f]/g, \"\")\n .toLowerCase();\n const scheme = /^([a-z][a-z0-9+.-]*):/.exec(normalized)?.[1];\n if (!scheme) return true;\n if (ALLOWED_URL_SCHEMES.has(scheme)) return true;\n return (\n tagName === \"img\" &&\n attributeName === \"src\" &&\n scheme === \"data\" &&\n normalized.startsWith(\"data:image/\") &&\n !normalized.startsWith(\"data:image/svg\")\n );\n}\n\nfunction cssUrlsAreSafe(\n value: string,\n allowedAssetUrls: ReadonlySet<string>,\n): boolean {\n const urlRe = /url\\(\\s*([\"']?)([^)'\"\\s]+)\\1\\s*\\)/gi;\n let matched = false;\n for (const match of value.matchAll(urlRe)) {\n matched = true;\n const url = match[2]?.trim() ?? \"\";\n if (!url.startsWith(\"#\") && !allowedAssetUrls.has(url)) return false;\n }\n return !/url\\s*\\(/i.test(value) || matched;\n}\n\nfunction declarationIsSafe(\n property: string,\n value: string,\n allowedAssetUrls: ReadonlySet<string>,\n): boolean {\n if (UNSAFE_CSS_PROPERTY_RE.test(property) || UNSAFE_CSS_VALUE_RE.test(value)) {\n return false;\n }\n if (!cssUrlsAreSafe(value, allowedAssetUrls)) return false;\n if (property === \"position\" && /\\b(?:fixed|sticky)\\b/i.test(value)) {\n return false;\n }\n if (property.startsWith(\"--\") && FIXED_POSITION_RE.test(value.trim())) {\n return false;\n }\n if (property === \"z-index\") {\n const normalized = value.trim().toLowerCase();\n if (normalized !== \"auto\") {\n const zIndex = Number(normalized);\n if (!Number.isInteger(zIndex) || Math.abs(zIndex) > 100) return false;\n }\n }\n for (const match of value.matchAll(/(-?\\d+(?:\\.\\d+)?)(px|vh|vw)/gi)) {\n const magnitude = Math.abs(Number(match[1]));\n const limit = match[2].toLowerCase() === \"px\" ? 4_000 : 200;\n if (magnitude > limit) return false;\n }\n return true;\n}\n\nfunction cleanCssContainer(\n container: Container,\n allowedAssetUrls: ReadonlySet<string>,\n): void {\n container.walkComments((comment) => {\n comment.remove();\n });\n container.walkAtRules((atRule) => {\n const name = atRule.name.toLowerCase();\n const isKeyframes = name === \"keyframes\" || name === \"-webkit-keyframes\";\n if (\n !ALLOWED_AT_RULES.has(name) ||\n !atRule.nodes?.length ||\n (name === \"font-face\" && atRule.parent?.type !== \"root\") ||\n (isKeyframes && !/^[a-z_][\\w-]*$/i.test(atRule.params.trim())) ||\n /<\\/?(?:style|script)/i.test(atRule.params)\n ) {\n atRule.remove();\n }\n });\n container.walkDecls((declaration) => {\n const property = declaration.prop.trim().toLowerCase();\n if (\n !property ||\n !declarationIsSafe(property, declaration.value, allowedAssetUrls)\n ) {\n declaration.remove();\n return;\n }\n });\n container.walkRules((rule) => {\n if (\n !rule.selector.trim() ||\n !rule.nodes?.length ||\n /<\\/?(?:style|script)/i.test(rule.selector)\n ) {\n rule.remove();\n }\n });\n container.walkAtRules(\"font-face\", (fontFace) => {\n const declarations = fontFace.nodes ?? [];\n const hasFamily = declarations.some(\n (node) => node.type === \"decl\" && node.prop.toLowerCase() === \"font-family\",\n );\n const hasSource = declarations.some(\n (node) => node.type === \"decl\" && node.prop.toLowerCase() === \"src\",\n );\n if (!hasFamily || !hasSource) fontFace.remove();\n });\n}\n\n/** ChatGPT HTML의 style 블록에서 실행·외부 로드 위험만 제거한다. */\nexport function sanitizeImportedCss(\n css: string,\n allowedAssetUrls: readonly string[] = [],\n): string {\n if (!css.trim()) return \"\";\n try {\n const root = postcss.parse(css);\n cleanCssContainer(root, new Set(allowedAssetUrls));\n return root.toString().trim();\n } catch {\n return \"\";\n }\n}\n\nfunction sanitizeInlineStyle(\n style: string,\n allowedAssetUrls: ReadonlySet<string>,\n): string {\n try {\n const root = postcss.parse(`x{${style}}`);\n cleanCssContainer(root, allowedAssetUrls);\n const rule = root.first;\n if (!rule || rule.type !== \"rule\") return \"\";\n return rule.nodes.map((node) => node.toString()).join(\";\");\n } catch {\n return \"\";\n }\n}\n\nfunction sanitizeImportedAttributes(\n tagName: string,\n attributes: sanitizeHtmlTypes.Attributes,\n allowedAssetUrls: ReadonlySet<string>,\n): sanitizeHtmlTypes.Tag {\n const next: sanitizeHtmlTypes.Attributes = {};\n for (const [name, value] of Object.entries(attributes)) {\n const attributeName = name.toLowerCase();\n if (attributeName === \"style\") {\n const style = sanitizeInlineStyle(value, allowedAssetUrls);\n if (style) next[name] = style;\n continue;\n }\n if (attributeName === \"srcset\") {\n const srcset = sanitizeSrcset(value);\n if (srcset) next[name] = srcset;\n continue;\n }\n if (\n (attributeName === \"fill\" || attributeName === \"stroke\") &&\n /url\\s*\\(/i.test(value) &&\n !/^url\\(#[a-z_][\\w:.-]*\\)$/i.test(value.trim())\n ) {\n continue;\n }\n if (\n (tagName === \"use\" || tagName === \"image\") &&\n (attributeName === \"href\" || attributeName === \"xlink:href\") &&\n !value.trim().startsWith(\"#\")\n ) {\n continue;\n }\n next[name] =\n URL_ATTRIBUTES.has(attributeName) &&\n !isSafeUrl(tagName, attributeName, value)\n ? \"#\"\n : value;\n }\n if (tagName === \"a\" && next.target === \"_blank\") {\n next.rel = \"noopener noreferrer\";\n }\n if (tagName === \"h1\") {\n next[ORIGINAL_H1_ATTRIBUTE] = \"h1\";\n return { tagName: \"h2\", attribs: next };\n }\n return { tagName, attribs: next };\n}\n\nfunction sanitizeSrcset(value: string): string {\n const candidates: string[] = [];\n for (const rawCandidate of value.split(\",\")) {\n const candidate = rawCandidate.trim();\n if (!candidate) continue;\n const [url, ...descriptorParts] = candidate.split(/\\s+/);\n if (!url || !isSafeUrl(\"img\", \"src\", url)) continue;\n const descriptor = descriptorParts.join(\" \");\n if (descriptor && !/^(?:\\d+(?:\\.\\d+)?x|\\d+w)$/.test(descriptor)) continue;\n candidates.push(descriptor ? `${url} ${descriptor}` : url);\n }\n return candidates.join(\", \");\n}\n\nconst IMPORTED_HTML_OPTIONS: sanitizeHtmlTypes.IOptions = {\n allowedTags: [\n \"a\", \"abbr\", \"address\", \"article\", \"aside\", \"b\", \"bdi\", \"bdo\",\n \"blockquote\", \"br\", \"caption\", \"cite\", \"code\", \"col\", \"colgroup\",\n \"dd\", \"del\", \"details\", \"dfn\", \"div\", \"dl\", \"dt\", \"em\",\n \"figcaption\", \"figure\", \"footer\", \"h1\", \"h2\", \"h3\", \"h4\", \"h5\", \"h6\",\n \"header\", \"hr\", \"i\", \"img\", \"kbd\", \"li\", \"main\", \"mark\", \"nav\",\n \"ol\", \"p\", \"picture\", \"pre\", \"q\", \"s\", \"samp\", \"section\", \"small\",\n \"source\", \"svg\", \"g\", \"path\", \"circle\", \"ellipse\", \"line\", \"polyline\",\n \"polygon\", \"rect\", \"defs\", \"lineargradient\", \"radialgradient\", \"stop\",\n \"use\", \"clippath\", \"mask\", \"pattern\", \"title\", \"desc\",\n \"span\", \"strong\", \"sub\", \"summary\", \"sup\", \"table\", \"tbody\", \"td\",\n \"tfoot\", \"th\", \"thead\", \"time\", \"tr\", \"u\", \"ul\", \"var\", \"wbr\",\n ],\n allowedAttributes: {\n \"*\": [\"aria-*\", \"class\", \"data-*\", \"dir\", \"id\", \"lang\", \"role\", \"style\", \"title\"],\n a: [\n \"href\",\n { name: \"rel\", multiple: true, values: [\"noopener\", \"noreferrer\"] },\n { name: \"target\", values: [\"_blank\"] },\n ],\n col: [\"span\", \"style\"],\n img: [\"alt\", \"height\", \"loading\", \"sizes\", \"src\", \"srcset\", \"style\", \"width\"],\n source: [\"height\", \"media\", \"sizes\", \"src\", \"srcset\", \"type\", \"width\"],\n svg: [\"aria-*\", \"class\", \"fill\", \"height\", \"preserveAspectRatio\", \"role\", \"stroke\", \"style\", \"viewBox\", \"viewbox\", \"width\", \"xmlns\"],\n g: [\"class\", \"fill\", \"stroke\", \"stroke-width\", \"style\", \"transform\"],\n path: [\"class\", \"d\", \"fill\", \"fill-rule\", \"stroke\", \"stroke-linecap\", \"stroke-linejoin\", \"stroke-width\", \"style\", \"transform\"],\n circle: [\"class\", \"cx\", \"cy\", \"fill\", \"r\", \"stroke\", \"stroke-width\", \"style\", \"transform\"],\n ellipse: [\"class\", \"cx\", \"cy\", \"fill\", \"rx\", \"ry\", \"stroke\", \"stroke-width\", \"style\", \"transform\"],\n line: [\"class\", \"stroke\", \"stroke-linecap\", \"stroke-width\", \"style\", \"transform\", \"x1\", \"x2\", \"y1\", \"y2\"],\n polyline: [\"class\", \"fill\", \"points\", \"stroke\", \"stroke-linecap\", \"stroke-linejoin\", \"stroke-width\", \"style\", \"transform\"],\n polygon: [\"class\", \"fill\", \"points\", \"stroke\", \"stroke-width\", \"style\", \"transform\"],\n rect: [\"class\", \"fill\", \"height\", \"rx\", \"ry\", \"stroke\", \"stroke-width\", \"style\", \"transform\", \"width\", \"x\", \"y\"],\n lineargradient: [\"gradientTransform\", \"gradientUnits\", \"id\", \"spreadMethod\", \"x1\", \"x2\", \"y1\", \"y2\"],\n radialgradient: [\"cx\", \"cy\", \"fx\", \"fy\", \"gradientTransform\", \"gradientUnits\", \"id\", \"r\", \"spreadMethod\"],\n stop: [\"offset\", \"stop-color\", \"stop-opacity\", \"style\"],\n use: [\"fill\", \"height\", \"href\", \"stroke\", \"style\", \"width\", \"x\", \"xlink:href\", \"y\"],\n clippath: [\"clipPathUnits\", \"id\", \"transform\"],\n mask: [\"height\", \"id\", \"maskContentUnits\", \"maskUnits\", \"width\", \"x\", \"y\"],\n pattern: [\"height\", \"id\", \"patternContentUnits\", \"patternTransform\", \"patternUnits\", \"preserveAspectRatio\", \"viewBox\", \"width\", \"x\", \"y\"],\n td: [\"colspan\", \"rowspan\", \"style\"],\n th: [\n \"colspan\",\n \"rowspan\",\n \"style\",\n { name: \"scope\", values: [\"col\", \"row\", \"colgroup\", \"rowgroup\"] },\n ],\n time: [\"datetime\"],\n },\n allowedSchemes: [\"http\", \"https\", \"mailto\", \"tel\"],\n allowedSchemesByTag: { img: [\"http\", \"https\", \"data\"] },\n allowedSchemesAppliedToAttributes: [\"href\", \"src\"],\n allowProtocolRelative: false,\n nonTextTags: [\"script\", \"style\", \"textarea\", \"option\", \"iframe\", \"object\", \"embed\", \"foreignobject\", \"math\", \"template\"],\n};\n\n/** class·id·안전한 inline style을 보존하는 원문 디자인 전용 HTML 경계. */\nexport function sanitizeImportedHtml(\n html: string,\n allowedAssetUrls: readonly string[] = [],\n): string {\n if (!html) return html;\n const allowedSet = new Set(allowedAssetUrls);\n return sanitizeHtmlLibrary(html, {\n ...IMPORTED_HTML_OPTIONS,\n transformTags: {\n \"*\": (tagName, attributes) =>\n sanitizeImportedAttributes(tagName, attributes, allowedSet),\n },\n });\n}\n\n/** 구 버전 저장값도 현재의 html/body 가상 루트 계약으로 올린다. */\nexport function ensureImportedHtmlDocumentRoots(\n html: string,\n allowedAssetUrls: readonly string[] = [],\n): string {\n const safeHtml = sanitizeImportedHtml(html, allowedAssetUrls).trim();\n if (!safeHtml) return safeHtml;\n if (\n /^<div\\b[^>]*\\bdata-rt-import-document(?:=(?:\"\"|'')|(?=[\\s>]))[^>]*>\\s*<div\\b[^>]*\\bdata-rt-import-body(?:=(?:\"\"|'')|(?=[\\s>]))/i.test(\n safeHtml,\n )\n ) {\n return safeHtml;\n }\n return `<div data-rt-import-document><div data-rt-import-body>${safeHtml}</div></div>`;\n}\n\nfunction normalizeDocumentSelector(selector: string): string {\n const typeBoundary = \"(?=$|[\\\\s>+~,.#:\\\\[\\\\)])\";\n return selector\n .replace(/html\\s*>\\s*body/gi, \"[data-rt-import-document] > [data-rt-import-body]\")\n .replace(/html\\s+body/gi, \"[data-rt-import-document] [data-rt-import-body]\")\n .replace(new RegExp(`(^|[\\\\s>+~,\\\\(])html${typeBoundary}`, \"gi\"), \"$1[data-rt-import-document]\")\n .replace(new RegExp(`(^|[\\\\s>+~,\\\\(])body${typeBoundary}`, \"gi\"), \"$1[data-rt-import-body]\")\n .replace(\n new RegExp(`(^|[\\\\s>+~,\\\\(])h1${typeBoundary}`, \"gi\"),\n `$1[${ORIGINAL_H1_ATTRIBUTE}=\"h1\"]`,\n )\n .replace(/:root/g, \"[data-rt-import-document]\");\n}\n\nfunction selectorTargetsDocumentBoundary(selector: string): boolean {\n return selector.split(\",\").some((part) => {\n const normalized = part.trim();\n const targetsDocumentRoot =\n normalized.startsWith(\"[data-rt-import-document]\") &&\n !/[\\s>+~]/.test(normalized.slice(\"[data-rt-import-document]\".length));\n const targetsBodyRoot =\n /\\[data-rt-import-body\\](?:[.#:][\\w-]+|\\[[^\\]]+\\])*\\s*$/.test(\n normalized,\n );\n return targetsDocumentRoot || targetsBodyRoot;\n });\n}\n\nfunction replaceAnimationName(value: string, names: ReadonlyMap<string, string>): string {\n let next = value;\n for (const [name, scopedName] of names) {\n const escaped = name.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n next = next.replace(new RegExp(`(^|[^\\\\w-])${escaped}(?=$|[^\\\\w-])`, \"g\"), `$1${scopedName}`);\n }\n return next;\n}\n\nfunction unquoteCssIdentifier(value: string): string | null {\n const normalized = value.trim();\n const match = /^(?:\"([^\"]+)\"|'([^']+)'|([a-z_][\\w -]*))$/i.exec(normalized);\n return (match?.[1] ?? match?.[2] ?? match?.[3] ?? \"\").trim() || null;\n}\n\nfunction replaceFontFamilyName(\n value: string,\n names: ReadonlyMap<string, string>,\n): string {\n let next = value;\n for (const [name, scopedName] of names) {\n const escaped = name.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n next = next.replace(\n new RegExp(`([\"'])${escaped}\\\\1|(^|[,\\\\s])${escaped}(?=$|[,\\\\s/])`, \"gi\"),\n (matched, quote: string | undefined, prefix: string | undefined) =>\n quote ? `${quote}${scopedName}${quote}` : `${prefix ?? \"\"}${scopedName}`,\n );\n }\n return next;\n}\n\n/** 저장된 안전 CSS가 같은 importedHtml 블록 밖으로 새지 않도록 범위를 고정한다. */\nexport function scopeImportedCss(\n css: string,\n scope: string,\n allowedAssetUrls: readonly string[] = [],\n): string {\n if (!IMPORT_SCOPE_RE.test(scope)) return \"\";\n const safeCss = sanitizeImportedCss(css, allowedAssetUrls);\n if (!safeCss) return \"\";\n try {\n const root = postcss.parse(safeCss);\n const keyframeNames = new Map<string, string>();\n const fontFamilyNames = new Map<string, string>();\n const fontFaces = root.nodes.filter(\n (node) => node.type === \"atrule\" && node.name.toLowerCase() === \"font-face\",\n );\n for (const fontFace of fontFaces) {\n if (fontFace.type !== \"atrule\") continue;\n const family = fontFace.nodes?.find(\n (node) => node.type === \"decl\" && node.prop.toLowerCase() === \"font-family\",\n );\n if (!family || family.type !== \"decl\") {\n fontFace.remove();\n continue;\n }\n const originalName = unquoteCssIdentifier(family.value);\n const hasSupportedSource = fontFace.nodes?.some(\n (node) =>\n node.type === \"decl\" &&\n node.prop.toLowerCase() === \"src\" &&\n /url\\(\\s*[\"']?https:\\/\\/[^)'\"\\s]+\\.woff2?(?:[?#][^)'\"\\s]*)?[\"']?\\s*\\)/i.test(\n node.value,\n ),\n );\n if (!originalName || !hasSupportedSource) {\n fontFace.remove();\n continue;\n }\n const scopedName = `${scope}-${originalName}`;\n fontFamilyNames.set(originalName, scopedName);\n family.value = family.value.replace(originalName, scopedName);\n }\n root.walkAtRules((atRule) => {\n const name = atRule.name.toLowerCase();\n if (name !== \"keyframes\" && name !== \"-webkit-keyframes\") return;\n const originalName = atRule.params.trim();\n const scopedName = `${scope}-${originalName}`;\n keyframeNames.set(originalName, scopedName);\n atRule.params = scopedName;\n });\n root.walkDecls((declaration) => {\n const property = declaration.prop.trim().toLowerCase();\n if (property === \"animation\" || property === \"animation-name\") {\n declaration.value = replaceAnimationName(declaration.value, keyframeNames);\n }\n if (property === \"font-family\" || property === \"font\") {\n declaration.value = replaceFontFamilyName(\n declaration.value,\n fontFamilyNames,\n );\n }\n });\n root.walkRules((rule: Rule) => {\n const parent = rule.parent;\n if (\n parent?.type === \"atrule\" &&\n (parent.name.toLowerCase() === \"keyframes\" ||\n parent.name.toLowerCase() === \"-webkit-keyframes\")\n ) {\n return;\n }\n rule.selector = normalizeDocumentSelector(rule.selector);\n if (selectorTargetsDocumentBoundary(rule.selector)) {\n rule.walkDecls((declaration) => {\n if (\n SCOPE_ROOT_BOUNDARY_PROPERTIES.has(\n declaration.prop.trim().toLowerCase(),\n )\n ) {\n declaration.remove();\n }\n });\n }\n });\n const scoped = postcss.atRule({\n name: \"scope\",\n params: `([data-rt-import-scope=\"${scope}\"])`,\n });\n const preservedFontFaces = fontFaces.filter((fontFace) => fontFace.parent);\n for (const fontFace of preservedFontFaces) fontFace.remove();\n scoped.append(root.nodes);\n return [...preservedFontFaces.map((fontFace) => fontFace.toString()), scoped.toString()]\n .filter(Boolean)\n .join(\"\\n\");\n } catch {\n return \"\";\n }\n}\n","// 본문 예약 내부 링크 토큰 — `[[internal:{키}|표시 문구]]`.\n//\n// 키는 대상 글의 정규 공개 경로 조각을 점으로 이은 값(`faq.두통.편두통.slug`,\n// `blog.my-post`). 토큰은 body_json 의 일반 텍스트로 저장되고, 편집기는 칩으로\n// 보여 주며, 공개 렌더러는 발행 원장의 경로와 대조해 링크로 바꾼다. 여기 정규식\n// 하나가 편집기·렌더러·FRONT 예제의 SSoT 다(어드민 `buildInternalContentLinkToken`\n// 이 만드는 형식과 같다).\n\nexport const INTERNAL_CONTENT_LINK_TOKEN_SOURCE = String.raw`\\[\\[internal:([a-z0-9가-힣]+(?:[.-][a-z0-9가-힣]+)*)\\|([^\\]\\n|]+)\\]\\]`;\n\nexport interface InternalContentLinkToken {\n /** 토큰이 시작하는 문자 인덱스(텍스트 기준). */\n readonly index: number;\n /** 원문 토큰 전체(`[[internal:…|…]]`). */\n readonly raw: string;\n /** 소문자로 정규화한 키. */\n readonly key: string;\n /** 표시 문구(앞뒤 공백 제거). */\n readonly label: string;\n}\n\n/** 텍스트 안의 토큰을 앞에서부터 모두 찾는다. 겹치지 않는다. */\nexport function findInternalContentLinkTokens(\n text: string,\n): InternalContentLinkToken[] {\n const pattern = new RegExp(INTERNAL_CONTENT_LINK_TOKEN_SOURCE, \"giu\");\n const tokens: InternalContentLinkToken[] = [];\n for (const match of text.matchAll(pattern)) {\n const [raw, key, label] = match;\n if (!key || !label || match.index === undefined) continue;\n tokens.push({\n index: match.index,\n raw,\n key: key.toLowerCase(),\n label: label.trim(),\n });\n }\n return tokens;\n}\n\n/** 공개 경로(`/faq/a/b/slug`) → 키(`faq.a.b.slug`). 조각이 없으면 null. */\nexport function internalContentKeyFromPath(\n path: string | null | undefined,\n): string | null {\n if (!path) return null;\n const segments = path\n .split(\"/\")\n .filter(Boolean)\n .map((segment) => {\n try {\n return decodeURIComponent(segment).trim().toLowerCase();\n } catch {\n return \"\";\n }\n });\n if (\n segments.length === 0 ||\n segments.some((segment) => segment === \"\" || segment.includes(\".\"))\n ) {\n return null;\n }\n return segments.join(\".\");\n}\n\n/** 키 → 공개 경로. 없으면 null(문구만 표시). */\nexport type InternalContentLinkResolver = (key: string) => string | null;\n\ninterface InternalContentPathEntry {\n /** 저장된 정규 공개 경로(`/faq/a/b/slug`). 없으면 대상이 아니다. */\n readonly path?: string | null;\n /** 옛 slug 목록 — 옛 키로 남은 본문도 현재 주소로 이어 준다. */\n readonly previousSlugs?: readonly string[] | null;\n}\n\n/**\n * 발행 글 목록에서 \"키 → 현재 경로\" 색인을 만든다. 옛 slug 도 같은 글로 등록한다\n * (경로 마지막 조각을 옛 slug 로 바꾼 키). 현재 경로가 옛 키와 겹치면 현재가 이긴다.\n */\nexport function internalContentPathIndex(\n entries: Iterable<InternalContentPathEntry>,\n): Map<string, string> {\n const index = new Map<string, string>();\n const previous = new Map<string, string>();\n for (const entry of entries) {\n const path = entry.path;\n const key = internalContentKeyFromPath(path);\n if (!path || !key) continue;\n index.set(key, path);\n const segments = key.split(\".\");\n for (const oldSlug of entry.previousSlugs ?? []) {\n const oldKey = [...segments.slice(0, -1), oldSlug.trim().toLowerCase()].join(\".\");\n if (oldKey && !oldKey.includes(\"..\")) previous.set(oldKey, path);\n }\n }\n for (const [key, path] of previous) {\n if (!index.has(key)) index.set(key, path);\n }\n return index;\n}\n\n/** Map 이나 함수 어느 쪽이든 resolver 로. */\nexport function internalContentLinkResolverFrom(\n source: InternalContentLinkResolver | ReadonlyMap<string, string> | null | undefined,\n): InternalContentLinkResolver {\n if (!source) return () => null;\n if (typeof source === \"function\") return source;\n return (key) => source.get(key.toLowerCase()) ?? null;\n}\n\nfunction isSafeInternalPath(path: string | null): path is string {\n return typeof path === \"string\" && path.startsWith(\"/\") && !path.startsWith(\"//\");\n}\n\nfunction escapeHtmlText(value: string): string {\n return value\n .replaceAll(\"&\", \"&amp;\")\n .replaceAll(\"<\", \"&lt;\")\n .replaceAll(\">\", \"&gt;\")\n .replaceAll('\"', \"&quot;\")\n .replaceAll(\"'\", \"&#39;\");\n}\n\n/** 링크로 렌더된 토큰의 class — 사이트 CSS 가 잡을 고리. */\nexport const INTERNAL_CONTENT_LINK_CLASS = \"rt-internal-link\";\n/** 대상이 아직 없어 문구만 남긴 토큰의 class·data 속성. */\nexport const INTERNAL_CONTENT_LINK_PENDING_CLASS = \"rt-internal-link--pending\";\nexport const INTERNAL_CONTENT_LINK_PENDING_ATTR = \"data-rt-internal-link-pending\";\n\n/**\n * 이미 이스케이프된 텍스트 한 구간의 토큰을 HTML 로 바꾼다. 텍스트 부분은 다시\n * 이스케이프하지 않는다(정화기가 이미 처리한 값). 토큰이 없으면 원문 그대로.\n */\nexport function renderInternalContentLinksInText(\n text: string,\n resolve: InternalContentLinkResolver,\n): { readonly html: string; readonly hasTokens: boolean } {\n const tokens = findInternalContentLinkTokens(text);\n if (tokens.length === 0) return { html: text, hasTokens: false };\n let cursor = 0;\n let html = \"\";\n for (const token of tokens) {\n html += text.slice(cursor, token.index);\n const path = resolve(token.key);\n const label = escapeHtmlText(token.label);\n html += isSafeInternalPath(path)\n ? `<a href=\"${escapeHtmlText(path)}\" class=\"${INTERNAL_CONTENT_LINK_CLASS}\">${label}</a>`\n : `<span class=\"${INTERNAL_CONTENT_LINK_CLASS} ${INTERNAL_CONTENT_LINK_PENDING_CLASS}\" ${INTERNAL_CONTENT_LINK_PENDING_ATTR}=\"${escapeHtmlText(token.key)}\">${label}</span>`;\n cursor = token.index + token.raw.length;\n }\n html += text.slice(cursor);\n return { html, hasTokens: true };\n}\n\nconst INTERNAL_LINK_BLOCKED_HTML_TAGS = new Set([\n \"a\",\n \"code\",\n \"pre\",\n \"script\",\n \"style\",\n \"textarea\",\n]);\nconst VOID_HTML_TAGS = new Set([\"br\", \"hr\", \"img\", \"input\", \"source\", \"wbr\"]);\n\nfunction htmlTagEnd(html: string, start: number): number {\n let quote: '\"' | \"'\" | null = null;\n for (let index = start + 1; index < html.length; index += 1) {\n const character = html[index];\n if (quote) {\n if (character === quote) quote = null;\n continue;\n }\n if (character === '\"' || character === \"'\") {\n quote = character;\n continue;\n }\n if (character === \">\") return index;\n }\n return -1;\n}\n\n/**\n * 정화된 HTML(원문 보존 블록 등)의 텍스트 구간에만 토큰을 적용한다. `<a>`·`<code>`·\n * `<pre>` 안과 태그 속성값은 건드리지 않는다.\n */\nexport function renderInternalContentLinksInHtml(\n html: string,\n resolve: InternalContentLinkResolver,\n): { readonly html: string; readonly hasTokens: boolean } {\n if (!html.includes(\"[[internal:\")) return { html, hasTokens: false };\n let blockedDepth = 0;\n let cursor = 0;\n let out = \"\";\n let hasTokens = false;\n const emitText = (text: string) => {\n if (blockedDepth > 0 || !text) {\n out += text;\n return;\n }\n const rendered = renderInternalContentLinksInText(text, resolve);\n hasTokens ||= rendered.hasTokens;\n out += rendered.html;\n };\n while (cursor < html.length) {\n const tagStart = html.indexOf(\"<\", cursor);\n if (tagStart < 0) {\n emitText(html.slice(cursor));\n break;\n }\n emitText(html.slice(cursor, tagStart));\n const tagEnd = htmlTagEnd(html, tagStart);\n if (tagEnd < 0) {\n out += html.slice(tagStart);\n break;\n }\n const tag = html.slice(tagStart, tagEnd + 1);\n out += tag;\n const parsed = /^<\\s*(\\/?)\\s*([a-z0-9-]+)/i.exec(tag);\n const tagName = parsed?.[2]?.toLowerCase();\n if (tagName && INTERNAL_LINK_BLOCKED_HTML_TAGS.has(tagName)) {\n const isClosing = parsed?.[1] === \"/\";\n const isVoid = VOID_HTML_TAGS.has(tagName) || /\\/\\s*>$/.test(tag);\n if (isClosing) blockedDepth = Math.max(0, blockedDepth - 1);\n else if (!isVoid) blockedDepth += 1;\n }\n cursor = tagEnd + 1;\n }\n return { html: out, hasTokens };\n}\n","// ADR-0034 §7.1.1 test #10 — Draft preview token sign/verify.\n//\n// HMAC-SHA256 signed token + DB row (post_preview_tokens_v2) lookup.\n// Atomic consume = consumedAt update at verify time.\n//\n// Token format: `<postId>.<expiresAt-ms>.<hmac-base64url>`\n\nimport { createHmac, randomBytes, timingSafeEqual } from \"node:crypto\";\n\nexport interface PreviewTokenPayload {\n postId: string;\n expiresAt: Date;\n}\n\nconst DELIMITER = \".\";\n\nfunction base64url(buf: Buffer): string {\n return buf.toString(\"base64url\");\n}\n\nfunction fromBase64url(s: string): Buffer {\n return Buffer.from(s, \"base64url\");\n}\n\nfunction sign(secret: string, message: string): string {\n return base64url(createHmac(\"sha256\", secret).update(message).digest());\n}\n\n/** 새 preview token 생성. DB insert 책임은 caller. */\nexport function issuePreviewToken(\n secret: string,\n payload: PreviewTokenPayload,\n): string {\n const exp = payload.expiresAt.getTime();\n const nonce = base64url(randomBytes(12));\n const message = `${payload.postId}${DELIMITER}${exp}${DELIMITER}${nonce}`;\n const sig = sign(secret, message);\n return `${message}${DELIMITER}${sig}`;\n}\n\n/** Token 검증 (서명 + 만료). DB row consumed_at update 는 caller (atomic). */\nexport function verifyPreviewToken(\n secret: string,\n token: string,\n): PreviewTokenPayload | null {\n const parts = token.split(DELIMITER);\n if (parts.length !== 4) return null;\n const [postId, expRaw, nonce, sig] = parts as [string, string, string, string];\n const exp = Number(expRaw);\n if (!Number.isFinite(exp)) return null;\n if (Date.now() > exp) return null;\n\n const expectedSig = sign(secret, `${postId}${DELIMITER}${exp}${DELIMITER}${nonce}`);\n const a = fromBase64url(sig);\n const b = fromBase64url(expectedSig);\n if (a.length !== b.length) return null;\n if (!timingSafeEqual(a, b)) return null;\n\n return { postId, expiresAt: new Date(exp) };\n}\n","import { z } from \"zod\";\n\nexport const VISUAL_EDIT_PROTOCOL = \"roottale:visual-edit\";\nexport const VISUAL_EDIT_PROTOCOL_VERSION = 1;\nexport const VISUAL_EDIT_ATTRIBUTE = \"data-rt-edit\";\n\nconst identifier = z.string().min(1).max(160).refine(value => !/[\\u0000-\\u001f]/.test(value));\nconst safeKey = identifier.refine(value => ![\"__proto__\", \"prototype\", \"constructor\"].includes(value));\nconst pathKey = safeKey.refine(value => !/^\\d+$/.test(value), \"반복 항목은 순번 대신 itemId로 선택합니다.\");\nconst version = z.number().int().min(0).max(Number.MAX_SAFE_INTEGER);\nconst session = z.string().uuid();\nconst position = z.object({ left: z.number().finite().min(0).max(10_000_000), top: z.number().finite().min(0).max(10_000_000) }).strict();\nconst pathname = z.string().min(1).max(2000).refine(value => value.startsWith(\"/\") && !value.startsWith(\"//\") && !/[\\\\?#\\u0000-\\u001f]/.test(value));\n\n/** 필드 정의의 key와 저장 경로는 구분한다. 반복행의 영속 주소는 _id다. */\nexport const visualEditAddressSchema = z.object({\n entityId: identifier,\n fieldKey: safeKey,\n itemId: identifier.optional(),\n path: z.array(pathKey).max(16).optional(),\n}).strict().refine(address => {\n const selector = [...(address.path ?? [])].reverse().find(part => part.startsWith(\"@\"));\n return !selector || !address.itemId || selector.slice(1) === address.itemId;\n}, \"itemId는 경로의 마지막 반복 항목 ID와 같아야 합니다.\");\n\nexport const visualPreviewTargetSchema = z.object({\n entityId: identifier,\n draftId: identifier,\n draftVersion: version,\n contractVersion: identifier,\n templateVersion: identifier,\n}).strict();\n\nexport const visualRenderReceiptSchema = visualPreviewTargetSchema.extend({\n dependencyVersions: z.record(safeKey, identifier).refine(value => Object.keys(value).length <= 256),\n}).strict();\n\nexport type VisualEditAddress = z.infer<typeof visualEditAddressSchema>;\nexport type VisualPreviewTarget = z.infer<typeof visualPreviewTargetSchema>;\nexport type VisualRenderReceipt = z.infer<typeof visualRenderReceiptSchema>;\nexport type VisualViewportPosition = z.infer<typeof position>;\nexport type VisualEditMode = \"select\" | \"navigate\";\n\nconst envelope = {\n protocol: z.literal(VISUAL_EDIT_PROTOCOL),\n protocolVersion: z.literal(VISUAL_EDIT_PROTOCOL_VERSION),\n session,\n sequence: z.number().int().positive().max(Number.MAX_SAFE_INTEGER),\n target: visualPreviewTargetSchema,\n};\n\nconst messageSchema = z.discriminatedUnion(\"type\", [\n z.object({ ...envelope, type: z.literal(\"connect\") }).strict(),\n z.object({ ...envelope, type: z.literal(\"ready\") }).strict(),\n z.object({ ...envelope, type: z.literal(\"select\"), address: visualEditAddressSchema, position: position.optional() }).strict(),\n z.object({ ...envelope, type: z.literal(\"focus\"), address: visualEditAddressSchema.optional(), position: position.optional() }).strict(),\n z.object({ ...envelope, type: z.literal(\"navigate\"), pathname }).strict(),\n z.object({ ...envelope, type: z.literal(\"mode\"), mode: z.enum([\"select\", \"navigate\"]) }).strict(),\n z.object({ ...envelope, type: z.literal(\"refresh\") }).strict(),\n z.object({ ...envelope, type: z.literal(\"viewport\"), position }).strict(),\n z.object({ ...envelope, type: z.literal(\"rendered\"), receipt: visualRenderReceiptSchema }).strict(),\n z.object({ ...envelope, type: z.literal(\"error\"), code: z.enum([\"unavailable\", \"superseded\", \"unsupported_contract\", \"expired\"]), message: z.string().max(500).optional() }).strict(),\n]);\n\nexport type VisualEditMessage = z.infer<typeof messageSchema>;\nexport type VisualEditPayload = VisualEditMessage extends infer Message\n ? Message extends VisualEditMessage ? Omit<Message, keyof typeof envelope> : never : never;\n\nexport function parseVisualEditAddress(value: unknown): VisualEditAddress | null {\n const result = visualEditAddressSchema.safeParse(value);\n return result.success ? result.data : null;\n}\n\nexport function parseVisualEditMessage(value: unknown): VisualEditMessage | null {\n try {\n if (JSON.stringify(value).length > 64_000) return null;\n const result = messageSchema.safeParse(value);\n if (!result.success) return null;\n const message = result.data;\n if (message.type === \"rendered\" && !sameVisualPreviewTarget(message.target, message.receipt)) return null;\n if (message.type === \"focus\" && message.address && message.address.entityId !== message.target.entityId) return null;\n return message;\n } catch { return null; }\n}\n\nexport function sameVisualPreviewTarget(a: VisualPreviewTarget, b: VisualPreviewTarget): boolean {\n return a.entityId === b.entityId && a.draftId === b.draftId && a.draftVersion === b.draftVersion\n && a.contractVersion === b.contractVersion && a.templateVersion === b.templateVersion;\n}\n\nexport function sameVisualDependencies(a: Record<string, string>, b: Record<string, string>): boolean {\n const keys = Object.keys(a);\n return keys.length === Object.keys(b).length && keys.every(key => Object.hasOwn(b, key) && a[key] === b[key]);\n}\n\nexport function visualEditAddressKey(address: VisualEditAddress): string {\n const path = address.path ?? [];\n // 중첩 반복행은 경로가 정본이다. itemId는 사이트가 생략할 수 있는 보조값이다.\n const itemId = [...path].reverse().find(part => part.startsWith(\"@\"))?.slice(1) ?? address.itemId ?? null;\n return JSON.stringify([address.entityId, address.fieldKey, itemId, path]);\n}\n\n/** 미리보기에서만 사용한다. RSC에서도 호출하는 순수 속성 생성 함수다. */\nexport function visualEditAttributes(address: VisualEditAddress, enabled = true): { \"data-rt-edit\"?: string } {\n const parsed = enabled ? parseVisualEditAddress(address) : null;\n return parsed ? { [VISUAL_EDIT_ATTRIBUTE]: JSON.stringify(parsed) } : {};\n}\n\nexport function isVisualEditOrigin(value: string): boolean {\n try {\n const url = new URL(value);\n const local = [\"localhost\", \"127.0.0.1\", \"[::1]\"].includes(url.hostname);\n return value === url.origin && (url.protocol === \"https:\" || (local && url.protocol === \"http:\"));\n } catch { return false; }\n}\n\n/** 내용·권한은 기존 서버 snapshot 경로가 소유한다. fragment에는 연결 식별자만 싣는다. */\nexport function buildVisualEditPreviewUrl(urlValue: string, editorOrigin: string, sessionId: string): string | null {\n try {\n const url = new URL(urlValue);\n if (url.username || url.password || !isVisualEditOrigin(url.origin) || !isVisualEditOrigin(editorOrigin) || !session.safeParse(sessionId).success) return null;\n url.hash = new URLSearchParams({ \"rt-visual-edit\": sessionId, origin: editorOrigin }).toString();\n return url.href;\n } catch { return null; }\n}\n","import sanitizeHtmlLibrary from \"sanitize-html\";\nimport type sanitizeHtmlTypes from \"sanitize-html\";\n\n// ADR-0034 §7.1.1 test #7 — XSS sanitize helper (allow-list, 2차 방어).\n//\n// Phase 1 = strict CSP (`script-src 'self'`) 가 1차 방어. 본 helper 는 렌더 직전\n// 2차 방어로 위험 태그/속성/URL scheme 을 strip 한다.\n//\n// Tiptap 에디터/renderer 실측 allowlist:\n// - StarterKit: p/h2-h4/ul/ol/li/blockquote/pre/code/hr/br + strong/em/s\n// - editor 추가 mark: u/mark/span(style: color/background) + TextAlign(style)\n// - media/table/custom nodes: img/figure/figcaption/table/thead/tbody/tfoot/tr/th/td/div\n// (thead/tfoot 는 이관된 원문 HTML 표에 실제로 쓰인다. allowlist 에서 빠져 있으면\n// sanitize-html 이 그 태그만 벗겨내 `<table><tr>…` 처럼 머리행이 섹션 밖으로\n// 빠져나온 깨진 표가 되므로 안전한 구조 태그로 명시 허용한다.)\n// - FAQ renderer: details/summary + rt-cms-* classes\n// Embed iframe 은 sanitize 경로가 아니라 Tiptap JSON `renderEmbed()` 에서\n// `isAllowedEmbedSrc` 로 검증 후 렌더된다. raw HTML sanitizer 에서는 iframe 전면 strip.\n\n// URL 속성(href/src/…) — deny-list 가 아니라 **scheme allow-list** 로 판정한다.\n// HTML entity / 제어문자 인코딩(`&#106;avascript:`, `java&#9;script:`,\n// `jav&#x0d;ascript:`)은 regex 리터럴 매칭을 우회하므로, 값에서 entity 를 decode\n// 하고 ASCII 공백/제어문자를 제거(브라우저 URL 파서와 동일 정규화)한 뒤 scheme 을\n// 검사한다. sanitize-html parser 가 처리한 속성에도 동일 probe 를 적용한다.\nconst URL_ATTRS = new Set([\"href\", \"src\", \"xlink:href\"]);\n\nconst ALLOWED_URL_SCHEMES = new Set([\"http\", \"https\", \"mailto\", \"tel\"]);\nconst MALFORMED_DANGEROUS_TOKEN_RE =\n /<\\/?[^>\\s/]*(?:script|style|iframe|object|embed|svg|math|template)[^>\\s/]*(?:\\s[^>]*)?>[\\s\\S]*?<\\/?[^>\\s/]*(?:script|style|iframe|object|embed|svg|math|template)[^>\\s/]*(?:\\s[^>]*)?>/gi;\nconst SLASH_EVENT_ATTR_IN_URL_RE = /\\/on[a-z]+=/i;\nconst ROOTTALE_CLASS_RE = /^rt-[a-z0-9_-]+$/;\nconst HEX_COLOR_RE = /^#[0-9a-f]{3}(?:[0-9a-f]{3})?$/i;\nconst TEXT_ALIGN_RE = /^(left|right|center|justify)$/;\nconst ASPECT_RATIO_RE = /^\\d+(?:\\.\\d+)?\\s*\\/\\s*\\d+(?:\\.\\d+)?$/;\nconst CSS_LENGTH_RE = /^(?:0|\\d+(?:\\.\\d+)?(?:px|em|rem|%))$/i;\nconst CSS_LENGTH_LIST_RE =\n /^(?:0|\\d+(?:\\.\\d+)?(?:px|em|rem|%))(?:\\s+(?:0|\\d+(?:\\.\\d+)?(?:px|em|rem|%))){0,3}$/i;\nconst CSS_LINE_HEIGHT_RE = /^(?:normal|\\d+(?:\\.\\d+)?(?:px|em|rem|%)?)$/i;\nconst CSS_FONT_WEIGHT_RE = /^(?:normal|bold|[1-9]00)$/i;\nconst CSS_BORDER_RE =\n /^(?:none|0|(?:\\d(?:\\.\\d+)?|[12]\\d(?:\\.\\d+)?|3[0-2](?:\\.0+)?)px\\s+(?:solid|dashed|dotted)\\s+#[0-9a-f]{3}(?:[0-9a-f]{3})?)$/i;\nconst CSS_TEXT_DECORATION_RE = /^(?:none|underline|line-through)$/i;\n\nconst CSS_MAGNITUDE_LIMITS: Record<string, Record<string, number>> = {\n \"font-size\": { \"\": 0, px: 96, em: 5, rem: 5, \"%\": 200 },\n \"line-height\": { \"\": 5, px: 96, em: 5, rem: 5, \"%\": 500 },\n margin: { \"\": 0, px: 2000, em: 20, rem: 20, \"%\": 200 },\n \"margin-bottom\": { \"\": 0, px: 2000, em: 20, rem: 20, \"%\": 200 },\n \"margin-left\": { \"\": 0, px: 2000, em: 20, rem: 20, \"%\": 200 },\n \"margin-right\": { \"\": 0, px: 2000, em: 20, rem: 20, \"%\": 200 },\n \"margin-top\": { \"\": 0, px: 2000, em: 20, rem: 20, \"%\": 200 },\n padding: { \"\": 0, px: 2000, em: 20, rem: 20, \"%\": 200 },\n \"padding-bottom\": { \"\": 0, px: 2000, em: 20, rem: 20, \"%\": 200 },\n \"padding-left\": { \"\": 0, px: 2000, em: 20, rem: 20, \"%\": 200 },\n \"padding-right\": { \"\": 0, px: 2000, em: 20, rem: 20, \"%\": 200 },\n \"padding-top\": { \"\": 0, px: 2000, em: 20, rem: 20, \"%\": 200 },\n width: { \"\": 0, px: 4000, em: 100, rem: 100, \"%\": 100 },\n \"max-width\": { \"\": 0, px: 4000, em: 100, rem: 100, \"%\": 100 },\n height: { \"\": 0, px: 4000, em: 100, rem: 100, \"%\": 100 },\n};\n\nfunction hasSafeCssMagnitude(property: string, value: string): boolean {\n const limits = CSS_MAGNITUDE_LIMITS[property];\n if (!limits) return true;\n const numericTokens = value.matchAll(/(\\d+(?:\\.\\d+)?)(px|em|rem|%)?/gi);\n for (const match of numericTokens) {\n const unit = (match[2] ?? \"\").toLowerCase();\n const limit = limits[unit];\n if (limit === undefined || Number(match[1]) > limit) return false;\n }\n return true;\n}\n\nfunction filterUnsafeCssMagnitudes(style: string): string {\n return style\n .split(\";\")\n .map((declaration) => declaration.trim())\n .filter(Boolean)\n .filter((declaration) => {\n const colon = declaration.indexOf(\":\");\n if (colon < 1) return false;\n const property = declaration.slice(0, colon).trim().toLowerCase();\n const value = declaration.slice(colon + 1).trim();\n return hasSafeCssMagnitude(property, value);\n })\n .join(\";\");\n}\n\n// scheme 구성에 악용되는 named entity 만 decode (`&colon;`/`&Tab;` 등).\nconst NAMED_ENTITY: Record<string, string> = {\n colon: \":\",\n tab: \"\\t\",\n newline: \"\\n\",\n sol: \"/\",\n semi: \";\",\n num: \"#\",\n period: \".\",\n comma: \",\",\n lpar: \"(\",\n rpar: \")\",\n};\n\nfunction fromCodePointSafe(n: number): string {\n if (!Number.isFinite(n) || n < 0 || n > 0x10ffff) return \"\";\n try {\n return String.fromCodePoint(n);\n } catch {\n return \"\";\n }\n}\n\n/** URL 값 정규화 — entity decode + 공백/제어문자 제거 + 소문자 (scheme 판정용). */\nfunction decodeUrlProbe(value: string): string {\n const decoded = value\n .replace(/&#x([0-9a-f]+);?/gi, (_, h) => fromCodePointSafe(parseInt(h, 16)))\n .replace(/&#(\\d+);?/g, (_, d) => fromCodePointSafe(parseInt(d, 10)))\n .replace(/&([a-z]+);/gi, (m, name) => NAMED_ENTITY[name.toLowerCase()] ?? m);\n // 브라우저는 scheme 판정 시 ASCII 공백/제어문자(탭/개행/CR/NUL 포함)를 제거한다.\n return [...decoded].filter((c) => { const n = c.codePointAt(0) ?? 0; return n > 0x20 && n !== 0x7f; }).join(\"\").toLowerCase();\n}\n\nfunction stripMalformedDangerousTokens(html: string): string {\n let out = html;\n for (let i = 0; i < 3; i++) {\n const next = out.replace(MALFORMED_DANGEROUS_TOKEN_RE, \"\");\n if (next === out) return out;\n out = next;\n }\n return out;\n}\n\n/**\n * 안전한 URL 값인지 — 상대경로/허용 scheme, data:image/* 는 img[src] 에서만(SVG 제외).\n */\nfunction isSafeUrlValue(tag: string, attr: string, value: string): boolean {\n const probe = decodeUrlProbe(value);\n if (SLASH_EVENT_ATTR_IN_URL_RE.test(probe)) return false;\n const scheme = /^([a-z][a-z0-9+.-]*):/.exec(probe)?.[1];\n if (!scheme) return true; // 상대경로 / fragment / query\n if (ALLOWED_URL_SCHEMES.has(scheme)) return true;\n if (\n scheme === \"data\" &&\n tag === \"img\" &&\n attr === \"src\" &&\n probe.startsWith(\"data:image/\")\n ) {\n // data:image/svg+xml 은 스크립트 실행 가능 → 차단.\n return !probe.startsWith(\"data:image/svg\");\n }\n return false;\n}\n\nfunction sanitizeUrlAttributes(\n tagName: string,\n attribs: sanitizeHtmlTypes.Attributes,\n): sanitizeHtmlTypes.Tag {\n const next: sanitizeHtmlTypes.Attributes = {};\n for (const [name, value] of Object.entries(attribs)) {\n const attr = name.toLowerCase();\n if (attr === \"style\") {\n const style = filterUnsafeCssMagnitudes(value);\n if (style) next[name] = style;\n continue;\n }\n next[name] = URL_ATTRS.has(attr) && !isSafeUrlValue(tagName, attr, value) ? \"#\" : value;\n }\n if (tagName === \"a\" && next.target === \"_blank\") {\n next.rel = \"noopener noreferrer\";\n }\n return { tagName, attribs: next };\n}\n\nconst SANITIZE_OPTIONS: sanitizeHtmlTypes.IOptions = {\n allowedTags: [\n \"a\",\n \"blockquote\",\n \"br\",\n \"cite\",\n \"code\",\n \"details\",\n \"div\",\n \"em\",\n \"figcaption\",\n \"figure\",\n \"h2\",\n \"h3\",\n \"h4\",\n \"hr\",\n \"img\",\n \"li\",\n \"mark\",\n \"ol\",\n \"p\",\n \"pre\",\n \"s\",\n \"section\",\n \"small\",\n \"span\",\n \"strong\",\n \"sub\",\n \"summary\",\n \"sup\",\n \"table\",\n \"tbody\",\n \"td\",\n \"tfoot\",\n \"th\",\n \"thead\",\n \"tr\",\n \"u\",\n \"ul\",\n ],\n allowedAttributes: {\n \"*\": [\"class\", \"data-*\", \"style\"],\n a: [\n \"href\",\n \"title\",\n { name: \"rel\", multiple: true, values: [\"noopener\", \"noreferrer\"] },\n { name: \"target\", values: [\"_blank\"] },\n ],\n details: [\"open\", \"class\", \"data-*\"],\n div: [\"class\", \"data-*\", \"style\"],\n h2: [\"id\", \"class\", \"data-*\", \"style\"],\n h3: [\"id\", \"class\", \"data-*\", \"style\"],\n h4: [\"id\", \"class\", \"data-*\", \"style\"],\n img: [\"src\", \"alt\", \"title\", \"width\", \"height\", \"loading\", \"class\", \"data-*\"],\n mark: [\"class\", \"data-*\", \"style\"],\n p: [\"class\", \"data-*\", \"style\"],\n span: [\"class\", \"data-*\", \"style\"],\n td: [\"colspan\", \"rowspan\", \"class\", \"data-*\"],\n // scope 는 표 머리행의 접근성 의미(스크린리더 열/행 연결) — 값 집합을 고정해 허용.\n th: [\n \"colspan\",\n \"rowspan\",\n \"class\",\n \"data-*\",\n { name: \"scope\", values: [\"col\", \"row\", \"colgroup\", \"rowgroup\"] },\n ],\n },\n allowedClasses: {\n \"*\": [ROOTTALE_CLASS_RE],\n },\n allowedStyles: {\n img: {\n display: [/^block$/i],\n \"margin-left\": [CSS_LENGTH_RE, /^auto$/i],\n \"margin-right\": [CSS_LENGTH_RE, /^auto$/i],\n },\n figure: {\n display: [/^block$/i],\n \"margin-left\": [CSS_LENGTH_RE, /^auto$/i],\n \"margin-right\": [CSS_LENGTH_RE, /^auto$/i],\n },\n \"*\": {\n \"aspect-ratio\": [ASPECT_RATIO_RE],\n background: [HEX_COLOR_RE],\n \"background-color\": [HEX_COLOR_RE],\n border: [CSS_BORDER_RE],\n \"border-bottom\": [CSS_BORDER_RE],\n \"border-left\": [CSS_BORDER_RE],\n \"border-right\": [CSS_BORDER_RE],\n \"border-top\": [CSS_BORDER_RE],\n \"border-collapse\": [/^(?:collapse|separate)$/i],\n color: [HEX_COLOR_RE],\n \"font-size\": [CSS_LENGTH_RE],\n \"font-weight\": [CSS_FONT_WEIGHT_RE],\n height: [CSS_LENGTH_RE, /^auto$/i],\n \"line-height\": [CSS_LINE_HEIGHT_RE],\n margin: [CSS_LENGTH_LIST_RE],\n \"margin-bottom\": [CSS_LENGTH_RE],\n \"margin-left\": [CSS_LENGTH_RE],\n \"margin-right\": [CSS_LENGTH_RE],\n \"margin-top\": [CSS_LENGTH_RE],\n \"max-width\": [CSS_LENGTH_RE, /^none$/i],\n padding: [CSS_LENGTH_LIST_RE],\n \"padding-bottom\": [CSS_LENGTH_RE],\n \"padding-left\": [CSS_LENGTH_RE],\n \"padding-right\": [CSS_LENGTH_RE],\n \"padding-top\": [CSS_LENGTH_RE],\n \"text-align\": [TEXT_ALIGN_RE],\n \"text-decoration\": [CSS_TEXT_DECORATION_RE],\n width: [CSS_LENGTH_RE, /^auto$/i],\n },\n },\n allowedSchemes: [\"http\", \"https\", \"mailto\", \"tel\"],\n allowedSchemesByTag: {\n // tag별 목록은 전역 allowedSchemes를 덮어쓴다. data만 쓰면 정상적인\n // http/https 이미지 src까지 제거되므로 이미지가 사용할 전체 scheme을 명시한다.\n img: [\"http\", \"https\", \"data\"],\n },\n allowedSchemesAppliedToAttributes: [\"href\", \"src\", \"xlink:href\"],\n allowProtocolRelative: false,\n nonTextTags: [\n \"script\",\n \"style\",\n \"textarea\",\n \"option\",\n \"iframe\",\n \"object\",\n \"embed\",\n \"svg\",\n \"math\",\n \"template\",\n ],\n transformTags: {\n \"*\": sanitizeUrlAttributes,\n },\n};\n\n/**\n * XSS sanitize — Tiptap HTML output 또는 rawHtml 의 안전 정리 (렌더 직전 적용).\n * Phase 1 acceptance: `<script>alert(1)</script>` 등 인라인 벡터가 inert.\n */\nexport function sanitizeHtml(html: string): string {\n if (!html) return html;\n return sanitizeHtmlLibrary(stripMalformedDangerousTokens(html), SANITIZE_OPTIONS);\n}\n\n/** 공개 렌더링 원장인 HTML에서 검사·검색용 텍스트를 서버에서도 동일하게 파생한다. */\nexport function htmlToPlainText(html: string): string {\n const safeHtml = sanitizeHtml(html);\n const withBlockBoundaries = safeHtml.replace(\n /<(?:br|hr)\\b[^>]*>|<\\/(?:blockquote|details|div|figcaption|figure|h[2-4]|li|p|pre|section|summary|table|tbody|td|th|tr)>/gi,\n \" \",\n );\n return sanitizeHtmlLibrary(withBlockBoundaries, {\n allowedTags: [],\n allowedAttributes: {},\n })\n .replace(/\\u00a0/g, \" \")\n .replace(/\\s+/g, \" \")\n .trim();\n}\n\n/**\n * 표준 CSP 헤더 값 (Phase 1 acceptance test #7 정합).\n * Astro middleware / Next.js header config 에서 사용.\n */\nexport const PHASE_1_CSP =\n \"default-src 'self'; \" +\n \"script-src 'self'; \" +\n \"style-src 'self' 'unsafe-inline'; \" +\n \"img-src 'self' data: https://imagedelivery.net https://*.r2.cloudflarestorage.com; \" +\n \"font-src 'self' data:; \" +\n \"connect-src 'self'; \" +\n // core/embed allowlist (embed.ts) 의 영상 iframe — youtube-nocookie / vimeo.\n \"frame-src https://www.youtube-nocookie.com https://www.youtube.com https://player.vimeo.com; \" +\n \"frame-ancestors 'none'; \" +\n \"base-uri 'self'; \" +\n \"form-action 'self'\";\n","// ADR-0034 §2 P5 — Stable block id (uuidv7 inject)\n//\n// Tiptap 의 BlockId extension 이 본 helper 를 사용해 새 block 에 uuidv7 inject.\n// 기존 block 의 `_id` 는 보존 (revision 간 정합).\n\nimport { uuidv7 } from \"uuidv7\";\n\nimport type { Block } from \"./block\";\n\n/**\n * Block 에 stable id 주입.\n * 이미 `_id` 있는 block 은 그대로, 없으면 uuidv7 생성. recursive (innerBlocks).\n */\nexport function ensureStableIds(block: Omit<Block, \"_id\"> & { _id?: string }): Block {\n return {\n ...block,\n _id: block._id ?? uuidv7(),\n innerBlocks: (block.innerBlocks ?? []).map((inner) =>\n ensureStableIds(inner as Omit<Block, \"_id\"> & { _id?: string }),\n ),\n };\n}\n\n/**\n * 한 번에 block tree 전체에 stable id 주입.\n * import 또는 새 post 생성 시 entrypoint.\n */\nexport function injectStableIdsTree(blocks: readonly (Omit<Block, \"_id\"> & { _id?: string })[]): Block[] {\n return blocks.map((b) => ensureStableIds(b));\n}\n","/**\n * 공통 블록(Site Patterns) — 여러 글에 같은 자리로 붙는 재사용 블록의 순수 규칙.\n *\n * 블록 본문은 ROOT-ADMIN 이 `site_patterns` 에 저장하고, \"어느 자리(slot)에 어느\n * 콘텐츠 모델(`model_key`)의 글이면 어떤 블록\"인지는 배치 규칙\n * (`settings.site_pattern_placements`)으로 정한다. 유형의 단위는 콘텐츠 모델이다 —\n * 컬렉션 설정(ADR-0060)은 ADR-0105 로 폐기 중이라 규칙 키로 쓰지 않는다. 공개 API 는 글마다 `pattern_slots`(자리 → 블록 key | null) 를 계산해\n * 내려주므로 FRONT 는 규칙을 몰라도 된다. 이 파일은 api-core(계산)·tenant-admin(편집)·\n * cms-renderer-next(렌더)가 같은 판정을 공유하기 위한 프레임워크 비의존 모듈이다.\n *\n * 규칙 판정:\n * - 자리는 `SITE_PATTERN_SLOTS` 에 선언된 것만 계산한다(미선언 자리는 무시).\n * - 글의 `modelKey` 와 같은 규칙이 있으면 그 규칙(블록 없음 = null 도 명시적 결정).\n * - 없으면 `modelKey: null`(모든 글 기본) 규칙.\n * - 가리키는 블록이 발행 상태가 아니면 null(FRONT 가 미발행 블록을 그리지 않게).\n */\n\n/** 블록을 놓을 수 있는 자리. key 는 공개 API `pattern_slots` 의 키와 같다. */\nexport const SITE_PATTERN_SLOTS = [\n { key: \"post_footer\", label: \"글 하단\", description: \"글 본문 바로 아래\" },\n] as const;\n\nexport type SitePatternSlotKey = (typeof SITE_PATTERN_SLOTS)[number][\"key\"];\n\nexport const POST_FOOTER_SLOT: SitePatternSlotKey = \"post_footer\";\n\n/** 배치 규칙을 담는 `settings` 키. */\nexport const SITE_PATTERN_PLACEMENTS_KEY = \"site_pattern_placements\";\n\n/** 블록 key 형식 — 소문자·숫자·`-`·`_`, 64자 이하, 영숫자로 시작. */\nexport const SITE_PATTERN_KEY_PATTERN = /^[a-z0-9][a-z0-9_-]{0,63}$/;\n\nexport function isValidSitePatternKey(key: string): boolean {\n return SITE_PATTERN_KEY_PATTERN.test(key);\n}\n\nexport function isSitePatternSlotKey(value: string): value is SitePatternSlotKey {\n return SITE_PATTERN_SLOTS.some((slot) => slot.key === value);\n}\n\n/**\n * 배치 규칙 1건. `modelKey: null` = 모든 글의 기본값, 문자열 = 그 콘텐츠 모델의 글만.\n * `patternKey: null` = 이 자리에 블록을 놓지 않는다(유형 규칙이면 기본값 덮어쓰기).\n */\nexport interface SitePatternPlacementRule {\n readonly slot: SitePatternSlotKey;\n readonly modelKey: string | null;\n readonly patternKey: string | null;\n}\n\nexport interface SitePatternPlacements {\n readonly rules: readonly SitePatternPlacementRule[];\n}\n\nexport const EMPTY_SITE_PATTERN_PLACEMENTS: SitePatternPlacements = { rules: [] };\n\n/** 글 1건의 자리별 블록 key. 선언된 모든 자리를 키로 가진다(없으면 null). */\nexport type PatternSlots = Record<string, string | null>;\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction readNullableKey(value: unknown): string | null | undefined {\n if (value === null) return null;\n if (typeof value === \"string\") {\n const trimmed = value.trim();\n return trimmed.length > 0 ? trimmed : null;\n }\n return undefined;\n}\n\n/**\n * 저장된 값(unknown) → 정규화된 배치 규칙. 형식이 틀린 항목·미선언 자리는 버리고,\n * 같은 (slot, modelKey) 는 마지막 항목이 이긴다. 이중 인코딩된 JSON 문자열도 받는다.\n * 2026-08-19 이전에 저장된 규칙은 `collectionKey` 필드를 썼다 — `modelKey` 가 없으면 그\n * 값을 읽는다(실측상 남은 규칙은 기본값 `null` 뿐이라 의미가 같다). 다시 저장하면\n * `modelKey` 로 쓰인다.\n */\nexport function readSitePatternPlacements(raw: unknown): SitePatternPlacements {\n let value = raw;\n if (typeof value === \"string\") {\n try {\n value = JSON.parse(value) as unknown;\n } catch {\n return EMPTY_SITE_PATTERN_PLACEMENTS;\n }\n }\n if (!isRecord(value) || !Array.isArray(value.rules)) {\n return EMPTY_SITE_PATTERN_PLACEMENTS;\n }\n const byTarget = new Map<string, SitePatternPlacementRule>();\n for (const item of value.rules) {\n if (!isRecord(item)) continue;\n const slot = typeof item.slot === \"string\" ? item.slot : \"\";\n if (!isSitePatternSlotKey(slot)) continue;\n const modelKey = readNullableKey(\n item.modelKey === undefined ? item.collectionKey : item.modelKey,\n );\n if (modelKey === undefined) continue;\n const patternKey = readNullableKey(item.patternKey);\n if (patternKey === undefined) continue;\n if (patternKey !== null && !isValidSitePatternKey(patternKey)) continue;\n byTarget.set(`${slot}\u0000${modelKey ?? \"\"}`, {\n slot,\n modelKey,\n patternKey,\n });\n }\n return { rules: [...byTarget.values()] };\n}\n\n/** (slot, modelKey) 에 해당하는 규칙. 없으면 undefined(= 기본값으로 위임). */\nexport function findSitePatternPlacementRule(\n placements: SitePatternPlacements,\n slot: SitePatternSlotKey,\n modelKey: string | null,\n): SitePatternPlacementRule | undefined {\n return placements.rules.find((rule) => rule.slot === slot && rule.modelKey === modelKey);\n}\n\n/**\n * 규칙 1건을 갈아 끼운 새 배치 규칙을 돌려준다(불변). `patternKey: undefined` 는\n * \"규칙 삭제 = 기본값으로 위임\"이다 — 유형 규칙에서 \"표시 안 함\"(null)과 구분된다.\n */\nexport function setSitePatternPlacementRule(\n placements: SitePatternPlacements,\n target: { slot: SitePatternSlotKey; modelKey: string | null },\n patternKey: string | null | undefined,\n): SitePatternPlacements {\n const rest = placements.rules.filter(\n (rule) => !(rule.slot === target.slot && rule.modelKey === target.modelKey),\n );\n if (patternKey === undefined) return { rules: rest };\n return {\n rules: [...rest, { slot: target.slot, modelKey: target.modelKey, patternKey }],\n };\n}\n\n/** 선언된 자리마다 null 인 빈 결과 — 규칙이나 발행 블록이 없을 때의 기본 응답. */\nexport function emptyPatternSlots(): PatternSlots {\n return Object.fromEntries(SITE_PATTERN_SLOTS.map((slot) => [slot.key, null]));\n}\n\n/**\n * 글 1건의 자리별 블록 key 를 계산한다. 글의 콘텐츠 모델(`modelKey`) 규칙 > 모든 글 기본\n * 규칙. `publishedKeys` 에 없는 블록(미발행·삭제)은 null.\n */\nexport function resolvePatternSlots(\n post: { readonly modelKey: string | null },\n placements: SitePatternPlacements,\n publishedKeys: ReadonlySet<string>,\n): PatternSlots {\n const slots = emptyPatternSlots();\n for (const slot of SITE_PATTERN_SLOTS) {\n const specific =\n post.modelKey !== null\n ? findSitePatternPlacementRule(placements, slot.key, post.modelKey)\n : undefined;\n const rule =\n specific ?? findSitePatternPlacementRule(placements, slot.key, null);\n const key = rule?.patternKey ?? null;\n slots[slot.key] = key !== null && publishedKeys.has(key) ? key : null;\n }\n return slots;\n}\n\n/**\n * 규칙에서 참조하는 블록 key 집합 — 어드민이 \"이 블록은 어디에 쓰이는가\"를 표시하거나\n * 삭제 전 경고할 때 쓴다.\n */\nexport function referencedSitePatternKeys(\n placements: SitePatternPlacements,\n): ReadonlySet<string> {\n return new Set(\n placements.rules\n .map((rule) => rule.patternKey)\n .filter((key): key is string => key !== null),\n );\n}\n\n// ── 표시 형태(presentation) ──────────────────────────────────────────────────\n// 블록의 \"어떻게 보일지\"는 어드민이 정하고 FRONT 는 값을 받아 그리기만 한다 — 사이트 CSS 에\n// 색·모양을 박지 않는다. 값은 작고 범용적인 것만: 카드/본문, 배경색, 링크를 버튼으로, 버튼 색.\n\nexport type SitePatternLayout = \"plain\" | \"card\";\nexport type SitePatternLinkStyle = \"text\" | \"buttons\";\n\nexport interface SitePatternPresentation {\n /** `plain` = 본문처럼 흐름에 놓임, `card` = 배경색이 있는 카드. */\n readonly layout: SitePatternLayout;\n /** 카드 배경색(hex). layout 이 card 일 때만 의미. */\n readonly background: string;\n /** 마지막 문단의 링크를 버튼으로 그릴지. */\n readonly linkStyle: SitePatternLinkStyle;\n /** 버튼 배경색(hex, 순서대로 1·2·3…; 링크가 더 많으면 마지막 색을 반복). 글자는 흰색. */\n readonly buttonColors: readonly string[];\n}\n\nexport const DEFAULT_SITE_PATTERN_PRESENTATION: SitePatternPresentation = {\n layout: \"plain\",\n background: \"#f8f9fa\",\n linkStyle: \"text\",\n buttonColors: [\"#03c75a\", \"#1a1a1a\", \"#666666\"],\n};\n\nexport const SITE_PATTERN_BUTTON_COLORS_MAX = 4;\n\nconst HEX_COLOR_RE = /^#[0-9a-f]{6}$/;\n\nexport function normalizeHexColor(value: unknown): string | null {\n if (typeof value !== \"string\") return null;\n let hex = value.trim().toLowerCase();\n if (/^#[0-9a-f]{3}$/.test(hex)) hex = `#${hex[1]}${hex[1]}${hex[2]}${hex[2]}${hex[3]}${hex[3]}`;\n return HEX_COLOR_RE.test(hex) ? hex : null;\n}\n\n/** 저장된 값(unknown) → 표시 형태. 틀린 값은 기본값으로 채운다(절대 throw 하지 않음). */\nexport function readSitePatternPresentation(raw: unknown): SitePatternPresentation {\n let value = raw;\n if (typeof value === \"string\") {\n try {\n value = JSON.parse(value) as unknown;\n } catch {\n value = null;\n }\n }\n const base = DEFAULT_SITE_PATTERN_PRESENTATION;\n if (!isRecord(value)) return base;\n const layout: SitePatternLayout = value.layout === \"card\" ? \"card\" : \"plain\";\n const linkStyle: SitePatternLinkStyle = value.linkStyle === \"buttons\" ? \"buttons\" : \"text\";\n const background = normalizeHexColor(value.background) ?? base.background;\n const colors = Array.isArray(value.buttonColors)\n ? value.buttonColors\n .map(normalizeHexColor)\n .filter((c): c is string => c !== null)\n .slice(0, SITE_PATTERN_BUTTON_COLORS_MAX)\n : [];\n return {\n layout,\n background,\n linkStyle,\n buttonColors: colors.length > 0 ? colors : base.buttonColors,\n };\n}\n\n/** 버튼 n번째(0부터)의 색 — 링크가 색 개수보다 많으면 마지막 색을 반복. */\nexport function sitePatternButtonColor(presentation: SitePatternPresentation, index: number): string {\n const colors = presentation.buttonColors;\n return colors[Math.min(index, colors.length - 1)] ?? DEFAULT_SITE_PATTERN_PRESENTATION.buttonColors[0]!;\n}\n\n/**\n * FRONT 가 래퍼 요소에 얹을 data 속성과 CSS 변수. 사이트 CSS 는 이 변수만 읽는다 —\n * `data-pattern-layout`·`data-pattern-link-style`, `--rt-pattern-bg`, `--rt-pattern-btn-1..n`.\n */\nexport function sitePatternPresentationAttributes(presentation: SitePatternPresentation): {\n readonly dataset: Readonly<Record<string, string>>;\n readonly cssVariables: Readonly<Record<string, string>>;\n} {\n const cssVariables: Record<string, string> = { \"--rt-pattern-bg\": presentation.background };\n presentation.buttonColors.forEach((color, index) => {\n cssVariables[`--rt-pattern-btn-${index + 1}`] = color;\n });\n return {\n dataset: {\n \"data-pattern-layout\": presentation.layout,\n \"data-pattern-link-style\": presentation.linkStyle,\n },\n cssVariables,\n };\n}\n","/** 편집기와 공개 렌더러가 공유하는 허용 서식. 기본값은 사이트 CSS를 따른다. */\nexport const CMS_FONT_SIZES = [14, 16, 18, 20, 24, 28, 32] as const;\nexport const CMS_LINE_HEIGHTS = [1.2, 1.5, 1.8, 2, 2.4] as const;\nexport const CMS_PARAGRAPH_SPACINGS = [0, 8, 16, 24, 32] as const;\nexport const CMS_IMAGE_WIDTHS = [25, 50, 75, 100] as const;\n\nexport function cmsFontSize(value: unknown): string | null {\n if (typeof value !== \"string\") return null;\n return CMS_FONT_SIZES.some((size) => value === `${size}px`) ? value : null;\n}\nexport function cmsLineHeight(value: unknown): number | null {\n if (typeof value !== \"number\" && typeof value !== \"string\") return null;\n return CMS_LINE_HEIGHTS.find((size) => String(size) === String(value)) ?? null;\n}\nexport function cmsParagraphSpacing(value: unknown): string | null {\n if (typeof value !== \"string\") return null;\n return CMS_PARAGRAPH_SPACINGS.some((size) => value === `${size}px`) ? value : null;\n}\nexport function cmsImageWidth(value: unknown): string | null {\n if (typeof value !== \"string\") return null;\n return CMS_IMAGE_WIDTHS.some((size) => value === `${size}%`) ? value : null;\n}\nexport function cmsImageAlign(value: unknown): \"left\" | \"center\" | \"right\" | null {\n return value === \"left\" || value === \"center\" || value === \"right\" ? value : null;\n}\nexport function cmsImageStyle(attrs: Record<string, unknown>) {\n const width = cmsImageWidth(attrs.displayWidth);\n const align = cmsImageAlign(attrs.imageAlign);\n return {\n ...(width ? { width, maxWidth: \"100%\", height: \"auto\" } : {}),\n ...(align ? {\n display: \"block\",\n marginLeft: align === \"left\" ? \"0\" : \"auto\",\n marginRight: align === \"right\" ? \"0\" : \"auto\",\n } : {}),\n };\n}\n","export interface SectionLock {\n move?: boolean;\n remove?: boolean;\n structure?: boolean;\n}\n\ninterface SectionNodeSnapshot {\n id: string;\n path: string;\n sectionType: string;\n attrs: Record<string, unknown>;\n props: unknown;\n}\n\nexport type SectionLockViolationCode =\n | \"locked_section_inserted\"\n | \"locked_section_removed\"\n | \"locked_section_moved\"\n | \"locked_section_lock_changed\"\n | \"locked_section_structure_changed\";\n\nexport interface SectionLockViolation {\n code: SectionLockViolationCode;\n sectionId: string;\n sectionType: string;\n field: string;\n}\n\nconst CONTENT_PROP_PATHS: Readonly<\n Record<string, readonly (readonly (string | number)[])[]>\n> = {\n hero: [\n [\"eyebrow\"],\n [\"title\"],\n [\"description\"],\n [\"imageSrc\"],\n [\"imageAlt\"],\n [\"cta\", \"label\"],\n [\"cta\", \"href\"],\n ],\n \"rich-text\": [[\"heading\"], [\"body\"]],\n services: [\n [\"heading\"],\n [\"description\"],\n [\"items\", Number.NaN, \"title\"],\n [\"items\", Number.NaN, \"description\"],\n [\"items\", Number.NaN, \"href\"],\n ],\n faq: [\n [\"heading\"],\n [\"items\", Number.NaN, \"question\"],\n [\"items\", Number.NaN, \"answer\"],\n ],\n \"cta-banner\": [[\"title\"], [\"description\"], [\"cta\", \"label\"], [\"cta\", \"href\"]],\n \"blog-list\": [[\"heading\"], [\"emptyMessage\"]],\n \"lead-form\": [[\"heading\"], [\"description\"], [\"submitLabel\"]],\n trust: [\n [\"heading\"],\n [\"metrics\", Number.NaN, \"value\"],\n [\"metrics\", Number.NaN, \"unit\"],\n [\"metrics\", Number.NaN, \"label\"],\n [\"metrics\", Number.NaN, \"source\"],\n [\"credentials\", Number.NaN, \"name\"],\n [\"credentials\", Number.NaN, \"issuer\"],\n [\"credentials\", Number.NaN, \"kind\"],\n [\"credentials\", Number.NaN, \"image\"],\n [\"logos\", Number.NaN, \"src\"],\n [\"logos\", Number.NaN, \"alt\"],\n [\"logos\", Number.NaN, \"href\"],\n [\"disclaimer\"],\n ],\n contact: [\n [\"heading\"],\n [\"form\", \"enabled\"],\n [\"phone\"],\n [\"address\"],\n [\"hours\", Number.NaN, \"day\"],\n [\"hours\", Number.NaN, \"open\"],\n [\"hours\", Number.NaN, \"close\"],\n [\"hours\", Number.NaN, \"note\"],\n [\"mapEmbedUrl\"],\n ],\n expertise: [\n [\"heading\"],\n [\"description\"],\n [\"items\", Number.NaN, \"label\"],\n [\"items\", Number.NaN, \"kicker\"],\n [\"items\", Number.NaN, \"title\"],\n [\"items\", Number.NaN, \"body\"],\n [\"items\", Number.NaN, \"imageSrc\"],\n [\"items\", Number.NaN, \"imageAlt\"],\n [\"items\", Number.NaN, \"tags\", Number.NaN],\n [\"items\", Number.NaN, \"href\"],\n [\"items\", Number.NaN, \"ctaLabel\"],\n ],\n spaces: [\n [\"heading\"],\n [\"description\"],\n [\"spaces\", Number.NaN, \"label\"],\n [\"spaces\", Number.NaN, \"kicker\"],\n [\"spaces\", Number.NaN, \"title\"],\n [\"spaces\", Number.NaN, \"description\"],\n [\"spaces\", Number.NaN, \"imageSrc\"],\n [\"spaces\", Number.NaN, \"imageAlt\"],\n [\"spaces\", Number.NaN, \"tags\", Number.NaN],\n [\"disclaimer\"],\n ],\n concerns: [\n [\"heading\"],\n [\"description\"],\n [\"figureImage\"],\n [\"figureAlt\"],\n [\"regions\", Number.NaN, \"label\"],\n [\"regions\", Number.NaN, \"title\"],\n [\"regions\", Number.NaN, \"description\"],\n [\"regions\", Number.NaN, \"items\", Number.NaN],\n [\"regions\", Number.NaN, \"href\"],\n [\"regions\", Number.NaN, \"ctaLabel\"],\n [\"disclaimer\"],\n ],\n};\n\nfunction valueEquals(left: unknown, right: unknown): boolean {\n return JSON.stringify(left) === JSON.stringify(right);\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return Boolean(value) && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction collectChangedPaths(\n before: unknown,\n after: unknown,\n prefix: readonly (string | number)[] = [],\n): Array<readonly (string | number)[]> {\n if (valueEquals(before, after)) return [];\n if (Array.isArray(before) && Array.isArray(after)) {\n if (before.length !== after.length) return [prefix];\n return before.flatMap((item, index) =>\n collectChangedPaths(item, after[index], [...prefix, index]),\n );\n }\n if ((isRecord(before) || before == null) && (isRecord(after) || after == null)) {\n const beforeRecord = isRecord(before) ? before : {};\n const afterRecord = isRecord(after) ? after : {};\n const keys = new Set([...Object.keys(beforeRecord), ...Object.keys(afterRecord)]);\n if (keys.size === 0) return [prefix];\n return [...keys].flatMap((key) =>\n collectChangedPaths(beforeRecord[key], afterRecord[key], [...prefix, key]),\n );\n }\n return [prefix];\n}\n\nfunction contentPathMatches(\n pattern: readonly (string | number)[],\n path: readonly (string | number)[],\n): boolean {\n if (pattern.length !== path.length) return false;\n return pattern.every((segment, index) => {\n const actual = path[index];\n return Number.isNaN(segment)\n ? typeof actual === \"number\"\n : segment === actual;\n });\n}\n\nexport function isSectionContentPropPath(\n sectionType: string,\n path: readonly (string | number)[],\n): boolean {\n const patterns = CONTENT_PROP_PATHS[sectionType] ?? [];\n return patterns.some((pattern) => contentPathMatches(pattern, path));\n}\n\nfunction collectSectionSnapshots(doc: unknown): SectionNodeSnapshot[] {\n const sections: SectionNodeSnapshot[] = [];\n function visit(node: unknown, path: string): void {\n if (!isRecord(node)) return;\n if (node.type === \"section\") {\n const attrs = isRecord(node.attrs) ? node.attrs : {};\n sections.push({\n id: typeof attrs._id === \"string\" ? attrs._id : \"\",\n path,\n sectionType: typeof attrs.sectionType === \"string\" ? attrs.sectionType : \"\",\n attrs,\n props: attrs.props ?? {},\n });\n }\n if (Array.isArray(node.content)) {\n node.content.forEach((child, index) => visit(child, `${path}.${index}`));\n }\n }\n visit(doc, \"doc\");\n return sections;\n}\n\n/** 고객 콘텐츠 저장 경계. 기존 lock 플래그와 무관하게 제작 구조를 보호한다. */\nexport function findSectionLockViolations(\n beforeDoc: unknown,\n afterDoc: unknown,\n): SectionLockViolation[] {\n const before = collectSectionSnapshots(beforeDoc);\n const after = collectSectionSnapshots(afterDoc);\n const afterById = new Map(after.map((section) => [section.id, section]));\n const beforeById = new Map(before.map((section) => [section.id, section]));\n const violations: SectionLockViolation[] = [];\n\n // ID 누락·중복은 동일 섹션 비교를 우회할 수 있으므로 실패로 처리한다.\n for (const sections of [before, after]) {\n const ids = new Set<string>();\n for (const section of sections) {\n if (!section.id || ids.has(section.id)) {\n violations.push({\n code: \"locked_section_structure_changed\",\n sectionId: section.id,\n sectionType: section.sectionType,\n field: \"_id\",\n });\n }\n ids.add(section.id);\n }\n }\n\n for (const section of before) {\n const next = afterById.get(section.id);\n if (!next) {\n violations.push({\n code: \"locked_section_removed\",\n sectionId: section.id,\n sectionType: section.sectionType,\n field: \"(node)\",\n });\n continue;\n }\n\n if (section.path !== next.path) {\n violations.push({\n code: \"locked_section_moved\",\n sectionId: section.id,\n sectionType: section.sectionType,\n field: \"(index)\",\n });\n }\n\n const fields = new Set([\n ...Object.keys(section.attrs),\n ...Object.keys(next.attrs),\n ]);\n fields.delete(\"props\");\n for (const field of fields) {\n if (!valueEquals(section.attrs[field], next.attrs[field])) {\n violations.push({\n code: field === \"lock\"\n ? \"locked_section_lock_changed\"\n : \"locked_section_structure_changed\",\n sectionId: section.id,\n sectionType: section.sectionType,\n field,\n });\n }\n }\n\n for (const path of collectChangedPaths(section.props, next.props)) {\n if (!isSectionContentPropPath(section.sectionType, path)) {\n violations.push({\n code: \"locked_section_structure_changed\",\n sectionId: section.id,\n sectionType: section.sectionType,\n field: `props.${path.join(\".\") || \"(root)\"}`,\n });\n }\n }\n }\n\n for (const section of after) {\n if (beforeById.has(section.id)) continue;\n violations.push({\n code: \"locked_section_inserted\",\n sectionId: section.id,\n sectionType: section.sectionType,\n field: \"(node)\",\n });\n }\n\n return violations;\n}\n\nexport function formatSectionLockViolations(\n violations: readonly SectionLockViolation[],\n): string {\n return violations\n .map((violation) => {\n const label = `${violation.sectionType || \"section\"} 섹션(${violation.sectionId})`;\n if (violation.code === \"locked_section_inserted\") return `${label} 추가`;\n if (violation.code === \"locked_section_removed\") return `${label} 삭제`;\n if (violation.code === \"locked_section_moved\") return `${label} 이동`;\n if (violation.code === \"locked_section_lock_changed\") {\n return `${label} 잠금 변경`;\n }\n return `${label} 구조 변경(${violation.field})`;\n })\n .join(\" / \");\n}\n","import type { MetadataRobotsPolicy, MetadataValues } from \"./metadata-contract\";\n\n/** 같은 필드의 우선순위: 직접 설정 → 콘텐츠/코드 기본값 → 사이트 기본값 → 코드 fallback. */\nexport interface MetadataResolutionInput {\n overrides?: MetadataValues | null;\n defaults?: MetadataValues | null;\n siteDefaults?: MetadataValues | null;\n fallback?: MetadataValues | null;\n robots?: MetadataRobotsPolicy;\n /** 상대 이미지 경로를 절대 주소로 바꿀 기준 URL. canonical 정책에는 관여하지 않는다. */\n imageBaseUrl?: string;\n}\n\nfunction text(value: unknown): string | undefined {\n return typeof value === \"string\" && value.trim() ? value.trim() : undefined;\n}\n\nfunction imageUrl(value: unknown, base?: string): string | undefined {\n const candidate = text(value);\n if (!candidate || candidate.startsWith(\"//\") || /[\\\\\\u0000-\\u0020\\u007f]/u.test(candidate)) return undefined;\n if (candidate.startsWith(\"/\") && !base) return candidate;\n try {\n const url = new URL(candidate, base);\n if (![\"http:\", \"https:\"].includes(url.protocol) || url.username || url.password) return undefined;\n return url.href;\n } catch {\n return undefined;\n }\n}\n\n/** 글·분류·코드 경로가 공유하는 순수 해석기. false는 상속을 덮는 명시값이다. */\nexport function resolveMetadata(input: MetadataResolutionInput): MetadataValues {\n const layers = [input.overrides, input.defaults, input.siteDefaults, input.fallback];\n const result: MetadataValues = {};\n for (const field of [\"title\", \"description\", \"ogImageAlt\"] as const) {\n for (const layer of layers) {\n const value = text(layer?.[field]);\n if (value !== undefined) { result[field] = value; break; }\n }\n }\n for (const layer of layers) {\n const value = imageUrl(layer?.ogImage, input.imageBaseUrl);\n if (value !== undefined) { result.ogImage = value; break; }\n }\n for (const field of [\"noindex\", \"nofollow\"] as const) {\n for (const layer of layers) {\n const value = layer?.[field];\n if (typeof value === \"boolean\") { result[field] = value; break; }\n }\n }\n if (input.robots?.forceNoindex) result.noindex = true;\n if (input.robots?.forceNofollow) result.nofollow = true;\n return result;\n}\n","import { z } from \"zod\";\n\nexport const METADATA_FIELDS = [\"title\", \"description\", \"ogImage\", \"ogImageAlt\", \"noindex\", \"nofollow\"] as const;\nexport const MetadataFieldSchema = z.enum(METADATA_FIELDS);\nexport type MetadataField = z.infer<typeof MetadataFieldSchema>;\nexport const MetadataTargetKindSchema = z.enum([\"route\", \"post\", \"term\"]);\nexport type MetadataTargetKind = z.infer<typeof MetadataTargetKindSchema>;\n\nconst textValue = (max: number) => z.string().trim().min(1).max(max);\nconst imageValue = textValue(2048).refine((value) => {\n if (/[\\\\\\u0000-\\u0020\\u007f]/u.test(value) || value.startsWith(\"//\")) return false;\n if (value.startsWith(\"/\")) return true;\n try {\n const url = new URL(value);\n return [\"http:\", \"https:\"].includes(url.protocol) && !url.username && !url.password;\n } catch { return false; }\n}, \"공유 이미지에는 사이트 내부 경로나 HTTP/HTTPS 주소를 입력해 주세요\");\n\nconst metadataFields = {\n // Existing content titles (500) plus a site title template (400) remain valid automatic values.\n title: textValue(1000),\n description: textValue(2000),\n ogImage: imageValue,\n // Automatic alt text may use the complete automatic title and its site suffix.\n ogImageAlt: textValue(1000),\n noindex: z.boolean(),\n nofollow: z.boolean(),\n};\n\n/** Empty/omitted values inherit defaults. Canonical remains owned by site routing. */\nexport const MetadataValuesSchema = z.object(metadataFields).partial().strict();\nexport type MetadataValues = z.infer<typeof MetadataValuesSchema>;\nexport const MetadataPatchSchema = z.object({\n title: textValue(300).nullable().optional(),\n description: metadataFields.description.nullable().optional(),\n ogImage: metadataFields.ogImage.nullable().optional(),\n ogImageAlt: textValue(500).nullable().optional(),\n noindex: metadataFields.noindex.nullable().optional(),\n nofollow: metadataFields.nofollow.nullable().optional(),\n}).strict();\nexport type MetadataPatch = z.infer<typeof MetadataPatchSchema>;\n\nexport const MetadataRobotsPolicySchema = z.object({\n forceNoindex: z.boolean().optional(),\n forceNofollow: z.boolean().optional(),\n /** Stable policy reason codes. They explain an enforced exclusion, never relax it. */\n noindexReasons: z.array(z.string().trim().min(1).max(200)).max(64).optional(),\n}).strict();\nexport type MetadataRobotsPolicy = z.infer<typeof MetadataRobotsPolicySchema>;\n\nexport interface MetadataTarget {\n readonly kind: MetadataTargetKind;\n readonly key: string;\n readonly locale: string;\n readonly label: string;\n readonly path: string | null;\n /** Opaque CAS token. Consumers must not parse or increment it. */\n readonly version: string;\n readonly overrides: MetadataValues;\n readonly defaults: MetadataValues;\n readonly fallback?: MetadataValues;\n readonly effective: MetadataValues;\n readonly supportedFields: MetadataField[];\n readonly robots?: MetadataRobotsPolicy;\n readonly connection: \"connected\" | \"unconnected\" | \"removed\";\n}\n\nexport interface PublicRouteMetadata {\n readonly routeKey: string;\n readonly locale: string;\n readonly path: string;\n readonly version: string;\n readonly overrides: MetadataValues;\n readonly defaults: MetadataValues;\n readonly fallback?: MetadataValues;\n readonly effective: MetadataValues;\n readonly supportedFields: MetadataField[];\n readonly robots?: MetadataRobotsPolicy;\n}\n\n/** Read recognized, valid legacy fields without losing false or accepting arbitrary head HTML. */\nexport function readMetadataValues(input: unknown): MetadataValues {\n if (!input || typeof input !== \"object\" || Array.isArray(input)) return {};\n const record = input as Record<string, unknown>;\n const output: Record<string, unknown> = {};\n for (const field of METADATA_FIELDS) {\n const parsed = metadataFields[field].safeParse(record[field]);\n if (parsed.success) output[field] = parsed.data;\n }\n return MetadataValuesSchema.parse(output);\n}\n\n/** Preserve legacy namespaces (including canonical) while changing only the requested fields. */\nexport function mergeMetadataPatch(current: Record<string, unknown>, input: MetadataPatch): Record<string, unknown> {\n const patch = MetadataPatchSchema.parse(input);\n const next = { ...current };\n for (const field of METADATA_FIELDS) {\n if (patch[field] === null) delete next[field];\n else if (patch[field] !== undefined) next[field] = patch[field];\n }\n return next;\n}\n","import { z } from \"zod\";\nimport { MetadataRobotsPolicySchema, MetadataValuesSchema, readMetadataValues, type MetadataValues } from \"./metadata-contract\";\nimport { cleanMetadataText, formatMetadataText, MetadataTextRuleSchema, type MetadataTextInput } from \"./metadata-text\";\nimport { contentPolicySafeHttpsUrl } from \"./content-policy\";\nimport { MetadataVideoThumbnailRuleSchema, metadataVideoThumbnail } from \"./metadata-video-thumbnail\";\nimport { ContentPolicyFieldNameSchema } from \"./content-policy-schema\";\nimport { collectMetadataDefaultFieldRefs } from \"./metadata-default-fields\";\nexport { MetadataTextRuleSchema, metadataBodyParagraphs } from \"./metadata-text\";\nexport type { MetadataTextRule } from \"./metadata-text\";\nexport { collectMetadataDefaultFieldRefs } from \"./metadata-default-fields\";\nexport { metadataVideoThumbnail } from \"./metadata-video-thumbnail\";\n\nconst imageOrigin = z.string().max(2048).refine((value) => {\n try { const url = new URL(value); return url.protocol === \"https:\" && url.origin === value; }\n catch { return false; }\n}, \"이미지 허용 목록에는 경로 없는 HTTPS origin만 선언합니다\");\n\nexport const MetadataDefaultRulesSchema = z.object({\n title: MetadataTextRuleSchema.optional(),\n titleTemplateMaxLength: z.number().int().min(1).max(1000).optional(),\n description: MetadataTextRuleSchema.optional(),\n /** Code-owned defaults precede site defaults but always follow manual overrides. */\n defaults: MetadataValuesSchema.optional(),\n robots: MetadataRobotsPolicySchema.optional(),\n useFeaturedImage: z.boolean().optional(),\n videoThumbnail: MetadataVideoThumbnailRuleSchema.optional(),\n useFirstBodyImage: z.boolean().optional(),\n useTermImage: z.boolean().optional(),\n imageAltSource: z.enum([\"content\", \"title\"]).optional(),\n legacyImageSource: z.literal(\"legacy-thumbnail\").optional(),\n imageAllowedOrigins: z.array(imageOrigin).min(1).max(32).optional(),\n legacyImageAllowedOrigins: z.array(imageOrigin).min(1).max(32).optional(),\n normalizeCloudflareImages: z.boolean().optional(),\n fallback: MetadataValuesSchema.optional(),\n}).strict().refine((rule) => !rule.legacyImageSource || Boolean(rule.legacyImageAllowedOrigins?.length), \"이전 이미지 원본에는 허용 origin 선언이 필요합니다\")\n .superRefine((rule, context) => {\n if (!rule.videoThumbnail) return;\n const refs = collectMetadataDefaultFieldRefs(rule);\n if (refs.length > 64) context.addIssue({ code: z.ZodIssueCode.custom, message: \"영상 자동값은 공개 필드 64개까지 참조할 수 있습니다\" });\n for (const { name } of refs) {\n if (!ContentPolicyFieldNameSchema.safeParse(name).success) context.addIssue({ code: z.ZodIssueCode.custom,\n message: \"영상 자동값의 텍스트·조건은 안전한 공개 필드 이름을 사용합니다\" });\n }\n });\nexport type MetadataDefaultRules = z.infer<typeof MetadataDefaultRulesSchema>;\n\nexport interface MetadataDefaultsDeclaration {\n posts?: { modelKeys?: string[]; titleTemplates?: Record<string, string>; defaultRules?: Record<string, MetadataDefaultRules> };\n terms?: { modelKeys?: string[]; titleTemplates?: Record<string, string>; defaultRules?: Record<string, MetadataDefaultRules> };\n}\nexport interface MetadataDefaultsInput extends MetadataTextInput {\n kind: \"posts\" | \"terms\";\n modelKey: string;\n description?: string | null;\n featuredImage?: string | null;\n firstBodyImage?: string | null;\n legacyImage?: string | null;\n /** Current public site origin, used by the historical image adapter's origin check. */\n imageBaseUrl?: string;\n}\n\n/** Keep the customer's existing CF Images /lg behavior without rewriting unrelated origins. */\nexport function metadataContentImageUrl(value: string, normalizeCloudflareImages = false): string {\n if (!normalizeCloudflareImages) return value;\n try {\n const url = new URL(value);\n const [account, id, variant] = url.pathname.split(\"/\").filter(Boolean);\n if (url.protocol === \"https:\" && url.hostname === \"imagedelivery.net\" && account && id && variant) {\n url.pathname = `/${account}/${id}/lg`;\n return url.href;\n }\n } catch { /* Relative URLs are resolved later using the verified site origin. */ }\n return value;\n}\n\n/** Historical thumbnails have their own narrow, explicitly declared origin list. */\nfunction legacyImageUrl(value: string | null | undefined, rule?: MetadataDefaultRules): string | undefined {\n if (!value || rule?.legacyImageSource !== \"legacy-thumbnail\") return undefined;\n try {\n const url = new URL(value);\n if (!rule.legacyImageAllowedOrigins?.includes(url.origin)) return undefined;\n return readMetadataValues({ ogImage: url.href }).ogImage;\n } catch { return undefined; }\n}\n\n/** Preserve the original importer: reject its first untrusted candidate without trying the next. */\nfunction contentCandidateAllowed(value: string, input: MetadataDefaultsInput, rule?: MetadataDefaultRules): boolean {\n if (!rule?.imageAllowedOrigins) return true;\n if (value.startsWith(\"/\") && !value.startsWith(\"//\")) return true;\n try {\n const url = new URL(value);\n if (url.protocol !== \"https:\") return false;\n const origins = [...rule.imageAllowedOrigins];\n if (input.imageBaseUrl) origins.push(new URL(input.imageBaseUrl).origin);\n return origins.includes(url.origin);\n } catch { return false; }\n}\n\n/** Receives automatic source values only. User-entered SEO never passes through cleanup/truncation. */\nexport function formatMetadataDefaults(input: MetadataDefaultsInput, declaration: MetadataDefaultsDeclaration): MetadataValues {\n const dynamic = declaration[input.kind];\n const rule = dynamic?.defaultRules?.[input.modelKey];\n const name = cleanMetadataText(input.title, rule?.title);\n const title = formatMetadataText(input.title, rule?.title, input, name);\n const template = dynamic?.titleTemplates?.[input.modelKey];\n const templatedTitle = title && template ? template.replace(\"%s\", () => title) : title;\n const finalTitle = templatedTitle && rule?.titleTemplateMaxLength && templatedTitle.length > rule.titleTemplateMaxLength ? title : templatedTitle;\n const description = formatMetadataText(input.description, rule?.description, input, name);\n // The existing public body helper accepts HTTPS or a root-relative source, never HTTP.\n const bodyImage = input.firstBodyImage && (input.firstBodyImage.startsWith(\"https://\") || /^\\/(?!\\/)/u.test(input.firstBodyImage)) ? input.firstBodyImage : undefined;\n const rawFeaturedImage = (input.kind === \"terms\" ? rule?.useTermImage : rule?.useFeaturedImage) === false ? undefined : input.featuredImage;\n // This opt-in preserves the source site's safe featured -> video fallback behavior.\n const featuredImage = rule?.videoThumbnail ? contentPolicySafeHttpsUrl(rawFeaturedImage) : rawFeaturedImage;\n const candidateImage = featuredImage\n || (input.kind === \"posts\" ? metadataVideoThumbnail(rule?.videoThumbnail, input.fields) : undefined)\n || (rule?.useFirstBodyImage ? bodyImage : undefined) || legacyImageUrl(input.legacyImage, rule);\n const ogImage = candidateImage && contentCandidateAllowed(candidateImage, input, rule)\n ? readMetadataValues({ ogImage: metadataContentImageUrl(candidateImage, rule?.normalizeCloudflareImages) }).ogImage : undefined;\n return {\n ...readMetadataValues(rule?.defaults),\n ...(finalTitle ? { title: finalTitle } : {}),\n ...(description ? { description } : {}),\n ...(ogImage ? { ogImage } : {}),\n };\n}\n","import { z } from \"zod\";\n\nconst fallbackText = z.string().max(2000);\nconst templateToken = /\\{\\{(?:title|field:[a-zA-Z][a-zA-Z0-9_-]{0,159})\\}\\}/gu;\nconst template = fallbackText.refine((value) => !/[{}]/u.test(value.replace(templateToken, \"\")),\n \"자동 텍스트에는 {{title}} 또는 {{field:필드이름}} 토큰만 사용합니다\");\n\n/** A fixed set of content rules; never accept executable expressions or caller regexes. */\nexport const MetadataTextRuleSchema = z.object({\n source: z.enum([\"content\", \"fallback\", \"template\"]).optional(),\n template: template.optional(),\n stripLeadingLabel: z.enum([\"question\", \"answer\"]).optional(),\n replaceLineBreakTokens: z.boolean().optional(),\n removeEmDashes: z.boolean().optional(),\n normalizeWhitespace: z.boolean().optional(),\n maxLength: z.number().int().min(1).max(2000).optional(),\n lengthUnit: z.enum([\"code-units\", \"code-points\"]).optional(),\n wordBoundaryMinLength: z.number().int().min(1).max(2000).optional(),\n ellipsis: z.string().max(10).optional(),\n bodyFallback: z.object({\n preferContaining: z.string().trim().min(1).max(100).optional(),\n stripLeadingLabel: z.literal(\"conclusion\").optional(),\n }).strict().optional(),\n fallback: fallbackText.optional(),\n fallbackWithParent: fallbackText.optional(),\n fallbackWithoutParent: fallbackText.optional(),\n fallbackBySlug: z.record(z.string().max(200), fallbackText).optional(),\n}).strict()\n .refine((rule) => !rule.ellipsis || (rule.maxLength !== undefined && rule.ellipsis.length < rule.maxLength), \"말줄임표에는 더 긴 최대 길이가 필요합니다\")\n .refine((rule) => rule.source === \"template\" ? rule.template !== undefined : rule.template === undefined, \"텍스트 template은 source: template과 함께 선언합니다\")\n .refine((rule) => !rule.wordBoundaryMinLength || Boolean(rule.maxLength && rule.wordBoundaryMinLength < rule.maxLength), \"단어 경계에는 더 긴 최대 길이가 필요합니다\");\nexport type MetadataTextRule = z.infer<typeof MetadataTextRuleSchema>;\n\nexport interface MetadataTextInput {\n title: string;\n slug?: string;\n parentId?: string | null;\n /** Plain scalar text fields, matching public post.fields / stored metaJson.acf. */\n fields?: Record<string, unknown> | null;\n bodyParagraphs?: readonly string[] | null;\n}\n\nexport function cleanMetadataText(value: string, rule?: MetadataTextRule): string {\n let text = value.trim();\n if (rule?.stripLeadingLabel === \"question\") text = text.replace(/^\\s*Q[..:]?\\s*/iu, \"\");\n if (rule?.stripLeadingLabel === \"answer\") text = text.replace(/^\\s*A[..:]?\\s*/iu, \"\");\n if (rule?.replaceLineBreakTokens) text = text.replace(/\\[\\[br(?::[^\\]]*)?\\]\\]/giu, \" \");\n if (rule?.removeEmDashes) text = text.replaceAll(\"—\", \"\");\n if (rule?.normalizeWhitespace) text = text.replace(/\\s+/gu, \" \");\n return text.trim();\n}\n\n/** Walk only content nodes. Attributes, HTML, image alt and unrelated objects are not prose. */\nexport function metadataBodyParagraphs(value: unknown): string[] {\n const paragraphs: string[] = [];\n const textParts = (node: unknown): string[] => {\n if (Array.isArray(node)) return node.flatMap(textParts);\n if (!node || typeof node !== \"object\") return [];\n const item = node as Record<string, unknown>;\n return [...(typeof item.text === \"string\" ? [item.text] : []), ...textParts(item.content)];\n };\n const walk = (node: unknown): void => {\n if (Array.isArray(node)) { node.forEach(walk); return; }\n if (!node || typeof node !== \"object\") return;\n const item = node as Record<string, unknown>;\n if (item.type === \"paragraph\") {\n const text = textParts(item.content).join(\" \").replace(/\\s+/gu, \" \").trim();\n if (text) paragraphs.push(text);\n } else walk(item.content);\n };\n walk(value);\n return paragraphs;\n}\n\nexport function formatMetadataText(value: string | null | undefined, rule: MetadataTextRule | undefined, input: MetadataTextInput, name: string): string | undefined {\n let text = rule?.source === \"fallback\" ? \"\" : cleanMetadataText(value ?? \"\", rule);\n if (rule?.source === \"template\") {\n text = cleanMetadataText((rule.template ?? \"\").replace(templateToken, (token) => {\n if (token === \"{{title}}\") return name;\n const field = token.slice(8, -2);\n const value = Object.hasOwn(input.fields ?? {}, field) ? input.fields?.[field] : undefined;\n return typeof value === \"string\" ? value.trim() : \"\";\n }), rule);\n }\n if (!text && rule?.bodyFallback) {\n const paragraphs = input.bodyParagraphs ?? [];\n const preferred = rule.bodyFallback.preferContaining;\n text = (preferred ? paragraphs.find((paragraph) => paragraph.includes(preferred)) : undefined) ?? paragraphs[0] ?? \"\";\n if (rule.bodyFallback.stripLeadingLabel === \"conclusion\") text = text.replace(/^(먼저\\s*)?결론부터\\s*(말씀드리면|말씀드릴게요)[.!?]?\\s*/u, \"\");\n text = cleanMetadataText(text, rule);\n }\n if (!text) {\n const slugFallback = input.slug && Object.hasOwn(rule?.fallbackBySlug ?? {}, input.slug) ? rule?.fallbackBySlug?.[input.slug] : undefined;\n const fallback = input.parentId\n ? rule?.fallbackWithParent ?? rule?.fallback\n : slugFallback ?? rule?.fallbackWithoutParent ?? rule?.fallback;\n text = cleanMetadataText((fallback ?? \"\").replaceAll(\"%s\", () => name), rule);\n }\n if (!text) return undefined;\n const units = rule?.lengthUnit === \"code-points\" ? [...text] : text.split(\"\");\n if (rule?.maxLength && units.length > rule.maxLength) {\n const suffix = rule.ellipsis ?? \"\";\n const suffixLength = rule.lengthUnit === \"code-points\" ? [...suffix].length : suffix.length;\n let head = units.slice(0, rule.maxLength - suffixLength).join(\"\");\n const boundary = head.lastIndexOf(\" \");\n if (rule.wordBoundaryMinLength && boundary >= rule.wordBoundaryMinLength) head = head.slice(0, boundary);\n return suffix ? head.trimEnd() + suffix : head;\n }\n return text;\n}\n","import { z } from \"zod\";\n\n/** Public field names only: no property paths, prototype keys, or executable expressions. */\nexport const ContentPolicyFieldNameSchema = z.string().min(1).max(64)\n .regex(/^[A-Za-z_][A-Za-z0-9_]*$/)\n .refine((name) => name !== \"prototype\" && !Object.hasOwn(Object.prototype, name));\n\nexport const ContentPolicyScalarSchema = z.union([\n z.string().max(2048), z.number().finite(), z.boolean(), z.null(),\n]);\nconst contentPolicyEquality = z.object({\n field: ContentPolicyFieldNameSchema,\n equals: ContentPolicyScalarSchema,\n}).strict();\n/** Exact scalar equality, or a flat AND with an optional NOT-AND exception. */\nexport const ContentPolicyWhenSchema = z.union([\n contentPolicyEquality,\n z.object({\n all: z.array(contentPolicyEquality).min(1).max(4).optional(),\n unlessAll: z.array(contentPolicyEquality).min(1).max(4).optional(),\n }).strict().refine((when) => when.all !== undefined || when.unlessAll !== undefined,\n \"콘텐츠 조건에는 all 또는 unlessAll 그룹이 필요합니다\"),\n]);\n\nexport const ContentPolicyInternalPathSchema = z.string().min(1).max(2048).refine((path) => {\n if (!path.startsWith(\"/\") || path.startsWith(\"//\") || /[\\\\?#\\u0000-\\u0020\\u007f]/u.test(path)) return false;\n try {\n return path.split(\"/\").every((part, index) => {\n if (index === 0) return true;\n const decoded = decodeURIComponent(part);\n return decoded !== \".\" && decoded !== \"..\" && !/[\\\\/\\u0000-\\u0020\\u007f]/u.test(decoded);\n });\n } catch { return false; }\n}, \"콘텐츠 경로는 안전한 사이트 내부 경로여야 합니다\");\n\nconst field = { field: ContentPolicyFieldNameSchema };\nconst scalarChecks = [\n z.object({ ...field, op: z.literal(\"equals\"), value: ContentPolicyScalarSchema }).strict(),\n z.object({ ...field, op: z.literal(\"notEquals\"), value: ContentPolicyScalarSchema }).strict(),\n z.object({ ...field, op: z.literal(\"oneOf\"), values: z.array(ContentPolicyScalarSchema).min(1).max(16), allowEmpty: z.boolean().optional() }).strict(),\n z.object({ ...field, op: z.literal(\"text\") }).strict(),\n z.object({ ...field, op: z.literal(\"date\") }).strict(),\n z.object({ ...field, op: z.literal(\"safeHttps\") }).strict(),\n z.object({ ...field, op: z.literal(\"allowedPath\"), paths: z.array(ContentPolicyInternalPathSchema).min(1).max(512) }).strict(),\n] as const;\n\n/** Repeater children cannot contain another repeater, body check, or conditional expression. */\nexport const ContentPolicyScalarCheckSchema = z.discriminatedUnion(\"op\", scalarChecks);\nexport type ContentPolicyScalarCheck = z.infer<typeof ContentPolicyScalarCheckSchema>;\n\nconst when = { when: ContentPolicyWhenSchema.optional() };\nexport const ContentPolicyCheckSchema = z.discriminatedUnion(\"op\", [\n scalarChecks[0].extend(when), scalarChecks[1].extend(when), scalarChecks[2].extend(when),\n scalarChecks[3].extend(when), scalarChecks[4].extend(when), scalarChecks[5].extend(when), scalarChecks[6].extend(when),\n z.object({ ...field, ...when, op: z.literal(\"some\"), checks: z.array(ContentPolicyScalarCheckSchema).min(1).max(8) }).strict(),\n z.object({\n ...when, op: z.literal(\"bodyContent\"),\n allowExcerpt: z.boolean().optional(),\n allowFeaturedImage: z.boolean().optional(),\n additionalUrl: z.object({ ...field, ...when }).strict().optional(),\n }).strict(),\n z.object({ ...when, op: z.literal(\"bodyText\") }).strict(),\n]);\nexport type ContentPolicyCheck = z.infer<typeof ContentPolicyCheckSchema>;\n\n/** Flat requirements with bounded scalar conditions and at most one repeater level. */\nconst contentPolicy = z.object({\n visibility: z.array(ContentPolicyCheckSchema).max(24),\n indexing: z.array(ContentPolicyCheckSchema).max(24),\n pathSegmentFormat: z.literal(\"lower-kebab\").optional(),\n}).strict();\nexport type ContentPolicy = z.infer<typeof contentPolicy>;\nexport const ContentPolicySchema = contentPolicy.superRefine((policy, ctx) => {\n const refs = collectContentPolicyFieldRefs(policy);\n if (refs.length > 64) ctx.addIssue({ code: z.ZodIssueCode.custom, message: \"콘텐츠 정책은 공개 필드 64개까지 참조할 수 있습니다\" });\n for (const ref of refs) {\n if ((ref.children?.length ?? 0) > 8) ctx.addIssue({ code: z.ZodIssueCode.custom, message: `반복 필드 ${ref.name}의 자식 참조는 8개까지 허용합니다` });\n }\n});\n\nexport interface ContentPolicyFieldRef {\n readonly name: string;\n readonly children?: string[];\n}\n\nexport function collectContentPolicyFieldRefs(policy: ContentPolicy): ContentPolicyFieldRef[] {\n const refs = new Map<string, Set<string>>();\n const add = (name: string, children: string[] = []) => {\n const names = refs.get(name) ?? new Set<string>();\n for (const child of children) names.add(child);\n refs.set(name, names);\n };\n const addWhen = (when: ContentPolicyCheck[\"when\"]) => {\n if (!when) return;\n if (\"field\" in when) add(when.field);\n else for (const condition of [...(when.all ?? []), ...(when.unlessAll ?? [])]) add(condition.field);\n };\n for (const check of [...policy.visibility, ...policy.indexing]) {\n addWhen(check.when);\n if (\"field\" in check) add(check.field, check.op === \"some\" ? check.checks.map((child) => child.field) : []);\n if (check.op === \"bodyContent\" && check.additionalUrl) {\n add(check.additionalUrl.field);\n addWhen(check.additionalUrl.when);\n }\n }\n return [...refs].map(([name, children]) => ({ name, ...(children.size ? { children: [...children] } : {}) }));\n}\n","import type { ContentPolicyCheck } from \"./content-policy-schema\";\n\n/** Shared internal condition gate for public content and metadata defaults. */\nexport function matchesContentPolicyWhen(when: ContentPolicyCheck[\"when\"], fields: Readonly<Record<string, unknown>>): boolean {\n if (!when) return true;\n const matches = ({ field, equals }: { field: string; equals: unknown }) => {\n const value = Object.hasOwn(fields, field) ? fields[field] : undefined;\n return value === equals;\n };\n if (\"field\" in when) return matches(when);\n return (when.all?.every(matches) ?? true) && !(when.unlessAll?.every(matches) ?? false);\n}\n","import { sanitizeHtml } from \"./sanitize\";\n\nexport type PostBodySource =\n | { readonly kind: \"html\"; readonly html: string }\n | { readonly kind: \"json\" };\n\n/** Imported HTML is authoritative when sanitization leaves visible content. */\nexport function resolvePostBodySource(bodyHtml: string | null | undefined): PostBodySource {\n if (typeof bodyHtml !== \"string\") return { kind: \"json\" };\n const trimmed = bodyHtml.trim();\n if (trimmed.length === 0) return { kind: \"json\" };\n const sanitized = sanitizeHtml(trimmed);\n const visible = /<(?:img|hr|br|iframe|video|audio)\\b/i.test(sanitized)\n || sanitized.replace(/<[^>]*>/g, \"\").replace(/&nbsp;/gi, \" \").trim().length > 0;\n return visible ? { kind: \"html\", html: sanitized } : { kind: \"json\" };\n}\n","import type { ContentPolicy, ContentPolicyCheck, ContentPolicyScalarCheck } from \"./content-policy-schema\";\nimport { ContentPolicyInternalPathSchema } from \"./content-policy-schema\";\nimport { matchesContentPolicyWhen } from \"./content-policy-condition\";\nimport { readMetadataValues, type MetadataRobotsPolicy } from \"./metadata-contract\";\nimport { resolvePostBodySource } from \"./post-body-source\";\n\n/** Only the SDK's public projection belongs here; never pass private ACF as fields. */\nexport interface ContentPolicyPost {\n readonly id: string;\n readonly title: string;\n readonly slug: string;\n readonly path: string | null;\n readonly status?: string;\n readonly publishedAt?: string | null;\n readonly modelKey?: string | null;\n readonly collectionKey?: string | null;\n readonly fields?: Readonly<Record<string, unknown>> | null;\n readonly bodyJson?: unknown;\n readonly bodyHtml?: string | null;\n readonly excerpt?: string | null;\n readonly featuredImageUrl?: string | null;\n readonly metaJson?: { readonly seo?: unknown } | null;\n readonly terms?: ReadonlyArray<{ readonly id: string; readonly taxonomy: string }>;\n}\n\nexport interface ContentPolicyOptions {\n readonly now?: number;\n /** Supply the shared model resolver result, including null. The policy does not own route templates. */\n readonly expectedPath?: string | null;\n readonly robots?: MetadataRobotsPolicy;\n}\n\nexport interface ContentPolicyResult {\n readonly visible: boolean;\n readonly indexable: boolean;\n readonly robots: MetadataRobotsPolicy & { readonly noindexReasons?: string[] };\n readonly reasons: string[];\n}\n\nconst text = (value: unknown): string => typeof value === \"string\" ? value.trim() : \"\";\nconst record = (value: unknown): Record<string, unknown> | null =>\n value !== null && typeof value === \"object\" && !Array.isArray(value) ? value as Record<string, unknown> : null;\n\nfunction fieldValue(fields: Readonly<Record<string, unknown>>, field: string): unknown {\n return Object.hasOwn(fields, field) ? fields[field] : undefined;\n}\n\n/** Same strict HTTPS boundary used for public images and source links. */\nexport function contentPolicySafeHttpsUrl(value: unknown): string | null {\n if (typeof value !== \"string\" || /[\\\\\\u0000-\\u0020\\u007f]/u.test(value)) return null;\n try {\n const url = new URL(value);\n return url.protocol === \"https:\" && !url.username && !url.password ? url.href : null;\n } catch { return null; }\n}\n\nfunction dateValue(value: unknown): number | null {\n if (!value || (typeof value !== \"string\" && typeof value !== \"number\")) return null;\n if (!Number.isFinite(Date.parse(String(value)))) return null;\n const date = new Date(value).getTime();\n return Number.isFinite(date) ? date : null;\n}\n\n/** Text nodes only: an image-only JSON document is not textual content. */\nexport function contentPolicyBodyText(bodyJson: unknown): string {\n const root = record(bodyJson);\n if (root?.type !== \"doc\" || !Array.isArray(root.content)) return \"\";\n const pending: Array<{ node: unknown; depth: number }> = [{ node: root, depth: 0 }];\n const output: string[] = [];\n let count = 0;\n while (pending.length) {\n const entry = pending.pop()!;\n if (++count > 100_000 || entry.depth > 128) throw new Error(\"Content policy body exceeds traversal limits\");\n const node = record(entry.node);\n if (!node) continue;\n if (node.type === \"text\") {\n const value = text(node.text);\n if (value) output.push(value);\n } else if (Array.isArray(node.content)) {\n for (let index = node.content.length - 1; index >= 0; index--) {\n pending.push({ node: node.content[index], depth: entry.depth + 1 });\n }\n }\n }\n return output.join(\" \");\n}\n\nexport function matchesContentScalarCheck(check: ContentPolicyScalarCheck, fields: Readonly<Record<string, unknown>>): boolean {\n const value = fieldValue(fields, check.field);\n switch (check.op) {\n case \"equals\": return value === check.value;\n case \"notEquals\": return value !== check.value;\n case \"oneOf\": return (check.allowEmpty === true && !value) || check.values.some((candidate) => candidate === value);\n case \"text\": return text(value).length > 0;\n case \"date\": return dateValue(value) !== null;\n case \"safeHttps\": return contentPolicySafeHttpsUrl(value) !== null;\n case \"allowedPath\": return check.paths.includes(text(value));\n }\n}\n\ninterface BodyFacts { readonly jsonText: string; readonly html: string; readonly text: string }\nfunction bodyFacts(post: ContentPolicyPost): BodyFacts {\n const jsonText = contentPolicyBodyText(post.bodyJson);\n const source = resolvePostBodySource(post.bodyHtml);\n const html = source.kind === \"html\" ? source.html : \"\";\n return { jsonText, html, text: source.kind === \"html\" ? html.replace(/<[^>]*>/g, \"\").replace(/&nbsp;/gi, \" \").trim() : jsonText };\n}\n\nfunction matchesCheck(check: ContentPolicyCheck, post: ContentPolicyPost, fields: Readonly<Record<string, unknown>>, getBody: () => BodyFacts): boolean {\n if (!matchesContentPolicyWhen(check.when, fields)) return true;\n if (check.op === \"some\") {\n const value = fieldValue(fields, check.field);\n return Array.isArray(value) && value.some((item) => {\n const child = record(item);\n return child !== null && check.checks.every((scalar) => matchesContentScalarCheck(scalar, child));\n });\n }\n if (check.op === \"bodyText\") return Boolean(getBody().text);\n if (check.op === \"bodyContent\") {\n const body = getBody();\n return Boolean(body.jsonText || body.html\n || (check.allowExcerpt && text(post.excerpt))\n || (check.allowFeaturedImage && contentPolicySafeHttpsUrl(post.featuredImageUrl))\n || (check.additionalUrl && matchesContentPolicyWhen(check.additionalUrl.when, fields)\n && contentPolicySafeHttpsUrl(fieldValue(fields, check.additionalUrl.field))));\n }\n return matchesContentScalarCheck(check, fields);\n}\n\nfunction validPath(policy: ContentPolicy, post: ContentPolicyPost, options: ContentPolicyOptions): boolean {\n const path = text(post.path).replace(/\\/+$/, \"\");\n if (!path || !ContentPolicyInternalPathSchema.safeParse(path).success) return false;\n const segments = path.slice(1).split(\"/\");\n if (segments.some((segment) => !segment)) return false;\n if (policy.pathSegmentFormat === \"lower-kebab\" && segments.some((segment) => !/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(segment))) return false;\n if (decodeURIComponent(segments.at(-1)!) !== post.slug) return false;\n if (options.expectedPath !== undefined) {\n return options.expectedPath !== null && path === options.expectedPath.replace(/\\/+$/, \"\");\n }\n return true;\n}\n\n/**\n * Evaluate opt-in policy only. Callers supply the model's expectedPath to enforce prefixes,\n * category selection and reserved slugs; without it only the common path boundary is checked.\n */\nexport function evaluateContentPolicy(policy: ContentPolicy, post: ContentPolicyPost, options: ContentPolicyOptions = {}): ContentPolicyResult {\n const visibility: string[] = [];\n if (post.status !== \"published\") visibility.push(\"content:not-published\");\n if (!text(post.id) || !text(post.title) || !text(post.slug)) visibility.push(\"content:invalid-identity\");\n const publishedAt = dateValue(post.publishedAt);\n if (publishedAt === null) visibility.push(\"content:invalid-published-at\");\n else if (publishedAt > (options.now ?? Date.now())) visibility.push(\"content:future-publication\");\n if (!validPath(policy, post, options)) visibility.push(\"content:invalid-path\");\n\n const fields = post.fields ?? {};\n let body: BodyFacts | undefined;\n const getBody = () => body ??= bodyFacts(post);\n const failures = (checks: ContentPolicyCheck[], stage: string) => checks.flatMap((check) =>\n matchesCheck(check, post, fields, getBody) ? [] : [`content:${stage}:${check.op}${\"field\" in check ? `:${check.field}` : \"\"}`]);\n if (visibility.length === 0) visibility.push(...failures(policy.visibility, \"visibility\"));\n const visible = visibility.length === 0;\n const indexing = visible ? failures(policy.indexing, \"indexing\") : [];\n const forced = !visible || indexing.length > 0 || options.robots?.forceNoindex === true;\n const inheritedReasons = options.robots?.forceNoindex\n ? options.robots.noindexReasons?.length ? options.robots.noindexReasons : [\"content:forced-noindex\"] : [];\n const noindexReasons = [...new Set([...visibility, ...indexing, ...inheritedReasons])].slice(0, 64);\n const manualNoindex = readMetadataValues(post.metaJson?.seo).noindex === true;\n return {\n visible,\n indexable: visible && !forced && !manualNoindex,\n robots: {\n ...options.robots,\n ...(forced ? { forceNoindex: true } : {}),\n ...(noindexReasons.length ? { noindexReasons } : {}),\n },\n reasons: [...noindexReasons, ...(manualNoindex ? [\"content:manual-noindex\"] : [])],\n };\n}\n","import { z } from \"zod\";\nimport { ContentPolicyFieldNameSchema, ContentPolicyWhenSchema } from \"./content-policy-schema\";\nimport { matchesContentPolicyWhen } from \"./content-policy-condition\";\nimport { contentPolicySafeHttpsUrl } from \"./content-policy\";\n\n/** A declared public URL field; never accept a caller-provided URL template or host. */\nexport const MetadataVideoThumbnailRuleSchema = z.object({\n source: z.literal(\"youtube\"),\n field: ContentPolicyFieldNameSchema,\n when: ContentPolicyWhenSchema.optional(),\n}).strict();\n\n/** Preserve the existing YouTube parser, including its strict ID and HTTPS boundaries. */\nexport function metadataVideoThumbnail(\n rule: z.infer<typeof MetadataVideoThumbnailRuleSchema> | undefined,\n fields: Readonly<Record<string, unknown>> | null | undefined,\n): string | undefined {\n if (!rule || !matchesContentPolicyWhen(rule.when, fields ?? {})) return undefined;\n const value = fields && Object.hasOwn(fields, rule.field) ? fields[rule.field] : undefined;\n const safe = contentPolicySafeHttpsUrl(value);\n if (!safe) return undefined;\n const url = new URL(safe);\n const id = url.hostname === \"youtu.be\" ? url.pathname.slice(1)\n : [\"www.youtube.com\", \"youtube.com\", \"m.youtube.com\"].includes(url.hostname)\n ? (url.pathname === \"/watch\" ? url.searchParams.get(\"v\") : url.pathname.match(/^\\/(?:shorts|embed)\\/([^/]+)$/)?.[1]) : null;\n return /^[a-zA-Z0-9_-]{11}$/.test(id ?? \"\") ? `https://i.ytimg.com/vi/${id}/hqdefault.jpg` : undefined;\n}\n","import type { z } from \"zod\";\nimport type { ContentPolicyFieldRef } from \"./content-policy-schema\";\nimport type { MetadataVideoThumbnailRuleSchema } from \"./metadata-video-thumbnail\";\n\ninterface FieldSources {\n title?: { source?: string; template?: string };\n description?: { source?: string; template?: string };\n videoThumbnail?: z.infer<typeof MetadataVideoThumbnailRuleSchema>;\n}\n\n/** Public source fields needed by one model's automatic values, without duplicates. */\nexport function collectMetadataDefaultFieldRefs(rule: FieldSources | undefined): ContentPolicyFieldRef[] {\n const fields = new Set<string>();\n const video = rule?.videoThumbnail;\n if (video) {\n fields.add(video.field);\n if (video.when) {\n if (\"field\" in video.when) fields.add(video.when.field);\n else for (const condition of [...(video.when.all ?? []), ...(video.when.unlessAll ?? [])]) fields.add(condition.field);\n }\n }\n for (const text of [rule?.title, rule?.description]) {\n if (text?.source !== \"template\") continue;\n for (const match of (text.template ?? \"\").matchAll(/\\{\\{field:([a-zA-Z][a-zA-Z0-9_-]{0,159})\\}\\}/gu)) fields.add(match[1]!);\n }\n return [...fields].map((name) => ({ name }));\n}\n","import { z } from \"zod\";\nimport { ContentPolicyFieldNameSchema, ContentPolicyInternalPathSchema, ContentPolicyScalarSchema, type ContentPolicy, type ContentPolicyFieldRef } from \"./content-policy-schema\";\nimport { evaluateContentPolicy, matchesContentScalarCheck, type ContentPolicyOptions, type ContentPolicyPost, type ContentPolicyResult } from \"./content-policy\";\nimport type { MetadataRobotsPolicy } from \"./metadata-contract\";\n\nconst field = { field: ContentPolicyFieldNameSchema };\nexport const ContentQueryFilterSchema = z.discriminatedUnion(\"op\", [\n z.object({ ...field, op: z.literal(\"equals\"), value: ContentPolicyScalarSchema }).strict(),\n z.object({ ...field, op: z.literal(\"notEquals\"), value: ContentPolicyScalarSchema }).strict(),\n z.object({ ...field, op: z.literal(\"oneOf\"), values: z.array(ContentPolicyScalarSchema).min(1).max(16), allowEmpty: z.boolean().optional() }).strict(),\n]);\n\n/** Ordering is fixed: optional pinned=true first, publication newest first, then id ascending. */\nexport const ContentQuerySchema = z.object({\n modelKeys: z.array(z.string().min(1).max(100).regex(/^[a-z][a-z0-9_-]*$/)).min(1).max(16),\n filters: z.array(ContentQueryFilterSchema).max(8).optional(),\n scope: z.enum([\"currentTerm\", \"currentPath\"]).optional(),\n pinnedField: ContentPolicyFieldNameSchema.optional(),\n pageSize: z.number().int().min(1).max(100),\n requireItems: z.boolean().optional(),\n}).strict();\nexport type ContentQuery = z.infer<typeof ContentQuerySchema>;\n\nexport function collectContentQueryFieldRefs(query: ContentQuery): ContentPolicyFieldRef[] {\n return [...new Set([...(query.filters ?? []).map((filter) => filter.field), ...(query.pinnedField ? [query.pinnedField] : [])])]\n .map((name) => ({ name }));\n}\n\nexport interface ContentQueryCandidate<T extends ContentPolicyPost = ContentPolicyPost> {\n readonly post: T;\n readonly policy: ContentPolicyResult;\n}\n\nexport interface ContentQueryOptions {\n /** A missing or failed cursor page must throw instead of becoming an empty noindex page. */\n readonly complete: boolean;\n readonly page?: number;\n readonly path?: string | null;\n readonly termId?: string | null;\n readonly robots?: MetadataRobotsPolicy;\n}\n\nexport interface ContentQueryResult<T extends ContentPolicyPost = ContentPolicyPost> {\n readonly items: T[];\n readonly total: number;\n readonly page: number;\n readonly pageCount: number;\n readonly visible: boolean;\n readonly indexable: boolean;\n readonly robots: MetadataRobotsPolicy & { readonly noindexReasons?: string[] };\n readonly reasons: string[];\n}\n\nfunction matchesScope(query: ContentQuery, post: ContentPolicyPost, options: ContentQueryOptions): boolean {\n if (query.scope === \"currentTerm\") return post.terms?.some((term) => term.id === options.termId) === true;\n if (query.scope === \"currentPath\") {\n const path = options.path!.replace(/\\/+$/, \"\");\n return post.path?.trim().startsWith(`${path}/`) === true;\n }\n return true;\n}\n\n/** Reuse already evaluated posts across routes and terms within a request. */\nexport function evaluateContentQuery<T extends ContentPolicyPost>(query: ContentQuery, candidates: ReadonlyArray<ContentQueryCandidate<T>>, options: ContentQueryOptions): ContentQueryResult<T> {\n if (!options.complete) throw new Error(\"Content query input is incomplete\");\n const page = options.page ?? 1;\n if (!Number.isSafeInteger(page) || page < 1) throw new Error(\"Content query page must be a positive safe integer\");\n if (query.scope === \"currentTerm\" && !options.termId) throw new Error(\"Content query requires the current term id\");\n if (query.scope === \"currentPath\" && (!options.path || !ContentPolicyInternalPathSchema.safeParse(options.path).success)) throw new Error(\"Content query requires a safe current path\");\n const visible = candidates.filter(({ policy }) => policy.visible);\n // Preserve the public list boundary: ambiguous canonical paths are never arbitrarily chosen.\n const pathCounts = new Map<string | null, number>();\n const canonical = (post: ContentPolicyPost) => post.path?.trim().replace(/\\/+$/, \"\") ?? null;\n for (const { post } of visible) pathCounts.set(canonical(post), (pathCounts.get(canonical(post)) ?? 0) + 1);\n const seen = new Set<string>();\n const selected = visible.filter(({ post }) => {\n if (pathCounts.get(canonical(post)) !== 1 || seen.has(post.id)) return false;\n seen.add(post.id);\n return query.modelKeys.includes(post.modelKey ?? post.collectionKey ?? \"\") && matchesScope(query, post, options)\n && (query.filters ?? []).every((filter) => matchesContentScalarCheck(filter, post.fields ?? {}));\n }).sort((left, right) => {\n if (query.pinnedField) {\n const delta = Number(right.post.fields?.[query.pinnedField] === true) - Number(left.post.fields?.[query.pinnedField] === true);\n if (delta) return delta;\n }\n return Date.parse(right.post.publishedAt!) - Date.parse(left.post.publishedAt!) || left.post.id.localeCompare(right.post.id);\n });\n const pageCount = Math.max(1, Math.ceil(selected.length / query.pageSize));\n const current = selected.slice((page - 1) * query.pageSize, page * query.pageSize);\n const pageVisible = page <= pageCount && (!query.requireItems || selected.length > 0);\n const hasIndexable = current.some(({ policy }) => policy.indexable);\n const forced = !hasIndexable || options.robots?.forceNoindex === true;\n const reasons = [...new Set([\n ...(page > pageCount ? [\"content:invalid-page\"] : query.requireItems && !selected.length ? [\"content:empty-required-list\"] : !current.length ? [\"content:empty-page\"] : !hasIndexable ? [\"content:no-indexable-items\"] : []),\n ...(options.robots?.forceNoindex ? options.robots.noindexReasons?.length ? options.robots.noindexReasons : [\"content:forced-noindex\"] : []),\n ])].slice(0, 64);\n return {\n items: current.map(({ post }) => post), total: selected.length, page, pageCount,\n visible: pageVisible, indexable: pageVisible && !forced,\n robots: { ...options.robots, ...(forced ? { forceNoindex: true } : {}), ...(reasons.length ? { noindexReasons: reasons } : {}) },\n reasons,\n };\n}\n\n/** Convenience entry point for FRONT consumers; DB readers may reuse evaluated candidates. */\nexport function selectContentQuery<T extends ContentPolicyPost>(query: ContentQuery, posts: ReadonlyArray<T>, policies: Readonly<Record<string, ContentPolicy>>, options: ContentQueryOptions & {\n readonly now?: number;\n readonly postOptions: (post: T) => ContentPolicyOptions & { expectedPath: string | null };\n}): ContentQueryResult<T> {\n if (!options.complete) throw new Error(\"Content query input is incomplete\");\n const candidates = posts.filter((post) => query.modelKeys.includes(post.modelKey ?? post.collectionKey ?? \"\")).map((post) => {\n const modelKey = post.modelKey ?? post.collectionKey ?? \"\";\n if (!Object.hasOwn(policies, modelKey)) throw new Error(`Content query has no policy for model ${modelKey}`);\n return { post, policy: evaluateContentPolicy(policies[modelKey], post, { now: options.now, ...options.postOptions(post) }) };\n });\n return evaluateContentQuery(query, candidates, options);\n}\n","import { ContentPolicyInternalPathSchema } from \"./content-policy-schema\";\nimport { faqPageJsonLd, type FaqEntry } from \"./faq\";\n\nexport interface ContentScope {\n readonly tenantId: string;\n readonly siteId: string;\n}\n\nexport interface ContentCandidate extends ContentScope {\n readonly id: string;\n readonly modelKey: string;\n readonly status: string;\n readonly termIds: readonly string[];\n readonly title: string;\n readonly path: string | null;\n readonly publishedAt: string | null;\n readonly position?: number;\n}\n\nexport interface RelatedContentOptions {\n readonly scope: ContentScope;\n readonly sourceId?: string;\n readonly termIds: readonly string[];\n readonly models: readonly { readonly modelKey: string; readonly maxCount: number }[];\n readonly includeDescendants?: boolean;\n /** Each value contains the caller's explicitly resolved descendant IDs, not slugs or labels. */\n readonly descendantTermIds?: Readonly<Record<string, readonly string[]>>;\n}\n\nexport interface RelatedContentGroup<T extends ContentCandidate = ContentCandidate> {\n readonly modelKey: string;\n readonly items: readonly T[];\n}\n\nexport type ContentCtaMode = \"auto\" | \"manual\" | \"off\";\nexport interface ContentCtaOptions {\n readonly scope: ContentScope;\n readonly sourceId?: string;\n readonly primaryTermId?: string | null;\n readonly detailModelKeys: readonly string[];\n readonly cta: { readonly mode: ContentCtaMode; readonly targetPostId?: string | null };\n}\n\nexport type ContentCtaHiddenReason = \"disabled\" | \"manual_target_missing\" | \"manual_target_unavailable\"\n | \"primary_term_missing\" | \"auto_target_missing\" | \"auto_target_ambiguous\";\nexport type ContentCtaResult<T extends ContentCandidate = ContentCandidate> =\n | { readonly state: \"resolved\"; readonly mode: \"auto\" | \"manual\"; readonly target: T }\n | { readonly state: \"hidden\"; readonly mode: ContentCtaMode; readonly reason: ContentCtaHiddenReason };\n\nfunction assertScope(scope: ContentScope): void {\n if (!scope.tenantId.trim() || !scope.siteId.trim()) throw new Error(\"Content scope requires tenantId and siteId\");\n}\n\n/** The caller must also apply model visibility policies and publication-time checks. */\nfunction publishedCandidates<T extends ContentCandidate>(\n candidates: readonly T[], scope: ContentScope, sourceId?: string,\n): T[] {\n const seen = new Set<string>();\n return candidates.filter((candidate) => {\n if (candidate.tenantId !== scope.tenantId || candidate.siteId !== scope.siteId || candidate.id === sourceId\n || candidate.status !== \"published\" || !candidate.id.trim() || !candidate.title.trim()\n || !ContentPolicyInternalPathSchema.safeParse(candidate.path).success\n || !Number.isFinite(Date.parse(candidate.publishedAt ?? \"\")) || seen.has(candidate.id)) return false;\n seen.add(candidate.id);\n return true;\n });\n}\n\n/** Newest publication first; IDs break equal-date ties independently of input order or locale. */\nfunction latestFirst(left: ContentCandidate, right: ContentCandidate): number {\n return Date.parse(right.publishedAt!) - Date.parse(left.publishedAt!)\n || (left.id < right.id ? -1 : left.id > right.id ? 1 : 0);\n}\n\n/** Empty classifications produce empty lists. No title, path, model or content is inferred. */\nexport function selectRelatedContent<T extends ContentCandidate>(\n candidates: readonly T[], options: RelatedContentOptions,\n): RelatedContentGroup<T>[] {\n assertScope(options.scope);\n const termIds = new Set(options.termIds.filter((id) => id.trim()));\n if (options.includeDescendants) {\n for (const termId of options.termIds) {\n if (!termId.trim() || !Object.hasOwn(options.descendantTermIds ?? {}, termId)) continue;\n for (const descendantId of options.descendantTermIds![termId] ?? []) {\n if (descendantId.trim()) termIds.add(descendantId);\n }\n }\n }\n const selected = publishedCandidates(candidates, options.scope, options.sourceId)\n .filter((candidate) => candidate.termIds.some((id) => termIds.has(id)))\n .sort(latestFirst);\n return options.models.map(({ modelKey, maxCount }) => {\n if (!Number.isSafeInteger(maxCount) || maxCount < 0) throw new Error(\"Related content maxCount must be a non-negative integer\");\n return { modelKey, items: selected.filter((candidate) => candidate.modelKey === modelKey).slice(0, maxCount) };\n });\n}\n\n/** Manual selection is authoritative: an unavailable target stays hidden instead of falling back. */\nexport function resolveContentCta<T extends ContentCandidate>(\n candidates: readonly T[], options: ContentCtaOptions,\n): ContentCtaResult<T> {\n assertScope(options.scope);\n const { mode, targetPostId } = options.cta;\n const hidden = (reason: ContentCtaHiddenReason): ContentCtaResult<T> => ({ state: \"hidden\", mode, reason });\n if (mode === \"off\") return hidden(\"disabled\");\n const published = publishedCandidates(candidates, options.scope, options.sourceId);\n if (mode === \"manual\") {\n if (!targetPostId?.trim()) return hidden(\"manual_target_missing\");\n const target = published.find((candidate) => candidate.id === targetPostId);\n return target ? { state: \"resolved\", mode, target } : hidden(\"manual_target_unavailable\");\n }\n if (!options.primaryTermId?.trim()) return hidden(\"primary_term_missing\");\n const targets = published.filter((candidate) => options.detailModelKeys.includes(candidate.modelKey)\n && candidate.termIds.includes(options.primaryTermId!));\n if (targets.length === 0) return hidden(\"auto_target_missing\");\n if (targets.length > 1) return hidden(\"auto_target_ambiguous\");\n return { state: \"resolved\", mode, target: targets[0]! };\n}\n\nexport interface VisibleFaqCandidate extends FaqEntry {\n readonly id: string;\n readonly visible: boolean;\n readonly deleted?: boolean;\n}\nexport interface VisibleFaqEntry extends FaqEntry { readonly id: string }\n\nfunction plainFact(value: string): string {\n return value.replace(/\\s+/gu, \" \").trim();\n}\n\n/** Render this same selection when passing the result to FAQ structured data. */\nexport function selectVisibleFaqEntries(entries: readonly VisibleFaqCandidate[]): VisibleFaqEntry[] {\n const seen = new Set<string>();\n const selected: VisibleFaqEntry[] = [];\n for (const entry of entries) {\n const id = entry.id.trim();\n const question = plainFact(entry.question);\n const answer = plainFact(entry.answer);\n if (!entry.visible || entry.deleted || !id || !question || !answer || seen.has(id)) continue;\n seen.add(id);\n selected.push({ id, question, answer });\n }\n return selected;\n}\n\nexport function visibleFaqPageJsonLd(entries: readonly VisibleFaqCandidate[]): Record<string, unknown> | null {\n return faqPageJsonLd(selectVisibleFaqEntries(entries));\n}\n\nexport interface ProfileQualificationInput {\n readonly qualification?: string | readonly string[] | null;\n readonly alumniOf?: string | readonly string[] | null;\n}\nexport interface ProfileQualificationProperties {\n readonly hasCredential?: readonly { readonly \"@type\": \"EducationalOccupationalCredential\"; readonly name: string }[];\n readonly alumniOf?: readonly { readonly \"@type\": \"EducationalOrganization\"; readonly name: string }[];\n}\n\nfunction enteredNames(value: string | readonly string[] | null | undefined): string[] {\n const values = typeof value === \"string\" ? [value] : value ?? [];\n return [...new Set(values.map(plainFact).filter(Boolean))];\n}\n\n/** These are Person properties. Never infer a credential or school from a title, role or biography. */\nexport function profileQualificationJsonLdProperties(input: ProfileQualificationInput): ProfileQualificationProperties {\n const qualifications = enteredNames(input.qualification);\n const alumni = enteredNames(input.alumniOf);\n return {\n ...(qualifications.length ? { hasCredential: qualifications.map((name) => ({ \"@type\": \"EducationalOccupationalCredential\" as const, name })) } : {}),\n ...(alumni.length ? { alumniOf: alumni.map((name) => ({ \"@type\": \"EducationalOrganization\" as const, name })) } : {}),\n };\n}\n","import { z } from \"zod\";\n\nconst itemIdSchema = z.string().uuid();\nconst forbiddenKeys = new Set([\"__proto__\", \"prototype\", \"constructor\"]);\n\ntype PathFrame =\n | { readonly kind: \"object\"; readonly parent: Record<string, unknown>; readonly key: string }\n | { readonly kind: \"array\"; readonly parent: readonly unknown[]; readonly index: number; readonly itemId: string };\n\nfunction isContentRecord(value: unknown): value is Record<string, unknown> {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) return false;\n const prototype: unknown = Object.getPrototypeOf(value);\n return prototype === Object.prototype || prototype === null;\n}\n\nfunction validatePath(path: readonly string[]): void {\n if (!Array.isArray(path) || !path.length || path.some((key) => typeof key !== \"string\" || !key\n || forbiddenKeys.has(key) || /^\\d+$/u.test(key) || /[\\u0000-\\u001f]/u.test(key))) {\n throw new Error(\"Content path must use safe property names and stable item IDs\");\n }\n}\n\nfunction findItemIndex(items: readonly unknown[], token: string): number {\n if (!token.startsWith(\"@\") || !itemIdSchema.safeParse(token.slice(1)).success) {\n throw new Error(\"Content repeater path requires @<uuid>\");\n }\n const targetId = token.slice(1);\n const seen = new Set<string>();\n let selected = -1;\n for (const [index, item] of items.entries()) {\n if (!isContentRecord(item) || !Object.hasOwn(item, \"_id\")) throw new Error(\"Content repeater item has no stable ID\");\n const parsed = itemIdSchema.safeParse(item._id);\n if (!parsed.success) throw new Error(\"Content repeater item has an invalid ID\");\n if (seen.has(parsed.data)) throw new Error(\"Content repeater item IDs are ambiguous\");\n seen.add(parsed.data);\n if (parsed.data === targetId) selected = index;\n }\n if (selected < 0) throw new Error(\"Content repeater item was not found\");\n return selected;\n}\n\nfunction locateContentValue(values: Readonly<Record<string, unknown>>, path: readonly string[]): {\n readonly value: unknown; readonly frames: readonly PathFrame[];\n} {\n validatePath(path);\n if (!isContentRecord(values)) throw new Error(\"Content values must be a serialized object\");\n const frames: PathFrame[] = [];\n let current: unknown = values;\n for (const key of path) {\n if (Array.isArray(current)) {\n const index = findItemIndex(current, key);\n frames.push({ kind: \"array\", parent: current, index, itemId: key.slice(1) });\n current = current[index];\n } else {\n if (!isContentRecord(current) || key.startsWith(\"@\") || !Object.hasOwn(current, key)) {\n throw new Error(\"Content property was not found\");\n }\n frames.push({ kind: \"object\", parent: current, key });\n current = current[key];\n }\n }\n return { value: current, frames };\n}\n\n/**\n * Paths start at the serialized ACF root. Each array traversal requires @<uuid>.\n * Unknown properties and repeaters with missing or duplicate IDs throw; no defaults are invented.\n */\nexport function getContentValue(values: Readonly<Record<string, unknown>>, path: readonly string[]): unknown {\n return locateContentValue(values, path).value;\n}\n\n/**\n * Clone only the changed branch and preserve stable IDs. Whole-array replacement is allowed.\n * Leaf permissions and all replacement-value validation belong to the compiled content contract.\n */\nexport function setContentValue(\n values: Readonly<Record<string, unknown>>, path: readonly string[], value: unknown,\n): Record<string, unknown> {\n const { frames } = locateContentValue(values, path);\n const leaf = frames[frames.length - 1]!;\n if ((leaf.kind === \"object\" && leaf.key === \"_id\") || (leaf.kind === \"array\"\n && (!isContentRecord(value) || !Object.hasOwn(value, \"_id\") || value._id !== leaf.itemId))) {\n throw new Error(\"Content edits must preserve stable item IDs\");\n }\n let patched: unknown = value;\n for (let index = frames.length - 1; index >= 0; index--) {\n const frame = frames[index]!;\n if (frame.kind === \"array\") {\n const items = [...frame.parent];\n items[frame.index] = patched;\n patched = items;\n } else {\n patched = { ...frame.parent, [frame.key]: patched };\n }\n }\n return patched as Record<string, unknown>;\n}\n","import type { ContentScope } from \"./content-relations\";\nimport { ContentPolicyInternalPathSchema } from \"./content-policy-schema\";\n\nexport interface ContentDependencyEntity extends ContentScope {\n readonly id: string;\n readonly modelKey: string;\n readonly termIds: readonly string[];\n readonly path: string | null;\n}\n\nexport interface ContentQueryDependency {\n readonly modelKeys: readonly string[];\n /** Omitted: all content in these models. Empty: nothing. Descendants must be explicitly expanded. */\n readonly termIds?: readonly string[];\n}\n\n/** A declared public route and the content whose changes can alter its rendered output. */\nexport interface ContentPathDependency extends ContentScope {\n readonly path: string;\n readonly entityIds?: readonly string[];\n readonly queries?: readonly ContentQueryDependency[];\n readonly singletonKeys?: readonly string[];\n}\n\nexport interface ContentDependencyChange {\n readonly scope: ContentScope;\n readonly before?: ContentDependencyEntity | null;\n readonly after?: ContentDependencyEntity | null;\n /** Scoped tombstone IDs still invalidate direct/manual references when the target no longer exists. */\n readonly entityIds?: readonly string[];\n readonly singletonKeys?: readonly string[];\n}\n\nfunction inScope(value: ContentScope, scope: ContentScope): boolean {\n return value.tenantId === scope.tenantId && value.siteId === scope.siteId;\n}\n\nfunction matchesQuery(entity: ContentDependencyEntity, query: ContentQueryDependency): boolean {\n return query.modelKeys.includes(entity.modelKey)\n && (query.termIds === undefined || entity.termIds.some((id) => id.trim() && query.termIds!.includes(id)));\n}\n\n/**\n * Query edges represent related lists, category hubs and auto CTAs; entity edges represent direct\n * includes and manual CTAs. Before and after facts are both needed on classification or URL moves.\n * Do not filter snapshots by their new publication status: unpublishing must evict previous output.\n * The caller owns the complete dependency graph and persisted before snapshot; no DB reads occur here.\n */\nexport function resolveContentDependencyPaths(\n dependencies: readonly ContentPathDependency[], change: ContentDependencyChange,\n): string[] {\n const { scope } = change;\n if (!scope.tenantId.trim() || !scope.siteId.trim()) throw new Error(\"Content scope requires tenantId and siteId\");\n const snapshots = [change.before, change.after].filter((entity): entity is ContentDependencyEntity =>\n entity != null && inScope(entity, scope));\n const entityIds = new Set([...snapshots.map(({ id }) => id), ...(change.entityIds ?? [])].filter((id) => id.trim()));\n const singletonKeys = new Set((change.singletonKeys ?? []).filter((key) => key.trim()));\n const paths = new Set<string>();\n const includePath = (path: string | null): void => {\n if (path !== null && ContentPolicyInternalPathSchema.safeParse(path).success) paths.add(path);\n };\n for (const snapshot of snapshots) includePath(snapshot.path);\n for (const dependency of dependencies) {\n if (!inScope(dependency, scope)) continue;\n const direct = dependency.entityIds?.some((id) => entityIds.has(id));\n const shared = dependency.singletonKeys?.some((key) => singletonKeys.has(key));\n const queried = dependency.queries?.some((query) => snapshots.some((entity) => matchesQuery(entity, query)));\n if (direct || shared || queried) includePath(dependency.path);\n }\n return [...paths].sort();\n}\n","/** 검색 관리의 등록·검토·공개 Proxy가 공유하는 정확 경로 계약. */\nexport type NormalizedRedirectSourceResult =\n | { readonly ok: true; readonly value: string }\n | { readonly ok: false; readonly code: \"invalid_source_path\" };\n\nexport type NormalizedRedirectTargetResult =\n | { readonly ok: true; readonly value: string; readonly kind: \"internal\" | \"external\" }\n | { readonly ok: false; readonly code: \"invalid_target\" };\n\nconst CONTROL_OR_BACKSLASH = /[\\\\\\u0000-\\u001f\\u007f]/u;\nconst PROTECTED_PREFIX = /^\\/(?:api|_next|_vercel|auth|admin|login|logout|revalidate|preview|assets|static|images|fonts|\\.well-known)(?:\\/|$)/i;\nconst STATIC_EXTENSION = /\\.(?:ico|png|jpe?g|gif|svg|webp|avif|css|js|txt|xml|json|woff2?|map)$/i;\n\nfunction decoded(value: string): string | null {\n try { return decodeURIComponent(value); } catch { return null; }\n}\n\n/** 쿼리·해시를 제외하고 한글·후행 슬래시를 같은 경로로 비교한다. */\nfunction trimInternalPath(path: string): string {\n if (!path.startsWith(\"/\")) path = `/${path}`;\n if (path.length > 1) path = path.replace(/\\/+$/, \"\");\n return path || \"/\";\n}\n\nexport function normalizeRedirectPath(raw: string): string {\n return trimInternalPath((decoded(raw) ?? raw).split(/[?#]/, 1)[0] ?? \"\");\n}\n\n/** 등록 제한과 Proxy의 자체 차단은 이 함수를 공유한다. */\nexport function isProtectedRedirectPath(path: string): boolean {\n const normalized = normalizeRedirectPath(path);\n return PROTECTED_PREFIX.test(normalized) || STATIC_EXTENSION.test(normalized);\n}\n\nfunction unsafePath(path: string): boolean {\n return CONTROL_OR_BACKSLASH.test(path) || path.startsWith(\"//\") ||\n /(?:^|\\/)\\.{1,2}(?:\\/|$)/.test(path) || /[%](?:2f|5c|0[0-9a-f]|1[0-9a-f]|7f)/i.test(path);\n}\n\nexport function normalizeRedirectSource(raw: string): NormalizedRedirectSourceResult {\n const invalid = { ok: false, code: \"invalid_source_path\" } as const;\n if (CONTROL_OR_BACKSLASH.test(raw)) return invalid;\n const trimmed = raw.trim();\n if (!trimmed || /^[a-z][a-z\\d+.-]*:/i.test(trimmed) || trimmed.startsWith(\"//\")) return invalid;\n const rawPath = trimmed.split(/[?#]/, 1)[0] ?? \"\";\n const path = decoded(rawPath);\n if (!path || unsafePath(path) || /[?#*]/.test(path)) return invalid;\n const normalized = trimInternalPath(path);\n if (isProtectedRedirectPath(normalized)) return invalid;\n return { ok: true, value: normalized };\n}\n\nexport function normalizeRedirectTarget(raw: string): NormalizedRedirectTargetResult {\n const invalid = { ok: false, code: \"invalid_target\" } as const;\n const decodedRaw = decoded(raw);\n if (decodedRaw === null || CONTROL_OR_BACKSLASH.test(raw) || CONTROL_OR_BACKSLASH.test(decodedRaw)) return invalid;\n const trimmed = raw.trim();\n if (!trimmed || trimmed.startsWith(\"//\")) return invalid;\n if (trimmed.startsWith(\"/\")) {\n const suffixIndex = trimmed.search(/[?#]/);\n const rawPath = suffixIndex === -1 ? trimmed : trimmed.slice(0, suffixIndex);\n const path = decoded(rawPath);\n if (!path || unsafePath(path) || /[?#]/.test(path)) return invalid;\n const suffix = suffixIndex === -1 ? \"\" : trimmed.slice(suffixIndex);\n return { ok: true, value: `${trimInternalPath(path)}${suffix}`, kind: \"internal\" };\n }\n try {\n const url = new URL(trimmed);\n if ((url.protocol !== \"http:\" && url.protocol !== \"https:\") || url.username || url.password) return invalid;\n return { ok: true, value: url.toString(), kind: \"external\" };\n } catch { return invalid; }\n}\n"],"mappings":"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","names":["category","chain","postcss","sanitizeHtmlLibrary","text","import_zod","import_sanitize_html","ALLOWED_URL_SCHEMES","fromCodePointSafe","decoded","sanitizeHtmlLibrary","HEX_COLOR_RE","isRecord","field","field","import_zod","record","field","import_zod","import_zod","text","field","value","import_zod","when","decoded","when","field","text","field","import_zod","text","template","import_zod","field","import_zod"]}