@roottale/cms-client 0.53.0 → 0.55.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/dist/server.js CHANGED
@@ -4,7 +4,7 @@ import {
4
4
 
5
5
  // src/server-base.ts
6
6
  var DEFAULT_BASE_URL = "https://api.roottale.com";
7
- var CLIENT_VERSION = true ? "0.53.0" : "dev";
7
+ var CLIENT_VERSION = true ? "0.55.0" : "dev";
8
8
  var CMS_CLIENT_VERSION = CLIENT_VERSION;
9
9
  function authHeaders(apiKey) {
10
10
  return {
@@ -55,6 +55,65 @@ function assertServer() {
55
55
  }
56
56
  }
57
57
 
58
+ // src/server-post-wire.ts
59
+ function postFromWire(wire) {
60
+ return {
61
+ id: wire.id,
62
+ tenantId: wire.tenant_id,
63
+ siteId: wire.site_id,
64
+ type: wire.type,
65
+ collectionKey: wire.collection_key ?? null,
66
+ modelKey: wire.model_key ?? null,
67
+ slug: wire.slug,
68
+ ...wire.locale !== void 0 ? { locale: wire.locale } : {},
69
+ ...wire.translations !== void 0 ? { translations: wire.translations } : {},
70
+ ...wire.previous_slugs !== void 0 ? { previousSlugs: wire.previous_slugs } : {},
71
+ title: wire.title,
72
+ excerpt: wire.excerpt,
73
+ featuredMediaId: wire.featured_media_id,
74
+ featuredImageUrl: wire.featured_media_url ?? null,
75
+ authorProfileId: wire.author_profile_id ?? null,
76
+ authorId: wire.author_id,
77
+ authorName: wire.author_name ?? null,
78
+ authorImageUrl: wire.author_image_url ?? null,
79
+ authorBio: wire.author_bio ?? null,
80
+ authorSlug: wire.author_slug ?? null,
81
+ bodyJson: wire.body_json,
82
+ // 구 서버(필드 없음) → null → body_json 폴백 = 종전 동작 그대로.
83
+ bodyHtml: wire.body_html ?? null,
84
+ status: wire.status,
85
+ metaJson: wire.meta_json,
86
+ fields: wire.fields ?? null,
87
+ fieldsMeta: (wire.fields_meta ?? []).map((entry) => ({
88
+ name: entry.name,
89
+ label: entry.label,
90
+ type: entry.type,
91
+ ...entry.choices ? {
92
+ choices: entry.choices.map((c) => ({
93
+ value: c.value,
94
+ label: c.label
95
+ }))
96
+ } : {}
97
+ })),
98
+ terms: wire.terms ?? [],
99
+ archiveRelated: (wire.archive_related ?? []).map((group) => ({
100
+ collectionKey: group.collection_key,
101
+ label: group.label,
102
+ items: group.items.map((item) => ({
103
+ id: item.id,
104
+ title: item.title,
105
+ slug: item.slug,
106
+ excerpt: item.excerpt,
107
+ collectionKey: item.collection_key
108
+ }))
109
+ })),
110
+ ...typeof wire.view_count === "number" ? { viewCount: wire.view_count } : {},
111
+ publishedAt: wire.published_at,
112
+ createdAt: wire.created_at,
113
+ updatedAt: wire.updated_at
114
+ };
115
+ }
116
+
58
117
  // src/server-posts-theme.ts
59
118
  var THEME_CACHE_TAG = "rt-theme";
60
119
  async function fetchTheme(options) {
@@ -104,6 +163,7 @@ async function fetchPosts(options) {
104
163
  if (options.author) params.set("author", options.author);
105
164
  if (options.siteId) params.set("site_id", options.siteId);
106
165
  if (options.collectionKey) params.set("collection_key", options.collectionKey);
166
+ if (options.modelKey) params.set("model_key", options.modelKey);
107
167
  if (options.excludeCollections) params.set("exclude_collections", "true");
108
168
  if (options.locale) params.set("locale", options.locale);
109
169
  const qs = params.toString();
@@ -114,7 +174,7 @@ async function fetchPosts(options) {
114
174
  }
115
175
  const json = await response.json();
116
176
  return {
117
- items: json.items.map(fromWire),
177
+ items: json.items.map(postFromWire),
118
178
  hasMore: json.has_more,
119
179
  nextCursor: json.next_cursor
120
180
  };
@@ -133,7 +193,7 @@ async function fetchPost(options) {
133
193
  throw await toApiError(response);
134
194
  }
135
195
  const json = await response.json();
136
- return fromWire(json);
196
+ return postFromWire(json);
137
197
  }
138
198
  async function fetchPageBySlug(options) {
139
199
  let cursor;
@@ -155,58 +215,6 @@ async function fetchPageBySlug(options) {
155
215
  } while (cursor);
156
216
  return null;
157
217
  }
158
- function fromWire(wire) {
159
- return {
160
- id: wire.id,
161
- tenantId: wire.tenant_id,
162
- siteId: wire.site_id,
163
- type: wire.type,
164
- collectionKey: wire.collection_key ?? null,
165
- slug: wire.slug,
166
- ...wire.locale !== void 0 ? { locale: wire.locale } : {},
167
- ...wire.translations !== void 0 ? { translations: wire.translations } : {},
168
- title: wire.title,
169
- excerpt: wire.excerpt,
170
- featuredMediaId: wire.featured_media_id,
171
- featuredImageUrl: wire.featured_media_url ?? null,
172
- authorId: wire.author_id,
173
- authorName: wire.author_name ?? null,
174
- authorImageUrl: wire.author_image_url ?? null,
175
- authorBio: wire.author_bio ?? null,
176
- authorSlug: wire.author_slug ?? null,
177
- bodyJson: wire.body_json,
178
- status: wire.status,
179
- metaJson: wire.meta_json,
180
- fields: wire.fields ?? null,
181
- fieldsMeta: (wire.fields_meta ?? []).map((entry) => ({
182
- name: entry.name,
183
- label: entry.label,
184
- type: entry.type,
185
- ...entry.choices ? {
186
- choices: entry.choices.map((c) => ({
187
- value: c.value,
188
- label: c.label
189
- }))
190
- } : {}
191
- })),
192
- terms: wire.terms ?? [],
193
- archiveRelated: (wire.archive_related ?? []).map((group) => ({
194
- collectionKey: group.collection_key,
195
- label: group.label,
196
- items: group.items.map((item) => ({
197
- id: item.id,
198
- title: item.title,
199
- slug: item.slug,
200
- excerpt: item.excerpt,
201
- collectionKey: item.collection_key
202
- }))
203
- })),
204
- ...typeof wire.view_count === "number" ? { viewCount: wire.view_count } : {},
205
- publishedAt: wire.published_at,
206
- createdAt: wire.created_at,
207
- updatedAt: wire.updated_at
208
- };
209
- }
210
218
 
211
219
  // src/server-blog.ts
212
220
  var BLOG_SETTINGS_CACHE_TAG = "rt-blog-settings";
@@ -301,12 +309,59 @@ async function fetchCollections(options) {
301
309
  pinning: o.pinning === true,
302
310
  icon: typeof o.icon === "string" ? o.icon : null,
303
311
  // 기본 true(하위 호환) — 명시적으로 false 인 경우만 목록 전용.
304
- detail: o.detail !== false
312
+ detail: o.detail !== false,
313
+ detailPath: o.detail_path === "category" ? "category" : "flat",
314
+ categoryPath: o.category_path === "direct" ? "direct" : "namespaced",
315
+ categoryCardinality: o.category_cardinality === "exactly-one" ? "exactly-one" : "multiple"
305
316
  }
306
317
  ];
307
318
  });
