@sleekcms/client 0.1.6 → 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 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.findPages('/blog');
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
- ### `findPages<T>(path, query?)`
99
+ ### `getPages(path, query?)`
100
100
 
101
- Find pages that start with the specified path.
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<T>` (async) or `T` (sync)
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.findPages('/blog');
113
+ const blogPosts = await client.getPages('/blog');
114
114
 
115
115
  // Get blog posts with custom query
116
- const publishedPosts = await client.findPages(
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.findPages('/blog', '[].title');
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.findPages('/blog');
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.findPages('/');
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.findPages<BlogPost[]>('/blog');
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 createClient(options) {
80
- const dev = isDevToken(options.siteToken);
81
- let cacheMode = !!options.cache || !!options.mock && dev;
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 ensureCacheLoaded() {
84
- if (cachedContent) return cachedContent;
85
- const data = await fetchSiteContent(options);
86
- cachedContent = data;
87
- return data;
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
- if (!query) {
95
- const data2 = await fetchSiteContent(options);
96
- cachedContent = data2;
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 findPages(path, query) {
119
+ async function getPages(path, query) {
104
120
  if (!path) {
105
- throw new Error("[SleekCMS] path is required for findPages");
121
+ throw new Error("[SleekCMS] path is required for getPages");
106
122
  }
107
- if (cacheMode) {
108
- const data = await ensureCacheLoaded();
109
- const pages2 = data.pages ?? [];
110
- const filtered2 = pages2.filter((p) => {
111
- const pth = typeof p._path === "string" ? p._path : "";
112
- return pth.startsWith(path);
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 pages = await fetchSiteContent(
117
- options,
118
- "pages"
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.startsWith(path);
135
+ return pth === path;
123
136
  });
124
- return applyJmes(filtered, query);
137
+ if (!page) {
138
+ throw new Error(`[SleekCMS] Page not found: ${path}`);
139
+ }
140
+ return page;
125
141
  }
126
- async function getImages() {
127
- if (cacheMode) {
128
- const data = await ensureCacheLoaded();
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 images = await fetchSiteContent(
132
- options,
133
- "images"
134
- );
135
- return images ?? {};
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
- if (cacheMode) {
140
- const data = await ensureCacheLoaded();
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
- if (cacheMode) {
152
- const data = await ensureCacheLoaded();
153
- const lists2 = data.lists ?? {};
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
- findPages,
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 data = await fetchSiteContent(options);
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 findPages(path, query) {
181
+ function getPages(path, query) {
178
182
  if (!path) {
179
- throw new Error("[SleekCMS] path is required for findPages");
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 filtered = pages.filter((p) => {
193
+ const page = pages.find((p) => {
183
194
  const pth = typeof p._path === "string" ? p._path : "";
184
- return pth.startsWith(path);
195
+ return pth === path;
185
196
  });
186
- return applyJmes(filtered, query);
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
- findPages,
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<T = SleekSiteContent>(query?: string): Promise<T>;
36
- findPages<T = unknown>(path: string, query?: string): Promise<T>;
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<T = SleekSiteContent>(query?: string): T;
46
- findPages<T = unknown>(path: string, query?: string): T;
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<T = SleekSiteContent>(query?: string): Promise<T>;
36
- findPages<T = unknown>(path: string, query?: string): Promise<T>;
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<T = SleekSiteContent>(query?: string): T;
46
- findPages<T = unknown>(path: string, query?: string): T;
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 CHANGED
@@ -1,4 +1,4 @@
1
- // src/index.ts
1
+ // src/lib.ts
2
2
  import * as jmespath from "jmespath";
3
3
  function isDevToken(token) {
4
4
  return token.startsWith("dev-");
@@ -7,149 +7,164 @@ function getBaseUrl(token) {
7
7
  let [env, siteId, ...rest] = token.split("-");
8
8
  return `https://${env}.sleekcms.com/${siteId}`;
9
9
  }
10
- async function fetchSiteContent(options, searchQuery) {
11
- const { siteToken, env = "latest", mock } = options;
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();
40
- }
41
10
  function applyJmes(data, query) {
42
11
  if (!query) return data;
43
12
  return jmespath.search(data, query);
44
13
  }
