@roottale/cms-client 0.70.1 → 0.72.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,37 @@
1
1
  # @roottale/cms-client
2
2
 
3
+ ## 0.72.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 280e850: 고정 페이지 문구·표·사진용 fields 편집 계약과 연결한 사업장 필드 선언을 추가한다.
8
+ 접수 마감의 미지정·해제·시각을 저장 및 공개 응답에서 구분한다. 의료진 시간표 표 입력,
9
+ 문자열 추천값·내부 의료진 주소 선택, 모델별 관리 제목·목록 필드·편집 영역 숨김을 지원한다.
10
+ 새 계약·모델을 동기화하기 전에 관리자와 API 파서를 먼저 배포해야 한다.
11
+ - fa50476: 전체 글을 조회하는 fetchAllPosts와 명시적인 페이지 상한·커서 정지 검사 옵션을 추가하고 외부 Next.js 예제의 조회 루프를 교체합니다.
12
+ - 4341571: 공개 콘텐츠에 섹션 표시 상태·대표 분류·CTA와 콘텐츠 버전·페이지 계약 key를 전달합니다. 미리보기는 지정한 작업본 버전의 전체 내용을 사용하고, 교체된 작업본은 `preview_superseded` 오류와 새 미리보기 안내로 처리합니다. SDK·렌더러·외부 연동 문서와 Next.js 예제를 함께 갱신합니다.
13
+
14
+ 실제화면 편집 계약에 등록된 템플릿은 허용된 최상위 필드의 저장 구조를 보존합니다. `visualEditing` 선언 사이트의 일반 필드 포맷에서도 반복 항목의 UUID `_id`를 유지해 순서 변경 후 편집 주소가 달라지지 않습니다. 미선언 사이트에는 새 응답 필드·반복행 ID·계약 기반 경로 계산을 적용하지 않습니다. `cms-fields`의 직접 소비자는 `ResolveContext.preserveRowIds`로 명시적으로 활성화할 수 있습니다.
15
+
16
+ - 9f0d086: `@roottale/cms-client/server`에 `parseContentEditorState(raw)`와 `ContentEditorStateParseResult`를 추가합니다. 성공은 `{ success: true, data }`, 잘못되거나 없는 입력은 `{ success: false, error }`로 반환하며 기본값을 보충하지 않습니다. 기존 `contentEditorFromWire`는 새 해석기를 사용하고 잘못된 입력의 `undefined` 반환을 유지합니다.
17
+ - e97a94f: 공개·미리보기 글에 optional contentEditorResult를 추가해 편집 상태 원본 부재와 해석 실패를 구분합니다. 기존 contentEditor 동작은 유지합니다.
18
+
19
+ 커버리지 검사에서 계약 컨테이너의 자식 주소와 flexible_content의 행별 layouts를 지원하고, visualEditorOrigins의 HTTP 허용 범위를 브리지와 같은 loopback으로 제한합니다. 연결 문서와 Next.js 예제를 갱신합니다.
20
+
21
+ ### Patch Changes
22
+
23
+ - Updated dependencies [c60835a]
24
+ - Updated dependencies [e97a94f]
25
+ - Updated dependencies [4341571]
26
+ - Updated dependencies [fa50476]
27
+ - @roottale/cms-core@0.72.0
28
+
29
+ ## 0.71.0
30
+
31
+ ### Patch Changes
32
+
33
+ - @roottale/cms-core@0.71.0
34
+
3
35
  ## 0.70.1
4
36
 
5
37
  ### Patch Changes
package/README.md CHANGED
@@ -139,3 +139,40 @@ registration, manual `/v1/cms/revalidate` usage, and troubleshooting, see
139
139
  ## License
140
140
 
141
141
  Proprietary (UNLICENSED). Issued under the RootTale customer contract.