308
319
  }
309
320
  var fetchCollectionsSafeDefault = [];
321
+ async function fetchContentModels(options) {
322
+ assertServer();
323
+ const baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
324
+ const params = new URLSearchParams();
325
+ if (options.siteId) params.set("site_id", options.siteId);
326
+ const qs = params.toString();
327
+ const response = await fetch(
328
+ `${baseUrl}/v1/cms/public/content-models${qs ? `?${qs}` : ""}`,
329
+ buildGetInit(options.apiKey, options)
330
+ );
331
+ if (!response.ok) throw await toApiError(response);
332
+ const json = await response.json();
333
+ return (Array.isArray(json.models) ? json.models : []).flatMap((model) => {
334
+ if (typeof model.key !== "string" || typeof model.label !== "string") return [];
335
+ const cardinality = model.cardinality === "singleton" ? "singleton" : "collection";
336
+ const preset = model.preset === "page" || model.preset === "article" || model.preset === "entity" ? model.preset : null;
337
+ const rawPresentation = model.presentation && typeof model.presentation === "object" && !Array.isArray(model.presentation) ? model.presentation : {};
338
+ const presentation = rawPresentation.kind === "fixed_page" && typeof rawPresentation.path === "string" ? {
339
+ kind: "fixed_page",
340
+ path: rawPresentation.path,
341
+ allowedSections: Array.isArray(rawPresentation.allowedSections) ? rawPresentation.allowedSections.filter(
342
+ (item) => typeof item === "string"
343
+ ) : [],
344
+ ...typeof rawPresentation.templateKey === "string" ? { templateKey: rawPresentation.templateKey } : {}
345
+ } : rawPresentation.kind === "detail" && typeof rawPresentation.detailPath === "string" ? {
346
+ kind: "detail",
347
+ detailPath: rawPresentation.detailPath,
348
+ ...typeof rawPresentation.templateKey === "string" ? { templateKey: rawPresentation.templateKey } : {}
349
+ } : rawPresentation.kind === "category_tree" && typeof rawPresentation.basePath === "string" && typeof rawPresentation.categoryDepth === "number" ? {
350
+ kind: "category_tree",
351
+ basePath: rawPresentation.basePath,
352
+ categoryDepth: rawPresentation.categoryDepth,
353
+ ...typeof rawPresentation.templateKey === "string" ? { templateKey: rawPresentation.templateKey } : {}
354
+ } : { kind: "data_only" };
355
+ return [{
356
+ key: model.key,
357
+ label: model.label,
358
+ cardinality,
359
+ preset,
360
+ source: model.source === "site" ? "site" : "code",
361
+ presentation
362
+ }];
363
+ });
364
+ }
310
365
  async function fetchAuthors(options) {
311
366
  assertServer();
312
367
  const baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
@@ -444,11 +499,20 @@ async function fetchCategoryCounts(options) {
444
499
  if (!slug || !name) return [];
445
500
  return [
446
501
  {
502
+ id: typeof o.id === "string" ? o.id : null,
447
503
  slug,
448
504
  name,
505
+ parentId: typeof o.parent_id === "string" ? o.parent_id : null,
449
506
  publishedPostCount: toNonNegativeInt(o.published_post_count),
450
507
  collectionKey: typeof o.collection_key === "string" ? o.collection_key : null,
451
- hubPromoted: o.hub_promoted === true
508
+ hubPromoted: o.hub_promoted === true,
509
+ description: typeof o.description === "string" ? o.description : null,
510
+ seoTitle: typeof o.seo_title === "string" ? o.seo_title : null,
511
+ seoDescription: typeof o.seo_description === "string" ? o.seo_description : null,
512
+ imageMediaId: typeof o.image_media_id === "string" ? o.image_media_id : null,
513
+ imageUrl: typeof o.image_url === "string" ? o.image_url : null,
514
+ imageAlt: typeof o.image_alt === "string" ? o.image_alt : null,
515
+ path: typeof o.path === "string" ? o.path : null
452
516
  }
453
517
  ];
454
518
  }),
