@sleekcms/client 0.1.6 → 1.0.1
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 +127 -105
- package/index.d.cts +9 -4
- package/index.d.ts +9 -4
- package/index.mjs +126 -106
- package/package.json +1 -1
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,157 +33,177 @@ __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-");
|
|
39
41
|
}
|
|
40
|
-
function getBaseUrl(token) {
|
|
42
|
+
function getBaseUrl(token, devEnv) {
|
|
41
43
|
let [env, siteId, ...rest] = token.split("-");
|
|
42
|
-
return `https://${env}.sleekcms.com/${siteId}`;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
|
|
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();
|
|
44
|
+
if (devEnv === "production") return `https://${env}.sleekcms.com/${siteId}`;
|
|
45
|
+
else if (devEnv === "development") return `https://${env}.sleekcms.net/${siteId}`;
|
|
46
|
+
else if (devEnv === "localhost") return `http://localhost:9001/${env}/${siteId}`;
|
|
47
|
+
else throw new Error(`[SleekCMS] Unknown devEnv: ${devEnv}`);
|
|
74
48
|
}
|
|
75
49
|
function applyJmes(data, query) {
|
|
76
50
|
if (!query) return data;
|
|
77
51
|
return jmespath.search(data, query);
|
|
78
52
|
}
|
|
79
|
-
function
|
|
80
|
-
const
|
|
81
|
-
|
|
53
|
+
function createFetchSiteContent(options) {
|
|
54
|
+
const { siteToken, env = "latest", mock, devEnv = "production" } = options;
|
|
55
|
+
const dev = isDevToken(siteToken);
|
|
56
|
+
let cacheMode = !!options.cache || !!mock && dev;
|
|
82
57
|
let cachedContent = null;
|
|
83
|
-
async function
|
|
84
|
-
if (
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
58
|
+
return async function fetchSiteContent(searchQuery) {
|
|
59
|
+
if (!siteToken) {
|
|
60
|
+
throw new Error("[SleekCMS] siteToken is required");
|
|
61
|
+
}
|
|
62
|
+
if (cachedContent) {
|
|
63
|
+
return applyJmes(cachedContent, searchQuery);
|
|
64
|
+
}
|
|
65
|
+
const baseUrl = getBaseUrl(siteToken, devEnv).replace(/\/$/, "");
|
|
66
|
+
const url = new URL(`${baseUrl}/${env}`);
|
|
67
|
+
if (searchQuery && !cacheMode && !cachedContent) {
|
|
68
|
+
url.searchParams.set("search", searchQuery);
|
|
69
|
+
}
|
|
70
|
+
if (mock && dev) {
|
|
71
|
+
url.searchParams.set("mock", "true");
|
|
93
72
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
73
|
+
const res = await fetch(url.toString(), {
|
|
74
|
+
method: "GET",
|
|
75
|
+
headers: {
|
|
76
|
+
"Content-Type": "application/json",
|
|
77
|
+
Authorization: siteToken
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
if (!res.ok) {
|
|
81
|
+
let message = res.statusText;
|
|
82
|
+
try {
|
|
83
|
+
const data2 = await res.json();
|
|
84
|
+
if (data2 && data2.message) message = data2.message;
|
|
85
|
+
} catch {
|
|
86
|
+
}
|
|
87
|
+
throw new Error(`[SleekCMS] Request failed (${res.status}): ${message}`);
|
|
88
|
+
}
|
|
89
|
+
const data = await res.json();
|
|
90
|
+
if (!searchQuery) {
|
|
91
|
+
cachedContent = data;
|
|
97
92
|
cacheMode = true;
|
|
98
|
-
return data2;
|
|
99
93
|
}
|
|
100
|
-
const data = await fetchSiteContent(options, query);
|
|
101
94
|
return data;
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// src/index.ts
|
|
99
|
+
function extractSlugs(pages, path) {
|
|
100
|
+
const slugs = [];
|
|
101
|
+
const pagesList = pages ?? [];
|
|
102
|
+
for (const page of pagesList) {
|
|
103
|
+
const pth = typeof page._path === "string" ? page._path : "";
|
|
104
|
+
if (pth.startsWith(path) && "_slug" in page && typeof page._slug === "string") {
|
|
105
|
+
slugs.push(page._slug);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return slugs;
|
|
109
|
+
}
|
|
110
|
+
function filterPagesByPath(pages, path) {
|
|
111
|
+
const pagesList = pages ?? [];
|
|
112
|
+
return pagesList.filter((p) => {
|
|
113
|
+
const pth = typeof p._path === "string" ? p._path : "";
|
|
114
|
+
return pth.startsWith(path);
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
function createClient(options) {
|
|
118
|
+
const fetchSiteContent = createFetchSiteContent(options);
|
|
119
|
+
async function getContent(query) {
|
|
120
|
+
return await fetchSiteContent(query);
|
|
102
121
|
}
|
|
103
|
-
async function
|
|
122
|
+
async function getPages(path, query) {
|
|
104
123
|
if (!path) {
|
|
105
|
-
throw new Error("[SleekCMS] path is required for
|
|
124
|
+
throw new Error("[SleekCMS] path is required for getPages");
|
|
106
125
|
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
return applyJmes(filtered2, query);
|
|
126
|
+
const data = await fetchSiteContent();
|
|
127
|
+
const filtered = filterPagesByPath(data.pages, path);
|
|
128
|
+
return applyJmes(filtered, query);
|
|
129
|
+
}
|
|
130
|
+
async function getPage(path) {
|
|
131
|
+
if (!path) {
|
|
132
|
+
throw new Error("[SleekCMS] path is required for getPage");
|
|
115
133
|
}
|
|
116
|
-
const
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
);
|
|
120
|
-
const filtered = (pages ?? []).filter((p) => {
|
|
134
|
+
const data = await fetchSiteContent();
|
|
135
|
+
const pages = data.pages ?? [];
|
|
136
|
+
const page = pages.find((p) => {
|
|
121
137
|
const pth = typeof p._path === "string" ? p._path : "";
|
|
122
|
-
return pth
|
|
138
|
+
return pth === path;
|
|
123
139
|
});
|
|
124
|
-
|
|
140
|
+
if (!page) {
|
|
141
|
+
throw new Error(`[SleekCMS] Page not found: ${path}`);
|
|
142
|
+
}
|
|
143
|
+
return page;
|
|
125
144
|
}
|
|
126
|
-
async function
|
|
127
|
-
if (
|
|
128
|
-
|
|
129
|
-
return data.images ?? {};
|
|
145
|
+
async function getSlugs(path) {
|
|
146
|
+
if (!path) {
|
|
147
|
+
throw new Error("[SleekCMS] path is required for getSlugs");
|
|
130
148
|
}
|
|
131
|
-
const
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
149
|
+
const data = await fetchSiteContent();
|
|
150
|
+
return extractSlugs(data.pages, path);
|
|
151
|
+
}
|
|
152
|
+
async function getImages() {
|
|
153
|
+
const data = await fetchSiteContent();
|
|
154
|
+
return data.images ?? {};
|
|
136
155
|
}
|
|
137
156
|
async function getImage(name) {
|
|
138
157
|
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;
|
|
158
|
+
const data = await fetchSiteContent();
|
|
159
|
+
return data.images ? data.images[name] : void 0;
|
|
148
160
|
}
|
|
149
161
|
async function getList(name) {
|
|
150
162
|
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;
|
|
163
|
+
const data = await fetchSiteContent();
|
|
164
|
+
const lists = data.lists ?? {};
|
|
165
|
+
const list = lists[name];
|
|
162
166
|
return Array.isArray(list) ? list : void 0;
|
|
163
167
|
}
|
|
164
168
|
return {
|
|
165
169
|
getContent,
|
|
166
|
-
|
|
170
|
+
getPages,
|
|
171
|
+
getPage,
|
|
172
|
+
getSlugs,
|
|
167
173
|
getImages,
|
|
168
174
|
getImage,
|
|
169
175
|
getList
|
|
170
176
|
};
|
|
171
177
|
}
|
|
172
178
|
async function createSyncClient(options) {
|
|
173
|
-
const
|
|
179
|
+
const fetchSiteContent = createFetchSiteContent(options);
|
|
180
|
+
const data = await fetchSiteContent();
|
|
174
181
|
function getContent(query) {
|
|
175
182
|
return applyJmes(data, query);
|
|
176
183
|
}
|
|
177
|
-
function
|
|
184
|
+
function getPages(path, query) {
|
|
185
|
+
if (!path) {
|
|
186
|
+
throw new Error("[SleekCMS] path is required for getPages");
|
|
187
|
+
}
|
|
188
|
+
const filtered = filterPagesByPath(data.pages, path);
|
|
189
|
+
return applyJmes(filtered, query);
|
|
190
|
+
}
|
|
191
|
+
function getPage(path) {
|
|
178
192
|
if (!path) {
|
|
179
|
-
throw new Error("[SleekCMS] path is required for
|
|
193
|
+
throw new Error("[SleekCMS] path is required for getPage");
|
|
180
194
|
}
|
|
181
195
|
const pages = data.pages ?? [];
|
|
182
|
-
const
|
|
196
|
+
const page = pages.find((p) => {
|
|
183
197
|
const pth = typeof p._path === "string" ? p._path : "";
|
|
184
|
-
return pth
|
|
198
|
+
return pth === path;
|
|
185
199
|
});
|
|
186
|
-
return
|
|
200
|
+
return page ?? null;
|
|
201
|
+
}
|
|
202
|
+
function getSlugs(path) {
|
|
203
|
+
if (!path) {
|
|
204
|
+
throw new Error("[SleekCMS] path is required for getSlugs");
|
|
205
|
+
}
|
|
206
|
+
return extractSlugs(data.pages, path);
|
|
187
207
|
}
|
|
188
208
|
function getImages() {
|
|
189
209
|
return data.images ?? {};
|
|
@@ -200,7 +220,9 @@ async function createSyncClient(options) {
|
|
|
200
220
|
}
|
|
201
221
|
return {
|
|
202
222
|
getContent,
|
|
203
|
-
|
|
223
|
+
getPages,
|
|
224
|
+
getPage,
|
|
225
|
+
getSlugs,
|
|
204
226
|
getImages,
|
|
205
227
|
getImage,
|
|
206
228
|
getList
|
package/index.d.cts
CHANGED
|
@@ -26,14 +26,17 @@ interface ClientOptions {
|
|
|
26
26
|
cache?: boolean;
|
|
27
27
|
mock?: boolean;
|
|
28
28
|
resolveEnv?: boolean;
|
|
29
|
+
devEnv?: string;
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
/**
|
|
32
33
|
* Async SleekCMS client: methods return Promises.
|
|
33
34
|
*/
|
|
34
35
|
interface SleekClient {
|
|
35
|
-
getContent
|
|
36
|
-
|
|
36
|
+
getContent(query?: string): Promise<SleekSiteContent>;
|
|
37
|
+
getPages(path: string, query?: string): Promise<SleekSiteContent["pages"]>;
|
|
38
|
+
getPage(path: string): Promise<Page>;
|
|
39
|
+
getSlugs(path: string): Promise<string[]>;
|
|
37
40
|
getImages(): Promise<SleekSiteContent["images"]>;
|
|
38
41
|
getImage(name: string): Promise<unknown | undefined>;
|
|
39
42
|
getList<T = unknown>(name: string): Promise<T[] | undefined>;
|
|
@@ -42,8 +45,10 @@ interface SleekClient {
|
|
|
42
45
|
* Sync client: prefetches full content once; subsequent calls are in-memory only.
|
|
43
46
|
*/
|
|
44
47
|
interface SleekSyncClient {
|
|
45
|
-
getContent
|
|
46
|
-
|
|
48
|
+
getContent(query?: string): SleekSiteContent;
|
|
49
|
+
getPages(path: string, query?: string): SleekSiteContent["pages"];
|
|
50
|
+
getPage(path: string): Page | null;
|
|
51
|
+
getSlugs(path: string): string[];
|
|
47
52
|
getImages(): SleekSiteContent["images"];
|
|
48
53
|
getImage(name: string): unknown | undefined;
|
|
49
54
|
getList<T = unknown>(name: string): T[] | undefined;
|
package/index.d.ts
CHANGED
|
@@ -26,14 +26,17 @@ interface ClientOptions {
|
|
|
26
26
|
cache?: boolean;
|
|
27
27
|
mock?: boolean;
|
|
28
28
|
resolveEnv?: boolean;
|
|
29
|
+
devEnv?: string;
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
/**
|
|
32
33
|
* Async SleekCMS client: methods return Promises.
|
|
33
34
|
*/
|
|
34
35
|
interface SleekClient {
|
|
35
|
-
getContent
|
|
36
|
-
|
|
36
|
+
getContent(query?: string): Promise<SleekSiteContent>;
|
|
37
|
+
getPages(path: string, query?: string): Promise<SleekSiteContent["pages"]>;
|
|
38
|
+
getPage(path: string): Promise<Page>;
|
|
39
|
+
getSlugs(path: string): Promise<string[]>;
|
|
37
40
|
getImages(): Promise<SleekSiteContent["images"]>;
|
|
38
41
|
getImage(name: string): Promise<unknown | undefined>;
|
|
39
42
|
getList<T = unknown>(name: string): Promise<T[] | undefined>;
|
|
@@ -42,8 +45,10 @@ interface SleekClient {
|
|
|
42
45
|
* Sync client: prefetches full content once; subsequent calls are in-memory only.
|
|
43
46
|
*/
|
|
44
47
|
interface SleekSyncClient {
|
|
45
|
-
getContent
|
|
46
|
-
|
|
48
|
+
getContent(query?: string): SleekSiteContent;
|
|
49
|
+
getPages(path: string, query?: string): SleekSiteContent["pages"];
|
|
50
|
+
getPage(path: string): Page | null;
|
|
51
|
+
getSlugs(path: string): string[];
|
|
47
52
|
getImages(): SleekSiteContent["images"];
|
|
48
53
|
getImage(name: string): unknown | undefined;
|
|
49
54
|
getList<T = unknown>(name: string): T[] | undefined;
|
package/index.mjs
CHANGED
|
@@ -1,155 +1,173 @@
|
|
|
1
|
-
// src/
|
|
1
|
+
// src/lib.ts
|
|
2
2
|
import * as jmespath from "jmespath";
|
|
3
3
|
function isDevToken(token) {
|
|
4
4
|
return token.startsWith("dev-");
|
|
5
5
|
}
|
|
6
|
-
function getBaseUrl(token) {
|
|
6
|
+
function getBaseUrl(token, devEnv) {
|
|
7
7
|
let [env, siteId, ...rest] = token.split("-");
|
|
8
|
-
return `https://${env}.sleekcms.com/${siteId}`;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
if (!siteToken) {
|
|
13
|
-
throw new Error("[SleekCMS] siteToken is required");
|
|
14
|
-
}
|
|
15
|
-
const baseUrl = getBaseUrl(siteToken).replace(/\/$/, "");
|
|
16
|
-
const url = new URL(`${baseUrl}/${env}`);
|
|
17
|
-
if (searchQuery) {
|
|
18
|
-
url.searchParams.set("search", searchQuery);
|
|
19
|
-
}
|
|
20
|
-
if (mock && isDevToken(siteToken)) {
|
|
21
|
-
url.searchParams.set("mock", "true");
|
|
22
|
-
}
|
|
23
|
-
const res = await fetch(url.toString(), {
|
|
24
|
-
method: "GET",
|
|
25
|
-
headers: {
|
|
26
|
-
"Content-Type": "application/json",
|
|
27
|
-
Authorization: siteToken
|
|
28
|
-
}
|
|
29
|
-
});
|
|
30
|
-
if (!res.ok) {
|
|
31
|
-
let message = res.statusText;
|
|
32
|
-
try {
|
|
33
|
-
const data = await res.json();
|
|
34
|
-
if (data && data.message) message = data.message;
|
|
35
|
-
} catch {
|
|
36
|
-
}
|
|
37
|
-
throw new Error(`[SleekCMS] Request failed (${res.status}): ${message}`);
|
|
38
|
-
}
|
|
39
|
-
return res.json();
|
|
8
|
+
if (devEnv === "production") return `https://${env}.sleekcms.com/${siteId}`;
|
|
9
|
+
else if (devEnv === "development") return `https://${env}.sleekcms.net/${siteId}`;
|
|
10
|
+
else if (devEnv === "localhost") return `http://localhost:9001/${env}/${siteId}`;
|
|
11
|
+
else throw new Error(`[SleekCMS] Unknown devEnv: ${devEnv}`);
|
|
40
12
|
}
|
|
41
13
|
function applyJmes(data, query) {
|
|
42
14
|
if (!query) return data;
|
|
43
15
|
return jmespath.search(data, query);
|
|
44
16
|
}
|
|
45
|
-
function
|
|
46
|
-
const
|
|
47
|
-
|
|
17
|
+
function createFetchSiteContent(options) {
|
|
18
|
+
const { siteToken, env = "latest", mock, devEnv = "production" } = options;
|
|
19
|
+
const dev = isDevToken(siteToken);
|
|
20
|
+
let cacheMode = !!options.cache || !!mock && dev;
|
|
48
21
|
let cachedContent = null;
|
|
49
|
-
async function
|
|
50
|
-
if (
|
|
51
|
-
|
|
52
|
-
cachedContent = data;
|
|
53
|
-
return data;
|
|
54
|
-
}
|
|
55
|
-
async function getContent(query) {
|
|
56
|
-
if (cacheMode) {
|
|
57
|
-
const data2 = await ensureCacheLoaded();
|
|
58
|
-
return applyJmes(data2, query);
|
|
22
|
+
return async function fetchSiteContent(searchQuery) {
|
|
23
|
+
if (!siteToken) {
|
|
24
|
+
throw new Error("[SleekCMS] siteToken is required");
|
|
59
25
|
}
|
|
60
|
-
if (
|
|
61
|
-
|
|
62
|
-
|
|
26
|
+
if (cachedContent) {
|
|
27
|
+
return applyJmes(cachedContent, searchQuery);
|
|
28
|
+
}
|
|
29
|
+
const baseUrl = getBaseUrl(siteToken, devEnv).replace(/\/$/, "");
|
|
30
|
+
const url = new URL(`${baseUrl}/${env}`);
|
|
31
|
+
if (searchQuery && !cacheMode && !cachedContent) {
|
|
32
|
+
url.searchParams.set("search", searchQuery);
|
|
33
|
+
}
|
|
34
|
+
if (mock && dev) {
|
|
35
|
+
url.searchParams.set("mock", "true");
|
|
36
|
+
}
|
|
37
|
+
const res = await fetch(url.toString(), {
|
|
38
|
+
method: "GET",
|
|
39
|
+
headers: {
|
|
40
|
+
"Content-Type": "application/json",
|
|
41
|
+
Authorization: siteToken
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
if (!res.ok) {
|
|
45
|
+
let message = res.statusText;
|
|
46
|
+
try {
|
|
47
|
+
const data2 = await res.json();
|
|
48
|
+
if (data2 && data2.message) message = data2.message;
|
|
49
|
+
} catch {
|
|
50
|
+
}
|
|
51
|
+
throw new Error(`[SleekCMS] Request failed (${res.status}): ${message}`);
|
|
52
|
+
}
|
|
53
|
+
const data = await res.json();
|
|
54
|
+
if (!searchQuery) {
|
|
55
|
+
cachedContent = data;
|
|
63
56
|
cacheMode = true;
|
|
64
|
-
return data2;
|
|
65
57
|
}
|
|
66
|
-
const data = await fetchSiteContent(options, query);
|
|
67
58
|
return data;
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// src/index.ts
|
|
63
|
+
function extractSlugs(pages, path) {
|
|
64
|
+
const slugs = [];
|
|
65
|
+
const pagesList = pages ?? [];
|
|
66
|
+
for (const page of pagesList) {
|
|
67
|
+
const pth = typeof page._path === "string" ? page._path : "";
|
|
68
|
+
if (pth.startsWith(path) && "_slug" in page && typeof page._slug === "string") {
|
|
69
|
+
slugs.push(page._slug);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return slugs;
|
|
73
|
+
}
|
|
74
|
+
function filterPagesByPath(pages, path) {
|
|
75
|
+
const pagesList = pages ?? [];
|
|
76
|
+
return pagesList.filter((p) => {
|
|
77
|
+
const pth = typeof p._path === "string" ? p._path : "";
|
|
78
|
+
return pth.startsWith(path);
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
function createClient(options) {
|
|
82
|
+
const fetchSiteContent = createFetchSiteContent(options);
|
|
83
|
+
async function getContent(query) {
|
|
84
|
+
return await fetchSiteContent(query);
|
|
68
85
|
}
|
|
69
|
-
async function
|
|
86
|
+
async function getPages(path, query) {
|
|
70
87
|
if (!path) {
|
|
71
|
-
throw new Error("[SleekCMS] path is required for
|
|
88
|
+
throw new Error("[SleekCMS] path is required for getPages");
|
|
72
89
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
return applyJmes(filtered2, query);
|
|
90
|
+
const data = await fetchSiteContent();
|
|
91
|
+
const filtered = filterPagesByPath(data.pages, path);
|
|
92
|
+
return applyJmes(filtered, query);
|
|
93
|
+
}
|
|
94
|
+
async function getPage(path) {
|
|
95
|
+
if (!path) {
|
|
96
|
+
throw new Error("[SleekCMS] path is required for getPage");
|
|
81
97
|
}
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
);
|
|
86
|
-
const filtered = (pages ?? []).filter((p) => {
|
|
98
|
+
const data = await fetchSiteContent();
|
|
99
|
+
const pages = data.pages ?? [];
|
|
100
|
+
const page = pages.find((p) => {
|
|
87
101
|
const pth = typeof p._path === "string" ? p._path : "";
|
|
88
|
-
return pth
|
|
102
|
+
return pth === path;
|
|
89
103
|
});
|
|
90
|
-
|
|
104
|
+
if (!page) {
|
|
105
|
+
throw new Error(`[SleekCMS] Page not found: ${path}`);
|
|
106
|
+
}
|
|
107
|
+
return page;
|
|
91
108
|
}
|
|
92
|
-
async function
|
|
93
|
-
if (
|
|
94
|
-
|
|
95
|
-
return data.images ?? {};
|
|
109
|
+
async function getSlugs(path) {
|
|
110
|
+
if (!path) {
|
|
111
|
+
throw new Error("[SleekCMS] path is required for getSlugs");
|
|
96
112
|
}
|
|
97
|
-
const
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
113
|
+
const data = await fetchSiteContent();
|
|
114
|
+
return extractSlugs(data.pages, path);
|
|
115
|
+
}
|
|
116
|
+
async function getImages() {
|
|
117
|
+
const data = await fetchSiteContent();
|
|
118
|
+
return data.images ?? {};
|
|
102
119
|
}
|
|
103
120
|
async function getImage(name) {
|
|
104
121
|
if (!name) return void 0;
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
return data.images ? data.images[name] : void 0;
|
|
108
|
-
}
|
|
109
|
-
const images = await fetchSiteContent(
|
|
110
|
-
options,
|
|
111
|
-
"images"
|
|
112
|
-
);
|
|
113
|
-
return images ? images[name] : void 0;
|
|
122
|
+
const data = await fetchSiteContent();
|
|
123
|
+
return data.images ? data.images[name] : void 0;
|
|
114
124
|
}
|
|
115
125
|
async function getList(name) {
|
|
116
126
|
if (!name) return void 0;
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
const list2 = lists2[name];
|
|
121
|
-
return Array.isArray(list2) ? list2 : void 0;
|
|
122
|
-
}
|
|
123
|
-
const lists = await fetchSiteContent(
|
|
124
|
-
options,
|
|
125
|
-
"lists"
|
|
126
|
-
);
|
|
127
|
-
const list = lists ? lists[name] : void 0;
|
|
127
|
+
const data = await fetchSiteContent();
|
|
128
|
+
const lists = data.lists ?? {};
|
|
129
|
+
const list = lists[name];
|
|
128
130
|
return Array.isArray(list) ? list : void 0;
|
|
129
131
|
}
|
|
130
132
|
return {
|
|
131
133
|
getContent,
|
|
132
|
-
|
|
134
|
+
getPages,
|
|
135
|
+
getPage,
|
|
136
|
+
getSlugs,
|
|
133
137
|
getImages,
|
|
134
138
|
getImage,
|
|
135
139
|
getList
|
|
136
140
|
};
|
|
137
141
|
}
|
|
138
142
|
async function createSyncClient(options) {
|
|
139
|
-
const
|
|
143
|
+
const fetchSiteContent = createFetchSiteContent(options);
|
|
144
|
+
const data = await fetchSiteContent();
|
|
140
145
|
function getContent(query) {
|
|
141
146
|
return applyJmes(data, query);
|
|
142
147
|
}
|
|
143
|
-
function
|
|
148
|
+
function getPages(path, query) {
|
|
144
149
|
if (!path) {
|
|
145
|
-
throw new Error("[SleekCMS] path is required for
|
|
150
|
+
throw new Error("[SleekCMS] path is required for getPages");
|
|
151
|
+
}
|
|
152
|
+
const filtered = filterPagesByPath(data.pages, path);
|
|
153
|
+
return applyJmes(filtered, query);
|
|
154
|
+
}
|
|
155
|
+
function getPage(path) {
|
|
156
|
+
if (!path) {
|
|
157
|
+
throw new Error("[SleekCMS] path is required for getPage");
|
|
146
158
|
}
|
|
147
159
|
const pages = data.pages ?? [];
|
|
148
|
-
const
|
|
160
|
+
const page = pages.find((p) => {
|
|
149
161
|
const pth = typeof p._path === "string" ? p._path : "";
|
|
150
|
-
return pth
|
|
162
|
+
return pth === path;
|
|
151
163
|
});
|
|
152
|
-
return
|
|
164
|
+
return page ?? null;
|
|
165
|
+
}
|
|
166
|
+
function getSlugs(path) {
|
|
167
|
+
if (!path) {
|
|
168
|
+
throw new Error("[SleekCMS] path is required for getSlugs");
|
|
169
|
+
}
|
|
170
|
+
return extractSlugs(data.pages, path);
|
|
153
171
|
}
|
|
154
172
|
function getImages() {
|
|
155
173
|
return data.images ?? {};
|
|
@@ -166,7 +184,9 @@ async function createSyncClient(options) {
|
|
|
166
184
|
}
|
|
167
185
|
return {
|
|
168
186
|
getContent,
|
|
169
|
-
|
|
187
|
+
getPages,
|
|
188
|
+
getPage,
|
|
189
|
+
getSlugs,
|
|
170
190
|
getImages,
|
|
171
191
|
getImage,
|
|
172
192
|
getList
|