142
+
143
+ ### 편집 상태 해석
144
+
145
+ `@roottale/cms-client/server`의 `parseContentEditorState(raw)`는 기본값을 보충하지
146
+ 않으며 `ContentEditorStateParseResult`를 반환합니다. 성공이면
147
+ `{ success: true, data: CmsContentEditorState }`, 입력이 없거나 잘못되면
148
+ `{ success: false, error: string }`입니다. 소비자는 값이 없는 경우의 기본값과
149
+ 잘못된 값의 오류 처리를 구분할 수 있습니다. 기존 wire 변환은 잘못된 상태를
150
+ 계속 `undefined`로 처리합니다.
151
+
152
+ 공개·미리보기 조회의 `CmsPostContent.contentEditorResult?`는 wire 원본이 있을 때
153
+ 성공·실패 결과를 보존합니다. 원본 필드가 없으면 결과 필드도 생략합니다. `null`은
154
+ 존재하는 잘못된 값이므로 실패입니다. 엄격한 사이트는 `contentEditor`만 확인하지 말고
155
+ `contentEditorResult`의 실패를 먼저 처리해야 숨긴 섹션을 기본값으로 다시 노출하지 않습니다.
156
+
157
+ ## 전체 글 조회
158
+
159
+ `fetchAllPosts`는 `@roottale/cms-client/server`의 추가 export입니다. `fetchPosts`의
160
+ 필터·캐시·signal 옵션을 그대로 받고 `cursor`는 내부에서 관리합니다.
161
+
162
+ ```ts
163
+ import { fetchAllPosts } from "@roottale/cms-client/server";
164
+
165
+ const posts = await fetchAllPosts({
166
+ apiKey: process.env.ROOTTALE_API_KEY!,
167
+ modelKey: "condition",
168
+ limit: 100,
169
+ maxPages: 100,
170
+ cursorGuard: "repeated",
171
+ });
172
+ ```
173
+
174
+ `maxPages`는 양의 정수로 반드시 지정합니다. `cursorGuard`도 필수이며
175
+ `"non-advancing"`은 다음 커서 누락·동일 커서를, `"repeated"`는 과거 커서로 돌아가는
176
+ 순환까지 거부합니다. `hasMore: false`에서 완료하며, 상한 초과·정지·통신 오류에는
177
+ 부분 목록을 반환하지 않고 예외를 전달합니다. 페이지 크기 `limit`의 기본값은
178
+ `fetchPosts`와 같은 20입니다. 사이트의 관련 콘텐츠 개수 제한은 호출 사이트에서 정합니다.
package/dist/server.d.ts CHANGED
@@ -5,6 +5,25 @@ export { parseAttributionJson } from './attribution.js';
5
5
  import { PublicRouteMetadata } from '@roottale/cms-core';
6
6
  export { MetadataDefaultRules, MetadataDefaultRulesSchema, MetadataDefaultsDeclaration, MetadataDefaultsInput, MetadataField, MetadataPatch, MetadataResolutionInput, MetadataRobotsPolicy, MetadataTextRule, MetadataValues, PublicRouteMetadata, formatMetadataDefaults, metadataBodyParagraphs, metadataContentImageUrl, readMetadataValues, resolveMetadata } from '@roottale/cms-core';
7
7
 
8
+ /** 공개 렌더링에 필요한 편집 상태. 내부 검수 정보는 포함하지 않는다. */
9
+ interface CmsContentEditorState {
10
+ sectionStates: Record<string, "visible" | "hidden" | "deleted">;
11
+ primaryTermId: string | null;
12
+ cta: {
13
+ mode: "auto" | "manual" | "off";
14
+ targetPostId: string | null;
15
+ };
16
+ }
17
+ type ContentEditorStateParseResult = {
18
+ success: true;
19
+ data: CmsContentEditorState;
20
+ } | {
21
+ success: false;
22
+ error: string;
23
+ };
24
+ /** 기본값을 보충하지 않는다. 잘못되거나 없는 값은 명시적인 실패 결과로 돌려준다. */
25
+ declare function parseContentEditorState(raw: unknown): ContentEditorStateParseResult;
26
+
8
27
  /**
9
28
  * `@roottale/cms-client/server` — RootTale CMS Public API server-side fetch client.
10
29
  *
@@ -81,6 +100,14 @@ interface CmsPostTranslationRef {
81
100
  slug: string;
82
101
  }
83
102
  interface CmsPostContent {
103
+ /** 반영된 콘텐츠 버전. 미리보기에서는 작업본의 기준 버전. 구 서버는 미포함. */
104
+ contentVersion?: number;
105
+ /** 사이트 콘텐츠 계약의 페이지 key. 연결되지 않은 콘텐츠는 null. */
106
+ contractPageKey?: string | null;
107
+ /** 섹션 표시 상태·대표 분류·CTA 연결. */
108
+ contentEditor?: CmsContentEditorState;
109
+ /** wire 원본이 있을 때의 엄격한 해석 결과. 원본 부재와 잘못된 상태를 구별한다. */
110
+ contentEditorResult?: ContentEditorStateParseResult;
84
111
  /** UUIDv7. */