45
- function createClient(options) {
46
- const dev = isDevToken(options.siteToken);
47
- let cacheMode = !!options.cache || !!options.mock && dev;
14
+ function createFetchSiteContent(options) {
15
+ const { siteToken, env = "latest", mock } = options;
16
+ const dev = isDevToken(siteToken);
17
+ let cacheMode = !!options.cache || !!mock && dev;
48
18
  let cachedContent = null;
49
- async function ensureCacheLoaded() {
50
- if (cachedContent) return cachedContent;
51
- const data = await fetchSiteContent(options);
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);
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");
59
33
  }
60
- if (!query) {
61
- const data2 = await fetchSiteContent(options);
62
- cachedContent = data2;
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;
63
53
  cacheMode = true;
64
- return data2;
65
54
  }
66
- const data = await fetchSiteContent(options, query);
67
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);
68
82
  }
69
- async function findPages(path, query) {
83
+ async function getPages(path, query) {
70
84
  if (!path) {
71
- throw new Error("[SleekCMS] path is required for findPages");
85
+ throw new Error("[SleekCMS] path is required for getPages");
72
86
  }
73
- if (cacheMode) {
74
- const data = await ensureCacheLoaded();
75
- const pages2 = data.pages ?? [];
76
- const filtered2 = pages2.filter((p) => {
77
- const pth = typeof p._path === "string" ? p._path : "";
78
- return pth.startsWith(path);
79
- });
80
- return applyJmes(filtered2, query);
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");
81
94
  }
82
- const pages = await fetchSiteContent(
83
- options,
84
- "pages"
85
- );
86
- const filtered = (pages ?? []).filter((p) => {
95
+ const data = await fetchSiteContent();
96
+ const pages = data.pages ?? [];
97
+ const page = pages.find((p) => {
87
98
  const pth = typeof p._path === "string" ? p._path : "";
88
- return pth.startsWith(path);
99
+ return pth === path;
89
100
  });
90
- return applyJmes(filtered, query);
101
+ if (!page) {
102
+ throw new Error(`[SleekCMS] Page not found: ${path}`);
103
+ }
104
+ return page;
91
105
  }
92
- async function getImages() {
93
- if (cacheMode) {
94
- const data = await ensureCacheLoaded();
95
- return data.images ?? {};
106
+ async function getSlugs(path) {
107
+ if (!path) {
108
+ throw new Error("[SleekCMS] path is required for getSlugs");
96
109
  }
97
- const images = await fetchSiteContent(
98
- options,
99
- "images"
100
- );
101
- return images ?? {};
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 ?? {};
102
116
  }
103
117
  async function getImage(name) {
104
118
  if (!name) return void 0;
105
- if (cacheMode) {
106
- const data = await ensureCacheLoaded();
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;
119
+ const data = await fetchSiteContent();
120
+ return data.images ? data.images[name] : void 0;
114
121
  }
115
122
  async function getList(name) {
116
123
  if (!name) return void 0;
117
- if (cacheMode) {
118
- const data = await ensureCacheLoaded();
119
- const lists2 = data.lists ?? {};
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;
124
+ const data = await fetchSiteContent();
125
+ const lists = data.lists ?? {};
126
+ const list = lists[name];
128
127
  return Array.isArray(list) ? list : void 0;
129
128
  }
130
129
  return {
131
130
  getContent,
132
- findPages,
131
+ getPages,
132
+ getPage,
133
+ getSlugs,
133
134
  getImages,
134
135
  getImage,
135
136
  getList
136
137
  };
137
138
  }
138
139
  async function createSyncClient(options) {
139
- const data = await fetchSiteContent(options);
140
+ const fetchSiteContent = createFetchSiteContent(options);
141
+ const data = await fetchSiteContent();
140
142
  function getContent(query) {
141
143
  return applyJmes(data, query);
142
144
  }
143
- function findPages(path, query) {
145
+ function getPages(path, query) {
144
146
  if (!path) {
145
- throw new Error("[SleekCMS] path is required for findPages");
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");
146
155
  }
147
156
  const pages = data.pages ?? [];
148
- const filtered = pages.filter((p) => {
157
+ const page = pages.find((p) => {
149
158
  const pth = typeof p._path === "string" ? p._path : "";
150
- return pth.startsWith(path);
159
+ return pth === path;
151
160
  });
152
- return applyJmes(filtered, query);
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);
153
168
  }
154
169
  function getImages() {
155
170
  return data.images ?? {};
@@ -166,7 +181,9 @@ async function createSyncClient(options) {
166
181
  }
167
182
  return {
168
183
  getContent,
169
- findPages,
184
+ getPages,
185
+ getPage,
186
+ getSlugs,
170
187
  getImages,
171
188
  getImage,
172
189
  getList
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sleekcms/client",
3
- "version": "0.1.6",
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",