@@ -522,6 +586,7 @@ async function searchPosts(options) {
522
586
  params.set("q", options.query);
523
587
  if (options.limit !== void 0) params.set("limit", String(options.limit));
524
588
  if (options.type) params.set("type", options.type);
589
+ if (options.locale) params.set("locale", options.locale);
525
590
  if (options.siteId) params.set("site_id", options.siteId);
526
591
  const url = `${baseUrl}/v1/cms/public/search?${params.toString()}`;
527
592
  const response = await fetch(url, buildGetInit(options.apiKey, options));
@@ -533,6 +598,9 @@ async function searchPosts(options) {
533
598
  return json.items.map((w) => ({
534
599
  id: w.id,
535
600
  type: w.type,
601
+ collectionKey: w.collection_key ?? null,
602
+ modelKey: w.model_key ?? null,
603
+ locale: w.locale,
536
604
  title: w.title,
537
605
  slug: w.slug,
538
606
  excerpt: w.excerpt,
@@ -540,6 +608,25 @@ async function searchPosts(options) {
540
608
  publishedAt: w.published_at
541
609
  }));
542
610
  }
611
+ function resolveSearchHitPath(hit, collections, locale) {
612
+ let path;
613
+ if (hit.type === "page") {
614
+ path = `/${hit.slug}`;
615
+ } else {
616
+ const collection = hit.collectionKey ? collections.find((candidate) => candidate.key === hit.collectionKey) : void 0;
617
+ if (!collection) {
618
+ path = `/blog/${hit.slug}`;
619
+ } else if (!collection.detail || collection.basePath.length === 0) {
620
+ path = null;
621
+ } else {
622
+ const basePath = `/${collection.basePath}`.replace(/\/{2,}/g, "/").replace(/\/$/, "");
623
+ path = `${basePath}/${hit.slug}`;
624
+ }
625
+ }
626
+ if (!path || !locale) return path;
627
+ const localePrefix = `/${locale.replace(/^\/+|\/+$/g, "")}`;
628
+ return `${localePrefix}${path}`;
629
+ }
543
630
  var MENUS_CACHE_TAG = "rt-menus";
544
631
  function menuItemFromWire(w) {
545
632
  const item = { id: w.id, label: w.label, url: w.url };
@@ -609,6 +696,39 @@ async function fetchRedirects(options) {
609
696
  }));
610
697
  }