85
112
  id: string;
86
113
  /** Owning tenant id. */
@@ -453,6 +480,15 @@ interface FetchPageBySlugOptions {
453
480
  }
454
481
  declare function fetchPageBySlug(options: FetchPageBySlugOptions): Promise<CmsPostContent | null>;
455
482
 
483
+ interface FetchAllPostsOptions extends Omit<FetchPostsOptions, "cursor"> {
484
+ /** 최대 조회 페이지 수. 초과하면 부분 결과를 반환하지 않고 실패한다. */
485
+ maxPages: number;
486
+ /** 같은 커서에서의 정지 또는 과거 커서로 돌아가는 순환까지 검사한다. */
487
+ cursorGuard: "non-advancing" | "repeated";
488
+ }
489
+ /** hasMore가 false일 때 완료한다. 통신·커서·상한 오류는 호출자에게 전달한다. */
490
+ declare function fetchAllPosts(options: FetchAllPostsOptions): Promise<CmsPostContent[]>;
491
+
456
492
  /**
457
493
  * 블로그 표시 설정 — admin /design 의 "블로그 표시" 에서 저장한 사이트 기본값.
458
494
  * renderer 가 prop 미지정 시 이 값을 기본으로 적용 (글별 override 는 post 의
@@ -824,6 +860,8 @@ interface BusinessOpeningHours {
824
860
  opens: string;
825
861
  /** HH:MM (24시간). */
826
862
  closes: string;
863
+ /** null = 접수 마감 안내 없음, undefined = 기존 미설정 행. */
864
+ receptionCloses?: string | null;
827
865
  }
828
866
  /**
829
867
  * 비즈니스 프로필 — admin "운영 > 비즈니스 프로필" 에서 저장한 사업장 정보.
@@ -916,6 +954,7 @@ type BusinessProfileWire = {
916
954
  days?: BusinessDayCode[];
917
955
  opens?: string;
918
956
  closes?: string;
957
+ reception_closes?: string | null;
919
958
  }[];
920
959
  price_range?: string | null;
921
960
  area_served?: string[];
@@ -1285,7 +1324,16 @@ interface FetchPostPreviewOptions {
1285
1324
  readonly siteId?: string;
1286
1325
  readonly signal?: AbortSignal;
1287
1326
  }
1327
+ interface CmsPostPreviewVisualTarget {
1328
+ readonly entityId: string;
1329
+ readonly draftId: string;
1330
+ readonly draftVersion: number;
1331
+ readonly contractVersion: string;
1332
+ readonly templateVersion: string;
1333
+ }
1288
1334
  interface CmsPostPreviewInfo {
1335
+ /** 이번 응답이 실제로 읽은 작업본과 사이트 템플릿 표식. 구 토큰은 미포함. */
1336
+ readonly visualTarget?: CmsPostPreviewVisualTarget;
1289
1337
  /** 토큰 만료 시각(ISO). */
1290
1338
  readonly expiresAt: string;
1291
1339
  /** 글의 실제 상태. 본문 `status` 는 템플릿 재사용을 위해 항상 `published`. */
@@ -1296,6 +1344,8 @@ interface CmsPostPreviewContent extends CmsPostContent {
1296
1344
  }
1297
1345
  /** 만료된 미리보기 링크(410 `preview_expired`)인지 구분한다. */
1298
1346
  declare function isPreviewExpiredError(error: unknown): boolean;
