@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 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,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
- 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();
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 createClient(options) {
80
- const dev = isDevToken(options.siteToken);
81
- let cacheMode = !!options.cache || !!options.mock && dev;
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 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);
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
- if (!query) {
95
- const data2 = await fetchSiteContent(options);
96
- cachedContent = data2;
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 findPages(path, query) {
122
+ async function getPages(path, query) {
104
123
  if (!path) {
105
- throw new Error("[SleekCMS] path is required for findPages");
124
+ throw new Error("[SleekCMS] path is required for getPages");
106
125
  }
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);
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 pages = await fetchSiteContent(
117
- options,
118
- "pages"
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.startsWith(path);
138
+ return pth === path;
123
139
  });
124
- return applyJmes(filtered, query);
140
+ if (!page) {
141
+ throw new Error(`[SleekCMS] Page not found: ${path}`);
142
+ }
143
+ return page;
125
144
  }
126
- async function getImages() {
127
- if (cacheMode) {
128
- const data = await ensureCacheLoaded();
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 images = await fetchSiteContent(
132
- options,
133
- "images"
134
- );
135
- return images ?? {};
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
- 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;
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
- 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;
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
- findPages,
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 data = await fetchSiteContent(options);
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 findPages(path, query) {
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 findPages");
193
+ throw new Error("[SleekCMS] path is required for getPage");
180
194
  }
181
195
  const pages = data.pages ?? [];
182
- const filtered = pages.filter((p) => {
196
+ const page = pages.find((p) => {
183
197
  const pth = typeof p._path === "string" ? p._path : "";
184
- return pth.startsWith(path);
198
+ return pth === path;
185
199
  });
186
- return applyJmes(filtered, query);
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
- findPages,
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<T = SleekSiteContent>(query?: string): Promise<T>;
36
- findPages<T = unknown>(path: string, query?: string): Promise<T>;
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<T = SleekSiteContent>(query?: string): T;
46
- findPages<T = unknown>(path: string, query?: string): T;
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<T = SleekSiteContent>(query?: string): Promise<T>;
36
- findPages<T = unknown>(path: string, query?: string): Promise<T>;
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<T = SleekSiteContent>(query?: string): T;
46
- findPages<T = unknown>(path: string, query?: string): T;
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/index.ts
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
- 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();
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 createClient(options) {
46
- const dev = isDevToken(options.siteToken);
47
- let cacheMode = !!options.cache || !!options.mock && dev;
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 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);
22
+ return async function fetchSiteContent(searchQuery) {
23
+ if (!siteToken) {
24
+ throw new Error("[SleekCMS] siteToken is required");
59
25
  }
60
- if (!query) {
61
- const data2 = await fetchSiteContent(options);
62
- cachedContent = data2;
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 findPages(path, query) {
86
+ async function getPages(path, query) {
70
87
  if (!path) {
71
- throw new Error("[SleekCMS] path is required for findPages");
88
+ throw new Error("[SleekCMS] path is required for getPages");
72
89
  }
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);
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 pages = await fetchSiteContent(
83
- options,
84
- "pages"
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.startsWith(path);
102
+ return pth === path;
89
103
  });
90
- return applyJmes(filtered, query);
104
+ if (!page) {
105
+ throw new Error(`[SleekCMS] Page not found: ${path}`);
106
+ }
107
+ return page;
91
108
  }
92
- async function getImages() {
93
- if (cacheMode) {
94
- const data = await ensureCacheLoaded();
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 images = await fetchSiteContent(
98
- options,
99
- "images"
100
- );
101
- return images ?? {};
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
- 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;
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
- 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;
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
- findPages,
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 data = await fetchSiteContent(options);
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 findPages(path, query) {
148
+ function getPages(path, query) {
144
149
  if (!path) {
145
- throw new Error("[SleekCMS] path is required for findPages");
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 filtered = pages.filter((p) => {
160
+ const page = pages.find((p) => {
149
161
  const pth = typeof p._path === "string" ? p._path : "";
150
- return pth.startsWith(path);
162
+ return pth === path;
151
163
  });
152
- return applyJmes(filtered, query);
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
- findPages,
187
+ getPages,
188
+ getPage,
189
+ getSlugs,
170
190
  getImages,
171
191
  getImage,
172
192
  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.1",
4
4
  "description": "Official SleekCMS content client for Node 18+ and browser",
5
5
  "type": "module",
6
6
  "main": "index.cjs",