@roottale/cms-renderer-next 0.52.0 → 0.53.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.
- package/CHANGELOG.md +53 -0
- package/README.md +33 -4
- package/dist/chunk-GVKENK6H.js +75 -0
- package/dist/chunk-GVKENK6H.js.map +1 -0
- package/dist/{chunk-U2IM6OCQ.js → chunk-HM5VP7SR.js} +1 -1
- package/dist/{chunk-U2IM6OCQ.js.map → chunk-HM5VP7SR.js.map} +1 -1
- package/dist/chunk-KSPPYX22.js +14 -0
- package/dist/chunk-KSPPYX22.js.map +1 -0
- package/dist/cms-public.css +65 -0
- package/dist/exposure-client.d.ts +9 -0
- package/dist/exposure-client.js +8 -0
- package/dist/routes.d.ts +10 -1
- package/dist/routes.js +36 -16
- package/dist/routes.js.map +1 -1
- package/dist/section-runtime.d.ts +46 -0
- package/dist/section-runtime.js +9 -0
- package/dist/server.d.ts +49 -3
- package/dist/server.js +1764 -34
- package/dist/server.js.map +1 -1
- package/package.json +8 -24
- package/dist/before-after-client.d.ts +0 -15
- package/dist/before-after-client.js +0 -8
- package/dist/chunk-27RT6B3O.js +0 -93
- package/dist/chunk-27RT6B3O.js.map +0 -1
- package/dist/chunk-2QU4JU46.js +0 -3927
- package/dist/chunk-2QU4JU46.js.map +0 -1
- package/dist/chunk-4RQ3SEDL.js +0 -106
- package/dist/chunk-4RQ3SEDL.js.map +0 -1
- package/dist/chunk-6GQCFFHU.js +0 -540
- package/dist/chunk-6GQCFFHU.js.map +0 -1
- package/dist/chunk-H46JUIHZ.js +0 -79
- package/dist/chunk-H46JUIHZ.js.map +0 -1
- package/dist/chunk-K62RR4UQ.js +0 -53
- package/dist/chunk-K62RR4UQ.js.map +0 -1
- package/dist/chunk-LEHHSI6G.js +0 -64
- package/dist/chunk-LEHHSI6G.js.map +0 -1
- package/dist/chunk-QYNEOFL7.js +0 -418
- package/dist/chunk-QYNEOFL7.js.map +0 -1
- package/dist/chunk-U5AKACPN.js +0 -113
- package/dist/chunk-U5AKACPN.js.map +0 -1
- package/dist/chunk-UM37DOIK.js +0 -961
- package/dist/chunk-UM37DOIK.js.map +0 -1
- package/dist/chunk-YLSQAT6B.js +0 -59
- package/dist/chunk-YLSQAT6B.js.map +0 -1
- package/dist/chunk-ZL2NA3QT.js +0 -313
- package/dist/chunk-ZL2NA3QT.js.map +0 -1
- package/dist/concerns-client.d.ts +0 -12
- package/dist/concerns-client.js +0 -8
- package/dist/contact-map-client.d.ts +0 -27
- package/dist/contact-map-client.js +0 -8
- package/dist/contact-map-client.js.map +0 -1
- package/dist/expertise-client.d.ts +0 -16
- package/dist/expertise-client.js +0 -9
- package/dist/expertise-client.js.map +0 -1
- package/dist/hero-carousel-client.d.ts +0 -52
- package/dist/hero-carousel-client.js +0 -9
- package/dist/hero-carousel-client.js.map +0 -1
- package/dist/seo-checks.d.ts +0 -87
- package/dist/seo-checks.js +0 -27
- package/dist/seo-checks.js.map +0 -1
- package/dist/site-config-DNpvlqA5.d.ts +0 -3961
- package/dist/spaces-client.d.ts +0 -10
- package/dist/spaces-client.js +0 -8
- package/dist/spaces-client.js.map +0 -1
- package/dist/team-expanding-stage-client.d.ts +0 -35
- package/dist/team-expanding-stage-client.js +0 -8
- package/dist/team-expanding-stage-client.js.map +0 -1
- package/dist/templates.css +0 -4145
- package/dist/templates.d.ts +0 -171
- package/dist/templates.js +0 -291
- package/dist/templates.js.map +0 -1
- package/dist/theme-css-vars-CD3jcEkp.d.ts +0 -7
- package/dist/ui-react-motion-client.d.ts +0 -76
- package/dist/ui-react-motion-client.js +0 -16
- package/dist/ui-react-motion-client.js.map +0 -1
- package/dist/validate.d.ts +0 -104
- package/dist/validate.js +0 -1204
- package/dist/validate.js.map +0 -1
- /package/dist/{before-after-client.js.map → exposure-client.js.map} +0 -0
- /package/dist/{concerns-client.js.map → section-runtime.js.map} +0 -0
package/dist/validate.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/templates/section-locks.ts","../../audit-medical/src/index.ts","../../audit-tax/src/index.ts","../src/templates/publish-gate.ts","../src/templates/validate-sections.ts"],"sourcesContent":["export interface SectionLock {\n move?: boolean;\n remove?: boolean;\n structure?: boolean;\n}\n\ninterface TiptapNodeLike {\n type?: string;\n attrs?: Record<string, unknown>;\n}\n\ninterface SectionNodeSnapshot {\n id: string;\n index: number;\n sectionType: string;\n attrs: Record<string, unknown>;\n props: unknown;\n lock: SectionLock;\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, \"x\"],\n [\"regions\", Number.NaN, \"y\"],\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 readSectionLock(value: unknown): SectionLock {\n if (!value || typeof value !== \"object\") return {};\n const input = value as Record<string, unknown>;\n return {\n ...(typeof input.move === \"boolean\" ? { move: input.move } : {}),\n ...(typeof input.remove === \"boolean\" ? { remove: input.remove } : {}),\n ...(typeof input.structure === \"boolean\"\n ? { structure: input.structure }\n : {}),\n };\n}\n\nfunction isSectionLocked(lock: SectionLock): boolean {\n return lock.move === true || lock.remove === true || lock.structure === true;\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) && isRecord(after)) {\n const keys = new Set([...Object.keys(before), ...Object.keys(after)]);\n return [...keys].flatMap((key) =>\n collectChangedPaths(before[key], after[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 content =\n doc && typeof doc === \"object\"\n ? (doc as { content?: unknown }).content\n : undefined;\n if (!Array.isArray(content)) return [];\n return content.flatMap((node, index) => {\n if (!node || typeof node !== \"object\") return [];\n const tiptapNode = node as TiptapNodeLike;\n if (tiptapNode.type !== \"section\") return [];\n const attrs = tiptapNode.attrs ?? {};\n const id = typeof attrs._id === \"string\" ? attrs._id : \"\";\n if (!id) return [];\n return [\n {\n id,\n index,\n sectionType:\n typeof attrs.sectionType === \"string\" ? attrs.sectionType : \"\",\n attrs,\n props: attrs.props ?? {},\n lock: readSectionLock(attrs.lock),\n },\n ];\n });\n}\n\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 for (const section of before) {\n const next = afterById.get(section.id);\n if (!next) {\n if (section.lock.remove === true) {\n violations.push({\n code: \"locked_section_removed\",\n sectionId: section.id,\n sectionType: section.sectionType,\n field: \"(node)\",\n });\n }\n continue;\n }\n\n if (section.lock.move === true && section.index !== next.index) {\n violations.push({\n code: \"locked_section_moved\",\n sectionId: section.id,\n sectionType: section.sectionType,\n field: \"(index)\",\n });\n }\n\n if (\n !valueEquals(section.lock, next.lock) &&\n (isSectionLocked(section.lock) || isSectionLocked(next.lock))\n ) {\n violations.push({\n code: \"locked_section_lock_changed\",\n sectionId: section.id,\n sectionType: section.sectionType,\n field: \"lock\",\n });\n }\n\n if (section.lock.structure !== true) continue;\n\n for (const field of [\"_id\", \"sectionType\", \"variant\", \"theme\"] as const) {\n if (!valueEquals(section.attrs[field], next.attrs[field])) {\n violations.push({\n code: \"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) || !isSectionLocked(section.lock)) 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","/**\n * `@roottale/audit-medical` — 의료법 §56 의료광고 lint 룰 (Phase 1 골격).\n *\n * 본 모듈은 *spec 기반 초안* — RootTale 운영자가 외부 변호사 분기별 검토\n * cycle (ADR-0012) 로 확정 후 production 적용. 현재는 admin compliance\n * dashboard 에서 *경고* 수준으로 노출.\n *\n * Input: Tiptap JSON content (tenant-admin 의 pages.contentJson 과 동일 schema).\n * Output: lint findings (severity / rule id / message / position).\n *\n * Rule ID 컨벤션: `MED-<3자리>` (외부 변호사 검토 후 확정).\n *\n * Reference:\n * - 의료법 §56 의료광고의 금지\n * - 의료법 시행령 §23 의료광고의 금지 기준\n * - 보건복지부 의료광고 사전심의 가이드라인 (https://www.mohw.go.kr)\n *\n * **DISCLAIMER**: 본 룰셋은 일반 spec 기반 초안. 실제 의료광고 적합성 판단은\n * 자율심의기구 (의료광고심의위원회) 사전심의 + 자격 있는 법률 자문 의무.\n * 본 lint 통과 != 법령 준수.\n */\n\nexport type Severity = \"critical\" | \"warning\" | \"info\";\n\nexport interface MedicalLintFinding {\n ruleId: string;\n severity: Severity;\n message: string;\n /** 매칭된 텍스트 snippet (앞뒤 20자) */\n snippet?: string;\n}\n\nexport interface MedicalLintRule {\n id: string;\n severity: Severity;\n description: string;\n /** 본문 (plain text) 매칭. 발견 시 finding 반환 */\n check: (text: string, contentJson: Record<string, unknown>) => MedicalLintFinding[];\n}\n\n/* ---------- helpers ---------- */\n\nfunction snippetFor(text: string, idx: number, term: string): string {\n const start = Math.max(0, idx - 20);\n const end = Math.min(text.length, idx + term.length + 20);\n return `…${text.slice(start, end)}…`;\n}\n\nfunction findAll(text: string, terms: readonly string[]): { term: string; idx: number }[] {\n const out: { term: string; idx: number }[] = [];\n for (const term of terms) {\n let from = 0;\n while (from <= text.length) {\n const idx = text.indexOf(term, from);\n if (idx === -1) break;\n out.push({ term, idx });\n from = idx + term.length;\n }\n }\n return out;\n}\n\nfunction ruleFromTerms(\n id: string,\n severity: Severity,\n description: string,\n terms: readonly string[],\n message: (term: string) => string,\n): MedicalLintRule {\n return {\n id,\n severity,\n description,\n check(text) {\n return findAll(text, terms).map(({ term, idx }) => ({\n ruleId: id,\n severity,\n message: message(term),\n snippet: snippetFor(text, idx, term),\n }));\n },\n };\n}\n\n/* ---------- rules (27개 — 의료법 §56 카테고리별) ---------- */\n\nconst ABSOLUTE_SUPERLATIVES = [\n \"최고\",\n \"최상\",\n \"최대\",\n \"유일한\",\n \"유일하게\",\n \"단 하나\",\n \"최초의\",\n \"독보적\",\n \"넘버원\",\n \"1위\",\n \"국내 1위\",\n \"세계 1위\",\n];\n\nconst GUARANTEE_TERMS = [\n \"100%\",\n \"100 %\",\n \"완전한\",\n \"완벽한\",\n \"확실한\",\n \"절대\",\n \"무조건\",\n \"반드시\",\n \"보장\",\n \"guarantee\",\n];\n\nconst COMPARISON_TERMS = [\n \"타 병원보다\",\n \"다른 병원보다\",\n \"타 의원보다\",\n \"타 의원과 비교\",\n \"다른 의사보다\",\n \"비교 시\",\n];\n\nconst RISK_OMISSION_TERMS = [\n \"부작용 없는\",\n \"부작용 없음\",\n \"안전한\",\n \"안심\",\n \"통증 없는\",\n \"통증 0\",\n \"위험 없는\",\n];\n\nconst CURE_PROMISE_TERMS = [\n \"완치\",\n \"치료 효과 보장\",\n \"재발 없는\",\n \"재발 0\",\n \"100% 치유\",\n \"기적의\",\n];\n\nconst PRICE_DISCOUNT_PROMOTION_TERMS = [\n \"이벤트가\",\n \"특별 할인\",\n \"할인 행사\",\n \"원 → \",\n \"원으로 할인\",\n \"파격가\",\n \"최저가\",\n];\n\nconst TESTIMONIAL_TERMS = [\"환자 후기\", \"환자 사례\", \"치료 사례\", \"전후 사진\"];\n\nconst UNAUTHORIZED_TITLE_TERMS = [\"전문병원\", \"지정병원\", \"최고의 의사\"];\n\nconst FOREIGN_PROMOTION_TERMS = [\"의료관광 전문\", \"외국인 환자 1위\"];\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction hasBeforeAfterSection(value: unknown): boolean {\n if (Array.isArray(value)) return value.some(hasBeforeAfterSection);\n if (!isRecord(value)) return false;\n if (value.type === \"before-after\") return true;\n if (value.type === \"section\") {\n const attrs = value.attrs;\n if (isRecord(attrs) && attrs.sectionType === \"before-after\") return true;\n }\n return Object.values(value).some(hasBeforeAfterSection);\n}\n\nexport const MEDICAL_RULES: readonly MedicalLintRule[] = [\n // MED-001~005: 절대표현 / 보장\n ruleFromTerms(\n \"MED-001\",\n \"critical\",\n \"절대 우위 표현 금지 (의료법 §56 ②8 과장)\",\n ABSOLUTE_SUPERLATIVES,\n (term) => `\"${term}\" 같은 절대 우위 표현은 사용할 수 없습니다.`,\n ),\n ruleFromTerms(\n \"MED-002\",\n \"critical\",\n \"치료 효과 보장 표현 금지 (의료법 §56 ②2)\",\n GUARANTEE_TERMS,\n (term) => `\"${term}\" 같은 보장성 표현은 의료광고에 사용할 수 없습니다.`,\n ),\n ruleFromTerms(\n \"MED-003\",\n \"critical\",\n \"다른 의료기관 비교 광고 금지 (의료법 §56 ②6)\",\n COMPARISON_TERMS,\n (term) => `다른 의료기관과 비교하는 표현 \"${term}\" 은 금지됩니다.`,\n ),\n ruleFromTerms(\n \"MED-004\",\n \"critical\",\n \"부작용·위험 누락 표현 금지 (의료법 §56 ②1)\",\n RISK_OMISSION_TERMS,\n (term) => `\"${term}\" 은 부작용·위험을 누락한 표현으로 분류될 수 있습니다.`,\n ),\n ruleFromTerms(\n \"MED-005\",\n \"critical\",\n \"완치·치유 보장 표현 금지 (의료법 §56 ②7)\",\n CURE_PROMISE_TERMS,\n (term) => `\"${term}\" 같은 완치·치유 보장 표현은 금지됩니다.`,\n ),\n // MED-006~010: 가격·이벤트 광고\n ruleFromTerms(\n \"MED-006\",\n \"warning\",\n \"할인·이벤트 광고 표시 (의료법 §56 ②8 환자 유인)\",\n PRICE_DISCOUNT_PROMOTION_TERMS,\n (term) => `\"${term}\" — 환자 유인 광고로 분류될 수 있어 사전심의 필요.`,\n ),\n ruleFromTerms(\n \"MED-007\",\n \"warning\",\n \"후기·사례 광고 (의료법 §56 ②4 치료 경험담)\",\n TESTIMONIAL_TERMS,\n (term) => `\"${term}\" — 치료 경험담은 자율심의기구 사전심의 후만 게재 가능.`,\n ),\n ruleFromTerms(\n \"MED-008\",\n \"critical\",\n \"미인증 명칭 사용 금지 (의료법 §56 ②10)\",\n UNAUTHORIZED_TITLE_TERMS,\n (term) =>\n `\"${term}\" — 보건복지부 지정·평가 없이 사용 금지. 인증 시 인증번호 명시 의무.`,\n ),\n ruleFromTerms(\n \"MED-009\",\n \"warning\",\n \"의료관광 광고 (의료법 §56 ② 외국인 환자 유치)\",\n FOREIGN_PROMOTION_TERMS,\n (term) =>\n `\"${term}\" — 외국인 환자 유치 광고는 의료해외진출법 추가 검토 필요.`,\n ),\n {\n id: \"MED-010\",\n severity: \"warning\",\n description: \"전문의 자격 표시 시 진료과목 명시 필요\",\n check(text) {\n if (/전문의/.test(text) && !/[가-힣]+과 전문의/.test(text)) {\n return [\n {\n ruleId: \"MED-010\",\n severity: \"warning\",\n message:\n \"전문의 표시 시 진료과목 명시 권장 (예: '내과 전문의', '피부과 전문의').\",\n },\n ];\n }\n return [];\n },\n },\n // MED-011~015: 필수 표시 누락\n {\n id: \"MED-011\",\n severity: \"info\",\n description: \"의료기관명 표시 권장\",\n check(text) {\n if (text.length > 100 && !/(의원|병원|치과|한의원|클리닉)/.test(text)) {\n return [\n {\n ruleId: \"MED-011\",\n severity: \"info\",\n message:\n \"본문에 의료기관명 (의원/병원/치과/한의원/클리닉) 표기 권장.\",\n },\n ];\n }\n return [];\n },\n },\n {\n id: \"MED-012\",\n severity: \"info\",\n description: \"의료인 이름·자격 표시 권장\",\n check(text) {\n if (\n text.length > 200 &&\n !/(원장|대표원장|의사|박사)/.test(text)\n ) {\n return [\n {\n ruleId: \"MED-012\",\n severity: \"info\",\n message: \"의료인 이름·자격 (원장/대표원장/의사 등) 표시 권장.\",\n },\n ];\n }\n return [];\n },\n },\n {\n id: \"MED-013\",\n severity: \"info\",\n description: \"사전심의 의무 광고 — 심의번호 표시\",\n check(text) {\n const promotional = /(전후|개선|효과|시술)/.test(text);\n const hasReview = /심의[ ]?번호|심의필|사전심의/.test(text);\n if (promotional && !hasReview) {\n return [\n {\n ruleId: \"MED-013\",\n severity: \"info\",\n message:\n \"시술 효과·전후 사진 광고는 자율심의 후 심의번호 표시 의무 검토.\",\n },\n ];\n }\n return [];\n },\n },\n {\n id: \"MED-014\",\n severity: \"warning\",\n description: \"신의료기술 평가 통과 여부 표시\",\n check(text) {\n if (/신의료기술|첨단의료/.test(text) && !/평가[ ]?통과|승인/.test(text)) {\n return [\n {\n ruleId: \"MED-014\",\n severity: \"warning\",\n message:\n \"신의료기술·첨단의료 표현은 보건복지부 평가 통과 여부 명시 필요.\",\n },\n ];\n }\n return [];\n },\n },\n {\n id: \"MED-015\",\n severity: \"warning\",\n description: \"임상시험 단계 미통과 표현 주의\",\n check(text) {\n if (/임상시험|연구중/.test(text) && !/평가[ ]?완료|승인/.test(text)) {\n return [\n {\n ruleId: \"MED-015\",\n severity: \"warning\",\n message: \"임상시험 단계 표현은 미승인 효과 광고로 분류될 수 있음.\",\n },\n ];\n }\n return [];\n },\n },\n // MED-016~020: 광고 매체 별\n {\n id: \"MED-016\",\n severity: \"info\",\n description: \"외부 링크 의료 정보 인용 시 출처 표기\",\n check(text) {\n if (/(논문|연구|study)/i.test(text) && !/(출처|reference|참고문헌)/i.test(text)) {\n return [\n {\n ruleId: \"MED-016\",\n severity: \"info\",\n message:\n \"논문·연구 인용 시 출처 명시 권장 (의료법 §56 ②4 객관적 근거).\",\n },\n ];\n }\n return [];\n },\n },\n ruleFromTerms(\n \"MED-017\",\n \"info\",\n \"조회수·만족도 자랑 표현 주의\",\n [\"만족도 1위\", \"방문수 1위\", \"조회수 1위\"],\n (term) =>\n `\"${term}\" — 자율심의기구 사전심의 + 객관적 근거 (조사 출처) 명시 필요.`,\n ),\n ruleFromTerms(\n \"MED-018\",\n \"warning\",\n \"특수효과 영상 사용 시 표시\",\n [\"before & after\", \"before and after\"],\n () =>\n \"전후 사진/영상 사용 시 시술 조건 (촬영 조건·개인차) 명시 의무 검토.\",\n ),\n ruleFromTerms(\n \"MED-019\",\n \"warning\",\n \"보험·실손보장 광고 (의료법 §56 ②8 환자 유인)\",\n [\"실손 보장\", \"전액 환급\", \"보험 적용\"],\n (term) => `\"${term}\" — 환자 유인 표현으로 분류될 수 있음. 사전심의 필요.`,\n ),\n ruleFromTerms(\n \"MED-020\",\n \"warning\",\n \"외국 의료인 자격 표시\",\n [\"미국 의사\", \"일본 의사\", \"MD from\"],\n (term) =>\n `\"${term}\" — 외국 자격은 국내 면허 보유 여부 명시 의무 (의료법 §27).`,\n ),\n // MED-021~025: vertical 외 보강\n ruleFromTerms(\n \"MED-021\",\n \"info\",\n \"온라인 예약 안내\",\n [\"예약 가능\", \"온라인 예약\"],\n () => \"온라인 예약은 의료법 §56 환자 유인 광고 경계 — 사전심의 검토.\",\n ),\n ruleFromTerms(\n \"MED-022\",\n \"info\",\n \"교통 편의 표현\",\n [\"주차 무료\", \"발렛 파킹\", \"셔틀 운영\"],\n (term) =>\n `\"${term}\" — 진료 외 혜택 강조는 환자 유인 광고로 분류될 수 있음.`,\n ),\n {\n id: \"MED-023\",\n severity: \"info\",\n description: \"외국어 표현 — 한국어 병기 권장\",\n check(text) {\n const ratio = (text.match(/[A-Za-z]/g)?.length ?? 0) / Math.max(text.length, 1);\n if (ratio > 0.3) {\n return [\n {\n ruleId: \"MED-023\",\n severity: \"info\",\n message:\n \"영문 비율 30% 초과 — 한국어 병기 권장 (가독성 + 의료광고 심의 가이드).\",\n },\n ];\n }\n return [];\n },\n },\n ruleFromTerms(\n \"MED-024\",\n \"critical\",\n \"비급여 진료비 명시 누락 (의료법 §45)\",\n [\"비급여 진료비 별도 문의\"],\n () =>\n \"비급여 진료비는 항목·가격 명시 의무 (의료법 §45, 비급여 진료비 고지).\",\n ),\n ruleFromTerms(\n \"MED-025\",\n \"info\",\n \"원격진료·비대면 진료 표현\",\n [\"원격진료\", \"비대면 진료\", \"온라인 진료\"],\n (term) =>\n `\"${term}\" — 현행 의료법 한시적 허용 범위 확인 + 출처 명시 권장.`,\n ),\n {\n id: \"MED-026\",\n severity: \"critical\",\n description: \"의료 사이트 치료 전후 사진 섹션 사용 금지\",\n check(_text, contentJson) {\n if (!hasBeforeAfterSection(contentJson)) return [];\n return [\n {\n ruleId: \"MED-026\",\n severity: \"critical\",\n message:\n \"의료 사이트에는 before-after 섹션을 사용할 수 없습니다. 치료 전후 사진 광고는 의료광고법상 제한됩니다.\",\n },\n ];\n },\n },\n {\n id: \"MED-027\",\n severity: \"critical\",\n description: \"환자 유인 — 진료·시술 무료 제공 표현 금지 (의료법 §27 ③)\",\n check(text) {\n const findings: MedicalLintFinding[] = [];\n const re = /(무료|공짜)\\s*(시술|수술|검진|진료|치료)/g;\n let match: RegExpExecArray | null;\n while ((match = re.exec(text)) !== null) {\n // 공익 안내 예외: 국가·공단 검진 제도 안내 (\"국가 무료검진 대상자 안내\")\n const before = text.slice(Math.max(0, match.index - 14), match.index);\n if (/(국가|국민건강보험|건강보험공단|공단|보건소)[^\\n.]{0,8}$/.test(before)) {\n continue;\n }\n findings.push({\n ruleId: \"MED-027\",\n severity: \"critical\",\n message: `\"${match[0]}\" — 본인부담금 면제·무료 진료 제공 표현은 환자 유인 행위로 금지됩니다 (의료법 §27 ③).`,\n snippet: snippetFor(text, match.index, match[0]),\n });\n }\n return findings;\n },\n },\n];\n\nexport const MEDICAL_LINT_RULESET = \"medical-2026-07-11\";\n\n/* ---------- engine ---------- */\n\ninterface TiptapNode {\n type?: string;\n text?: string;\n content?: TiptapNode[];\n}\n\nfunction extractText(doc: unknown): string {\n const out: string[] = [];\n const walk = (node: unknown): void => {\n if (!node || typeof node !== \"object\") return;\n const n = node as TiptapNode;\n if (typeof n.text === \"string\") out.push(n.text);\n if (Array.isArray(n.content)) n.content.forEach(walk);\n };\n walk(doc);\n return out.join(\"\\n\");\n}\n\nexport interface LintInput {\n title: string;\n excerpt?: string | null;\n contentJson: Record<string, unknown>;\n}\n\nexport interface LintResult {\n findings: MedicalLintFinding[];\n /** severity 별 count */\n counts: Record<Severity, number>;\n}\n\nexport function lintMedicalPage(input: LintInput): LintResult {\n const text = [\n input.title,\n input.excerpt ?? \"\",\n extractText(input.contentJson),\n ].join(\"\\n\");\n\n const findings: MedicalLintFinding[] = [];\n for (const rule of MEDICAL_RULES) {\n findings.push(...rule.check(text, input.contentJson));\n }\n const counts: Record<Severity, number> = {\n critical: 0,\n warning: 0,\n info: 0,\n };\n for (const f of findings) counts[f.severity] += 1;\n return { findings, counts };\n}\n","/**\n * `@roottale/audit-tax` — 세무사법 광고 lint 룰 (Phase 1 골격).\n *\n * Phase 1 = spec 기반 초안. 한국세무사회 광고 가이드 + 세무사법 정합. 외부\n * 변호사 검토 cycle (ADR-0012) 후 production 적용.\n *\n * Rule ID 컨벤션: `TAX-<3자리>`.\n *\n * Reference (법령 정본 = roottale-docs operations/legal/tax-ad-law.md):\n * - 세무사법 제12조의7 (광고) — 2026-06-24 시행 신설 (구 표기 §10의2 는 등록 조항 오기,\n * 2026-07-11 대조 리포트로 정정)\n * - 세무사법 시행령 제33조 (광고, 2026-06-09 신설) — ① 사무소명·성명 표시 의무\n * ② 1호 §14의2 연고 선전 / 2호 평균환급액 / 4호 사무직원 오인 / 5호 결과예측\n * / 6호 무료·최저가 / 7호 국세청장 고시 (행정예고 추적 중)\n * - 한국세무사회 「세무사 광고에 관한 규정」 (2024-05-05 시행)\n * - 표시광고법 §3 (병행 적용 — 삼쩜삼 과징금 선례)\n *\n * **DISCLAIMER**: spec 기반 초안. 한국세무사회 윤리위원회 사전 검토 + 자격\n * 있는 법률 자문 의무. lint 통과 != 법령 준수.\n */\n\nexport type Severity = \"critical\" | \"warning\" | \"info\";\n\nexport interface TaxLintFinding {\n ruleId: string;\n severity: Severity;\n message: string;\n snippet?: string;\n}\n\nexport interface TaxLintRule {\n id: string;\n severity: Severity;\n description: string;\n check: (text: string) => TaxLintFinding[];\n}\n\nfunction snippetFor(text: string, idx: number, term: string): string {\n const start = Math.max(0, idx - 20);\n const end = Math.min(text.length, idx + term.length + 20);\n return `…${text.slice(start, end)}…`;\n}\n\nfunction findAll(text: string, terms: readonly string[]): { term: string; idx: number }[] {\n const out: { term: string; idx: number }[] = [];\n for (const term of terms) {\n let from = 0;\n while (from <= text.length) {\n const idx = text.indexOf(term, from);\n if (idx === -1) break;\n out.push({ term, idx });\n from = idx + term.length;\n }\n }\n return out;\n}\n\nfunction ruleFromTerms(\n id: string,\n severity: Severity,\n description: string,\n terms: readonly string[],\n message: (term: string) => string,\n): TaxLintRule {\n return {\n id,\n severity,\n description,\n check(text) {\n return findAll(text, terms).map(({ term, idx }) => ({\n ruleId: id,\n severity,\n message: message(term),\n snippet: snippetFor(text, idx, term),\n }));\n },\n };\n}\n\nconst SUPERLATIVES = [\"최고의 세무사\", \"최대 환급\", \"유일한\", \"1위 세무사\", \"넘버원\"];\nconst GUARANTEE_TERMS = [\n \"환급 보장\",\n \"100% 환급\",\n \"절세 보장\",\n \"절대 환급\",\n \"절세 100%\",\n];\nconst COMPARISON_TERMS = [\"다른 세무사보다\", \"타 세무법인보다\"];\nconst REFUND_PROMISE_TERMS = [\"환급률 1위\", \"최대 환급액 보장\", \"수십억 환급\"];\nconst PROMOTION_TERMS = [\n \"무료 상담 + 할인\",\n \"수수료 50% 할인\",\n \"수임료 면제\",\n // 시행령 6호 — 객관적 근거 없는 무료·최저가 표시 (2026-07-11)\n \"수임료 무료\",\n \"최저 수수료\",\n \"최저가\",\n];\n\nexport const TAX_RULES: readonly TaxLintRule[] = [\n ruleFromTerms(\n \"TAX-001\",\n \"critical\",\n \"절대 우위 표현 금지 (세무사법 제12조의7 ②3 과장·오도)\",\n SUPERLATIVES,\n (term) => `\"${term}\" 같은 절대 우위 표현은 광고에 사용할 수 없습니다.`,\n ),\n ruleFromTerms(\n \"TAX-002\",\n \"critical\",\n \"결과 보장 표현 금지 (세무사법 제12조의7 ②4 부당한 기대)\",\n GUARANTEE_TERMS,\n (term) => `\"${term}\" — 환급·절세 결과 보장 광고는 금지됩니다.`,\n ),\n ruleFromTerms(\n \"TAX-003\",\n \"critical\",\n \"다른 세무사 비교 광고 금지 (세무사법 제12조의7 ②5 비방·비교)\",\n COMPARISON_TERMS,\n (term) => `다른 세무사·세무법인 비교 표현 \"${term}\" 은 금지됩니다.`,\n ),\n ruleFromTerms(\n \"TAX-004\",\n \"warning\",\n \"환급액 강조 (시행령 §33②2 — 산정기준·적용대상·조건 표시 의무)\",\n REFUND_PROMISE_TERMS,\n (term) =>\n `\"${term}\" — 환급액 강조는 평균·중간값 + 출처 명시 필요 (윤리규정).`,\n ),\n ruleFromTerms(\n \"TAX-005\",\n \"warning\",\n \"할인·무료·최저가 광고 (시행령 §33②6 — 근거 없는 무료·최저가)\",\n PROMOTION_TERMS,\n (term) => `\"${term}\" — 수임료 면제·과도한 할인은 유인성 광고로 분류 가능.`,\n ),\n {\n id: \"TAX-006\",\n severity: \"info\",\n description: \"세무사 자격 명시 권장\",\n check(text) {\n if (\n text.length > 100 &&\n !/세무사|세무법인|회계사|CPA/.test(text)\n ) {\n return [\n {\n ruleId: \"TAX-006\",\n severity: \"info\",\n message: \"세무사 자격 (세무사/세무법인/회계사 등) 명시 권장.\",\n },\n ];\n }\n return [];\n },\n },\n {\n id: \"TAX-007\",\n severity: \"warning\",\n description: \"사무소명·등록 성명 표시 의무 (시행령 §33①)\",\n check(text) {\n if (text.length > 200 && !/사무소|법인|등록번호/.test(text)) {\n return [\n {\n ruleId: \"TAX-007\",\n severity: \"warning\",\n message:\n \"광고에는 사무소명·등록된 성명 표시가 의무입니다 (세무사법 시행령, 2026-06-24 시행).\",\n },\n ];\n }\n return [];\n },\n },\n ruleFromTerms(\n \"TAX-008\",\n \"warning\",\n \"후기·사례 광고 — 평균/한계 명시 필요\",\n [\"환급 사례\", \"고객 후기\", \"성공 사례\"],\n (term) =>\n `\"${term}\" — 개별 사례는 평균·한계 (개인차) 명시 필요 (윤리규정).`,\n ),\n ruleFromTerms(\n \"TAX-009\",\n \"info\",\n \"외국어 자격 표시 — 국내 자격 병기\",\n [\"US CPA\", \"AICPA\", \"Big4\"],\n (term) => `\"${term}\" — 외국 자격 표시 시 국내 세무사 자격 보유 여부 명시.`,\n ),\n {\n id: \"TAX-010\",\n severity: \"critical\",\n description: \"절대 우위 표현 — 후치형 어순 ('세무 상담 1위', TAX-001 보완)\",\n check(text) {\n // 환급 계열(\"환급률 1위\")은 TAX-004(warning, 근거·평균 명시)의 관할 — 제외.\n const re = /(세무사?|세무법인|기장|상담)[^\\n.]{0,10}?(1위|넘버원)/g;\n const findings: TaxLintFinding[] = [];\n let match: RegExpExecArray | null;\n while ((match = re.exec(text)) !== null) {\n findings.push({\n ruleId: \"TAX-010\",\n severity: \"critical\",\n message: `\"${match[0]}\" — 후치형 절대 우위 표현도 광고에 사용할 수 없습니다 (세무사법 제12조의7 ②3).`,\n snippet: snippetFor(text, match.index, match[0]),\n });\n }\n return findings;\n },\n },\n {\n id: \"TAX-011\",\n severity: \"critical\",\n description: \"세무공무원 연고 선전 금지 (세무사법 §14의2 + 시행령 §33②1)\",\n check(text) {\n // \"국세청 출신\" 자체는 합법 경력 표시 (제12조의7 ①) — 연고(인맥·연줄) 결합만 금지.\n const re = /(국세청|세무서|세무공무원|국세공무원)[^\\n.]{0,10}?(인맥|연줄|연고)/g;\n const findings: TaxLintFinding[] = [];\n let match: RegExpExecArray | null;\n while ((match = re.exec(text)) !== null) {\n findings.push({\n ruleId: \"TAX-011\",\n severity: \"critical\",\n message: `\"${match[0]}\" — 세무공무원과의 연고 등 사적 관계로 영향력을 미칠 수 있는 것처럼 선전하는 광고는 금지됩니다 (세무사법 §14의2).`,\n snippet: snippetFor(text, match.index, match[0]),\n });\n }\n return findings;\n },\n },\n ruleFromTerms(\n \"TAX-012\",\n \"critical\",\n \"행정처분·판결 등 결과 예측 광고 금지 (시행령 §33②5)\",\n [\"승소 보장\", \"인용 보장\", \"취소 보장\", \"세무조사 무마\", \"환급 확정\"],\n (term) =>\n `\"${term}\" — 행정기관 처분·법원 판결 등의 결과를 예측·확정하는 광고는 금지됩니다 (세무사법 시행령, 삼쩜삼 과징금 선례).`,\n ),\n];\n\nexport const TAX_LINT_RULESET = \"tax-2026-07-11b\";\n\n/* ---------- engine ---------- */\n\ninterface TiptapNode {\n type?: string;\n text?: string;\n content?: TiptapNode[];\n}\n\nfunction extractText(doc: unknown): string {\n const out: string[] = [];\n const walk = (node: unknown): void => {\n if (!node || typeof node !== \"object\") return;\n const n = node as TiptapNode;\n if (typeof n.text === \"string\") out.push(n.text);\n if (Array.isArray(n.content)) n.content.forEach(walk);\n };\n walk(doc);\n return out.join(\"\\n\");\n}\n\nexport interface LintInput {\n title: string;\n excerpt?: string | null;\n contentJson: Record<string, unknown>;\n}\n\nexport interface LintResult {\n findings: TaxLintFinding[];\n counts: Record<Severity, number>;\n}\n\nexport function lintTaxPage(input: LintInput): LintResult {\n const text = [\n input.title,\n input.excerpt ?? \"\",\n extractText(input.contentJson),\n ].join(\"\\n\");\n\n const findings: TaxLintFinding[] = [];\n for (const rule of TAX_RULES) {\n findings.push(...rule.check(text));\n }\n const counts: Record<Severity, number> = {\n critical: 0,\n warning: 0,\n info: 0,\n };\n for (const f of findings) counts[f.severity] += 1;\n return { findings, counts };\n}\n","import {\n MEDICAL_LINT_RULESET,\n lintMedicalPage,\n type Severity,\n} from \"@roottale/audit-medical\";\nimport { TAX_LINT_RULESET, lintTaxPage } from \"@roottale/audit-tax\";\n\nimport {\n runReadabilityChecks,\n runSeoChecks,\n type SeoCheckInput,\n type TiptapNode,\n} from \"./seo-checks.js\";\n\nexport const PUBLISH_GATE_RULESET = \"publish-gate-2026-07-02\";\n\nexport type PublishGateError = {\n readonly code: string;\n readonly message: string;\n readonly ruleId?: string;\n readonly severity?: Severity;\n readonly snippet?: string;\n};\n\nexport type PublishGateRuleset = {\n readonly publish: typeof PUBLISH_GATE_RULESET;\n readonly medical?: typeof MEDICAL_LINT_RULESET;\n readonly tax?: typeof TAX_LINT_RULESET;\n};\n\nexport type PublishGateResult = {\n readonly errors: readonly PublishGateError[];\n /** 비차단 소견 (warning·info) — 발행은 통과, meta.compliance 에 기록 (2026-07-11) */\n readonly warnings: readonly PublishGateError[];\n readonly ruleset: PublishGateRuleset;\n};\n\nexport type PublishGateDoc = {\n readonly title?: string | null;\n readonly excerpt?: string | null;\n readonly bodyJson: Record<string, unknown>;\n readonly metaJson?: Record<string, unknown> | null;\n /**\n * seo-checks 의 slug-length 체크용(선택). 미전달 시 \"\" — 항상 good 판정이라\n * 안전한 기본값(fail-open, 차단 아님).\n */\n readonly slug?: string | null;\n /**\n * seo-checks 의 featured-image 체크용(선택). 미전달 시 false — 항상 warn\n * 판정(경고일 뿐 warnings 도 아님, bad 만 승격되므로 무영향).\n */\n readonly hasFeaturedImage?: boolean;\n};\n\n/**\n * h1 모델 (W6-1) — 문서 타입별로 h1 소유자가 다르다:\n * - `\"page\"`: 본문(hero 섹션 또는 heading level=1)이 유일 h1. 렌더러가 `doc.title`\n * 을 h1 으로 그리지 않으므로 body h1Labels.length !== 1 → 차단.\n * - `\"post\"`: 렌더러(`RootTaleBlogPost`)가 `post.title` 을 h1 으로 그린다 —\n * 본문에 h1 이 있으면 실렌더 h1=2 가 되므로 body h1Labels.length !== 0 → 차단.\n * `documentType` 은 fail-closed 로 필수(optional 금지) — 신규 호출부가 실수로\n * type 분기를 빼먹으면 컴파일 에러가 나야 한다.\n */\nexport type PublishGateOptions = {\n readonly vertical: string | null | undefined;\n readonly complianceFlags?: readonly string[];\n readonly documentType: \"page\" | \"post\";\n};\n\nexport type PublishOverrideInput = {\n readonly reason: string;\n readonly userId: string;\n readonly at: string;\n};\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction recordOrEmpty(value: unknown): Record<string, unknown> {\n return isRecord(value) ? value : {};\n}\n\nfunction stringOrEmpty(value: unknown): string {\n return typeof value === \"string\" ? value.trim() : \"\";\n}\n\n/** 섹션 props 안에서 카피가 아닌 기계 필드 — snippet/lint 수집 제외 (W6-6). */\nconst NON_COPY_PROP_KEYS = new Set([\"href\", \"image\", \"url\", \"src\", \"id\", \"_id\"]);\n\nfunction collectPropsCopy(value: unknown, out: string[]): void {\n if (typeof value === \"string\") {\n out.push(value);\n return;\n }\n if (Array.isArray(value)) {\n for (const item of value) collectPropsCopy(item, out);\n return;\n }\n if (!isRecord(value)) return;\n for (const [key, child] of Object.entries(value)) {\n if (NON_COPY_PROP_KEYS.has(key)) continue;\n collectPropsCopy(child, out);\n }\n}\n\n/**\n * Tiptap doc 에서 사람이 읽는 텍스트만 구조 기반으로 수집한다 (W6-6):\n * `text` 필드 + `content` 재귀 + 섹션 노드의 `attrs.props` 카피(기계 필드 제외).\n * 과거에는 Object.values 전체를 훑어 노드명(\"paragraph\" 등)·variant·href 가\n * lint snippet 에 섞여 나왔다 — 단, 섹션 props 카피는 의료 lint 대상이므로\n * audit-medical 의 extractText(text+content만)처럼 좁히면 게이트 약화 회귀.\n */\nfunction collectText(value: unknown, out: string[]): void {\n if (Array.isArray(value)) {\n for (const item of value) collectText(item, out);\n return;\n }\n if (!isRecord(value)) return;\n if (typeof value.text === \"string\") out.push(value.text);\n if (value.type === \"section\") {\n const attrs = recordOrEmpty(value.attrs);\n collectPropsCopy(attrs.props, out);\n }\n if (Array.isArray(value.content)) collectText(value.content, out);\n}\n\n/** @internal 테스트 전용 export — 수집 계약(노이즈 0·props 카피 포함) 검증용. */\nexport function textFrom(value: unknown): string {\n const out: string[] = [];\n collectText(value, out);\n return out.join(\"\\n\");\n}\n\nfunction seoDescription(doc: PublishGateDoc): string {\n const meta = recordOrEmpty(doc.metaJson);\n const seo = recordOrEmpty(meta.seo);\n return stringOrEmpty(seo.description) || stringOrEmpty(doc.excerpt);\n}\n\n/**\n * PublishGateDoc → SeoCheckInput 변환 (W4-5 PR 1). metaJson.seo 는\n * readSeoMeta()(tenant-admin posts.ts)가 쓰는 형태와 동일 — title/description/\n * canonical/noindex. 클라이언트 SeoChecklist 와 같은 `runSeoChecks`/\n * `runReadabilityChecks` 를 실행하므로(단일소스) 같은 입력이면 같은 판정.\n */\nfunction toSeoCheckInput(doc: PublishGateDoc): SeoCheckInput {\n const meta = recordOrEmpty(doc.metaJson);\n const seo = recordOrEmpty(meta.seo);\n return {\n title: stringOrEmpty(doc.title),\n seoTitle: stringOrEmpty(seo.title),\n seoDescription: stringOrEmpty(seo.description),\n excerpt: stringOrEmpty(doc.excerpt),\n slug: doc.slug?.trim() ?? \"\",\n bodyJson: doc.bodyJson as unknown as TiptapNode,\n hasFeaturedImage: doc.hasFeaturedImage === true,\n canonical: typeof seo.canonical === \"string\" ? seo.canonical : undefined,\n noindex: seo.noindex === true,\n };\n}\n\nfunction collectH1Labels(doc: Record<string, unknown>): string[] {\n const labels: string[] = [];\n const walk = (node: unknown): void => {\n if (!isRecord(node)) return;\n const type = stringOrEmpty(node.type);\n if (type === \"heading\") {\n const attrs = recordOrEmpty(node.attrs);\n if (Number(attrs.level ?? 2) === 1) labels.push(textFrom(node));\n }\n if (type === \"section\") {\n const attrs = recordOrEmpty(node.attrs);\n if (attrs.sectionType === \"hero\") {\n const props = recordOrEmpty(attrs.props);\n const title = stringOrEmpty(props.title);\n if (title) labels.push(title);\n }\n }\n const content = node.content;\n if (Array.isArray(content)) {\n for (const child of content) walk(child);\n }\n };\n walk(doc);\n return labels;\n}\n\nfunction hasBeforeAfterSection(value: unknown): boolean {\n if (Array.isArray(value)) return value.some(hasBeforeAfterSection);\n if (!isRecord(value)) return false;\n if (value.type === \"before-after\") return true;\n if (value.type === \"section\") {\n const attrs = recordOrEmpty(value.attrs);\n return attrs.sectionType === \"before-after\";\n }\n return Object.values(value).some(hasBeforeAfterSection);\n}\n\nexport function assertPublishable(\n doc: PublishGateDoc,\n options: PublishGateOptions,\n): PublishGateResult {\n const errors: PublishGateError[] = [];\n const warnings: PublishGateError[] = [];\n const ruleset: {\n publish: typeof PUBLISH_GATE_RULESET;\n medical?: typeof MEDICAL_LINT_RULESET;\n tax?: typeof TAX_LINT_RULESET;\n } = { publish: PUBLISH_GATE_RULESET };\n const h1Labels = collectH1Labels(doc.bodyJson);\n\n if (options.documentType === \"post\") {\n // post = 렌더러가 title 을 h1 으로 그린다 — 본문에 h1 이 있으면 실렌더 h1=2.\n if (h1Labels.length !== 0) {\n errors.push({\n code: \"SEO_H1_COUNT\",\n message: `post 는 제목이 h1 이며 본문에 h1 금지, 현재 ${h1Labels.length}개입니다.`,\n });\n }\n } else {\n // page = 본문(hero 또는 heading1)이 유일 h1 — 렌더러는 title 을 h1 으로 그리지 않는다.\n if (h1Labels.length !== 1) {\n errors.push({\n code: \"SEO_H1_COUNT\",\n message: `h1은 정확히 1개여야 합니다. 현재 ${h1Labels.length}개입니다.`,\n });\n }\n }\n if (!stringOrEmpty(doc.title)) {\n errors.push({\n code: \"SEO_TITLE_REQUIRED\",\n message: \"발행하려면 글 제목이 필요합니다.\",\n });\n }\n if (!seoDescription(doc)) {\n errors.push({\n code: \"SEO_META_DESCRIPTION_REQUIRED\",\n message: \"발행하려면 SEO meta description 또는 excerpt가 필요합니다.\",\n });\n }\n\n const vertical = stringOrEmpty(options.vertical);\n const includesBeforeAfter = hasBeforeAfterSection(doc.bodyJson);\n if (!vertical && includesBeforeAfter) {\n errors.push({\n code: \"VERTICAL_UNKNOWN\",\n severity: \"warning\",\n message:\n \"before-after 섹션이 있지만 사이트 vertical 을 확인할 수 없어 발행을 차단합니다.\",\n });\n }\n\n if (vertical === \"medical\" || options.complianceFlags?.includes(\"medical-review\")) {\n ruleset.medical = MEDICAL_LINT_RULESET;\n const sectionText = textFrom(doc.bodyJson);\n const lint = lintMedicalPage({\n title: doc.title ?? \"\",\n excerpt: doc.excerpt,\n contentJson: {\n type: \"doc\",\n content: [doc.bodyJson, { type: \"text\", text: sectionText }],\n },\n });\n for (const finding of lint.findings) {\n const entry = {\n code: \"MEDICAL_COMPLIANCE\",\n ruleId: finding.ruleId,\n severity: finding.severity,\n message: finding.message,\n snippet: finding.snippet,\n };\n if (finding.severity === \"critical\") errors.push(entry);\n else warnings.push(entry);\n }\n }\n\n // 세무 vertical — audit-tax 배선 (2026-07-11 구멍 1 해소: manifest 선언만 있고\n // 게이트 미연결이던 격차. critical 만 차단, 의료와 동일 semantics).\n if (vertical === \"tax\" || options.complianceFlags?.includes(\"tax-review\")) {\n ruleset.tax = TAX_LINT_RULESET;\n const sectionText = textFrom(doc.bodyJson);\n const lint = lintTaxPage({\n title: doc.title ?? \"\",\n excerpt: doc.excerpt,\n contentJson: {\n type: \"doc\",\n content: [doc.bodyJson, { type: \"text\", text: sectionText }],\n },\n });\n for (const finding of lint.findings) {\n const entry = {\n code: \"TAX_COMPLIANCE\",\n ruleId: finding.ruleId,\n severity: finding.severity,\n message: finding.message,\n snippet: finding.snippet,\n };\n if (finding.severity === \"critical\") errors.push(entry);\n else warnings.push(entry);\n }\n }\n\n // SEO 점수 + readability(W4-5 PR 1, seo-suite-rankmath-parity.md §3①) — 클라\n // 패널(tenant-admin SeoChecklist)과 같은 함수를 서버에서 재실행. bad 항목만\n // warnings 로 기록(비차단) — warn 은 실시간 패널 전용 권고라 게이트에 안 올림,\n // 점수를 차단 기준으로 쓰지 않는다는 원칙(§3① 명시)과 동일 이유.\n const seoCheckInput = toSeoCheckInput(doc);\n const seoFindings = [\n ...runSeoChecks(seoCheckInput),\n ...runReadabilityChecks(seoCheckInput.bodyJson),\n ];\n for (const finding of seoFindings) {\n if (finding.level !== \"bad\") continue;\n warnings.push({\n code: \"SEO_CHECK\",\n ruleId: finding.id,\n severity: \"warning\",\n message: finding.hint,\n });\n }\n\n return { errors, warnings, ruleset };\n}\n\nexport function writePublishGateMeta(\n metaJson: Record<string, unknown> | null | undefined,\n result: PublishGateResult,\n): Record<string, unknown> {\n const meta = { ...recordOrEmpty(metaJson) };\n const current = recordOrEmpty(meta.compliance);\n meta.compliance = {\n ...current,\n blocked: result.errors.length > 0,\n ruleset: result.ruleset,\n errors: result.errors,\n warnings: result.warnings,\n checkedAt: new Date().toISOString(),\n ...(result.errors.length === 0 ? { override: null } : {}),\n };\n return meta;\n}\n\nexport function writePublishOverrideMeta(\n metaJson: Record<string, unknown> | null | undefined,\n override: PublishOverrideInput,\n): Record<string, unknown> {\n const meta = { ...recordOrEmpty(metaJson) };\n const compliance = recordOrEmpty(meta.compliance);\n meta.compliance = {\n ...compliance,\n blocked: false,\n override,\n };\n return meta;\n}\n\nexport function isComplianceBlocked(\n metaJson: Record<string, unknown> | null | undefined,\n): boolean {\n const compliance = recordOrEmpty(recordOrEmpty(metaJson).compliance);\n return compliance.blocked === true && !hasPublishOverride(metaJson);\n}\n\nexport function hasPublishOverride(\n metaJson: Record<string, unknown> | null | undefined,\n): boolean {\n const compliance = recordOrEmpty(recordOrEmpty(metaJson).compliance);\n const override = recordOrEmpty(compliance.override);\n return (\n stringOrEmpty(override.reason).length > 0 &&\n stringOrEmpty(override.userId).length > 0\n );\n}\n","/**\n * `@roottale/cms-renderer-next/validate` — 섹션 노드 저장 검증 (ADR-0066 §5 / §2-A).\n *\n * 위치 근거(codex v4-R1 + W1-1 위임 결정):\n * - 검증은 **SectionSchema(정의 SSoT)** 로 하는데, 그 스키마는\n * `cms-renderer-next/templates/site-config.ts` 에 있다.\n * - 청사진(blueprint R1-4)은 \"cms-core 위치\"를 제안했으나, `cms-core → cms-renderer-next`\n * 는 **역방향 import(금지 — cms-core 가 base 패키지)**. 따라서 SectionSchema 를\n * 소비하는 검증기는 cms-renderer-next 안에 있어야 한다.\n * - 순수 zod(React/cms-client 무의존)이라 별도 진입점 `./validate` 로 노출해\n * admin `posts.ts`(\"use server\") 가 렌더러 번들 전체를 끌어오지 않고 import 한다.\n * - 조립 게이트(R3-a-3)도 같은 함수를 `@roottale/cms-renderer-next/validate` 로\n * 공유한다(이중 구현 금지).\n *\n * 섹션 = generic Tiptap 노드 `{ type:\"section\", attrs:{ sectionType, variant, theme, props } }`\n * (ADR-0055 §3-2). 검증은 `{ type: attrs.sectionType, variant, theme, props }` 를\n * SectionSchema 로 parse — 빌트인 type 은 스키마로 강제(union fall-through 차단),\n * 미지 type 은 CustomSection 으로 통과(렌더러 customSections 로 해석).\n */\nimport { SectionSchema } from \"./site-config.js\";\n\nexport {\n findSectionLockViolations,\n formatSectionLockViolations,\n isSectionContentPropPath,\n type SectionLock,\n type SectionLockViolation,\n type SectionLockViolationCode,\n} from \"./section-locks.js\";\n\nexport {\n PUBLISH_GATE_RULESET,\n assertPublishable,\n hasPublishOverride,\n isComplianceBlocked,\n writePublishGateMeta,\n writePublishOverrideMeta,\n type PublishGateDoc,\n type PublishGateError,\n type PublishGateOptions,\n type PublishGateResult,\n type PublishGateRuleset,\n type PublishOverrideInput,\n} from \"./publish-gate.js\";\n\n/** 검증 실패 1건. path = 문서 내 섹션 노드 위치(0-base), field = zod issue 경로. */\nexport interface SectionValidationIssue {\n /** 문서 최상위 content 배열에서 섹션 노드의 index. */\n index: number;\n sectionType: string;\n /** zod issue 경로 (예: \"props.title\"). */\n field: string;\n message: string;\n}\n\nexport interface SectionValidationResult {\n valid: boolean;\n issues: SectionValidationIssue[];\n}\n\ninterface TiptapNodeLike {\n type?: string;\n attrs?: Record<string, unknown>;\n content?: unknown;\n text?: unknown;\n}\n\n/** 문서 최상위 content 에서 섹션 노드를 index 와 함께 수집. */\nfunction collectSectionNodes(\n doc: unknown,\n): Array<{ index: number; node: TiptapNodeLike }> {\n const content =\n doc && typeof doc === \"object\"\n ? (doc as { content?: unknown }).content\n : undefined;\n if (!Array.isArray(content)) return [];\n const out: Array<{ index: number; node: TiptapNodeLike }> = [];\n content.forEach((node, index) => {\n if (node && typeof node === \"object\" && (node as TiptapNodeLike).type === \"section\") {\n out.push({ index, node: node as TiptapNodeLike });\n }\n });\n return out;\n}\n\n/**\n * 섹션 노드의 attrs → SectionSchema 입력 `{type, variant, theme, props}`.\n * sectionType 이 SectionSchema 의 `type` 이 된다.\n */\nfunction sectionInputFromAttrs(attrs: Record<string, unknown> | undefined): {\n type: string;\n variant?: unknown;\n theme?: unknown;\n props?: unknown;\n} {\n const a = attrs ?? {};\n return {\n type: typeof a.sectionType === \"string\" ? a.sectionType : \"\",\n variant: a.variant ?? undefined,\n theme: a.theme ?? undefined,\n props: a.props ?? {},\n };\n}\n\n/**\n * 문서 내 모든 섹션 노드를 SectionSchema 로 검증. 섹션 노드가 없으면 valid=true\n * (no-op). 하나라도 스키마 위반이면 valid=false + issue 목록.\n *\n * posts.ts(create/update) 가 이 결과를 보고 저장을 거부한다. rawHtml 생성 금지 —\n * 실패는 저장 거부일 뿐 노드를 변형하지 않는다.\n */\nexport function validateSectionNodes(doc: unknown): SectionValidationResult {\n const sections = collectSectionNodes(doc);\n const issues: SectionValidationIssue[] = [];\n\n for (const { index, node } of sections) {\n const input = sectionInputFromAttrs(node.attrs);\n const result = SectionSchema.safeParse(input);\n if (!result.success) {\n for (const issue of result.error.issues) {\n issues.push({\n index,\n sectionType: input.type || \"(미지정)\",\n field: issue.path.join(\".\") || \"(root)\",\n message: issue.message,\n });\n }\n }\n }\n\n return { valid: issues.length === 0, issues };\n}\n\n/** issue 목록을 사람이 읽는 한 줄 메시지로 (저장 거부 오류 문구용). */\nexport function formatSectionIssues(issues: SectionValidationIssue[]): string {\n return issues\n .map(\n (i) =>\n `${i.sectionType} 섹션(${i.index + 1}번째)의 ${i.field}: ${i.message}`,\n )\n .join(\" / \");\n}\n"],"mappings":"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","names":["isRecord","snippetFor","findAll","ruleFromTerms","GUARANTEE_TERMS","COMPARISON_TERMS","extractText","isRecord","hasBeforeAfterSection"]}
|
|
File without changes
|
|
File without changes
|