1347
+ /** 저장 후 교체된 작업본의 이전 링크(409)인지 구분한다. 새 미리보기를 요청해야 한다. */
1348
+ declare function isPreviewSupersededError(error: unknown): boolean;
1299
1349
  /**
1300
1350
  * 토큰이 없거나 다른 사이트의 것이면 `null`(404). 만료는 throw(410) — 두 경우를
1301
1351
  * 다르게 안내할 수 있게 구분한다.
@@ -1707,4 +1757,4 @@ declare function formatKoreanPhoneNumber(input: string): string;
1707
1757
  */
1708
1758
  declare function submitInquiry(options: SubmitInquiryOptions): Promise<SubmitInquiryResult>;
1709
1759
 
1710
- export { type ActionDockAction, type ActionDockKind, type ActionDockPosition, type ActionDockSettings, type AnalyticsTagConfig, type AnalyticsTagProvider, type AnnouncementItem, type AnnouncementSettings, type AnnouncementTone, type AnnouncementVariant, type ArticleSchemaInput, BLOG_SETTINGS_CACHE_TAG, BUSINESS_CACHE_TAG, type BusinessDayCode, type BusinessOpeningHours, type BusinessType, CMS_CLIENT_VERSION, COLLECTIONS_CACHE_TAG, CONTENT_POLICY_CACHE_TAG, CmsApiError, type CmsArchiveRelatedGroup, type CmsArchiveRelatedPost, type CmsCategoryCount, type CmsCategoryCounts, type CmsContentModel, type CmsExposureCampaign, type CmsExposureDecision, type CmsFieldMetaEntry, type CmsPostContent, type CmsPostContentPage, type CmsPostPreviewContent, type CmsPostPreviewInfo, type CmsPostTranslationRef, type CmsPostType, type CmsRouteCollection, type CmsSearchHit, type CmsSitePattern, type CmsSitePatternPresentation, type CmsTaxonomyKind, type CmsTermRef, DEFAULT_BLOG_SETTINGS, DEFAULT_SITE_PATTERN_PRESENTATION, EXPOSURE_CACHE_TAG, type FetchAnalyticsOptions, type FetchAuthorsOptions, type FetchBlogSettingsOptions, type FetchBusinessProfileOptions, type FetchCategoryCountsOptions, type FetchCollectionsOptions, type FetchContentModelsOptions, type FetchExposureDecisionOptions, type FetchExposureOptions, type FetchInquiryFormOptions, type FetchMenuOptions, type FetchMenusOptions, type FetchPageBySlugOptions, type FetchPostOptions, type FetchPostPreviewOptions, type FetchPostsOptions, type FetchRedirectsOptions, type FetchRouteMetadataOptions, type FetchSiteInquiriesOptions, type FetchSiteInquiryOptions, type FetchSitePatternsOptions, type FetchThemeOptions, type FormInquiryReceipt, INQUIRY_ERROR_MESSAGES, InquiryAttribution, type InquiryBoardSettings, type InquiryVertical, LOCAL_BUSINESS_MAX_SERVICES, LOCAL_BUSINESS_SERVICE_MAX_LENGTH, type LocalBusinessProfileInput, MENUS_CACHE_TAG, METADATA_CACHE_TAG, POST_FOOTER_PATTERN_SLOT, type PublishedInquiryForm, type ResolvedPostDisplay, type RootTaleAuthor, type RootTaleBlogPostCta, type RootTaleBlogSettings, type RootTaleBusinessProfile, type RootTaleMenu, type RootTaleMenuItem, type RootTaleRedirect, type RootTaleSiteProfile, type RootTaleSitemapSettings, type RootTaleTheme, type RssFeedInput, type RssItemInput, SETTINGS_CACHE_TAGS, SITE_METADATA_DEFAULTS_CACHE_TAG, SITE_PATTERNS_CACHE_TAG, type SearchPostsOptions, type SiteAnalyticsTags, type SiteInquiryDetail, type SiteInquiryListItem, type SiteInquiryListPage, type SiteInquiryStatus, type SiteNavColumn, type SiteNavCta, type SiteNavGroup, type SiteNavLink, type SiteNavSettings, type SiteVerificationMetadata, type SiteVerificationSettings, type SubmitFormInquiryFields, type SubmitFormInquiryOptions, type SubmitFormInquiryResult, type SubmitInquiryFields, type SubmitInquiryOptions, type SubmitInquiryResult, type SubmitSiteInquiryFields, type SubmitSiteInquiryOptions, type SubmitSiteInquiryResult, THEME_CACHE_TAG, articleSchema, breadcrumbSchema, businessProfileFromWire, collectionPageSchema, contentIdMeta, faqSchema, fetchAnalyticsConfig, fetchAuthors, fetchBlogSettings, fetchBusinessProfile, fetchCategoryCounts, fetchCollections, fetchCollectionsSafeDefault, fetchContentModels, fetchExposure, fetchExposureDecision, fetchInquiryForm, fetchMenu, fetchMenus, fetchPageBySlug, fetchPost, fetchPostPreview, fetchPosts, fetchRedirects, fetchRouteMetadata, fetchSiteInquiries, fetchSiteInquiry, fetchSitePatterns, fetchTheme, formatKoreanPhoneNumber, generateRssXml, inquiryErrorMessage, isPreviewExpiredError, jsonLdJson, jsonLdScript, localBusinessSchema, metadataContractChecksum, organizationSchema, parseInquiryRedirect, profilePageSchema, resolvePostDisplay, resolveSearchHitPath, routeMetadataCacheTag, rssItemsFromPosts, searchPosts, selectRelatedPosts, selectSitePatternForSlot, sitePatternPresentationAttributes, siteVerificationMetadata, submitFormInquiry, submitInquiry, submitSiteInquiry, websiteSchema };
1760
+ export { type ActionDockAction, type ActionDockKind, type ActionDockPosition, type ActionDockSettings, type AnalyticsTagConfig, type AnalyticsTagProvider, type AnnouncementItem, type AnnouncementSettings, type AnnouncementTone, type AnnouncementVariant, type ArticleSchemaInput, BLOG_SETTINGS_CACHE_TAG, BUSINESS_CACHE_TAG, type BusinessDayCode, type BusinessOpeningHours, type BusinessType, CMS_CLIENT_VERSION, COLLECTIONS_CACHE_TAG, CONTENT_POLICY_CACHE_TAG, CmsApiError, type CmsArchiveRelatedGroup, type CmsArchiveRelatedPost, type CmsCategoryCount, type CmsCategoryCounts, type CmsContentEditorState, type CmsContentModel, type CmsExposureCampaign, type CmsExposureDecision, type CmsFieldMetaEntry, type CmsPostContent, type CmsPostContentPage, type CmsPostPreviewContent, type CmsPostPreviewInfo, type CmsPostPreviewVisualTarget, type CmsPostTranslationRef, type CmsPostType, type CmsRouteCollection, type CmsSearchHit, type CmsSitePattern, type CmsSitePatternPresentation, type CmsTaxonomyKind, type CmsTermRef, type ContentEditorStateParseResult, DEFAULT_BLOG_SETTINGS, DEFAULT_SITE_PATTERN_PRESENTATION, EXPOSURE_CACHE_TAG, type FetchAllPostsOptions, type FetchAnalyticsOptions, type FetchAuthorsOptions, type FetchBlogSettingsOptions, type FetchBusinessProfileOptions, type FetchCategoryCountsOptions, type FetchCollectionsOptions, type FetchContentModelsOptions, type FetchExposureDecisionOptions, type FetchExposureOptions, type FetchInquiryFormOptions, type FetchMenuOptions, type FetchMenusOptions, type FetchPageBySlugOptions, type FetchPostOptions, type FetchPostPreviewOptions, type FetchPostsOptions, type FetchRedirectsOptions, type FetchRouteMetadataOptions, type FetchSiteInquiriesOptions, type FetchSiteInquiryOptions, type FetchSitePatternsOptions, type FetchThemeOptions, type FormInquiryReceipt, INQUIRY_ERROR_MESSAGES, InquiryAttribution, type InquiryBoardSettings, type InquiryVertical, LOCAL_BUSINESS_MAX_SERVICES, LOCAL_BUSINESS_SERVICE_MAX_LENGTH, type LocalBusinessProfileInput, MENUS_CACHE_TAG, METADATA_CACHE_TAG, POST_FOOTER_PATTERN_SLOT, type PublishedInquiryForm, type ResolvedPostDisplay, type RootTaleAuthor, type RootTaleBlogPostCta, type RootTaleBlogSettings, type RootTaleBusinessProfile, type RootTaleMenu, type RootTaleMenuItem, type RootTaleRedirect, type RootTaleSiteProfile, type RootTaleSitemapSettings, type RootTaleTheme, type RssFeedInput, type RssItemInput, SETTINGS_CACHE_TAGS, SITE_METADATA_DEFAULTS_CACHE_TAG, SITE_PATTERNS_CACHE_TAG, type SearchPostsOptions, type SiteAnalyticsTags, type SiteInquiryDetail, type SiteInquiryListItem, type SiteInquiryListPage, type SiteInquiryStatus, type SiteNavColumn, type SiteNavCta, type SiteNavGroup, type SiteNavLink, type SiteNavSettings, type SiteVerificationMetadata, type SiteVerificationSettings, type SubmitFormInquiryFields, type SubmitFormInquiryOptions, type SubmitFormInquiryResult, type SubmitInquiryFields, type SubmitInquiryOptions, type SubmitInquiryResult, type SubmitSiteInquiryFields, type SubmitSiteInquiryOptions, type SubmitSiteInquiryResult, THEME_CACHE_TAG, articleSchema, breadcrumbSchema, businessProfileFromWire, collectionPageSchema, contentIdMeta, faqSchema, fetchAllPosts, fetchAnalyticsConfig, fetchAuthors, fetchBlogSettings, fetchBusinessProfile, fetchCategoryCounts, fetchCollections, fetchCollectionsSafeDefault, fetchContentModels, fetchExposure, fetchExposureDecision, fetchInquiryForm, fetchMenu, fetchMenus, fetchPageBySlug, fetchPost, fetchPostPreview, fetchPosts, fetchRedirects, fetchRouteMetadata, fetchSiteInquiries, fetchSiteInquiry, fetchSitePatterns, fetchTheme, formatKoreanPhoneNumber, generateRssXml, inquiryErrorMessage, isPreviewExpiredError, isPreviewSupersededError, jsonLdJson, jsonLdScript, localBusinessSchema, metadataContractChecksum, organizationSchema, parseContentEditorState, parseInquiryRedirect, profilePageSchema, resolvePostDisplay, resolveSearchHitPath, routeMetadataCacheTag, rssItemsFromPosts, searchPosts, selectRelatedPosts, selectSitePatternForSlot, sitePatternPresentationAttributes, siteVerificationMetadata, submitFormInquiry, submitInquiry, submitSiteInquiry, websiteSchema };
package/dist/server.js CHANGED
@@ -8,7 +8,7 @@ var DEFAULT_SERVER_FETCH_TIMEOUT_MS = 1e4;
8
8
  function requestSignal(signal) {
9
9
  return signal ?? AbortSignal.timeout(DEFAULT_SERVER_FETCH_TIMEOUT_MS);
10
10
  }
