@sleekcms/client 0.1.5 → 1.0.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 +148 -10
- package/index.cjs +122 -103
- package/index.d.cts +8 -4
- package/index.d.ts +8 -4
- package/index.mjs +195 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -22,7 +22,7 @@ const client = createClient({
|
|
|
22
22
|
const content = await client.getContent();
|
|
23
23
|
|
|
24
24
|
// Get specific pages
|
|
25
|
-
const blogPosts = await client.
|
|
25
|
+
const blogPosts = await client.getPages('/blog');
|
|
26
26
|
```
|
|
27
27
|
|
|
28
28
|
## Client Types
|
|
@@ -96,30 +96,60 @@ const homePage = await client.getContent('pages[?_path == `/`] | [0]');
|
|
|
96
96
|
const siteTitle = await client.getContent<string>('config.title');
|
|
97
97
|
```
|
|
98
98
|
|
|
99
|
-
### `
|
|
99
|
+
### `getPages(path, query?)`
|
|
100
100
|
|
|
101
|
-
|
|
101
|
+
Get pages that start with the specified path.
|
|
102
102
|
|
|
103
103
|
**Parameters:**
|
|
104
104
|
- `path` (required): Path prefix to filter pages (e.g., `/blog`, `/products`)
|
|
105
105
|
- `query` (optional): JMESPath query to further filter results
|
|
106
106
|
|
|
107
|
-
**Returns:** `Promise<
|
|
107
|
+
**Returns:** `Promise<SleekSiteContent["pages"]>` (async) or `SleekSiteContent["pages"]` (sync)
|
|
108
108
|
|
|
109
109
|
**Examples:**
|
|
110
110
|
|
|
111
111
|
```typescript
|
|
112
112
|
// Get all blog posts
|
|
113
|
-
const blogPosts = await client.
|
|
113
|
+
const blogPosts = await client.getPages('/blog');
|
|
114
114
|
|
|
115
115
|
// Get blog posts with custom query
|
|
116
|
-
const publishedPosts = await client.
|
|
116
|
+
const publishedPosts = await client.getPages(
|
|
117
117
|
'/blog',
|
|
118
118
|
'[?published == `true`]'
|
|
119
119
|
);
|
|
120
120
|
|
|
121
121
|
// Get post titles only
|
|
122
|
-
const titles = await client.
|
|
122
|
+
const titles = await client.getPages('/blog', '[].title');
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### `getPage(path)`
|
|
126
|
+
|
|
127
|
+
Get a single page by its exact path.
|
|
128
|
+
|
|
129
|
+
**Parameters:**
|
|
130
|
+
- `path` (required): Exact page path (e.g., `/blog/my-post`, `/about`)
|
|
131
|
+
|
|
132
|
+
**Returns:** `Promise<Page>` (async) or `Page | null` (sync)
|
|
133
|
+
|
|
134
|
+
**Note:** Async client throws an error if page is not found. Sync client returns `null`.
|
|
135
|
+
|
|
136
|
+
**Examples:**
|
|
137
|
+
|
|
138
|
+
```typescript
|
|
139
|
+
// Async client - throws if not found
|
|
140
|
+
try {
|
|
141
|
+
const aboutPage = await client.getPage('/about');
|
|
142
|
+
console.log(aboutPage.title);
|
|
143
|
+
} catch (error) {
|
|
144
|
+
console.error('Page not found');
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Sync client - returns null if not found
|
|
148
|
+
const syncClient = await createSyncClient({ siteToken: '...' });
|
|
149
|
+
const aboutPage = syncClient.getPage('/about');
|
|
150
|
+
if (aboutPage) {
|
|
151
|
+
console.log(aboutPage.title);
|
|
152
|
+
}
|
|
123
153
|
```
|
|
124
154
|
|
|
125
155
|
### `getImages()`
|
|
@@ -167,6 +197,27 @@ const categories = await client.getList('categories');
|
|
|
167
197
|
// [{ label: "Technology", value: "tech" }, ...]
|
|
168
198
|
```
|
|
169
199
|
|
|
200
|
+
### `getSlugs(path)`
|
|
201
|
+
|
|
202
|
+
Get an array of slugs from pages that start with the specified path. Only includes pages that have a `_slug` property.
|
|
203
|
+
|
|
204
|
+
**Parameters:**
|
|
205
|
+
- `path` (required): Path prefix to filter pages (e.g., `/blog`, `/products`)
|
|
206
|
+
|
|
207
|
+
**Returns:** `Promise<string[]>` (async) or `string[]` (sync)
|
|
208
|
+
|
|
209
|
+
**Example:**
|
|
210
|
+
|
|
211
|
+
```typescript
|
|
212
|
+
// Get all blog post slugs
|
|
213
|
+
const slugs = await client.getSlugs('/blog');
|
|
214
|
+
// ["my-first-post", "second-post", "latest-update"]
|
|
215
|
+
|
|
216
|
+
// Useful for generating static paths in Next.js
|
|
217
|
+
const productSlugs = await client.getSlugs('/products');
|
|
218
|
+
// ["laptop", "keyboard", "mouse"]
|
|
219
|
+
```
|
|
220
|
+
|
|
170
221
|
## Content Structure
|
|
171
222
|
|
|
172
223
|
The SleekCMS content has the following structure:
|
|
@@ -228,7 +279,7 @@ export default async function BlogPage() {
|
|
|
228
279
|
env: 'published',
|
|
229
280
|
});
|
|
230
281
|
|
|
231
|
-
const posts = await client.
|
|
282
|
+
const posts = await client.getPages('/blog');
|
|
232
283
|
|
|
233
284
|
return (
|
|
234
285
|
<div>
|
|
@@ -255,12 +306,96 @@ const client = await createSyncClient({
|
|
|
255
306
|
});
|
|
256
307
|
|
|
257
308
|
// Generate static pages
|
|
258
|
-
const pages = client.
|
|
309
|
+
const pages = client.getPages('/');
|
|
259
310
|
const images = client.getImages();
|
|
260
311
|
|
|
261
312
|
// All subsequent calls are synchronous and instant
|
|
262
313
|
```
|
|
263
314
|
|
|
315
|
+
### Next.js Static Params (generateStaticParams)
|
|
316
|
+
|
|
317
|
+
Use `getSlugs()` to generate static paths for dynamic routes in Next.js:
|
|
318
|
+
|
|
319
|
+
```typescript
|
|
320
|
+
// app/blog/[slug]/page.tsx
|
|
321
|
+
import { createClient } from '@sleekcms/client';
|
|
322
|
+
|
|
323
|
+
// Generate static paths at build time
|
|
324
|
+
export async function generateStaticParams() {
|
|
325
|
+
const client = createClient({
|
|
326
|
+
siteToken: process.env.SLEEKCMS_TOKEN!,
|
|
327
|
+
env: 'published',
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
const slugs = await client.getSlugs('/blog');
|
|
331
|
+
|
|
332
|
+
return slugs.map((slug) => ({
|
|
333
|
+
slug: slug,
|
|
334
|
+
}));
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// Render the page
|
|
338
|
+
export default async function BlogPost({ params }: { params: { slug: string } }) {
|
|
339
|
+
const client = createClient({
|
|
340
|
+
siteToken: process.env.SLEEKCMS_TOKEN!,
|
|
341
|
+
env: 'published',
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
const post = await client.getPage(`/blog/${params.slug}`);
|
|
345
|
+
|
|
346
|
+
return (
|
|
347
|
+
<article>
|
|
348
|
+
<h1>{post.title}</h1>
|
|
349
|
+
<div>{post.content}</div>
|
|
350
|
+
</article>
|
|
351
|
+
);
|
|
352
|
+
}
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
**Pages Router Example:**
|
|
356
|
+
|
|
357
|
+
```typescript
|
|
358
|
+
// pages/blog/[slug].tsx
|
|
359
|
+
import { createClient } from '@sleekcms/client';
|
|
360
|
+
import type { GetStaticPaths, GetStaticProps } from 'next';
|
|
361
|
+
|
|
362
|
+
export const getStaticPaths: GetStaticPaths = async () => {
|
|
363
|
+
const client = createClient({
|
|
364
|
+
siteToken: process.env.SLEEKCMS_TOKEN!,
|
|
365
|
+
env: 'published',
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
const slugs = await client.getSlugs('/blog');
|
|
369
|
+
|
|
370
|
+
return {
|
|
371
|
+
paths: slugs.map((slug) => ({ params: { slug } })),
|
|
372
|
+
fallback: false,
|
|
373
|
+
};
|
|
374
|
+
};
|
|
375
|
+
|
|
376
|
+
export const getStaticProps: GetStaticProps = async ({ params }) => {
|
|
377
|
+
const client = createClient({
|
|
378
|
+
siteToken: process.env.SLEEKCMS_TOKEN!,
|
|
379
|
+
env: 'published',
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
const post = await client.getPage(`/blog/${params!.slug}`);
|
|
383
|
+
|
|
384
|
+
return {
|
|
385
|
+
props: { post },
|
|
386
|
+
};
|
|
387
|
+
};
|
|
388
|
+
|
|
389
|
+
export default function BlogPost({ post }: any) {
|
|
390
|
+
return (
|
|
391
|
+
<article>
|
|
392
|
+
<h1>{post.title}</h1>
|
|
393
|
+
<div>{post.content}</div>
|
|
394
|
+
</article>
|
|
395
|
+
);
|
|
396
|
+
}
|
|
397
|
+
```
|
|
398
|
+
|
|
264
399
|
### Browser Usage
|
|
265
400
|
|
|
266
401
|
```typescript
|
|
@@ -316,7 +451,10 @@ interface BlogPost {
|
|
|
316
451
|
_path: string;
|
|
317
452
|
}
|
|
318
453
|
|
|
319
|
-
const posts = await client.
|
|
454
|
+
const posts = await client.getPages('/blog');
|
|
455
|
+
|
|
456
|
+
// Get a single page with error handling
|
|
457
|
+
const aboutPage = await client.getPage('/about');
|
|
320
458
|
```
|
|
321
459
|
|
|
322
460
|
## License
|
package/index.cjs
CHANGED
|
@@ -33,6 +33,8 @@ __export(index_exports, {
|
|
|
33
33
|
createSyncClient: () => createSyncClient
|
|
34
34
|
});
|
|
35
35
|
module.exports = __toCommonJS(index_exports);
|
|
36
|
+
|
|
37
|
+
// src/lib.ts
|
|
36
38
|
var jmespath = __toESM(require("jmespath"), 1);
|
|
37
39
|
function isDevToken(token) {
|
|
38
40
|
return token.startsWith("dev-");
|
|
@@ -41,149 +43,164 @@ function getBaseUrl(token) {
|
|
|
41
43
|
let [env, siteId, ...rest] = token.split("-");
|
|
42
44
|
return `https://${env}.sleekcms.com/${siteId}`;
|
|
43
45
|
}
|
|
44
|
-
async function fetchSiteContent(options, searchQuery) {
|
|
45
|
-
const { siteToken, env = "latest", mock } = options;
|
|
46
|
-
if (!siteToken) {
|
|
47
|
-
throw new Error("[SleekCMS] siteToken is required");
|
|
48
|
-
}
|
|
49
|
-
const baseUrl = getBaseUrl(siteToken).replace(/\/$/, "");
|
|
50
|
-
const url = new URL(`${baseUrl}/${env}`);
|
|
51
|
-
if (searchQuery) {
|
|
52
|
-
url.searchParams.set("search", searchQuery);
|
|
53
|
-
}
|
|
54
|
-
if (mock && isDevToken(siteToken)) {
|
|
55
|
-
url.searchParams.set("mock", "true");
|
|
56
|
-
}
|
|
57
|
-
const res = await fetch(url.toString(), {
|
|
58
|
-
method: "GET",
|
|
59
|
-
headers: {
|
|
60
|
-
"Content-Type": "application/json",
|
|
61
|
-
Authorization: siteToken
|
|
62
|
-
}
|
|
63
|
-
});
|
|
64
|
-
if (!res.ok) {
|
|
65
|
-
let message = res.statusText;
|
|
66
|
-
try {
|
|
67
|
-
const data = await res.json();
|
|
68
|
-
if (data && data.message) message = data.message;
|
|
69
|
-
} catch {
|
|
70
|
-
}
|
|
71
|
-
throw new Error(`[SleekCMS] Request failed (${res.status}): ${message}`);
|
|
72
|
-
}
|
|
73
|
-
return res.json();
|
|
74
|
-
}
|
|
75
46
|
function applyJmes(data, query) {
|
|
76
47
|
if (!query) return data;
|
|
77
48
|
return jmespath.search(data, query);
|
|
78
49
|
}
|
|
79
|
-
function
|
|
80
|
-
const
|
|
81
|
-
|
|
50
|
+
function createFetchSiteContent(options) {
|
|
51
|
+
const { siteToken, env = "latest", mock } = options;
|
|
52
|
+
const dev = isDevToken(siteToken);
|
|
53
|
+
let cacheMode = !!options.cache || !!mock && dev;
|
|
82
54
|
let cachedContent = null;
|
|
83
|
-
async function
|
|
84
|
-
if (
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
async function getContent(query) {
|
|
90
|
-
if (cacheMode) {
|
|
91
|
-
const data2 = await ensureCacheLoaded();
|
|
92
|
-
return applyJmes(data2, query);
|
|
55
|
+
return async function fetchSiteContent(searchQuery) {
|
|
56
|
+
if (!siteToken) {
|
|
57
|
+
throw new Error("[SleekCMS] siteToken is required");
|
|
58
|
+
}
|
|
59
|
+
if (cachedContent) {
|
|
60
|
+
return applyJmes(cachedContent, searchQuery);
|
|
93
61
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
62
|
+
const baseUrl = getBaseUrl(siteToken).replace(/\/$/, "");
|
|
63
|
+
const url = new URL(`${baseUrl}/${env}`);
|
|
64
|
+
if (searchQuery && !cacheMode && !cachedContent) {
|
|
65
|
+
url.searchParams.set("search", searchQuery);
|
|
66
|
+
}
|
|
67
|
+
if (mock && dev) {
|
|
68
|
+
url.searchParams.set("mock", "true");
|
|
69
|
+
}
|
|
70
|
+
const res = await fetch(url.toString(), {
|
|
71
|
+
method: "GET",
|
|
72
|
+
headers: {
|
|
73
|
+
"Content-Type": "application/json",
|
|
74
|
+
Authorization: siteToken
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
if (!res.ok) {
|
|
78
|
+
let message = res.statusText;
|
|
79
|
+
try {
|
|
80
|
+
const data2 = await res.json();
|
|
81
|
+
if (data2 && data2.message) message = data2.message;
|
|
82
|
+
} catch {
|
|
83
|
+
}
|
|
84
|
+
throw new Error(`[SleekCMS] Request failed (${res.status}): ${message}`);
|
|
85
|
+
}
|
|
86
|
+
const data = await res.json();
|
|
87
|
+
if (!searchQuery) {
|
|
88
|
+
cachedContent = data;
|
|
97
89
|
cacheMode = true;
|
|
98
|
-
return data2;
|
|
99
90
|
}
|
|
100
|
-
const data = await fetchSiteContent(options, query);
|
|
101
91
|
return data;
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// src/index.ts
|
|
96
|
+
function extractSlugs(pages, path) {
|
|
97
|
+
const slugs = [];
|
|
98
|
+
const pagesList = pages ?? [];
|
|
99
|
+
for (const page of pagesList) {
|
|
100
|
+
const pth = typeof page._path === "string" ? page._path : "";
|
|
101
|
+
if (pth.startsWith(path) && "_slug" in page && typeof page._slug === "string") {
|
|
102
|
+
slugs.push(page._slug);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return slugs;
|
|
106
|
+
}
|
|
107
|
+
function filterPagesByPath(pages, path) {
|
|
108
|
+
const pagesList = pages ?? [];
|
|
109
|
+
return pagesList.filter((p) => {
|
|
110
|
+
const pth = typeof p._path === "string" ? p._path : "";
|
|
111
|
+
return pth.startsWith(path);
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
function createClient(options) {
|
|
115
|
+
const fetchSiteContent = createFetchSiteContent(options);
|
|
116
|
+
async function getContent(query) {
|
|
117
|
+
return await fetchSiteContent(query);
|
|
102
118
|
}
|
|
103
|
-
async function
|
|
119
|
+
async function getPages(path, query) {
|
|
104
120
|
if (!path) {
|
|
105
|
-
throw new Error("[SleekCMS] path is required for
|
|
121
|
+
throw new Error("[SleekCMS] path is required for getPages");
|
|
106
122
|
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
return applyJmes(filtered2, query);
|
|
123
|
+
const data = await fetchSiteContent();
|
|
124
|
+
const filtered = filterPagesByPath(data.pages, path);
|
|
125
|
+
return applyJmes(filtered, query);
|
|
126
|
+
}
|
|
127
|
+
async function getPage(path) {
|
|
128
|
+
if (!path) {
|
|
129
|
+
throw new Error("[SleekCMS] path is required for getPage");
|
|
115
130
|
}
|
|
116
|
-
const
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
);
|
|
120
|
-
const filtered = (pages ?? []).filter((p) => {
|
|
131
|
+
const data = await fetchSiteContent();
|
|
132
|
+
const pages = data.pages ?? [];
|
|
133
|
+
const page = pages.find((p) => {
|
|
121
134
|
const pth = typeof p._path === "string" ? p._path : "";
|
|
122
|
-
return pth
|
|
135
|
+
return pth === path;
|
|
123
136
|
});
|
|
124
|
-
|
|
137
|
+
if (!page) {
|
|
138
|
+
throw new Error(`[SleekCMS] Page not found: ${path}`);
|
|
139
|
+
}
|
|
140
|
+
return page;
|
|
125
141
|
}
|
|
126
|
-
async function
|
|
127
|
-
if (
|
|
128
|
-
|
|
129
|
-
return data.images ?? {};
|
|
142
|
+
async function getSlugs(path) {
|
|
143
|
+
if (!path) {
|
|
144
|
+
throw new Error("[SleekCMS] path is required for getSlugs");
|
|
130
145
|
}
|
|
131
|
-
const
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
146
|
+
const data = await fetchSiteContent();
|
|
147
|
+
return extractSlugs(data.pages, path);
|
|
148
|
+
}
|
|
149
|
+
async function getImages() {
|
|
150
|
+
const data = await fetchSiteContent();
|
|
151
|
+
return data.images ?? {};
|
|
136
152
|
}
|
|
137
153
|
async function getImage(name) {
|
|
138
154
|
if (!name) return void 0;
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
return data.images ? data.images[name] : void 0;
|
|
142
|
-
}
|
|
143
|
-
const images = await fetchSiteContent(
|
|
144
|
-
options,
|
|
145
|
-
"images"
|
|
146
|
-
);
|
|
147
|
-
return images ? images[name] : void 0;
|
|
155
|
+
const data = await fetchSiteContent();
|
|
156
|
+
return data.images ? data.images[name] : void 0;
|
|
148
157
|
}
|
|
149
158
|
async function getList(name) {
|
|
150
159
|
if (!name) return void 0;
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
const list2 = lists2[name];
|
|
155
|
-
return Array.isArray(list2) ? list2 : void 0;
|
|
156
|
-
}
|
|
157
|
-
const lists = await fetchSiteContent(
|
|
158
|
-
options,
|
|
159
|
-
"lists"
|
|
160
|
-
);
|
|
161
|
-
const list = lists ? lists[name] : void 0;
|
|
160
|
+
const data = await fetchSiteContent();
|
|
161
|
+
const lists = data.lists ?? {};
|
|
162
|
+
const list = lists[name];
|
|
162
163
|
return Array.isArray(list) ? list : void 0;
|
|
163
164
|
}
|
|
164
165
|
return {
|
|
165
166
|
getContent,
|
|
166
|
-
|
|
167
|
+
getPages,
|
|
168
|
+
getPage,
|
|
169
|
+
getSlugs,
|
|
167
170
|
getImages,
|
|
168
171
|
getImage,
|
|
169
172
|
getList
|
|
170
173
|
};
|
|
171
174
|
}
|
|
172
175
|
async function createSyncClient(options) {
|
|
173
|
-
const
|
|
176
|
+
const fetchSiteContent = createFetchSiteContent(options);
|
|
177
|
+
const data = await fetchSiteContent();
|
|
174
178
|
function getContent(query) {
|
|
175
179
|
return applyJmes(data, query);
|
|
176
180
|
}
|
|
177
|
-
function
|
|
181
|
+
function getPages(path, query) {
|
|
178
182
|
if (!path) {
|
|
179
|
-
throw new Error("[SleekCMS] path is required for
|
|
183
|
+
throw new Error("[SleekCMS] path is required for getPages");
|
|
184
|
+
}
|
|
185
|
+
const filtered = filterPagesByPath(data.pages, path);
|
|
186
|
+
return applyJmes(filtered, query);
|
|
187
|
+
}
|
|
188
|
+
function getPage(path) {
|
|
189
|
+
if (!path) {
|
|
190
|
+
throw new Error("[SleekCMS] path is required for getPage");
|
|
180
191
|
}
|
|
181
192
|
const pages = data.pages ?? [];
|
|
182
|
-
const
|
|
193
|
+
const page = pages.find((p) => {
|
|
183
194
|
const pth = typeof p._path === "string" ? p._path : "";
|
|
184
|
-
return pth
|
|
195
|
+
return pth === path;
|
|
185
196
|
});
|
|
186
|
-
return
|
|
197
|
+
return page ?? null;
|
|
198
|
+
}
|
|
199
|
+
function getSlugs(path) {
|
|
200
|
+
if (!path) {
|
|
201
|
+
throw new Error("[SleekCMS] path is required for getSlugs");
|
|
202
|
+
}
|
|
203
|
+
return extractSlugs(data.pages, path);
|
|
187
204
|
}
|
|
188
205
|
function getImages() {
|
|
189
206
|
return data.images ?? {};
|
|
@@ -200,7 +217,9 @@ async function createSyncClient(options) {
|
|
|
200
217
|
}
|
|
201
218
|
return {
|
|
202
219
|
getContent,
|
|
203
|
-
|
|
220
|
+
getPages,
|
|
221
|
+
getPage,
|
|
222
|
+
getSlugs,
|
|
204
223
|
getImages,
|
|
205
224
|
getImage,
|
|
206
225
|
getList
|
package/index.d.cts
CHANGED
|
@@ -32,8 +32,10 @@ interface ClientOptions {
|
|
|
32
32
|
* Async SleekCMS client: methods return Promises.
|
|
33
33
|
*/
|
|
34
34
|
interface SleekClient {
|
|
35
|
-
getContent
|
|
36
|
-
|
|
35
|
+
getContent(query?: string): Promise<SleekSiteContent>;
|
|
36
|
+
getPages(path: string, query?: string): Promise<SleekSiteContent["pages"]>;
|
|
37
|
+
getPage(path: string): Promise<Page>;
|
|
38
|
+
getSlugs(path: string): Promise<string[]>;
|
|
37
39
|
getImages(): Promise<SleekSiteContent["images"]>;
|
|
38
40
|
getImage(name: string): Promise<unknown | undefined>;
|
|
39
41
|
getList<T = unknown>(name: string): Promise<T[] | undefined>;
|
|
@@ -42,8 +44,10 @@ interface SleekClient {
|
|
|
42
44
|
* Sync client: prefetches full content once; subsequent calls are in-memory only.
|
|
43
45
|
*/
|
|
44
46
|
interface SleekSyncClient {
|
|
45
|
-
getContent
|
|
46
|
-
|
|
47
|
+
getContent(query?: string): SleekSiteContent;
|
|
48
|
+
getPages(path: string, query?: string): SleekSiteContent["pages"];
|
|
49
|
+
getPage(path: string): Page | null;
|
|
50
|
+
getSlugs(path: string): string[];
|
|
47
51
|
getImages(): SleekSiteContent["images"];
|
|
48
52
|
getImage(name: string): unknown | undefined;
|
|
49
53
|
getList<T = unknown>(name: string): T[] | undefined;
|
package/index.d.ts
CHANGED
|
@@ -32,8 +32,10 @@ interface ClientOptions {
|
|
|
32
32
|
* Async SleekCMS client: methods return Promises.
|
|
33
33
|
*/
|
|
34
34
|
interface SleekClient {
|
|
35
|
-
getContent
|
|
36
|
-
|
|
35
|
+
getContent(query?: string): Promise<SleekSiteContent>;
|
|
36
|
+
getPages(path: string, query?: string): Promise<SleekSiteContent["pages"]>;
|
|
37
|
+
getPage(path: string): Promise<Page>;
|
|
38
|
+
getSlugs(path: string): Promise<string[]>;
|
|
37
39
|
getImages(): Promise<SleekSiteContent["images"]>;
|
|
38
40
|
getImage(name: string): Promise<unknown | undefined>;
|
|
39
41
|
getList<T = unknown>(name: string): Promise<T[] | undefined>;
|
|
@@ -42,8 +44,10 @@ interface SleekClient {
|
|
|
42
44
|
* Sync client: prefetches full content once; subsequent calls are in-memory only.
|
|
43
45
|
*/
|
|
44
46
|
interface SleekSyncClient {
|
|
45
|
-
getContent
|
|
46
|
-
|
|
47
|
+
getContent(query?: string): SleekSiteContent;
|
|
48
|
+
getPages(path: string, query?: string): SleekSiteContent["pages"];
|
|
49
|
+
getPage(path: string): Page | null;
|
|
50
|
+
getSlugs(path: string): string[];
|
|
47
51
|
getImages(): SleekSiteContent["images"];
|
|
48
52
|
getImage(name: string): unknown | undefined;
|
|
49
53
|
getList<T = unknown>(name: string): T[] | undefined;
|
package/index.mjs
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
// src/lib.ts
|
|
2
|
+
import * as jmespath from "jmespath";
|
|
3
|
+
function isDevToken(token) {
|
|
4
|
+
return token.startsWith("dev-");
|
|
5
|
+
}
|
|
6
|
+
function getBaseUrl(token) {
|
|
7
|
+
let [env, siteId, ...rest] = token.split("-");
|
|
8
|
+
return `https://${env}.sleekcms.com/${siteId}`;
|
|
9
|
+
}
|
|
10
|
+
function applyJmes(data, query) {
|
|
11
|
+
if (!query) return data;
|
|
12
|
+
return jmespath.search(data, query);
|
|
13
|
+
}
|
|
14
|
+
function createFetchSiteContent(options) {
|
|
15
|
+
const { siteToken, env = "latest", mock } = options;
|
|
16
|
+
const dev = isDevToken(siteToken);
|
|
17
|
+
let cacheMode = !!options.cache || !!mock && dev;
|
|
18
|
+
let cachedContent = null;
|
|
19
|
+
return async function fetchSiteContent(searchQuery) {
|
|
20
|
+
if (!siteToken) {
|
|
21
|
+
throw new Error("[SleekCMS] siteToken is required");
|
|
22
|
+
}
|
|
23
|
+
if (cachedContent) {
|
|
24
|
+
return applyJmes(cachedContent, searchQuery);
|
|
25
|
+
}
|
|
26
|
+
const baseUrl = getBaseUrl(siteToken).replace(/\/$/, "");
|
|
27
|
+
const url = new URL(`${baseUrl}/${env}`);
|
|
28
|
+
if (searchQuery && !cacheMode && !cachedContent) {
|
|
29
|
+
url.searchParams.set("search", searchQuery);
|
|
30
|
+
}
|
|
31
|
+
if (mock && dev) {
|
|
32
|
+
url.searchParams.set("mock", "true");
|
|
33
|
+
}
|
|
34
|
+
const res = await fetch(url.toString(), {
|
|
35
|
+
method: "GET",
|
|
36
|
+
headers: {
|
|
37
|
+
"Content-Type": "application/json",
|
|
38
|
+
Authorization: siteToken
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
if (!res.ok) {
|
|
42
|
+
let message = res.statusText;
|
|
43
|
+
try {
|
|
44
|
+
const data2 = await res.json();
|
|
45
|
+
if (data2 && data2.message) message = data2.message;
|
|
46
|
+
} catch {
|
|
47
|
+
}
|
|
48
|
+
throw new Error(`[SleekCMS] Request failed (${res.status}): ${message}`);
|
|
49
|
+
}
|
|
50
|
+
const data = await res.json();
|
|
51
|
+
if (!searchQuery) {
|
|
52
|
+
cachedContent = data;
|
|
53
|
+
cacheMode = true;
|
|
54
|
+
}
|
|
55
|
+
return data;
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// src/index.ts
|
|
60
|
+
function extractSlugs(pages, path) {
|
|
61
|
+
const slugs = [];
|
|
62
|
+
const pagesList = pages ?? [];
|
|
63
|
+
for (const page of pagesList) {
|
|
64
|
+
const pth = typeof page._path === "string" ? page._path : "";
|
|
65
|
+
if (pth.startsWith(path) && "_slug" in page && typeof page._slug === "string") {
|
|
66
|
+
slugs.push(page._slug);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return slugs;
|
|
70
|
+
}
|
|
71
|
+
function filterPagesByPath(pages, path) {
|
|
72
|
+
const pagesList = pages ?? [];
|
|
73
|
+
return pagesList.filter((p) => {
|
|
74
|
+
const pth = typeof p._path === "string" ? p._path : "";
|
|
75
|
+
return pth.startsWith(path);
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
function createClient(options) {
|
|
79
|
+
const fetchSiteContent = createFetchSiteContent(options);
|
|
80
|
+
async function getContent(query) {
|
|
81
|
+
return await fetchSiteContent(query);
|
|
82
|
+
}
|
|
83
|
+
async function getPages(path, query) {
|
|
84
|
+
if (!path) {
|
|
85
|
+
throw new Error("[SleekCMS] path is required for getPages");
|
|
86
|
+
}
|
|
87
|
+
const data = await fetchSiteContent();
|
|
88
|
+
const filtered = filterPagesByPath(data.pages, path);
|
|
89
|
+
return applyJmes(filtered, query);
|
|
90
|
+
}
|
|
91
|
+
async function getPage(path) {
|
|
92
|
+
if (!path) {
|
|
93
|
+
throw new Error("[SleekCMS] path is required for getPage");
|
|
94
|
+
}
|
|
95
|
+
const data = await fetchSiteContent();
|
|
96
|
+
const pages = data.pages ?? [];
|
|
97
|
+
const page = pages.find((p) => {
|
|
98
|
+
const pth = typeof p._path === "string" ? p._path : "";
|
|
99
|
+
return pth === path;
|
|
100
|
+
});
|
|
101
|
+
if (!page) {
|
|
102
|
+
throw new Error(`[SleekCMS] Page not found: ${path}`);
|
|
103
|
+
}
|
|
104
|
+
return page;
|
|
105
|
+
}
|
|
106
|
+
async function getSlugs(path) {
|
|
107
|
+
if (!path) {
|
|
108
|
+
throw new Error("[SleekCMS] path is required for getSlugs");
|
|
109
|
+
}
|
|
110
|
+
const data = await fetchSiteContent();
|
|
111
|
+
return extractSlugs(data.pages, path);
|
|
112
|
+
}
|
|
113
|
+
async function getImages() {
|
|
114
|
+
const data = await fetchSiteContent();
|
|
115
|
+
return data.images ?? {};
|
|
116
|
+
}
|
|
117
|
+
async function getImage(name) {
|
|
118
|
+
if (!name) return void 0;
|
|
119
|
+
const data = await fetchSiteContent();
|
|
120
|
+
return data.images ? data.images[name] : void 0;
|
|
121
|
+
}
|
|
122
|
+
async function getList(name) {
|
|
123
|
+
if (!name) return void 0;
|
|
124
|
+
const data = await fetchSiteContent();
|
|
125
|
+
const lists = data.lists ?? {};
|
|
126
|
+
const list = lists[name];
|
|
127
|
+
return Array.isArray(list) ? list : void 0;
|
|
128
|
+
}
|
|
129
|
+
return {
|
|
130
|
+
getContent,
|
|
131
|
+
getPages,
|
|
132
|
+
getPage,
|
|
133
|
+
getSlugs,
|
|
134
|
+
getImages,
|
|
135
|
+
getImage,
|
|
136
|
+
getList
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
async function createSyncClient(options) {
|
|
140
|
+
const fetchSiteContent = createFetchSiteContent(options);
|
|
141
|
+
const data = await fetchSiteContent();
|
|
142
|
+
function getContent(query) {
|
|
143
|
+
return applyJmes(data, query);
|
|
144
|
+
}
|
|
145
|
+
function getPages(path, query) {
|
|
146
|
+
if (!path) {
|
|
147
|
+
throw new Error("[SleekCMS] path is required for getPages");
|
|
148
|
+
}
|
|
149
|
+
const filtered = filterPagesByPath(data.pages, path);
|
|
150
|
+
return applyJmes(filtered, query);
|
|
151
|
+
}
|
|
152
|
+
function getPage(path) {
|
|
153
|
+
if (!path) {
|
|
154
|
+
throw new Error("[SleekCMS] path is required for getPage");
|
|
155
|
+
}
|
|
156
|
+
const pages = data.pages ?? [];
|
|
157
|
+
const page = pages.find((p) => {
|
|
158
|
+
const pth = typeof p._path === "string" ? p._path : "";
|
|
159
|
+
return pth === path;
|
|
160
|
+
});
|
|
161
|
+
return page ?? null;
|
|
162
|
+
}
|
|
163
|
+
function getSlugs(path) {
|
|
164
|
+
if (!path) {
|
|
165
|
+
throw new Error("[SleekCMS] path is required for getSlugs");
|
|
166
|
+
}
|
|
167
|
+
return extractSlugs(data.pages, path);
|
|
168
|
+
}
|
|
169
|
+
function getImages() {
|
|
170
|
+
return data.images ?? {};
|
|
171
|
+
}
|
|
172
|
+
function getImage(name) {
|
|
173
|
+
if (!name) return void 0;
|
|
174
|
+
return data.images ? data.images[name] : void 0;
|
|
175
|
+
}
|
|
176
|
+
function getList(name) {
|
|
177
|
+
if (!name) return void 0;
|
|
178
|
+
const lists = data.lists ?? {};
|
|
179
|
+
const list = lists[name];
|
|
180
|
+
return Array.isArray(list) ? list : void 0;
|
|
181
|
+
}
|
|
182
|
+
return {
|
|
183
|
+
getContent,
|
|
184
|
+
getPages,
|
|
185
|
+
getPage,
|
|
186
|
+
getSlugs,
|
|
187
|
+
getImages,
|
|
188
|
+
getImage,
|
|
189
|
+
getList
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
export {
|
|
193
|
+
createClient,
|
|
194
|
+
createSyncClient
|
|
195
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sleekcms/client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "Official SleekCMS content client for Node 18+ and browser",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.cjs",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
}
|
|
16
16
|
},
|
|
17
17
|
"scripts": {
|
|
18
|
-
"build": "npm run clean && tsup
|
|
18
|
+
"build": "npm run clean && tsup && cp README.md dist/ && cp .npmrc dist/ && jq '.private = false | .files = [\"index.cjs\", \"index.mjs\", \"index.d.ts\", \"index.d.cts\", \"README.md\"]' package.json > dist/package.json",
|
|
19
19
|
"clean": "rimraf dist || true",
|
|
20
20
|
"test": "vitest run",
|
|
21
21
|
"test:watch": "vitest",
|