611
698
 
699
+ // src/server-exposure.ts
700
+ var EXPOSURE_CACHE_TAG = "rt-exposures";
701
+ async function fetchExposure(options) {
702
+ assertServer();
703
+ const baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
704
+ const params = new URLSearchParams({ slot_key: options.slotKey, path: options.path });
705
+ if (options.siteId) params.set("site_id", options.siteId);
706
+ const response = await fetch(
707
+ `${baseUrl}/v1/cms/public/exposures?${params.toString()}`,
708
+ buildGetInit(options.apiKey, options)
709
+ );
710
+ if (!response.ok) throw await toApiError(response);
711
+ const json = await response.json();
712
+ const campaign = json.campaign;
713
+ if (!campaign) return null;
714
+ return {
715
+ id: campaign.id,
716
+ slotKey: campaign.slot_key,
717
+ kind: campaign.kind,
718
+ content: {
719
+ variant: campaign.content.variant,
720
+ ...campaign.content.eyebrow ? { eyebrow: campaign.content.eyebrow } : {},
721
+ title: campaign.content.title,
722
+ ...campaign.content.description ? { description: campaign.content.description } : {},
723
+ ...campaign.content.asset_url ? { assetUrl: campaign.content.asset_url } : {},
724
+ ...campaign.content.asset_alt ? { assetAlt: campaign.content.asset_alt } : {},
725
+ ...campaign.content.link_label ? { linkLabel: campaign.content.link_label } : {},
726
+ ...campaign.content.link_url ? { linkUrl: campaign.content.link_url } : {}
727
+ },
728
+ repeat: campaign.repeat
729
+ };
730
+ }
731
+
612
732
  // src/server-site-inquiries.ts