11
- var CLIENT_VERSION = true ? "0.70.1" : "dev";
11
+ var CLIENT_VERSION = true ? "0.72.0" : "dev";
12
12
  var CMS_CLIENT_VERSION = CLIENT_VERSION;
13
13
  function authHeaders(apiKey) {
14
14
  return {
@@ -59,6 +59,29 @@ function assertServer() {
59
59
  }
60
60
  }
61
61
 
62
+ // src/server-content-editor.ts
63
+ function parseContentEditorState(raw) {
64
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return { success: false, error: "\uD3B8\uC9D1 \uC0C1\uD0DC\uB294 \uAC1D\uCCB4\uC5EC\uC57C \uD569\uB2C8\uB2E4." };
65
+ const value = raw;
66
+ if (!value.sectionStates || typeof value.sectionStates !== "object" || Array.isArray(value.sectionStates)) return { success: false, error: "\uC139\uC158 \uD45C\uC2DC \uC0C1\uD0DC\uB294 \uAC1D\uCCB4\uC5EC\uC57C \uD569\uB2C8\uB2E4." };
67
+ const sectionStates = {};
68
+ for (const [key, state] of Object.entries(value.sectionStates)) {
69
+ if (state !== "visible" && state !== "hidden" && state !== "deleted") return { success: false, error: `\uC798\uBABB\uB41C \uC139\uC158 \uD45C\uC2DC \uC0C1\uD0DC: ${key}` };
70
+ Object.defineProperty(sectionStates, key, { value: state, enumerable: true, writable: true, configurable: true });
71
+ }
72
+ if (value.primaryTermId !== null && typeof value.primaryTermId !== "string") return { success: false, error: "\uB300\uD45C \uC8FC\uC81C\uB294 \uBB38\uC790\uC5F4 \uB610\uB294 null\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4." };
73
+ if (!value.cta || typeof value.cta !== "object" || Array.isArray(value.cta)) return { success: false, error: "\uC9C4\uB8CC \uC548\uB0B4 \uCE74\uB4DC \uC124\uC815\uC740 \uAC1D\uCCB4\uC5EC\uC57C \uD569\uB2C8\uB2E4." };
74
+ const cta = value.cta;
75
+ if (cta.mode !== "auto" && cta.mode !== "manual" && cta.mode !== "off") return { success: false, error: "\uC798\uBABB\uB41C \uC9C4\uB8CC \uC548\uB0B4 \uCE74\uB4DC \uBAA8\uB4DC\uC785\uB2C8\uB2E4." };
76
+ if (cta.targetPostId !== null && typeof cta.targetPostId !== "string") return { success: false, error: "\uC9C4\uB8CC \uC548\uB0B4 \uCE74\uB4DC \uB300\uC0C1\uC740 \uBB38\uC790\uC5F4 \uB610\uB294 null\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4." };
77
+ if (cta.mode === "manual" && !cta.targetPostId) return { success: false, error: "\uC218\uB3D9 \uC9C4\uB8CC \uC548\uB0B4 \uCE74\uB4DC\uC5D0\uB294 \uB300\uC0C1\uC774 \uD544\uC694\uD569\uB2C8\uB2E4." };
78
+ return { success: true, data: { sectionStates, primaryTermId: value.primaryTermId, cta: { mode: cta.mode, targetPostId: cta.targetPostId } } };
79
+ }
80
+ function contentEditorFromWire(raw) {
81
+ const result = parseContentEditorState(raw);
82
+ return result.success ? result.data : void 0;
83
+ }
84
+
62
85
  // src/server-post-wire.ts
63
86
  function patternSlotsFromWire(raw) {
64
87
  if (!raw || typeof raw !== "object" || Array.isArray(raw)) return {};
@@ -91,6 +114,7 @@ function relatedPostsFromWire(raw) {
91
114
  return out;
92
115
  }
93
116
  function postFromWire(wire) {
117
+ const contentEditor = contentEditorFromWire(wire.content_editor);
94
118
  const imagePosition = (value) => typeof value === "number" && Number.isInteger(value) && value >= 0 && value <= 100 ? value : null;
95
119
  return {
96
120
  id: wire.id,
@@ -99,6 +123,10 @@ function postFromWire(wire) {
99
123
  type: wire.type,
100
124
  collectionKey: wire.collection_key ?? null,
101
125
  modelKey: wire.model_key ?? null,
126
+ ...Number.isInteger(wire.content_version) && (wire.content_version ?? 0) > 0 ? { contentVersion: wire.content_version } : {},
127
+ ...wire.contract_page_key !== void 0 ? { contractPageKey: wire.contract_page_key } : {},
128
+ ...contentEditor ? { contentEditor } : {},
129
+ ...wire.content_editor !== void 0 ? { contentEditorResult: parseContentEditorState(wire.content_editor) } : {},
102
130
  slug: wire.slug,
103
131
  ...wire.locale !== void 0 ? { locale: wire.locale } : {},
104
132
  ...wire.translations !== void 0 ? { translations: wire.translations } : {},
@@ -261,6 +289,31 @@ async function fetchPageBySlug(options) {
261
289
  return null;
262
290
  }
263
291
 
292
+ // src/server-all-posts.ts
293
+ async function fetchAllPosts(options) {
294
+ const { maxPages, cursorGuard, ...query } = options;
295
+ if (!Number.isSafeInteger(maxPages) || maxPages < 1) {
296
+ throw new Error("CMS maxPages must be a positive safe integer.");
297
+ }
298
+ if (cursorGuard !== "non-advancing" && cursorGuard !== "repeated") {
299
+ throw new Error("CMS cursorGuard must be non-advancing or repeated.");
300
+ }
301
+ const posts = [];
302
+ const cursors = /* @__PURE__ */ new Set();
303
+ let cursor;
304
+ for (let page = 0; page < maxPages; page += 1) {
305
+ const result = await fetchPosts({ ...query, ...cursor ? { cursor } : {} });
306
+ posts.push(...result.items);
307
+ if (!result.hasMore) return posts;
308
+ if (!result.nextCursor || result.nextCursor === cursor || cursorGuard === "repeated" && cursors.has(result.nextCursor)) {
309
+ throw new Error("CMS \uD398\uC774\uC9C0 \uCEE4\uC11C\uAC00 \uC9C4\uD589\uB418\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.");
310
+ }
311
+ cursor = result.nextCursor;
312
+ cursors.add(cursor);
313
+ }
314
+ throw new Error("CMS \uCF58\uD150\uCE20 \uD398\uC774\uC9C0 \uC0C1\uD55C\uC744 \uCD08\uACFC\uD588\uC2B5\uB2C8\uB2E4.");
315
+ }
316
+
264
317
  // src/server-blog.ts
265
318
  var BLOG_SETTINGS_CACHE_TAG = "rt-blog-settings";
266
319
  async function fetchBlogSettings(options) {
@@ -601,7 +654,12 @@ function businessProfileFromWire(json) {
601
654
  const openingHours = [];
602
655
  for (const row of json.opening_hours ?? []) {
603
656
  if (Array.isArray(row.days) && row.days.length > 0 && typeof row.opens === "string" && typeof row.closes === "string") {
604
- openingHours.push({ days: row.days, opens: row.opens, closes: row.closes });
657
+ openingHours.push({
658
+ days: row.days,
659
+ opens: row.opens,
660
+ closes: row.closes,
661
+ ...row.reception_closes !== void 0 ? { receptionCloses: row.reception_closes } : {}
662
+ });
605
663
  }
606
664
  }
607
665
  return {
@@ -1112,6 +1170,9 @@ function codeForStatus(status) {
1112
1170
  function isPreviewExpiredError(error) {
1113
1171
  return error instanceof CmsApiError && (error.code === "preview_expired" || error.status === 410);
1114
1172
  }
1173
+ function isPreviewSupersededError(error) {
1174
+ return error instanceof CmsApiError && error.code === "preview_superseded";
1175
+ }
1115
1176
  async function fetchPostPreview(options) {
1116
1177
  assertServer();
1117
1178
  const token = options.token.trim();
@@ -1133,7 +1194,8 @@ async function fetchPostPreview(options) {
1133
1194
  ...postFromWire(json),
1134
1195
  preview: {
1135
1196
  expiresAt: json.preview.expires_at,
1136
- sourceStatus: json.preview.source_status
1197
+ sourceStatus: json.preview.source_status,
1198
+ ...json.preview.visual_target ? { visualTarget: json.preview.visual_target } : {}
1137
1199
  }
1138
1200
  };
1139
1201
  }
@@ -1741,6 +1803,7 @@ export {
1741
1803
  collectionPageSchema,
1742
1804
  contentIdMeta,
1743
1805
  faqSchema,
1806
+ fetchAllPosts,
1744
1807
  fetchAnalyticsConfig,
1745
1808
  fetchAuthors,
1746
1809
  fetchBlogSettings,
@@ -1769,6 +1832,7 @@ export {
1769
1832
  generateRssXml,
1770
1833
  inquiryErrorMessage,
1771
1834
  isPreviewExpiredError,
1835
+ isPreviewSupersededError,
1772
1836
  jsonLdJson,
1773
1837
  jsonLdScript,
1774
1838
  localBusinessSchema,
@@ -1777,6 +1841,7 @@ export {
1777
1841
  metadataContractChecksum,
1778
1842
  organizationSchema,
1779
1843
  parseAttributionJson,
1844
+ parseContentEditorState,
1780
1845
  parseInquiryRedirect,
1781
1846
  profilePageSchema,
1782
1847
  readMetadataValues,