@utilsy/cms-nextjs 0.1.0 → 0.2.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/README.md +91 -4
- package/dist/{client-C3E94syg.d.cts → client-DEovSyC5.d.cts} +80 -9
- package/dist/{client-C3E94syg.d.ts → client-DEovSyC5.d.ts} +80 -9
- package/dist/index.cjs +133 -48
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -3
- package/dist/index.d.ts +5 -3
- package/dist/index.js +132 -49
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +56 -0
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +23 -2
- package/dist/react.d.ts +23 -2
- package/dist/react.js +55 -1
- package/dist/react.js.map +1 -1
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @utilsy/cms-nextjs
|
|
2
2
|
|
|
3
|
-
Headless Next.js SDK for [Utilsy gateway-cms](https://github.com/utilsy/utilsy-cms-nextjs-sdk) public blog
|
|
3
|
+
Headless Next.js SDK for [Utilsy gateway-cms](https://github.com/utilsy/utilsy-cms-nextjs-sdk) public CMS APIs: blog (`/public/blog`) and dynamic content types (`/public/api`). Provides a typed HTTP client, domain mappers, server-friendly fetch helpers, and React data hooks—bring your own UI.
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
@@ -25,7 +25,7 @@ export const cms = createCmsClient({
|
|
|
25
25
|
});
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
-
**Main API gateway** (prefix before `/public/blog`):
|
|
28
|
+
**Main API gateway** (prefix before `/public/blog` and `/public/api`):
|
|
29
29
|
|
|
30
30
|
```ts
|
|
31
31
|
export const cms = createCmsClient({
|
|
@@ -62,7 +62,35 @@ export default async function BlogPage() {
|
|
|
62
62
|
}
|
|
63
63
|
```
|
|
64
64
|
|
|
65
|
-
### 3.
|
|
65
|
+
### 3. Server Component (filtered content by type)
|
|
66
|
+
|
|
67
|
+
```tsx
|
|
68
|
+
import { cms } from "@/lib/cms";
|
|
69
|
+
|
|
70
|
+
export default async function ServicesPage() {
|
|
71
|
+
const result = await cms.content.listMappedEntries(
|
|
72
|
+
"services",
|
|
73
|
+
{ page: 1, limit: 12, filters: { featured: true } },
|
|
74
|
+
{ next: { revalidate: 60, tags: ["cms:services"] } },
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
if (!result?.entries.length) {
|
|
78
|
+
return <p>No services yet.</p>;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return (
|
|
82
|
+
<ul>
|
|
83
|
+
{result.entries.map((entry) => (
|
|
84
|
+
<li key={entry.id}>
|
|
85
|
+
<a href={`/services/${entry.data.slug}`}>{String(entry.data.title ?? "")}</a>
|
|
86
|
+
</li>
|
|
87
|
+
))}
|
|
88
|
+
</ul>
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### 4. Client engagement (likes & comments)
|
|
66
94
|
|
|
67
95
|
```tsx
|
|
68
96
|
"use client";
|
|
@@ -97,6 +125,41 @@ function Engagement({ postId }: { postId: string }) {
|
|
|
97
125
|
}
|
|
98
126
|
```
|
|
99
127
|
|
|
128
|
+
### 5. Client content list (hooks)
|
|
129
|
+
|
|
130
|
+
```tsx
|
|
131
|
+
"use client";
|
|
132
|
+
|
|
133
|
+
import { CmsProvider, useContentEntries } from "@utilsy/cms-nextjs/react";
|
|
134
|
+
import { cms } from "@/lib/cms";
|
|
135
|
+
|
|
136
|
+
export function ServicesList() {
|
|
137
|
+
return (
|
|
138
|
+
<CmsProvider client={cms}>
|
|
139
|
+
<ServicesListInner />
|
|
140
|
+
</CmsProvider>
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function ServicesListInner() {
|
|
145
|
+
const { entries, loading, error } = useContentEntries("services", {
|
|
146
|
+
page: 1,
|
|
147
|
+
limit: 20,
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
if (loading) return <p>Loading…</p>;
|
|
151
|
+
if (error) return <p>Error: {error.message}</p>;
|
|
152
|
+
|
|
153
|
+
return (
|
|
154
|
+
<ul>
|
|
155
|
+
{entries.map((e) => (
|
|
156
|
+
<li key={e.id}>{String(e.data.title ?? "")}</li>
|
|
157
|
+
))}
|
|
158
|
+
</ul>
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
100
163
|
## Environment variables
|
|
101
164
|
|
|
102
165
|
| Variable | Description |
|
|
@@ -114,10 +177,19 @@ The CMS resolves the site from:
|
|
|
114
177
|
|
|
115
178
|
For local dev or gateway-only deployments, always set `siteId` in the client config.
|
|
116
179
|
|
|
180
|
+
## Content types and filters
|
|
181
|
+
|
|
182
|
+
- Endpoint: `GET /public/api/{contentTypeApiId}` (published entries only).
|
|
183
|
+
- Content types must have `apiAccess: PUBLIC`.
|
|
184
|
+
- `filters` are exact matches on `data.<field>` (JSON query param). Example: `{ category: "news" }`.
|
|
185
|
+
- Use `contentTypeApiId` (kebab-case slug from CMS), not the Mongo content type id.
|
|
186
|
+
|
|
117
187
|
## API reference
|
|
118
188
|
|
|
119
189
|
### Client (`createCmsClient`)
|
|
120
190
|
|
|
191
|
+
#### Blog
|
|
192
|
+
|
|
121
193
|
| Method | Description |
|
|
122
194
|
|--------|-------------|
|
|
123
195
|
| `blog.listPosts(query?, init?)` | Paginated list (summary fields only) |
|
|
@@ -132,6 +204,19 @@ For local dev or gateway-only deployments, always set `siteId` in the client con
|
|
|
132
204
|
| `blog.listMappedCategories(...)` | Mapped categories |
|
|
133
205
|
| `blog.listMappedComments(...)` | Mapped comments |
|
|
134
206
|
|
|
207
|
+
#### Content
|
|
208
|
+
|
|
209
|
+
| Method | Description |
|
|
210
|
+
|--------|-------------|
|
|
211
|
+
| `content.listEntries(contentTypeApiId, query?, init?)` | Paginated raw DTOs |
|
|
212
|
+
| `content.getEntry(contentTypeApiId, idOrSlug, init?)` | Single raw DTO |
|
|
213
|
+
| `content.listMappedEntries(...)` | List + `mapCmsEntryToContentEntry` |
|
|
214
|
+
| `content.getMappedEntry(...)` | Single mapped `ContentEntry` |
|
|
215
|
+
|
|
216
|
+
Query params for `listEntries` / `listMappedEntries`: `page`, `limit`, `search`, `sort`, `order`, `filters` (object).
|
|
217
|
+
|
|
218
|
+
Helpers: `serializeContentFilters`, `mapCmsEntryToContentEntry`.
|
|
219
|
+
|
|
135
220
|
### React (`@utilsy/cms-nextjs/react`)
|
|
136
221
|
|
|
137
222
|
| Export | Description |
|
|
@@ -142,10 +227,12 @@ For local dev or gateway-only deployments, always set `siteId` in the client con
|
|
|
142
227
|
| `useBlogEngagement` | Load engagement stats |
|
|
143
228
|
| `useBlogLike` | Like toggle + counts |
|
|
144
229
|
| `useBlogComments` | List + submit comments |
|
|
230
|
+
| `useContentEntries` | List mapped entries by content type |
|
|
231
|
+
| `useContentEntry` | Single mapped entry by id or slug |
|
|
145
232
|
|
|
146
233
|
## CORS
|
|
147
234
|
|
|
148
|
-
Public
|
|
235
|
+
Public CMS reads from the browser require either:
|
|
149
236
|
|
|
150
237
|
- Same-origin proxy (e.g. Next.js Route Handler forwarding to gateway), or
|
|
151
238
|
- Server Components / Route Handlers calling the SDK server-side.
|
|
@@ -11,6 +11,13 @@ type FetchRequestInit = RequestInit & {
|
|
|
11
11
|
cache?: RequestCache;
|
|
12
12
|
};
|
|
13
13
|
|
|
14
|
+
type RequestContext = {
|
|
15
|
+
baseUrl: string;
|
|
16
|
+
siteId?: string;
|
|
17
|
+
fetchFn: typeof fetch;
|
|
18
|
+
defaultHeaders: Record<string, string>;
|
|
19
|
+
};
|
|
20
|
+
|
|
14
21
|
/** Wire DTO from CMS public blog API */
|
|
15
22
|
type CmsBlogPostDto = {
|
|
16
23
|
id: string;
|
|
@@ -131,12 +138,8 @@ type ListBlogPostsQuery = {
|
|
|
131
138
|
tag?: string;
|
|
132
139
|
};
|
|
133
140
|
|
|
134
|
-
type BlogRequestContext = {
|
|
135
|
-
baseUrl: string;
|
|
141
|
+
type BlogRequestContext = RequestContext & {
|
|
136
142
|
blogBasePath: string;
|
|
137
|
-
siteId?: string;
|
|
138
|
-
fetchFn: typeof fetch;
|
|
139
|
-
defaultHeaders: Record<string, string>;
|
|
140
143
|
};
|
|
141
144
|
declare function createBlogApi(ctx: BlogRequestContext): {
|
|
142
145
|
listPosts(query?: ListBlogPostsQuery, init?: FetchRequestInit): Promise<CmsBlogPostsPage | null>;
|
|
@@ -162,14 +165,81 @@ declare function createBlogApi(ctx: BlogRequestContext): {
|
|
|
162
165
|
};
|
|
163
166
|
type BlogApi = ReturnType<typeof createBlogApi>;
|
|
164
167
|
|
|
168
|
+
type ContentFilters = Record<string, unknown>;
|
|
169
|
+
declare function serializeContentFilters(filters: ContentFilters): string;
|
|
170
|
+
|
|
171
|
+
type ListContentEntriesQuery = {
|
|
172
|
+
page?: number;
|
|
173
|
+
limit?: number;
|
|
174
|
+
search?: string;
|
|
175
|
+
sort?: string;
|
|
176
|
+
order?: "asc" | "desc";
|
|
177
|
+
filters?: ContentFilters;
|
|
178
|
+
};
|
|
179
|
+
type CmsContentTypeDto = {
|
|
180
|
+
_id?: string;
|
|
181
|
+
id?: string;
|
|
182
|
+
name?: string;
|
|
183
|
+
apiId?: string;
|
|
184
|
+
fields?: unknown[];
|
|
185
|
+
status?: string;
|
|
186
|
+
};
|
|
187
|
+
type CmsContentEntryDto = {
|
|
188
|
+
_id?: string;
|
|
189
|
+
id?: string;
|
|
190
|
+
contentTypeId?: string;
|
|
191
|
+
contentType?: CmsContentTypeDto;
|
|
192
|
+
siteId?: string;
|
|
193
|
+
status?: string;
|
|
194
|
+
data?: Record<string, unknown>;
|
|
195
|
+
createdAt?: string;
|
|
196
|
+
updatedAt?: string;
|
|
197
|
+
};
|
|
198
|
+
type CmsContentEntriesPage = {
|
|
199
|
+
message?: string;
|
|
200
|
+
docs: CmsContentEntryDto[];
|
|
201
|
+
totalDocs: number;
|
|
202
|
+
};
|
|
203
|
+
type ContentTypeMeta = {
|
|
204
|
+
id: string;
|
|
205
|
+
name: string;
|
|
206
|
+
apiId: string;
|
|
207
|
+
fields?: unknown[];
|
|
208
|
+
status?: string;
|
|
209
|
+
};
|
|
210
|
+
type ContentEntry = {
|
|
211
|
+
id: string;
|
|
212
|
+
contentTypeId: string;
|
|
213
|
+
contentType?: ContentTypeMeta;
|
|
214
|
+
status: string;
|
|
215
|
+
data: Record<string, unknown>;
|
|
216
|
+
createdAt?: string;
|
|
217
|
+
updatedAt?: string;
|
|
218
|
+
};
|
|
219
|
+
type MappedContentEntriesPage = {
|
|
220
|
+
entries: ContentEntry[];
|
|
221
|
+
totalDocs: number;
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
type ContentRequestContext = RequestContext & {
|
|
225
|
+
contentBasePath: string;
|
|
226
|
+
};
|
|
227
|
+
declare function createContentApi(ctx: ContentRequestContext): {
|
|
228
|
+
listEntries(contentTypeApiId: string, query?: ListContentEntriesQuery, init?: FetchRequestInit): Promise<CmsContentEntriesPage | null>;
|
|
229
|
+
getEntry(contentTypeApiId: string, idOrSlug: string, init?: FetchRequestInit): Promise<CmsContentEntryDto | null>;
|
|
230
|
+
listMappedEntries(contentTypeApiId: string, query?: ListContentEntriesQuery, init?: FetchRequestInit): Promise<MappedContentEntriesPage | null>;
|
|
231
|
+
getMappedEntry(contentTypeApiId: string, idOrSlug: string, init?: FetchRequestInit): Promise<ContentEntry | null>;
|
|
232
|
+
};
|
|
233
|
+
type ContentApi = ReturnType<typeof createContentApi>;
|
|
234
|
+
|
|
165
235
|
type CmsClientConfig = {
|
|
166
236
|
/** Base URL, e.g. https://cms-gateway.example.com */
|
|
167
237
|
baseUrl: string;
|
|
168
|
-
/** CMS site Mongo id — appended as ?siteId= on every
|
|
238
|
+
/** CMS site Mongo id — appended as ?siteId= on every public request */
|
|
169
239
|
siteId?: string;
|
|
170
240
|
/**
|
|
171
|
-
* Path prefix before /public/blog.
|
|
172
|
-
* Default '' for gateway-cms direct (/public/blog/...).
|
|
241
|
+
* Path prefix before /public/blog and /public/api.
|
|
242
|
+
* Default '' for gateway-cms direct (/public/blog/..., /public/api/...).
|
|
173
243
|
* Use '/api/backend/cms' when routing through the main API gateway.
|
|
174
244
|
*/
|
|
175
245
|
pathPrefix?: string;
|
|
@@ -178,7 +248,8 @@ type CmsClientConfig = {
|
|
|
178
248
|
};
|
|
179
249
|
type CmsClient = {
|
|
180
250
|
blog: BlogApi;
|
|
251
|
+
content: ContentApi;
|
|
181
252
|
};
|
|
182
253
|
declare function createCmsClient(config: CmsClientConfig): CmsClient;
|
|
183
254
|
|
|
184
|
-
export { type BlogApi as B, type CmsApiResponse as C, type FetchRequestInit as F, type ListBlogPostsQuery as L, type BlogAuthor as a, type BlogCategory as b, type BlogComment as c, type BlogCommentReply as d, type BlogEngagement as e, type BlogLikeResult as f, type BlogPost as g, type CmsBlogCategoryDto as h, type CmsBlogCommentDto as i, type CmsBlogPostDto as j, type CmsBlogPostsPage as k, type CmsClient as l, type CmsClientConfig as m, type
|
|
255
|
+
export { type BlogApi as B, type CmsApiResponse as C, type FetchRequestInit as F, type ListBlogPostsQuery as L, type MappedContentEntriesPage as M, type BlogAuthor as a, type BlogCategory as b, type BlogComment as c, type BlogCommentReply as d, type BlogEngagement as e, type BlogLikeResult as f, type BlogPost as g, type CmsBlogCategoryDto as h, type CmsBlogCommentDto as i, type CmsBlogPostDto as j, type CmsBlogPostsPage as k, type CmsClient as l, type CmsClientConfig as m, type CmsContentEntriesPage as n, type CmsContentEntryDto as o, type CmsContentTypeDto as p, type ContentApi as q, type ContentEntry as r, type ContentFilters as s, type ContentTypeMeta as t, type CreateBlogCommentInput as u, type CreateBlogCommentResult as v, type ListContentEntriesQuery as w, createCmsClient as x, serializeContentFilters as y };
|
|
@@ -11,6 +11,13 @@ type FetchRequestInit = RequestInit & {
|
|
|
11
11
|
cache?: RequestCache;
|
|
12
12
|
};
|
|
13
13
|
|
|
14
|
+
type RequestContext = {
|
|
15
|
+
baseUrl: string;
|
|
16
|
+
siteId?: string;
|
|
17
|
+
fetchFn: typeof fetch;
|
|
18
|
+
defaultHeaders: Record<string, string>;
|
|
19
|
+
};
|
|
20
|
+
|
|
14
21
|
/** Wire DTO from CMS public blog API */
|
|
15
22
|
type CmsBlogPostDto = {
|
|
16
23
|
id: string;
|
|
@@ -131,12 +138,8 @@ type ListBlogPostsQuery = {
|
|
|
131
138
|
tag?: string;
|
|
132
139
|
};
|
|
133
140
|
|
|
134
|
-
type BlogRequestContext = {
|
|
135
|
-
baseUrl: string;
|
|
141
|
+
type BlogRequestContext = RequestContext & {
|
|
136
142
|
blogBasePath: string;
|
|
137
|
-
siteId?: string;
|
|
138
|
-
fetchFn: typeof fetch;
|
|
139
|
-
defaultHeaders: Record<string, string>;
|
|
140
143
|
};
|
|
141
144
|
declare function createBlogApi(ctx: BlogRequestContext): {
|
|
142
145
|
listPosts(query?: ListBlogPostsQuery, init?: FetchRequestInit): Promise<CmsBlogPostsPage | null>;
|
|
@@ -162,14 +165,81 @@ declare function createBlogApi(ctx: BlogRequestContext): {
|
|
|
162
165
|
};
|
|
163
166
|
type BlogApi = ReturnType<typeof createBlogApi>;
|
|
164
167
|
|
|
168
|
+
type ContentFilters = Record<string, unknown>;
|
|
169
|
+
declare function serializeContentFilters(filters: ContentFilters): string;
|
|
170
|
+
|
|
171
|
+
type ListContentEntriesQuery = {
|
|
172
|
+
page?: number;
|
|
173
|
+
limit?: number;
|
|
174
|
+
search?: string;
|
|
175
|
+
sort?: string;
|
|
176
|
+
order?: "asc" | "desc";
|
|
177
|
+
filters?: ContentFilters;
|
|
178
|
+
};
|
|
179
|
+
type CmsContentTypeDto = {
|
|
180
|
+
_id?: string;
|
|
181
|
+
id?: string;
|
|
182
|
+
name?: string;
|
|
183
|
+
apiId?: string;
|
|
184
|
+
fields?: unknown[];
|
|
185
|
+
status?: string;
|
|
186
|
+
};
|
|
187
|
+
type CmsContentEntryDto = {
|
|
188
|
+
_id?: string;
|
|
189
|
+
id?: string;
|
|
190
|
+
contentTypeId?: string;
|
|
191
|
+
contentType?: CmsContentTypeDto;
|
|
192
|
+
siteId?: string;
|
|
193
|
+
status?: string;
|
|
194
|
+
data?: Record<string, unknown>;
|
|
195
|
+
createdAt?: string;
|
|
196
|
+
updatedAt?: string;
|
|
197
|
+
};
|
|
198
|
+
type CmsContentEntriesPage = {
|
|
199
|
+
message?: string;
|
|
200
|
+
docs: CmsContentEntryDto[];
|
|
201
|
+
totalDocs: number;
|
|
202
|
+
};
|
|
203
|
+
type ContentTypeMeta = {
|
|
204
|
+
id: string;
|
|
205
|
+
name: string;
|
|
206
|
+
apiId: string;
|
|
207
|
+
fields?: unknown[];
|
|
208
|
+
status?: string;
|
|
209
|
+
};
|
|
210
|
+
type ContentEntry = {
|
|
211
|
+
id: string;
|
|
212
|
+
contentTypeId: string;
|
|
213
|
+
contentType?: ContentTypeMeta;
|
|
214
|
+
status: string;
|
|
215
|
+
data: Record<string, unknown>;
|
|
216
|
+
createdAt?: string;
|
|
217
|
+
updatedAt?: string;
|
|
218
|
+
};
|
|
219
|
+
type MappedContentEntriesPage = {
|
|
220
|
+
entries: ContentEntry[];
|
|
221
|
+
totalDocs: number;
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
type ContentRequestContext = RequestContext & {
|
|
225
|
+
contentBasePath: string;
|
|
226
|
+
};
|
|
227
|
+
declare function createContentApi(ctx: ContentRequestContext): {
|
|
228
|
+
listEntries(contentTypeApiId: string, query?: ListContentEntriesQuery, init?: FetchRequestInit): Promise<CmsContentEntriesPage | null>;
|
|
229
|
+
getEntry(contentTypeApiId: string, idOrSlug: string, init?: FetchRequestInit): Promise<CmsContentEntryDto | null>;
|
|
230
|
+
listMappedEntries(contentTypeApiId: string, query?: ListContentEntriesQuery, init?: FetchRequestInit): Promise<MappedContentEntriesPage | null>;
|
|
231
|
+
getMappedEntry(contentTypeApiId: string, idOrSlug: string, init?: FetchRequestInit): Promise<ContentEntry | null>;
|
|
232
|
+
};
|
|
233
|
+
type ContentApi = ReturnType<typeof createContentApi>;
|
|
234
|
+
|
|
165
235
|
type CmsClientConfig = {
|
|
166
236
|
/** Base URL, e.g. https://cms-gateway.example.com */
|
|
167
237
|
baseUrl: string;
|
|
168
|
-
/** CMS site Mongo id — appended as ?siteId= on every
|
|
238
|
+
/** CMS site Mongo id — appended as ?siteId= on every public request */
|
|
169
239
|
siteId?: string;
|
|
170
240
|
/**
|
|
171
|
-
* Path prefix before /public/blog.
|
|
172
|
-
* Default '' for gateway-cms direct (/public/blog/...).
|
|
241
|
+
* Path prefix before /public/blog and /public/api.
|
|
242
|
+
* Default '' for gateway-cms direct (/public/blog/..., /public/api/...).
|
|
173
243
|
* Use '/api/backend/cms' when routing through the main API gateway.
|
|
174
244
|
*/
|
|
175
245
|
pathPrefix?: string;
|
|
@@ -178,7 +248,8 @@ type CmsClientConfig = {
|
|
|
178
248
|
};
|
|
179
249
|
type CmsClient = {
|
|
180
250
|
blog: BlogApi;
|
|
251
|
+
content: ContentApi;
|
|
181
252
|
};
|
|
182
253
|
declare function createCmsClient(config: CmsClientConfig): CmsClient;
|
|
183
254
|
|
|
184
|
-
export { type BlogApi as B, type CmsApiResponse as C, type FetchRequestInit as F, type ListBlogPostsQuery as L, type BlogAuthor as a, type BlogCategory as b, type BlogComment as c, type BlogCommentReply as d, type BlogEngagement as e, type BlogLikeResult as f, type BlogPost as g, type CmsBlogCategoryDto as h, type CmsBlogCommentDto as i, type CmsBlogPostDto as j, type CmsBlogPostsPage as k, type CmsClient as l, type CmsClientConfig as m, type
|
|
255
|
+
export { type BlogApi as B, type CmsApiResponse as C, type FetchRequestInit as F, type ListBlogPostsQuery as L, type MappedContentEntriesPage as M, type BlogAuthor as a, type BlogCategory as b, type BlogComment as c, type BlogCommentReply as d, type BlogEngagement as e, type BlogLikeResult as f, type BlogPost as g, type CmsBlogCategoryDto as h, type CmsBlogCommentDto as i, type CmsBlogPostDto as j, type CmsBlogPostsPage as k, type CmsClient as l, type CmsClientConfig as m, type CmsContentEntriesPage as n, type CmsContentEntryDto as o, type CmsContentTypeDto as p, type ContentApi as q, type ContentEntry as r, type ContentFilters as s, type ContentTypeMeta as t, type CreateBlogCommentInput as u, type CreateBlogCommentResult as v, type ListContentEntriesQuery as w, createCmsClient as x, serializeContentFilters as y };
|
package/dist/index.cjs
CHANGED
|
@@ -1,5 +1,47 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
// src/http.ts
|
|
4
|
+
function appendSiteId(path, siteId) {
|
|
5
|
+
if (!siteId) return path;
|
|
6
|
+
const sep = path.includes("?") ? "&" : "?";
|
|
7
|
+
return `${path}${sep}siteId=${encodeURIComponent(siteId)}`;
|
|
8
|
+
}
|
|
9
|
+
function buildPublicUrl(ctx, path) {
|
|
10
|
+
return `${ctx.baseUrl}${appendSiteId(path, ctx.siteId)}`;
|
|
11
|
+
}
|
|
12
|
+
async function unwrapResponse(res) {
|
|
13
|
+
const json = await res.json();
|
|
14
|
+
if (!res.ok) {
|
|
15
|
+
const message = json.message ?? `Request failed (${res.status})`;
|
|
16
|
+
throw new Error(message);
|
|
17
|
+
}
|
|
18
|
+
const hasEntryIdentity = "_id" in json || "id" in json;
|
|
19
|
+
const hasListDocs = "docs" in json && Array.isArray(json.docs);
|
|
20
|
+
const isDataEnvelope = json.data !== void 0 && "message" in json && !hasListDocs && !hasEntryIdentity;
|
|
21
|
+
if (isDataEnvelope) {
|
|
22
|
+
return json.data;
|
|
23
|
+
}
|
|
24
|
+
return json;
|
|
25
|
+
}
|
|
26
|
+
async function publicGet(ctx, path, init) {
|
|
27
|
+
const res = await ctx.fetchFn(buildPublicUrl(ctx, path), {
|
|
28
|
+
...init,
|
|
29
|
+
headers: {
|
|
30
|
+
"Content-Type": "application/json",
|
|
31
|
+
...ctx.defaultHeaders,
|
|
32
|
+
...init?.headers
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
return unwrapResponse(res);
|
|
36
|
+
}
|
|
37
|
+
async function publicGetNullable(ctx, path, init) {
|
|
38
|
+
try {
|
|
39
|
+
return await publicGet(ctx, path, init);
|
|
40
|
+
} catch {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
3
45
|
// src/blog/mappers.ts
|
|
4
46
|
function mediaUrl(value) {
|
|
5
47
|
if (!value) return void 0;
|
|
@@ -68,42 +110,6 @@ function mapCmsCategoryToBlogCategory(dto) {
|
|
|
68
110
|
}
|
|
69
111
|
|
|
70
112
|
// 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
113
|
function createBlogApi(ctx) {
|
|
108
114
|
const prefix = ctx.blogBasePath;
|
|
109
115
|
return {
|
|
@@ -135,10 +141,10 @@ function createBlogApi(ctx) {
|
|
|
135
141
|
);
|
|
136
142
|
},
|
|
137
143
|
async createComment(postId, body) {
|
|
138
|
-
const url =
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
)
|
|
144
|
+
const url = buildPublicUrl(
|
|
145
|
+
ctx,
|
|
146
|
+
`${prefix}/posts/${encodeURIComponent(postId)}/comments`
|
|
147
|
+
);
|
|
142
148
|
const res = await ctx.fetchFn(url, {
|
|
143
149
|
method: "POST",
|
|
144
150
|
headers: {
|
|
@@ -147,7 +153,7 @@ function createBlogApi(ctx) {
|
|
|
147
153
|
},
|
|
148
154
|
body: JSON.stringify(body)
|
|
149
155
|
});
|
|
150
|
-
return
|
|
156
|
+
return unwrapResponse(res);
|
|
151
157
|
},
|
|
152
158
|
getEngagement(postId, options, init) {
|
|
153
159
|
return publicGetNullable(
|
|
@@ -163,10 +169,7 @@ function createBlogApi(ctx) {
|
|
|
163
169
|
);
|
|
164
170
|
},
|
|
165
171
|
async toggleLike(postId, options) {
|
|
166
|
-
const url = `${
|
|
167
|
-
`${prefix}/posts/${encodeURIComponent(postId)}/like`,
|
|
168
|
-
ctx.siteId
|
|
169
|
-
)}`;
|
|
172
|
+
const url = buildPublicUrl(ctx, `${prefix}/posts/${encodeURIComponent(postId)}/like`);
|
|
170
173
|
const res = await ctx.fetchFn(url, {
|
|
171
174
|
method: "POST",
|
|
172
175
|
headers: {
|
|
@@ -175,7 +178,7 @@ function createBlogApi(ctx) {
|
|
|
175
178
|
...options?.visitorId ? { "x-visitor-id": options.visitorId } : {}
|
|
176
179
|
}
|
|
177
180
|
});
|
|
178
|
-
return
|
|
181
|
+
return unwrapResponse(res);
|
|
179
182
|
},
|
|
180
183
|
async listMappedPosts(query, init) {
|
|
181
184
|
const page = await this.listPosts(query, init);
|
|
@@ -205,6 +208,85 @@ function createBlogApi(ctx) {
|
|
|
205
208
|
};
|
|
206
209
|
}
|
|
207
210
|
|
|
211
|
+
// src/content/filters.ts
|
|
212
|
+
function serializeContentFilters(filters) {
|
|
213
|
+
return JSON.stringify(filters);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// src/content/mappers.ts
|
|
217
|
+
function resolveId(dto) {
|
|
218
|
+
if (dto.id) return String(dto.id);
|
|
219
|
+
if (dto._id) return String(dto._id);
|
|
220
|
+
return "";
|
|
221
|
+
}
|
|
222
|
+
function mapContentType(dto) {
|
|
223
|
+
if (!dto) return void 0;
|
|
224
|
+
const id = resolveId(dto);
|
|
225
|
+
return {
|
|
226
|
+
id,
|
|
227
|
+
name: dto.name ?? "",
|
|
228
|
+
apiId: dto.apiId ?? "",
|
|
229
|
+
fields: dto.fields,
|
|
230
|
+
status: dto.status
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
function mapCmsEntryToContentEntry(dto) {
|
|
234
|
+
const contentTypeId = dto.contentTypeId != null ? String(dto.contentTypeId) : dto.contentType ? resolveId(dto.contentType) : "";
|
|
235
|
+
return {
|
|
236
|
+
id: resolveId(dto),
|
|
237
|
+
contentTypeId,
|
|
238
|
+
contentType: mapContentType(dto.contentType),
|
|
239
|
+
status: dto.status ?? "PUBLISHED",
|
|
240
|
+
data: dto.data ?? {},
|
|
241
|
+
createdAt: dto.createdAt,
|
|
242
|
+
updatedAt: dto.updatedAt
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// src/content/endpoints.ts
|
|
247
|
+
function buildListQueryString(query) {
|
|
248
|
+
const qs = new URLSearchParams();
|
|
249
|
+
if (query?.page != null) qs.set("page", String(query.page));
|
|
250
|
+
if (query?.limit != null) qs.set("limit", String(query.limit));
|
|
251
|
+
if (query?.search) qs.set("search", query.search);
|
|
252
|
+
if (query?.sort) qs.set("sort", query.sort);
|
|
253
|
+
if (query?.order) qs.set("order", query.order);
|
|
254
|
+
if (query?.filters && Object.keys(query.filters).length > 0) {
|
|
255
|
+
qs.set("filters", serializeContentFilters(query.filters));
|
|
256
|
+
}
|
|
257
|
+
return qs.toString();
|
|
258
|
+
}
|
|
259
|
+
function createContentApi(ctx) {
|
|
260
|
+
const prefix = ctx.contentBasePath;
|
|
261
|
+
return {
|
|
262
|
+
listEntries(contentTypeApiId, query, init) {
|
|
263
|
+
const queryStr = buildListQueryString(query);
|
|
264
|
+
const path = `${prefix}/${encodeURIComponent(contentTypeApiId)}${queryStr ? `?${queryStr}` : ""}`;
|
|
265
|
+
return publicGetNullable(ctx, path, init);
|
|
266
|
+
},
|
|
267
|
+
getEntry(contentTypeApiId, idOrSlug, init) {
|
|
268
|
+
return publicGetNullable(
|
|
269
|
+
ctx,
|
|
270
|
+
`${prefix}/${encodeURIComponent(contentTypeApiId)}/${encodeURIComponent(idOrSlug)}`,
|
|
271
|
+
init
|
|
272
|
+
);
|
|
273
|
+
},
|
|
274
|
+
async listMappedEntries(contentTypeApiId, query, init) {
|
|
275
|
+
const page = await this.listEntries(contentTypeApiId, query, init);
|
|
276
|
+
if (!page?.docs) return null;
|
|
277
|
+
return {
|
|
278
|
+
entries: page.docs.map(mapCmsEntryToContentEntry),
|
|
279
|
+
totalDocs: page.totalDocs
|
|
280
|
+
};
|
|
281
|
+
},
|
|
282
|
+
async getMappedEntry(contentTypeApiId, idOrSlug, init) {
|
|
283
|
+
const dto = await this.getEntry(contentTypeApiId, idOrSlug, init);
|
|
284
|
+
if (!dto) return null;
|
|
285
|
+
return mapCmsEntryToContentEntry(dto);
|
|
286
|
+
}
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
|
|
208
290
|
// src/client.ts
|
|
209
291
|
function normalizeBaseUrl(url) {
|
|
210
292
|
return url.replace(/\/$/, "");
|
|
@@ -218,22 +300,25 @@ function createCmsClient(config) {
|
|
|
218
300
|
const baseUrl = normalizeBaseUrl(config.baseUrl);
|
|
219
301
|
const pathPrefix = normalizePathPrefix(config.pathPrefix ?? "");
|
|
220
302
|
const blogBasePath = `${pathPrefix}/public/blog`.replace(/\/+/g, "/");
|
|
303
|
+
const contentBasePath = `${pathPrefix}/public/api`.replace(/\/+/g, "/");
|
|
221
304
|
const ctx = {
|
|
222
305
|
baseUrl,
|
|
223
|
-
blogBasePath,
|
|
224
306
|
siteId: config.siteId,
|
|
225
307
|
fetchFn: config.fetch ?? globalThis.fetch.bind(globalThis),
|
|
226
308
|
defaultHeaders: config.defaultHeaders ?? {}
|
|
227
309
|
};
|
|
228
310
|
return {
|
|
229
|
-
blog: createBlogApi(ctx)
|
|
311
|
+
blog: createBlogApi({ ...ctx, blogBasePath }),
|
|
312
|
+
content: createContentApi({ ...ctx, contentBasePath })
|
|
230
313
|
};
|
|
231
314
|
}
|
|
232
315
|
|
|
233
316
|
exports.createCmsClient = createCmsClient;
|
|
234
317
|
exports.mapCmsCategoryToBlogCategory = mapCmsCategoryToBlogCategory;
|
|
235
318
|
exports.mapCmsCommentToBlogComment = mapCmsCommentToBlogComment;
|
|
319
|
+
exports.mapCmsEntryToContentEntry = mapCmsEntryToContentEntry;
|
|
236
320
|
exports.mapCmsPostToBlogPost = mapCmsPostToBlogPost;
|
|
237
321
|
exports.mediaUrl = mediaUrl;
|
|
322
|
+
exports.serializeContentFilters = serializeContentFilters;
|
|
238
323
|
//# sourceMappingURL=index.cjs.map
|
|
239
324
|
//# sourceMappingURL=index.cjs.map
|