613
733
  async function fetchSiteInquiries(options) {
614
734
  assertServer();
@@ -694,13 +814,44 @@ function fromDetailWire(item) {
694
814
  };
695
815
  }
696
816
 
817
+ // src/server-preview.ts
818
+ function isPreviewExpiredError(error) {
819
+ return error instanceof CmsApiError && (error.code === "preview_expired" || error.status === 410);
820
+ }
821
+ async function fetchPostPreview(options) {
822
+ assertServer();
823
+ const token = options.token.trim();
824
+ if (!token) return null;
825
+ const baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
826
+ const params = new URLSearchParams({ token });
827
+ if (options.siteId) params.set("site_id", options.siteId);
828
+ const url = `${baseUrl}/v1/cms/public/posts/preview?${params.toString()}`;
829
+ const response = await fetch(url, {
830
+ ...buildGetInit(options.apiKey, { signal: options.signal }),
831
+ cache: "no-store"
832
+ });
833
+ if (response.status === 404) return null;
834
+ if (!response.ok) {
835
+ throw await toApiError(response);
836
+ }
837
+ const json = await response.json();
838
+ return {
839
+ ...postFromWire(json),
840
+ preview: {
841
+ expiresAt: json.preview.expires_at,
842
+ sourceStatus: json.preview.source_status
843
+ }
844
+ };
845
+ }
846
+
697
847
  // src/server-cache-tags.ts
698
848
  var SETTINGS_CACHE_TAGS = [
699
849
  THEME_CACHE_TAG,
700
850
  BUSINESS_CACHE_TAG,
701
851
  MENUS_CACHE_TAG,
702
852
  BLOG_SETTINGS_CACHE_TAG,
703
- COLLECTIONS_CACHE_TAG
853
+ COLLECTIONS_CACHE_TAG,
854
+ EXPOSURE_CACHE_TAG
704
855
  ];
705
856
 
706
857
  // src/seo.ts
@@ -1132,6 +1283,7 @@ export {
1132
1283
  COLLECTIONS_CACHE_TAG,
1133
1284
  CmsApiError,
1134
1285
  DEFAULT_BLOG_SETTINGS,
1286
+ EXPOSURE_CACHE_TAG,
1135
1287
  INQUIRY_ERROR_MESSAGES,
1136
1288
  LOCAL_BUSINESS_MAX_SERVICES,
1137
1289
  LOCAL_BUSINESS_SERVICE_MAX_LENGTH,
@@ -1151,10 +1303,13 @@ export {
1151
1303
  fetchCategoryCounts,
1152
1304
  fetchCollections,
1153
1305
  fetchCollectionsSafeDefault,
1306
+ fetchContentModels,
1307
+ fetchExposure,
1154
1308
  fetchMenu,
1155
1309
  fetchMenus,
1156
1310
  fetchPageBySlug,
1157
1311
  fetchPost,
1312
+ fetchPostPreview,
1158
1313
  fetchPosts,
1159
1314
  fetchRedirects,
1160
1315
  fetchSiteInquiries,
@@ -1163,6 +1318,7 @@ export {
1163
1318
  formatKoreanPhoneNumber,
1164
1319
  generateRssXml,
1165
1320
  inquiryErrorMessage,
1321
+ isPreviewExpiredError,
1166
1322
  jsonLdJson,
1167
1323
  jsonLdScript,
1168
1324
  localBusinessSchema,
@@ -1171,6 +1327,7 @@ export {
1171
1327
  parseInquiryRedirect,
1172
1328
  profilePageSchema,
1173
1329
  resolvePostDisplay,
1330
+ resolveSearchHitPath,
1174
1331
  rssItemsFromPosts,
1175
1332
  searchPosts,
1176
1333
  selectRelatedPosts,