@utilsy/cms-nextjs 0.1.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/index.cjs ADDED
@@ -0,0 +1,239 @@
1
+ 'use strict';
2
+
3
+ // src/blog/mappers.ts
4
+ function mediaUrl(value) {
5
+ if (!value) return void 0;
6
+ if (typeof value === "string") return value;
7
+ if (typeof value === "object" && value !== null && "url" in value) {
8
+ return String(value.url);
9
+ }
10
+ return void 0;
11
+ }
12
+ function mapCmsPostToBlogPost(dto) {
13
+ const data = dto.data ?? {};
14
+ const title = String(dto.title ?? data.title ?? "Untitled");
15
+ const slug = String(dto.slug ?? data.slug ?? dto.id);
16
+ const excerpt = String(dto.excerpt ?? data.excerpt ?? "");
17
+ const body = String(dto.content ?? data.content ?? "");
18
+ const tags = dto.tags ?? (Array.isArray(data.tags) ? data.tags : []);
19
+ const category = dto.categories?.[0]?.name ?? "General";
20
+ const publishedAt = String(
21
+ dto.published_at ?? data.published_at ?? dto.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
22
+ );
23
+ const authorName = String(data.author_name ?? "Editor");
24
+ const imageSrc = mediaUrl(dto.featured_image ?? data.featured_image);
25
+ const wordCount = body.replace(/<[^>]+>/g, " ").split(/\s+/).filter(Boolean).length;
26
+ return {
27
+ postId: dto.id,
28
+ slug,
29
+ title,
30
+ excerpt,
31
+ category,
32
+ tags,
33
+ publishedAt,
34
+ readMinutes: Math.max(1, Math.ceil(wordCount / 200)),
35
+ author: {
36
+ name: authorName,
37
+ avatarInitials: authorName.slice(0, 2).toUpperCase()
38
+ },
39
+ imageSrc,
40
+ imageAlt: title,
41
+ body,
42
+ stats: dto.stats
43
+ };
44
+ }
45
+ function mapCmsCommentToBlogComment(dto) {
46
+ return {
47
+ id: dto.id,
48
+ name: dto.name,
49
+ message: dto.message,
50
+ avatarUrl: dto.avatarUrl,
51
+ postedAt: dto.postedAt,
52
+ replies: (dto.replyComment ?? []).map((r) => ({
53
+ id: r.id,
54
+ userId: r.userId,
55
+ message: r.message,
56
+ postedAt: r.postedAt
57
+ }))
58
+ };
59
+ }
60
+ function mapCmsCategoryToBlogCategory(dto) {
61
+ return {
62
+ id: dto.id,
63
+ name: dto.name,
64
+ slug: dto.slug,
65
+ description: dto.description,
66
+ sortOrder: dto.sortOrder
67
+ };
68
+ }
69
+
70
+ // src/blog/endpoints.ts
71
+ function appendSiteId(path, siteId) {
72
+ if (!siteId) return path;
73
+ const sep = path.includes("?") ? "&" : "?";
74
+ return `${path}${sep}siteId=${encodeURIComponent(siteId)}`;
75
+ }
76
+ async function unwrap(res) {
77
+ const json = await res.json();
78
+ if (!res.ok) {
79
+ const message = json.message ?? `Request failed (${res.status})`;
80
+ throw new Error(message);
81
+ }
82
+ const data = json.data ?? json.data;
83
+ if (data === void 0) {
84
+ throw new Error("Response missing data");
85
+ }
86
+ return data;
87
+ }
88
+ async function publicGet(ctx, path, init) {
89
+ const url = `${ctx.baseUrl}${appendSiteId(path, ctx.siteId)}`;
90
+ const res = await ctx.fetchFn(url, {
91
+ ...init,
92
+ headers: {
93
+ "Content-Type": "application/json",
94
+ ...ctx.defaultHeaders,
95
+ ...init?.headers
96
+ }
97
+ });
98
+ return unwrap(res);
99
+ }
100
+ async function publicGetNullable(ctx, path, init) {
101
+ try {
102
+ return await publicGet(ctx, path, init);
103
+ } catch {
104
+ return null;
105
+ }
106
+ }
107
+ function createBlogApi(ctx) {
108
+ const prefix = ctx.blogBasePath;
109
+ return {
110
+ listPosts(query, init) {
111
+ const qs = new URLSearchParams();
112
+ if (query?.page) qs.set("page", String(query.page));
113
+ if (query?.limit) qs.set("limit", String(query.limit));
114
+ if (query?.search) qs.set("search", query.search);
115
+ if (query?.tag) qs.set("tag", query.tag);
116
+ const queryStr = qs.toString();
117
+ const path = `${prefix}/posts${queryStr ? `?${queryStr}` : ""}`;
118
+ return publicGetNullable(ctx, path, init);
119
+ },
120
+ getPostBySlug(slug, init) {
121
+ return publicGetNullable(
122
+ ctx,
123
+ `${prefix}/posts/${encodeURIComponent(slug)}`,
124
+ init
125
+ );
126
+ },
127
+ listCategories(init) {
128
+ return publicGetNullable(ctx, `${prefix}/categories`, init);
129
+ },
130
+ listComments(postId, init) {
131
+ return publicGetNullable(
132
+ ctx,
133
+ `${prefix}/posts/${encodeURIComponent(postId)}/comments`,
134
+ init
135
+ );
136
+ },
137
+ async createComment(postId, body) {
138
+ const url = `${ctx.baseUrl}${appendSiteId(
139
+ `${prefix}/posts/${encodeURIComponent(postId)}/comments`,
140
+ ctx.siteId
141
+ )}`;
142
+ const res = await ctx.fetchFn(url, {
143
+ method: "POST",
144
+ headers: {
145
+ "Content-Type": "application/json",
146
+ ...ctx.defaultHeaders
147
+ },
148
+ body: JSON.stringify(body)
149
+ });
150
+ return unwrap(res);
151
+ },
152
+ getEngagement(postId, options, init) {
153
+ return publicGetNullable(
154
+ ctx,
155
+ `${prefix}/posts/${encodeURIComponent(postId)}/engagement`,
156
+ {
157
+ ...init,
158
+ headers: {
159
+ ...init?.headers,
160
+ ...options?.visitorId ? { "x-visitor-id": options.visitorId } : {}
161
+ }
162
+ }
163
+ );
164
+ },
165
+ async toggleLike(postId, options) {
166
+ const url = `${ctx.baseUrl}${appendSiteId(
167
+ `${prefix}/posts/${encodeURIComponent(postId)}/like`,
168
+ ctx.siteId
169
+ )}`;
170
+ const res = await ctx.fetchFn(url, {
171
+ method: "POST",
172
+ headers: {
173
+ "Content-Type": "application/json",
174
+ ...ctx.defaultHeaders,
175
+ ...options?.visitorId ? { "x-visitor-id": options.visitorId } : {}
176
+ }
177
+ });
178
+ return unwrap(res);
179
+ },
180
+ async listMappedPosts(query, init) {
181
+ const page = await this.listPosts(query, init);
182
+ if (!page?.docs?.length) return null;
183
+ return {
184
+ posts: page.docs.map(mapCmsPostToBlogPost),
185
+ total: page.total,
186
+ page: page.page,
187
+ limit: page.limit
188
+ };
189
+ },
190
+ async getMappedPostBySlug(slug, init) {
191
+ const dto = await this.getPostBySlug(slug, init);
192
+ if (!dto) return null;
193
+ return mapCmsPostToBlogPost(dto);
194
+ },
195
+ async listMappedCategories(init) {
196
+ const dtos = await this.listCategories(init);
197
+ if (!dtos) return null;
198
+ return dtos.map(mapCmsCategoryToBlogCategory);
199
+ },
200
+ async listMappedComments(postId, init) {
201
+ const dtos = await this.listComments(postId, init);
202
+ if (!dtos) return null;
203
+ return dtos.map(mapCmsCommentToBlogComment);
204
+ }
205
+ };
206
+ }
207
+
208
+ // src/client.ts
209
+ function normalizeBaseUrl(url) {
210
+ return url.replace(/\/$/, "");
211
+ }
212
+ function normalizePathPrefix(prefix) {
213
+ const trimmed = prefix.replace(/\/$/, "");
214
+ if (!trimmed) return "";
215
+ return trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
216
+ }
217
+ function createCmsClient(config) {
218
+ const baseUrl = normalizeBaseUrl(config.baseUrl);
219
+ const pathPrefix = normalizePathPrefix(config.pathPrefix ?? "");
220
+ const blogBasePath = `${pathPrefix}/public/blog`.replace(/\/+/g, "/");
221
+ const ctx = {
222
+ baseUrl,
223
+ blogBasePath,
224
+ siteId: config.siteId,
225
+ fetchFn: config.fetch ?? globalThis.fetch.bind(globalThis),
226
+ defaultHeaders: config.defaultHeaders ?? {}
227
+ };
228
+ return {
229
+ blog: createBlogApi(ctx)
230
+ };
231
+ }
232
+
233
+ exports.createCmsClient = createCmsClient;
234
+ exports.mapCmsCategoryToBlogCategory = mapCmsCategoryToBlogCategory;
235
+ exports.mapCmsCommentToBlogComment = mapCmsCommentToBlogComment;
236
+ exports.mapCmsPostToBlogPost = mapCmsPostToBlogPost;
237
+ exports.mediaUrl = mediaUrl;
238
+ //# sourceMappingURL=index.cjs.map
239
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/blog/mappers.ts","../src/blog/endpoints.ts","../src/client.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["import type {\n BlogCategory,\n BlogComment,\n BlogPost,\n CmsBlogCategoryDto,\n CmsBlogCommentDto,\n CmsBlogPostDto,\n} from \"./types.js\";\n\nexport function mediaUrl(value: unknown): string | undefined {\n if (!value) return undefined;\n if (typeof value === \"string\") return value;\n if (typeof value === \"object\" && value !== null && \"url\" in value) {\n return String((value as { url: string }).url);\n }\n return undefined;\n}\n\nexport function mapCmsPostToBlogPost(dto: CmsBlogPostDto): BlogPost {\n const data = dto.data ?? {};\n const title = String(dto.title ?? data.title ?? \"Untitled\");\n const slug = String(dto.slug ?? data.slug ?? dto.id);\n const excerpt = String(dto.excerpt ?? data.excerpt ?? \"\");\n const body = String(dto.content ?? data.content ?? \"\");\n const tags = dto.tags ?? (Array.isArray(data.tags) ? (data.tags as string[]) : []);\n const category = dto.categories?.[0]?.name ?? \"General\";\n const publishedAt = String(\n dto.published_at ?? data.published_at ?? dto.createdAt ?? new Date().toISOString(),\n );\n const authorName = String(data.author_name ?? \"Editor\");\n const imageSrc = mediaUrl(dto.featured_image ?? data.featured_image);\n\n const wordCount = body.replace(/<[^>]+>/g, \" \").split(/\\s+/).filter(Boolean).length;\n\n return {\n postId: dto.id,\n slug,\n title,\n excerpt,\n category,\n tags,\n publishedAt,\n readMinutes: Math.max(1, Math.ceil(wordCount / 200)),\n author: {\n name: authorName,\n avatarInitials: authorName.slice(0, 2).toUpperCase(),\n },\n imageSrc,\n imageAlt: title,\n body,\n stats: dto.stats,\n };\n}\n\nexport function mapCmsCommentToBlogComment(dto: CmsBlogCommentDto): BlogComment {\n return {\n id: dto.id,\n name: dto.name,\n message: dto.message,\n avatarUrl: dto.avatarUrl,\n postedAt: dto.postedAt,\n replies: (dto.replyComment ?? []).map((r) => ({\n id: r.id,\n userId: r.userId,\n message: r.message,\n postedAt: r.postedAt,\n })),\n };\n}\n\nexport function mapCmsCategoryToBlogCategory(dto: CmsBlogCategoryDto): BlogCategory {\n return {\n id: dto.id,\n name: dto.name,\n slug: dto.slug,\n description: dto.description,\n sortOrder: dto.sortOrder,\n };\n}\n","import type { CmsApiResponse, FetchRequestInit } from \"../types.js\";\nimport {\n mapCmsCategoryToBlogCategory,\n mapCmsCommentToBlogComment,\n mapCmsPostToBlogPost,\n} from \"./mappers.js\";\nimport type {\n BlogCategory,\n BlogComment,\n BlogEngagement,\n BlogLikeResult,\n BlogPost,\n CmsBlogCategoryDto,\n CmsBlogCommentDto,\n CmsBlogPostDto,\n CmsBlogPostsPage,\n CreateBlogCommentInput,\n CreateBlogCommentResult,\n ListBlogPostsQuery,\n} from \"./types.js\";\n\nexport type BlogRequestContext = {\n baseUrl: string;\n blogBasePath: string;\n siteId?: string;\n fetchFn: typeof fetch;\n defaultHeaders: Record<string, string>;\n};\n\nfunction appendSiteId(path: string, siteId?: string): string {\n if (!siteId) return path;\n const sep = path.includes(\"?\") ? \"&\" : \"?\";\n return `${path}${sep}siteId=${encodeURIComponent(siteId)}`;\n}\n\nasync function unwrap<T>(res: Response): Promise<T> {\n const json = (await res.json()) as CmsApiResponse<T> | { data: T };\n if (!res.ok) {\n const message =\n (json as CmsApiResponse<T>).message ??\n (`Request failed (${res.status})` as string);\n throw new Error(message);\n }\n const data = (json as CmsApiResponse<T>).data ?? (json as { data: T }).data;\n if (data === undefined) {\n throw new Error(\"Response missing data\");\n }\n return data;\n}\n\nasync function publicGet<T>(\n ctx: BlogRequestContext,\n path: string,\n init?: FetchRequestInit,\n): Promise<T> {\n const url = `${ctx.baseUrl}${appendSiteId(path, ctx.siteId)}`;\n const res = await ctx.fetchFn(url, {\n ...init,\n headers: {\n \"Content-Type\": \"application/json\",\n ...ctx.defaultHeaders,\n ...(init?.headers as Record<string, string> | undefined),\n },\n });\n return unwrap<T>(res);\n}\n\nasync function publicGetNullable<T>(\n ctx: BlogRequestContext,\n path: string,\n init?: FetchRequestInit,\n): Promise<T | null> {\n try {\n return await publicGet<T>(ctx, path, init);\n } catch {\n return null;\n }\n}\n\nexport function createBlogApi(ctx: BlogRequestContext) {\n const prefix = ctx.blogBasePath;\n\n return {\n listPosts(query?: ListBlogPostsQuery, init?: FetchRequestInit) {\n const qs = new URLSearchParams();\n if (query?.page) qs.set(\"page\", String(query.page));\n if (query?.limit) qs.set(\"limit\", String(query.limit));\n if (query?.search) qs.set(\"search\", query.search);\n if (query?.tag) qs.set(\"tag\", query.tag);\n const queryStr = qs.toString();\n const path = `${prefix}/posts${queryStr ? `?${queryStr}` : \"\"}`;\n return publicGetNullable<CmsBlogPostsPage>(ctx, path, init);\n },\n\n getPostBySlug(slug: string, init?: FetchRequestInit) {\n return publicGetNullable<CmsBlogPostDto>(\n ctx,\n `${prefix}/posts/${encodeURIComponent(slug)}`,\n init,\n );\n },\n\n listCategories(init?: FetchRequestInit) {\n return publicGetNullable<CmsBlogCategoryDto[]>(ctx, `${prefix}/categories`, init);\n },\n\n listComments(postId: string, init?: FetchRequestInit) {\n return publicGetNullable<CmsBlogCommentDto[]>(\n ctx,\n `${prefix}/posts/${encodeURIComponent(postId)}/comments`,\n init,\n );\n },\n\n async createComment(postId: string, body: CreateBlogCommentInput) {\n const url = `${ctx.baseUrl}${appendSiteId(\n `${prefix}/posts/${encodeURIComponent(postId)}/comments`,\n ctx.siteId,\n )}`;\n const res = await ctx.fetchFn(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n ...ctx.defaultHeaders,\n },\n body: JSON.stringify(body),\n });\n return unwrap<CreateBlogCommentResult>(res);\n },\n\n getEngagement(postId: string, options?: { visitorId?: string }, init?: FetchRequestInit) {\n return publicGetNullable<BlogEngagement>(\n ctx,\n `${prefix}/posts/${encodeURIComponent(postId)}/engagement`,\n {\n ...init,\n headers: {\n ...(init?.headers as Record<string, string> | undefined),\n ...(options?.visitorId ? { \"x-visitor-id\": options.visitorId } : {}),\n },\n },\n );\n },\n\n async toggleLike(postId: string, options?: { visitorId?: string }) {\n const url = `${ctx.baseUrl}${appendSiteId(\n `${prefix}/posts/${encodeURIComponent(postId)}/like`,\n ctx.siteId,\n )}`;\n const res = await ctx.fetchFn(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n ...ctx.defaultHeaders,\n ...(options?.visitorId ? { \"x-visitor-id\": options.visitorId } : {}),\n },\n });\n return unwrap<BlogLikeResult>(res);\n },\n\n async listMappedPosts(query?: ListBlogPostsQuery, init?: FetchRequestInit) {\n const page = await this.listPosts(query, init);\n if (!page?.docs?.length) return null;\n return {\n posts: page.docs.map(mapCmsPostToBlogPost),\n total: page.total,\n page: page.page,\n limit: page.limit,\n };\n },\n\n async getMappedPostBySlug(slug: string, init?: FetchRequestInit): Promise<BlogPost | null> {\n const dto = await this.getPostBySlug(slug, init);\n if (!dto) return null;\n return mapCmsPostToBlogPost(dto);\n },\n\n async listMappedCategories(init?: FetchRequestInit): Promise<BlogCategory[] | null> {\n const dtos = await this.listCategories(init);\n if (!dtos) return null;\n return dtos.map(mapCmsCategoryToBlogCategory);\n },\n\n async listMappedComments(postId: string, init?: FetchRequestInit): Promise<BlogComment[] | null> {\n const dtos = await this.listComments(postId, init);\n if (!dtos) return null;\n return dtos.map(mapCmsCommentToBlogComment);\n },\n };\n}\n\nexport type BlogApi = ReturnType<typeof createBlogApi>;\n","import { createBlogApi } from \"./blog/endpoints.js\";\nimport type { BlogApi } from \"./blog/endpoints.js\";\n\nexport type CmsClientConfig = {\n /** Base URL, e.g. https://cms-gateway.example.com */\n baseUrl: string;\n /** CMS site Mongo id — appended as ?siteId= on every blog request */\n siteId?: string;\n /**\n * Path prefix before /public/blog.\n * Default '' for gateway-cms direct (/public/blog/...).\n * Use '/api/backend/cms' when routing through the main API gateway.\n */\n pathPrefix?: string;\n fetch?: typeof fetch;\n defaultHeaders?: Record<string, string>;\n};\n\nexport type CmsClient = {\n blog: BlogApi;\n};\n\nfunction normalizeBaseUrl(url: string): string {\n return url.replace(/\\/$/, \"\");\n}\n\nfunction normalizePathPrefix(prefix: string): string {\n const trimmed = prefix.replace(/\\/$/, \"\");\n if (!trimmed) return \"\";\n return trimmed.startsWith(\"/\") ? trimmed : `/${trimmed}`;\n}\n\nexport function createCmsClient(config: CmsClientConfig): CmsClient {\n const baseUrl = normalizeBaseUrl(config.baseUrl);\n const pathPrefix = normalizePathPrefix(config.pathPrefix ?? \"\");\n const blogBasePath = `${pathPrefix}/public/blog`.replace(/\\/+/g, \"/\");\n\n const ctx = {\n baseUrl,\n blogBasePath,\n siteId: config.siteId,\n fetchFn: config.fetch ?? globalThis.fetch.bind(globalThis),\n defaultHeaders: config.defaultHeaders ?? {},\n };\n\n return {\n blog: createBlogApi(ctx),\n };\n}\n"]}
@@ -0,0 +1,9 @@
1
+ import { h as CmsBlogCategoryDto, b as BlogCategory, i as CmsBlogCommentDto, c as BlogComment, j as CmsBlogPostDto, g as BlogPost } from './client-C3E94syg.cjs';
2
+ export { B as BlogApi, a as BlogAuthor, d as BlogCommentReply, e as BlogEngagement, f as BlogLikeResult, C as CmsApiResponse, k as CmsBlogPostsPage, l as CmsClient, m as CmsClientConfig, n as CreateBlogCommentInput, o as CreateBlogCommentResult, F as FetchRequestInit, L as ListBlogPostsQuery, p as createCmsClient } from './client-C3E94syg.cjs';
3
+
4
+ declare function mediaUrl(value: unknown): string | undefined;
5
+ declare function mapCmsPostToBlogPost(dto: CmsBlogPostDto): BlogPost;
6
+ declare function mapCmsCommentToBlogComment(dto: CmsBlogCommentDto): BlogComment;
7
+ declare function mapCmsCategoryToBlogCategory(dto: CmsBlogCategoryDto): BlogCategory;
8
+
9
+ export { BlogCategory, BlogComment, BlogPost, CmsBlogCategoryDto, CmsBlogCommentDto, CmsBlogPostDto, mapCmsCategoryToBlogCategory, mapCmsCommentToBlogComment, mapCmsPostToBlogPost, mediaUrl };
@@ -0,0 +1,9 @@
1
+ import { h as CmsBlogCategoryDto, b as BlogCategory, i as CmsBlogCommentDto, c as BlogComment, j as CmsBlogPostDto, g as BlogPost } from './client-C3E94syg.js';
2
+ export { B as BlogApi, a as BlogAuthor, d as BlogCommentReply, e as BlogEngagement, f as BlogLikeResult, C as CmsApiResponse, k as CmsBlogPostsPage, l as CmsClient, m as CmsClientConfig, n as CreateBlogCommentInput, o as CreateBlogCommentResult, F as FetchRequestInit, L as ListBlogPostsQuery, p as createCmsClient } from './client-C3E94syg.js';
3
+
4
+ declare function mediaUrl(value: unknown): string | undefined;
5
+ declare function mapCmsPostToBlogPost(dto: CmsBlogPostDto): BlogPost;
6
+ declare function mapCmsCommentToBlogComment(dto: CmsBlogCommentDto): BlogComment;
7
+ declare function mapCmsCategoryToBlogCategory(dto: CmsBlogCategoryDto): BlogCategory;
8
+
9
+ export { BlogCategory, BlogComment, BlogPost, CmsBlogCategoryDto, CmsBlogCommentDto, CmsBlogPostDto, mapCmsCategoryToBlogCategory, mapCmsCommentToBlogComment, mapCmsPostToBlogPost, mediaUrl };
package/dist/index.js ADDED
@@ -0,0 +1,233 @@
1
+ // src/blog/mappers.ts
2
+ function mediaUrl(value) {
3
+ if (!value) return void 0;
4
+ if (typeof value === "string") return value;
5
+ if (typeof value === "object" && value !== null && "url" in value) {
6
+ return String(value.url);
7
+ }
8
+ return void 0;
9
+ }
10
+ function mapCmsPostToBlogPost(dto) {
11
+ const data = dto.data ?? {};
12
+ const title = String(dto.title ?? data.title ?? "Untitled");
13
+ const slug = String(dto.slug ?? data.slug ?? dto.id);
14
+ const excerpt = String(dto.excerpt ?? data.excerpt ?? "");
15
+ const body = String(dto.content ?? data.content ?? "");
16
+ const tags = dto.tags ?? (Array.isArray(data.tags) ? data.tags : []);
17
+ const category = dto.categories?.[0]?.name ?? "General";
18
+ const publishedAt = String(
19
+ dto.published_at ?? data.published_at ?? dto.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
20
+ );
21
+ const authorName = String(data.author_name ?? "Editor");
22
+ const imageSrc = mediaUrl(dto.featured_image ?? data.featured_image);
23
+ const wordCount = body.replace(/<[^>]+>/g, " ").split(/\s+/).filter(Boolean).length;
24
+ return {
25
+ postId: dto.id,
26
+ slug,
27
+ title,
28
+ excerpt,
29
+ category,
30
+ tags,
31
+ publishedAt,
32
+ readMinutes: Math.max(1, Math.ceil(wordCount / 200)),
33
+ author: {
34
+ name: authorName,
35
+ avatarInitials: authorName.slice(0, 2).toUpperCase()
36
+ },
37
+ imageSrc,
38
+ imageAlt: title,
39
+ body,
40
+ stats: dto.stats
41
+ };
42
+ }
43
+ function mapCmsCommentToBlogComment(dto) {
44
+ return {
45
+ id: dto.id,
46
+ name: dto.name,
47
+ message: dto.message,
48
+ avatarUrl: dto.avatarUrl,
49
+ postedAt: dto.postedAt,
50
+ replies: (dto.replyComment ?? []).map((r) => ({
51
+ id: r.id,
52
+ userId: r.userId,
53
+ message: r.message,
54
+ postedAt: r.postedAt
55
+ }))
56
+ };
57
+ }
58
+ function mapCmsCategoryToBlogCategory(dto) {
59
+ return {
60
+ id: dto.id,
61
+ name: dto.name,
62
+ slug: dto.slug,
63
+ description: dto.description,
64
+ sortOrder: dto.sortOrder
65
+ };
66
+ }
67
+
68
+ // src/blog/endpoints.ts
69
+ function appendSiteId(path, siteId) {
70
+ if (!siteId) return path;
71
+ const sep = path.includes("?") ? "&" : "?";
72
+ return `${path}${sep}siteId=${encodeURIComponent(siteId)}`;
73
+ }
74
+ async function unwrap(res) {
75
+ const json = await res.json();
76
+ if (!res.ok) {
77
+ const message = json.message ?? `Request failed (${res.status})`;
78
+ throw new Error(message);
79
+ }
80
+ const data = json.data ?? json.data;
81
+ if (data === void 0) {
82
+ throw new Error("Response missing data");
83
+ }
84
+ return data;
85
+ }
86
+ async function publicGet(ctx, path, init) {
87
+ const url = `${ctx.baseUrl}${appendSiteId(path, ctx.siteId)}`;
88
+ const res = await ctx.fetchFn(url, {
89
+ ...init,
90
+ headers: {
91
+ "Content-Type": "application/json",
92
+ ...ctx.defaultHeaders,
93
+ ...init?.headers
94
+ }
95
+ });
96
+ return unwrap(res);
97
+ }
98
+ async function publicGetNullable(ctx, path, init) {
99
+ try {
100
+ return await publicGet(ctx, path, init);
101
+ } catch {
102
+ return null;
103
+ }
104
+ }
105
+ function createBlogApi(ctx) {
106
+ const prefix = ctx.blogBasePath;
107
+ return {
108
+ listPosts(query, init) {
109
+ const qs = new URLSearchParams();
110
+ if (query?.page) qs.set("page", String(query.page));
111
+ if (query?.limit) qs.set("limit", String(query.limit));
112
+ if (query?.search) qs.set("search", query.search);
113
+ if (query?.tag) qs.set("tag", query.tag);
114
+ const queryStr = qs.toString();
115
+ const path = `${prefix}/posts${queryStr ? `?${queryStr}` : ""}`;
116
+ return publicGetNullable(ctx, path, init);
117
+ },
118
+ getPostBySlug(slug, init) {
119
+ return publicGetNullable(
120
+ ctx,
121
+ `${prefix}/posts/${encodeURIComponent(slug)}`,
122
+ init
123
+ );
124
+ },
125
+ listCategories(init) {
126
+ return publicGetNullable(ctx, `${prefix}/categories`, init);
127
+ },
128
+ listComments(postId, init) {
129
+ return publicGetNullable(
130
+ ctx,
131
+ `${prefix}/posts/${encodeURIComponent(postId)}/comments`,
132
+ init
133
+ );
134
+ },
135
+ async createComment(postId, body) {
136
+ const url = `${ctx.baseUrl}${appendSiteId(
137
+ `${prefix}/posts/${encodeURIComponent(postId)}/comments`,
138
+ ctx.siteId
139
+ )}`;
140
+ const res = await ctx.fetchFn(url, {
141
+ method: "POST",
142
+ headers: {
143
+ "Content-Type": "application/json",
144
+ ...ctx.defaultHeaders
145
+ },
146
+ body: JSON.stringify(body)
147
+ });
148
+ return unwrap(res);
149
+ },
150
+ getEngagement(postId, options, init) {
151
+ return publicGetNullable(
152
+ ctx,
153
+ `${prefix}/posts/${encodeURIComponent(postId)}/engagement`,
154
+ {
155
+ ...init,
156
+ headers: {
157
+ ...init?.headers,
158
+ ...options?.visitorId ? { "x-visitor-id": options.visitorId } : {}
159
+ }
160
+ }
161
+ );
162
+ },
163
+ async toggleLike(postId, options) {
164
+ const url = `${ctx.baseUrl}${appendSiteId(
165
+ `${prefix}/posts/${encodeURIComponent(postId)}/like`,
166
+ ctx.siteId
167
+ )}`;
168
+ const res = await ctx.fetchFn(url, {
169
+ method: "POST",
170
+ headers: {
171
+ "Content-Type": "application/json",
172
+ ...ctx.defaultHeaders,
173
+ ...options?.visitorId ? { "x-visitor-id": options.visitorId } : {}
174
+ }
175
+ });
176
+ return unwrap(res);
177
+ },
178
+ async listMappedPosts(query, init) {
179
+ const page = await this.listPosts(query, init);
180
+ if (!page?.docs?.length) return null;
181
+ return {
182
+ posts: page.docs.map(mapCmsPostToBlogPost),
183
+ total: page.total,
184
+ page: page.page,
185
+ limit: page.limit
186
+ };
187
+ },
188
+ async getMappedPostBySlug(slug, init) {
189
+ const dto = await this.getPostBySlug(slug, init);
190
+ if (!dto) return null;
191
+ return mapCmsPostToBlogPost(dto);
192
+ },
193
+ async listMappedCategories(init) {
194
+ const dtos = await this.listCategories(init);
195
+ if (!dtos) return null;
196
+ return dtos.map(mapCmsCategoryToBlogCategory);
197
+ },
198
+ async listMappedComments(postId, init) {
199
+ const dtos = await this.listComments(postId, init);
200
+ if (!dtos) return null;
201
+ return dtos.map(mapCmsCommentToBlogComment);
202
+ }
203
+ };
204
+ }
205
+
206
+ // src/client.ts
207
+ function normalizeBaseUrl(url) {
208
+ return url.replace(/\/$/, "");
209
+ }
210
+ function normalizePathPrefix(prefix) {
211
+ const trimmed = prefix.replace(/\/$/, "");
212
+ if (!trimmed) return "";
213
+ return trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
214
+ }
215
+ function createCmsClient(config) {
216
+ const baseUrl = normalizeBaseUrl(config.baseUrl);
217
+ const pathPrefix = normalizePathPrefix(config.pathPrefix ?? "");
218
+ const blogBasePath = `${pathPrefix}/public/blog`.replace(/\/+/g, "/");
219
+ const ctx = {
220
+ baseUrl,
221
+ blogBasePath,
222
+ siteId: config.siteId,
223
+ fetchFn: config.fetch ?? globalThis.fetch.bind(globalThis),
224
+ defaultHeaders: config.defaultHeaders ?? {}
225
+ };
226
+ return {
227
+ blog: createBlogApi(ctx)
228
+ };
229
+ }
230
+
231
+ export { createCmsClient, mapCmsCategoryToBlogCategory, mapCmsCommentToBlogComment, mapCmsPostToBlogPost, mediaUrl };
232
+ //# sourceMappingURL=index.js.map
233
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/blog/mappers.ts","../src/blog/endpoints.ts","../src/client.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type {\n BlogCategory,\n BlogComment,\n BlogPost,\n CmsBlogCategoryDto,\n CmsBlogCommentDto,\n CmsBlogPostDto,\n} from \"./types.js\";\n\nexport function mediaUrl(value: unknown): string | undefined {\n if (!value) return undefined;\n if (typeof value === \"string\") return value;\n if (typeof value === \"object\" && value !== null && \"url\" in value) {\n return String((value as { url: string }).url);\n }\n return undefined;\n}\n\nexport function mapCmsPostToBlogPost(dto: CmsBlogPostDto): BlogPost {\n const data = dto.data ?? {};\n const title = String(dto.title ?? data.title ?? \"Untitled\");\n const slug = String(dto.slug ?? data.slug ?? dto.id);\n const excerpt = String(dto.excerpt ?? data.excerpt ?? \"\");\n const body = String(dto.content ?? data.content ?? \"\");\n const tags = dto.tags ?? (Array.isArray(data.tags) ? (data.tags as string[]) : []);\n const category = dto.categories?.[0]?.name ?? \"General\";\n const publishedAt = String(\n dto.published_at ?? data.published_at ?? dto.createdAt ?? new Date().toISOString(),\n );\n const authorName = String(data.author_name ?? \"Editor\");\n const imageSrc = mediaUrl(dto.featured_image ?? data.featured_image);\n\n const wordCount = body.replace(/<[^>]+>/g, \" \").split(/\\s+/).filter(Boolean).length;\n\n return {\n postId: dto.id,\n slug,\n title,\n excerpt,\n category,\n tags,\n publishedAt,\n readMinutes: Math.max(1, Math.ceil(wordCount / 200)),\n author: {\n name: authorName,\n avatarInitials: authorName.slice(0, 2).toUpperCase(),\n },\n imageSrc,\n imageAlt: title,\n body,\n stats: dto.stats,\n };\n}\n\nexport function mapCmsCommentToBlogComment(dto: CmsBlogCommentDto): BlogComment {\n return {\n id: dto.id,\n name: dto.name,\n message: dto.message,\n avatarUrl: dto.avatarUrl,\n postedAt: dto.postedAt,\n replies: (dto.replyComment ?? []).map((r) => ({\n id: r.id,\n userId: r.userId,\n message: r.message,\n postedAt: r.postedAt,\n })),\n };\n}\n\nexport function mapCmsCategoryToBlogCategory(dto: CmsBlogCategoryDto): BlogCategory {\n return {\n id: dto.id,\n name: dto.name,\n slug: dto.slug,\n description: dto.description,\n sortOrder: dto.sortOrder,\n };\n}\n","import type { CmsApiResponse, FetchRequestInit } from \"../types.js\";\nimport {\n mapCmsCategoryToBlogCategory,\n mapCmsCommentToBlogComment,\n mapCmsPostToBlogPost,\n} from \"./mappers.js\";\nimport type {\n BlogCategory,\n BlogComment,\n BlogEngagement,\n BlogLikeResult,\n BlogPost,\n CmsBlogCategoryDto,\n CmsBlogCommentDto,\n CmsBlogPostDto,\n CmsBlogPostsPage,\n CreateBlogCommentInput,\n CreateBlogCommentResult,\n ListBlogPostsQuery,\n} from \"./types.js\";\n\nexport type BlogRequestContext = {\n baseUrl: string;\n blogBasePath: string;\n siteId?: string;\n fetchFn: typeof fetch;\n defaultHeaders: Record<string, string>;\n};\n\nfunction appendSiteId(path: string, siteId?: string): string {\n if (!siteId) return path;\n const sep = path.includes(\"?\") ? \"&\" : \"?\";\n return `${path}${sep}siteId=${encodeURIComponent(siteId)}`;\n}\n\nasync function unwrap<T>(res: Response): Promise<T> {\n const json = (await res.json()) as CmsApiResponse<T> | { data: T };\n if (!res.ok) {\n const message =\n (json as CmsApiResponse<T>).message ??\n (`Request failed (${res.status})` as string);\n throw new Error(message);\n }\n const data = (json as CmsApiResponse<T>).data ?? (json as { data: T }).data;\n if (data === undefined) {\n throw new Error(\"Response missing data\");\n }\n return data;\n}\n\nasync function publicGet<T>(\n ctx: BlogRequestContext,\n path: string,\n init?: FetchRequestInit,\n): Promise<T> {\n const url = `${ctx.baseUrl}${appendSiteId(path, ctx.siteId)}`;\n const res = await ctx.fetchFn(url, {\n ...init,\n headers: {\n \"Content-Type\": \"application/json\",\n ...ctx.defaultHeaders,\n ...(init?.headers as Record<string, string> | undefined),\n },\n });\n return unwrap<T>(res);\n}\n\nasync function publicGetNullable<T>(\n ctx: BlogRequestContext,\n path: string,\n init?: FetchRequestInit,\n): Promise<T | null> {\n try {\n return await publicGet<T>(ctx, path, init);\n } catch {\n return null;\n }\n}\n\nexport function createBlogApi(ctx: BlogRequestContext) {\n const prefix = ctx.blogBasePath;\n\n return {\n listPosts(query?: ListBlogPostsQuery, init?: FetchRequestInit) {\n const qs = new URLSearchParams();\n if (query?.page) qs.set(\"page\", String(query.page));\n if (query?.limit) qs.set(\"limit\", String(query.limit));\n if (query?.search) qs.set(\"search\", query.search);\n if (query?.tag) qs.set(\"tag\", query.tag);\n const queryStr = qs.toString();\n const path = `${prefix}/posts${queryStr ? `?${queryStr}` : \"\"}`;\n return publicGetNullable<CmsBlogPostsPage>(ctx, path, init);\n },\n\n getPostBySlug(slug: string, init?: FetchRequestInit) {\n return publicGetNullable<CmsBlogPostDto>(\n ctx,\n `${prefix}/posts/${encodeURIComponent(slug)}`,\n init,\n );\n },\n\n listCategories(init?: FetchRequestInit) {\n return publicGetNullable<CmsBlogCategoryDto[]>(ctx, `${prefix}/categories`, init);\n },\n\n listComments(postId: string, init?: FetchRequestInit) {\n return publicGetNullable<CmsBlogCommentDto[]>(\n ctx,\n `${prefix}/posts/${encodeURIComponent(postId)}/comments`,\n init,\n );\n },\n\n async createComment(postId: string, body: CreateBlogCommentInput) {\n const url = `${ctx.baseUrl}${appendSiteId(\n `${prefix}/posts/${encodeURIComponent(postId)}/comments`,\n ctx.siteId,\n )}`;\n const res = await ctx.fetchFn(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n ...ctx.defaultHeaders,\n },\n body: JSON.stringify(body),\n });\n return unwrap<CreateBlogCommentResult>(res);\n },\n\n getEngagement(postId: string, options?: { visitorId?: string }, init?: FetchRequestInit) {\n return publicGetNullable<BlogEngagement>(\n ctx,\n `${prefix}/posts/${encodeURIComponent(postId)}/engagement`,\n {\n ...init,\n headers: {\n ...(init?.headers as Record<string, string> | undefined),\n ...(options?.visitorId ? { \"x-visitor-id\": options.visitorId } : {}),\n },\n },\n );\n },\n\n async toggleLike(postId: string, options?: { visitorId?: string }) {\n const url = `${ctx.baseUrl}${appendSiteId(\n `${prefix}/posts/${encodeURIComponent(postId)}/like`,\n ctx.siteId,\n )}`;\n const res = await ctx.fetchFn(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n ...ctx.defaultHeaders,\n ...(options?.visitorId ? { \"x-visitor-id\": options.visitorId } : {}),\n },\n });\n return unwrap<BlogLikeResult>(res);\n },\n\n async listMappedPosts(query?: ListBlogPostsQuery, init?: FetchRequestInit) {\n const page = await this.listPosts(query, init);\n if (!page?.docs?.length) return null;\n return {\n posts: page.docs.map(mapCmsPostToBlogPost),\n total: page.total,\n page: page.page,\n limit: page.limit,\n };\n },\n\n async getMappedPostBySlug(slug: string, init?: FetchRequestInit): Promise<BlogPost | null> {\n const dto = await this.getPostBySlug(slug, init);\n if (!dto) return null;\n return mapCmsPostToBlogPost(dto);\n },\n\n async listMappedCategories(init?: FetchRequestInit): Promise<BlogCategory[] | null> {\n const dtos = await this.listCategories(init);\n if (!dtos) return null;\n return dtos.map(mapCmsCategoryToBlogCategory);\n },\n\n async listMappedComments(postId: string, init?: FetchRequestInit): Promise<BlogComment[] | null> {\n const dtos = await this.listComments(postId, init);\n if (!dtos) return null;\n return dtos.map(mapCmsCommentToBlogComment);\n },\n };\n}\n\nexport type BlogApi = ReturnType<typeof createBlogApi>;\n","import { createBlogApi } from \"./blog/endpoints.js\";\nimport type { BlogApi } from \"./blog/endpoints.js\";\n\nexport type CmsClientConfig = {\n /** Base URL, e.g. https://cms-gateway.example.com */\n baseUrl: string;\n /** CMS site Mongo id — appended as ?siteId= on every blog request */\n siteId?: string;\n /**\n * Path prefix before /public/blog.\n * Default '' for gateway-cms direct (/public/blog/...).\n * Use '/api/backend/cms' when routing through the main API gateway.\n */\n pathPrefix?: string;\n fetch?: typeof fetch;\n defaultHeaders?: Record<string, string>;\n};\n\nexport type CmsClient = {\n blog: BlogApi;\n};\n\nfunction normalizeBaseUrl(url: string): string {\n return url.replace(/\\/$/, \"\");\n}\n\nfunction normalizePathPrefix(prefix: string): string {\n const trimmed = prefix.replace(/\\/$/, \"\");\n if (!trimmed) return \"\";\n return trimmed.startsWith(\"/\") ? trimmed : `/${trimmed}`;\n}\n\nexport function createCmsClient(config: CmsClientConfig): CmsClient {\n const baseUrl = normalizeBaseUrl(config.baseUrl);\n const pathPrefix = normalizePathPrefix(config.pathPrefix ?? \"\");\n const blogBasePath = `${pathPrefix}/public/blog`.replace(/\\/+/g, \"/\");\n\n const ctx = {\n baseUrl,\n blogBasePath,\n siteId: config.siteId,\n fetchFn: config.fetch ?? globalThis.fetch.bind(globalThis),\n defaultHeaders: config.defaultHeaders ?? {},\n };\n\n return {\n blog: createBlogApi(ctx),\n };\n}\n"]}