@sleekcms/client 0.1.2 → 0.1.5

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/index.d.cts ADDED
@@ -0,0 +1,60 @@
1
+ type Entry = Record<string, unknown>;
2
+ type Page = {
3
+ _path: string;
4
+ [key: string]: unknown;
5
+ };
6
+ type Image = {
7
+ url: string;
8
+ [key: string]: unknown;
9
+ };
10
+ type List = Array<{
11
+ label: string;
12
+ value: string;
13
+ }>;
14
+ interface SleekSiteContent {
15
+ entries?: Record<string, Entry> | Record<string, Entry[]>;
16
+ pages?: Array<Page>;
17
+ images?: Record<string, Image>;
18
+ lists?: Record<string, List>;
19
+ config?: {
20
+ title?: string;
21
+ };
22
+ }
23
+ interface ClientOptions {
24
+ siteToken: string;
25
+ env?: string;
26
+ cache?: boolean;
27
+ mock?: boolean;
28
+ resolveEnv?: boolean;
29
+ }
30
+
31
+ /**
32
+ * Async SleekCMS client: methods return Promises.
33
+ */
34
+ interface SleekClient {
35
+ getContent<T = SleekSiteContent>(query?: string): Promise<T>;
36
+ findPages<T = unknown>(path: string, query?: string): Promise<T>;
37
+ getImages(): Promise<SleekSiteContent["images"]>;
38
+ getImage(name: string): Promise<unknown | undefined>;
39
+ getList<T = unknown>(name: string): Promise<T[] | undefined>;
40
+ }
41
+ /**
42
+ * Sync client: prefetches full content once; subsequent calls are in-memory only.
43
+ */
44
+ interface SleekSyncClient {
45
+ getContent<T = SleekSiteContent>(query?: string): T;
46
+ findPages<T = unknown>(path: string, query?: string): T;
47
+ getImages(): SleekSiteContent["images"];
48
+ getImage(name: string): unknown | undefined;
49
+ getList<T = unknown>(name: string): T[] | undefined;
50
+ }
51
+ declare function createClient(options: ClientOptions): SleekClient;
52
+ /**
53
+ * Create a sync SleekCMS client.
54
+ *
55
+ * - Prefetches full content once (no search=).
56
+ * - All operations (including JMESPath) are local and synchronous.
57
+ */
58
+ declare function createSyncClient(options: ClientOptions): Promise<SleekSyncClient>;
59
+
60
+ export { type ClientOptions, type SleekClient, type SleekSiteContent, type SleekSyncClient, createClient, createSyncClient };
package/index.d.ts ADDED
@@ -0,0 +1,60 @@
1
+ type Entry = Record<string, unknown>;
2
+ type Page = {
3
+ _path: string;
4
+ [key: string]: unknown;
5
+ };
6
+ type Image = {
7
+ url: string;
8
+ [key: string]: unknown;
9
+ };
10
+ type List = Array<{
11
+ label: string;
12
+ value: string;
13
+ }>;
14
+ interface SleekSiteContent {
15
+ entries?: Record<string, Entry> | Record<string, Entry[]>;
16
+ pages?: Array<Page>;
17
+ images?: Record<string, Image>;
18
+ lists?: Record<string, List>;
19
+ config?: {
20
+ title?: string;
21
+ };
22
+ }
23
+ interface ClientOptions {
24
+ siteToken: string;
25
+ env?: string;
26
+ cache?: boolean;
27
+ mock?: boolean;
28
+ resolveEnv?: boolean;
29
+ }
30
+
31
+ /**
32
+ * Async SleekCMS client: methods return Promises.
33
+ */
34
+ interface SleekClient {
35
+ getContent<T = SleekSiteContent>(query?: string): Promise<T>;
36
+ findPages<T = unknown>(path: string, query?: string): Promise<T>;
37
+ getImages(): Promise<SleekSiteContent["images"]>;
38
+ getImage(name: string): Promise<unknown | undefined>;
39
+ getList<T = unknown>(name: string): Promise<T[] | undefined>;
40
+ }
41
+ /**
42
+ * Sync client: prefetches full content once; subsequent calls are in-memory only.
43
+ */
44
+ interface SleekSyncClient {
45
+ getContent<T = SleekSiteContent>(query?: string): T;
46
+ findPages<T = unknown>(path: string, query?: string): T;
47
+ getImages(): SleekSiteContent["images"];
48
+ getImage(name: string): unknown | undefined;
49
+ getList<T = unknown>(name: string): T[] | undefined;
50
+ }
51
+ declare function createClient(options: ClientOptions): SleekClient;
52
+ /**
53
+ * Create a sync SleekCMS client.
54
+ *
55
+ * - Prefetches full content once (no search=).
56
+ * - All operations (including JMESPath) are local and synchronous.
57
+ */
58
+ declare function createSyncClient(options: ClientOptions): Promise<SleekSyncClient>;
59
+
60
+ export { type ClientOptions, type SleekClient, type SleekSiteContent, type SleekSyncClient, createClient, createSyncClient };
package/package.json CHANGED
@@ -1,24 +1,25 @@
1
1
  {
2
2
  "name": "@sleekcms/client",
3
- "version": "0.1.2",
3
+ "version": "0.1.5",
4
4
  "description": "Official SleekCMS content client for Node 18+ and browser",
5
5
  "type": "module",
6
- "main": "dist/index.cjs",
7
- "module": "dist/index.mjs",
8
- "types": "dist/index.d.ts",
6
+ "main": "index.cjs",
7
+ "module": "index.mjs",
8
+ "types": "index.d.ts",
9
+ "private": false,
9
10
  "exports": {
10
11
  ".": {
11
- "types": "./dist/index.d.ts",
12
- "import": "./dist/index.mjs",
13
- "require": "./dist/index.cjs"
12
+ "types": "./index.d.ts",
13
+ "import": "./index.mjs",
14
+ "require": "./index.cjs"
14
15
  }
15
16
  },
16
17
  "scripts": {
17
- "build": "tsup src/index.ts --dts --format esm,cjs",
18
+ "build": "npm run clean && tsup src/index.ts --dts --format esm,cjs && cp README.md dist/ && jq '.private = false | .files = [\"index.cjs\", \"index.mjs\", \"index.d.ts\", \"index.d.cts\", \"README.md\"]' package.json > dist/package.json",
18
19
  "clean": "rimraf dist || true",
19
20
  "test": "vitest run",
20
21
  "test:watch": "vitest",
21
- "prepublishOnly": "npm run build"
22
+ "publish:dist": "cd dist && npm publish --access public"
22
23
  },
23
24
  "dependencies": {
24
25
  "jmespath": "^0.16.0"
@@ -34,5 +35,12 @@
34
35
  "tsup": "^8.5.1",
35
36
  "typescript": "^5.4.0",
36
37
  "vitest": "^4.0.15"
37
- }
38
+ },
39
+ "files": [
40
+ "index.cjs",
41
+ "index.mjs",
42
+ "index.d.ts",
43
+ "index.d.cts",
44
+ "README.md"
45
+ ]
38
46
  }
@@ -1,355 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
2
- import { createClient, createSyncClient } from "../src/index";
3
- import type { SleekSiteContent } from "../src/types";
4
-
5
- // Mock data
6
- const mockSiteContent: SleekSiteContent = {
7
- pages: [
8
- { _path: "/", title: "Home", published: true },
9
- { _path: "/blog/post-1", title: "Post 1", published: true, category: "tech" },
10
- { _path: "/blog/post-2", title: "Post 2", published: false, category: "tech" },
11
- { _path: "/about", title: "About", published: true }
12
- ],
13
- images: {
14
- logo: { url: "https://example.com/logo.png", width: 200, height: 100 },
15
- hero: { url: "https://example.com/hero.jpg", width: 1200, height: 600 }
16
- },
17
- lists: {
18
- categories: [
19
- { label: "Technology", value: "tech" },
20
- { label: "Business", value: "business" }
21
- ]
22
- },
23
- config: {
24
- title: "Test Site"
25
- }
26
- };
27
-
28
- describe("SleekCMS Client", () => {
29
- let fetchSpy: any;
30
-
31
- beforeEach(() => {
32
- fetchSpy = vi.spyOn(globalThis, "fetch");
33
- });
34
-
35
- afterEach(() => {
36
- vi.restoreAllMocks();
37
- });
38
-
39
- describe("Search Query Functionality", () => {
40
- it("should send search query as URL parameter when query is provided", async () => {
41
- fetchSpy.mockResolvedValueOnce({
42
- ok: true,
43
- json: async () => ["Post 1", "Post 2"]
44
- });
45
-
46
- const client = createClient({
47
- siteToken: "prod-site123"
48
- });
49
-
50
- const result = await client.getContent('pages[?published == `true`]');
51
-
52
- expect(fetchSpy).toHaveBeenCalledTimes(1);
53
- const calledUrl = fetchSpy.mock.calls[0][0];
54
- expect(calledUrl).toContain("search=");
55
- expect(calledUrl).toContain('pages');
56
- expect(calledUrl).toContain('published');
57
- });
58
-
59
- it("should apply JMESPath query on server-side with search parameter", async () => {
60
- const filteredPages = [
61
- { _path: "/", title: "Home", published: true },
62
- { _path: "/about", title: "About", published: true }
63
- ];
64
-
65
- fetchSpy.mockResolvedValueOnce({
66
- ok: true,
67
- json: async () => filteredPages
68
- });
69
-
70
- const client = createClient({
71
- siteToken: "prod-site123"
72
- });
73
-
74
- const result = await client.getContent('pages[?published == `true`]');
75
-
76
- expect(result).toEqual(filteredPages);
77
- expect(fetchSpy).toHaveBeenCalledTimes(1);
78
- });
79
-
80
- it("should apply local JMESPath query in cache mode", async () => {
81
- fetchSpy.mockResolvedValueOnce({
82
- ok: true,
83
- json: async () => mockSiteContent
84
- });
85
-
86
- const client = createClient({
87
- siteToken: "prod-site123",
88
- cache: true
89
- });
90
-
91
- // First call to load cache
92
- const allContent = await client.getContent();
93
- expect(fetchSpy).toHaveBeenCalledTimes(1);
94
-
95
- // Second call with query should use local cache
96
- const pages = await client.getContent<any[]>('pages');
97
- expect(fetchSpy).toHaveBeenCalledTimes(1); // No additional fetch
98
- expect(pages).toHaveLength(4);
99
- expect(pages).toEqual(mockSiteContent.pages);
100
- });
101
-
102
- it("should handle complex JMESPath queries with findPages", async () => {
103
- fetchSpy.mockResolvedValueOnce({
104
- ok: true,
105
- json: async () => mockSiteContent
106
- });
107
-
108
- const client = createClient({
109
- siteToken: "prod-site123",
110
- cache: true
111
- });
112
-
113
- await client.getContent(); // Load cache
114
-
115
- const result = await client.findPages(
116
- "/blog",
117
- "[?published == `true`].title"
118
- );
119
-
120
- expect(result).toEqual(["Post 1"]);
121
- expect(fetchSpy).toHaveBeenCalledTimes(1);
122
- });
123
- });
124
-
125
- describe("Cache Behavior - Async Client", () => {
126
- it("should fetch full content once when cache is true", async () => {
127
- fetchSpy.mockResolvedValueOnce({
128
- ok: true,
129
- json: async () => mockSiteContent
130
- });
131
-
132
- const client = createClient({
133
- siteToken: "prod-site123",
134
- cache: true
135
- });
136
-
137
- // First call
138
- const content1 = await client.getContent();
139
- expect(fetchSpy).toHaveBeenCalledTimes(1);
140
- expect(content1).toEqual(mockSiteContent);
141
-
142
- // Second call should use cache
143
- const content2 = await client.getContent();
144
- expect(fetchSpy).toHaveBeenCalledTimes(1); // No additional fetch
145
- expect(content2).toEqual(mockSiteContent);
146
- });
147
-
148
- it("should use cached data for all methods after initial fetch", async () => {
149
- fetchSpy.mockResolvedValueOnce({
150
- ok: true,
151
- json: async () => mockSiteContent
152
- });
153
-
154
- const client = createClient({
155
- siteToken: "prod-site123",
156
- cache: true
157
- });
158
-
159
- // Initial fetch
160
- await client.getContent();
161
- expect(fetchSpy).toHaveBeenCalledTimes(1);
162
-
163
- // All subsequent calls should use cache
164
- const images = await client.getImages();
165
- expect(images).toEqual(mockSiteContent.images);
166
- expect(fetchSpy).toHaveBeenCalledTimes(1);
167
-
168
- const logo = await client.getImage("logo");
169
- expect(logo).toEqual(mockSiteContent.images!.logo);
170
- expect(fetchSpy).toHaveBeenCalledTimes(1);
171
-
172
- const list = await client.getList("categories");
173
- expect(list).toEqual(mockSiteContent.lists!.categories);
174
- expect(fetchSpy).toHaveBeenCalledTimes(1);
175
-
176
- const pages = await client.findPages("/blog");
177
- expect(pages).toHaveLength(2);
178
- expect(fetchSpy).toHaveBeenCalledTimes(1);
179
- });
180
-
181
- it("should enable cache automatically with mock=true and dev token", async () => {
182
- fetchSpy.mockResolvedValueOnce({
183
- ok: true,
184
- json: async () => mockSiteContent
185
- });
186
-
187
- const client = createClient({
188
- siteToken: "dev-site123",
189
- mock: true
190
- });
191
-
192
- // First call
193
- await client.getContent();
194
- expect(fetchSpy).toHaveBeenCalledTimes(1);
195
-
196
- // Second call should use cache (auto-enabled due to mock + dev)
197
- await client.getContent();
198
- expect(fetchSpy).toHaveBeenCalledTimes(1);
199
- });
200
-
201
- it("should fetch on each call when cache is false and query is provided", async () => {
202
- fetchSpy
203
- .mockResolvedValueOnce({
204
- ok: true,
205
- json: async () => mockSiteContent.pages
206
- })
207
- .mockResolvedValueOnce({
208
- ok: true,
209
- json: async () => [{ _path: "/new", title: "New Page" }]
210
- });
211
-
212
- const client = createClient({
213
- siteToken: "prod-site123",
214
- cache: false
215
- });
216
-
217
- const pages1 = await client.getContent('pages');
218
- expect(fetchSpy).toHaveBeenCalledTimes(1);
219
- expect(Array.isArray(pages1)).toBe(true);
220
-
221
- const pages2 = await client.getContent('pages');
222
- expect(fetchSpy).toHaveBeenCalledTimes(2); // New fetch
223
- expect(pages2).toEqual([{ _path: "/new", title: "New Page" }]);
224
- });
225
-
226
- it("should enable caching after first getContent() call without query", async () => {
227
- fetchSpy.mockResolvedValueOnce({
228
- ok: true,
229
- json: async () => mockSiteContent
230
- });
231
-
232
- const client = createClient({
233
- siteToken: "prod-site123",
234
- cache: false
235
- });
236
-
237
- // First call without query enables cache
238
- await client.getContent();
239
- expect(fetchSpy).toHaveBeenCalledTimes(1);
240
-
241
- // Subsequent calls use cache
242
- await client.getImages();
243
- expect(fetchSpy).toHaveBeenCalledTimes(1);
244
-
245
- await client.getList("categories");
246
- expect(fetchSpy).toHaveBeenCalledTimes(1);
247
- });
248
-
249
- it("should apply JMESPath queries locally when cache is enabled", async () => {
250
- fetchSpy.mockResolvedValueOnce({
251
- ok: true,
252
- json: async () => mockSiteContent
253
- });
254
-
255
- const client = createClient({
256
- siteToken: "prod-site123",
257
- cache: true
258
- });
259
-
260
- // Load cache
261
- await client.getContent();
262
- expect(fetchSpy).toHaveBeenCalledTimes(1);
263
-
264
- // Query should be applied locally
265
- const publishedPages = await client.getContent<any[]>(
266
- 'pages[?published == `true`]'
267
- );
268
- expect(publishedPages).toHaveLength(3);
269
- expect(publishedPages.every((p: any) => p.published === true)).toBe(true);
270
- expect(fetchSpy).toHaveBeenCalledTimes(1); // No additional fetch
271
- });
272
-
273
- it("should handle concurrent requests with cache enabled", async () => {
274
- fetchSpy.mockResolvedValue({
275
- ok: true,
276
- json: async () => mockSiteContent
277
- });
278
-
279
- const client = createClient({
280
- siteToken: "prod-site123",
281
- cache: true
282
- });
283
-
284
- // Multiple concurrent requests - may fetch multiple times initially
285
- // but should eventually use cache
286
- const [content1, content2, content3] = await Promise.all([
287
- client.getContent(),
288
- client.getContent(),
289
- client.getContent()
290
- ]);
291
-
292
- expect(content1).toEqual(mockSiteContent);
293
- expect(content2).toEqual(mockSiteContent);
294
- expect(content3).toEqual(mockSiteContent);
295
- });
296
- });
297
-
298
- describe("Sync Client", () => {
299
- it("should prefetch all content and work synchronously", async () => {
300
- fetchSpy.mockResolvedValueOnce({
301
- ok: true,
302
- json: async () => mockSiteContent
303
- });
304
-
305
- const client = await createSyncClient({
306
- siteToken: "prod-site123"
307
- });
308
-
309
- // Initial fetch during creation
310
- expect(fetchSpy).toHaveBeenCalledTimes(1);
311
-
312
- // All methods are synchronous and use cached data
313
- const content = client.getContent();
314
- expect(content).toEqual(mockSiteContent);
315
-
316
- const images = client.getImages();
317
- expect(images).toEqual(mockSiteContent.images);
318
-
319
- const pages = client.findPages("/blog");
320
- expect(pages).toHaveLength(2);
321
-
322
- // Still only one fetch
323
- expect(fetchSpy).toHaveBeenCalledTimes(1);
324
- });
325
- });
326
-
327
- describe("Error Handling", () => {
328
- it("should throw error when siteToken is missing", async () => {
329
- const client = createClient({
330
- siteToken: ""
331
- });
332
-
333
- await expect(client.getContent()).rejects.toThrow(
334
- "[SleekCMS] siteToken is required"
335
- );
336
- });
337
-
338
- it("should handle HTTP errors gracefully", async () => {
339
- fetchSpy.mockResolvedValueOnce({
340
- ok: false,
341
- status: 404,
342
- statusText: "Not Found",
343
- json: async () => ({ message: "Site not found" })
344
- });
345
-
346
- const client = createClient({
347
- siteToken: "prod-invalid"
348
- });
349
-
350
- await expect(client.getContent()).rejects.toThrow(
351
- "[SleekCMS] Request failed (404): Site not found"
352
- );
353
- });
354
- });
355
- });
package/src/index.ts DELETED
@@ -1,260 +0,0 @@
1
- import * as jmespath from "jmespath";
2
- import type { SleekSiteContent, ClientOptions } from "./types";
3
-
4
- function isDevToken(token: string): boolean {
5
- return token.startsWith("dev-");
6
- }
7
-
8
- function getBaseUrl(token: string): string {
9
- let [env, siteId, ...rest] = token.split("-");
10
- return `https://${env}.sleekcms.com/${siteId}`;
11
- }
12
-
13
- /**
14
- * Low-level fetch helper.
15
- * - If `searchQuery` is provided, it's sent as `?search=<JMESPath>`.
16
- * - If options.mock === true and token is dev-..., `mock=true` is added.
17
- */
18
- async function fetchSiteContent(
19
- options: ClientOptions,
20
- searchQuery?: string
21
- ): Promise<any> {
22
- const { siteToken, env = "latest", mock } = options;
23
-
24
- if (!siteToken) {
25
- throw new Error("[SleekCMS] siteToken is required");
26
- }
27
-
28
- const baseUrl = getBaseUrl(siteToken).replace(/\/$/, "");
29
- const url = new URL(`${baseUrl}/${env}`);
30
-
31
- if (searchQuery) {
32
- url.searchParams.set("search", searchQuery);
33
- }
34
-
35
- if (mock && isDevToken(siteToken)) {
36
- url.searchParams.set("mock", "true");
37
- }
38
-
39
- const res = await fetch(url.toString(), {
40
- method: "GET",
41
- headers: {
42
- "Content-Type": "application/json",
43
- Authorization: siteToken
44
- }
45
- });
46
-
47
- if (!res.ok) {
48
- let message = res.statusText;
49
- try {
50
- const data = (await res.json()) as { message?: string };
51
- if (data && data.message) message = data.message;
52
- } catch {
53
- // ignore
54
- }
55
- throw new Error(`[SleekCMS] Request failed (${res.status}): ${message}`);
56
- }
57
-
58
- return res.json();
59
- }
60
-
61
- function applyJmes<T = unknown>(data: unknown, query?: string): T {
62
- if (!query) return data as T;
63
- return jmespath.search(data, query) as T;
64
- }
65
-
66
- export type { SleekSiteContent, ClientOptions };
67
-
68
- /**
69
- * Async SleekCMS client: methods return Promises.
70
- */
71
- export interface SleekClient {
72
- getContent<T = SleekSiteContent>(query?: string): Promise<T>;
73
- findPages<T = unknown>(path: string, query?: string): Promise<T>;
74
- getImages(): Promise<SleekSiteContent["images"]>;
75
- getImage(name: string): Promise<unknown | undefined>;
76
- getList<T = unknown>(name: string): Promise<T[] | undefined>;
77
- }
78
-
79
- /**
80
- * Sync client: prefetches full content once; subsequent calls are in-memory only.
81
- */
82
- export interface SleekSyncClient {
83
- getContent<T = SleekSiteContent>(query?: string): T;
84
- findPages<T = unknown>(path: string, query?: string): T;
85
- getImages(): SleekSiteContent["images"];
86
- getImage(name: string): unknown | undefined;
87
- getList<T = unknown>(name: string): T[] | undefined;
88
- }
89
-
90
- export function createClient(options: ClientOptions): SleekClient {
91
- const dev = isDevToken(options.siteToken);
92
-
93
- let cacheMode = !!options.cache || (!!options.mock && dev);
94
- let cachedContent: SleekSiteContent | null = null;
95
-
96
- async function ensureCacheLoaded(): Promise<SleekSiteContent> {
97
- if (cachedContent) return cachedContent;
98
- const data = (await fetchSiteContent(options)) as SleekSiteContent;
99
- cachedContent = data;
100
- return data;
101
- }
102
-
103
- async function getContent<T = SleekSiteContent>(query?: string): Promise<T> {
104
- if (cacheMode) {
105
- const data = await ensureCacheLoaded();
106
- return applyJmes<T>(data, query);
107
- }
108
-
109
- if (!query) {
110
- const data = (await fetchSiteContent(options)) as SleekSiteContent;
111
- cachedContent = data;
112
- cacheMode = true;
113
- return data as T;
114
- }
115
-
116
- const data = await fetchSiteContent(options, query);
117
- return data as T;
118
- }
119
-
120
- async function findPages<T = unknown>(
121
- path: string,
122
- query?: string
123
- ): Promise<T> {
124
- if (!path) {
125
- throw new Error("[SleekCMS] path is required for findPages");
126
- }
127
-
128
- if (cacheMode) {
129
- const data = await ensureCacheLoaded();
130
- const pages = data.pages ?? [];
131
- const filtered = pages.filter((p) => {
132
- const pth = typeof p._path === "string" ? p._path : "";
133
- return pth.startsWith(path);
134
- });
135
- return applyJmes<T>(filtered, query);
136
- }
137
-
138
- const pages = (await fetchSiteContent(
139
- options,
140
- "pages"
141
- )) as SleekSiteContent["pages"];
142
-
143
- const filtered = (pages ?? []).filter((p) => {
144
- const pth = typeof p._path === "string" ? p._path : "";
145
- return pth.startsWith(path);
146
- });
147
-
148
- return applyJmes<T>(filtered, query);
149
- }
150
-
151
- async function getImages(): Promise<SleekSiteContent["images"]> {
152
- if (cacheMode) {
153
- const data = await ensureCacheLoaded();
154
- return data.images ?? {};
155
- }
156
-
157
- const images = (await fetchSiteContent(
158
- options,
159
- "images"
160
- )) as SleekSiteContent["images"];
161
- return images ?? {};
162
- }
163
-
164
- async function getImage(name: string): Promise<unknown | undefined> {
165
- if (!name) return undefined;
166
-
167
- if (cacheMode) {
168
- const data = await ensureCacheLoaded();
169
- return data.images ? data.images[name] : undefined;
170
- }
171
-
172
- const images = (await fetchSiteContent(
173
- options,
174
- "images"
175
- )) as SleekSiteContent["images"];
176
- return images ? images[name] : undefined;
177
- }
178
-
179
- async function getList<T = unknown>(
180
- name: string
181
- ): Promise<T[] | undefined> {
182
- if (!name) return undefined;
183
-
184
- if (cacheMode) {
185
- const data = await ensureCacheLoaded();
186
- const lists = data.lists ?? {};
187
- const list = lists[name];
188
- return Array.isArray(list) ? (list as T[]) : undefined;
189
- }
190
-
191
- const lists = (await fetchSiteContent(
192
- options,
193
- "lists"
194
- )) as SleekSiteContent["lists"];
195
- const list = lists ? lists[name] : undefined;
196
- return Array.isArray(list) ? (list as T[]) : undefined;
197
- }
198
-
199
- return {
200
- getContent,
201
- findPages,
202
- getImages,
203
- getImage,
204
- getList
205
- };
206
- }
207
-
208
- /**
209
- * Create a sync SleekCMS client.
210
- *
211
- * - Prefetches full content once (no search=).
212
- * - All operations (including JMESPath) are local and synchronous.
213
- */
214
- export async function createSyncClient(
215
- options: ClientOptions
216
- ): Promise<SleekSyncClient> {
217
- const data = (await fetchSiteContent(options)) as SleekSiteContent;
218
-
219
- function getContent<T = SleekSiteContent>(query?: string): T {
220
- return applyJmes<T>(data, query);
221
- }
222
-
223
- function findPages<T = unknown>(path: string, query?: string): T {
224
- if (!path) {
225
- throw new Error("[SleekCMS] path is required for findPages");
226
- }
227
-
228
- const pages = data.pages ?? [];
229
- const filtered = pages.filter((p) => {
230
- const pth = typeof p._path === "string" ? p._path : "";
231
- return pth.startsWith(path);
232
- });
233
-
234
- return applyJmes<T>(filtered, query);
235
- }
236
-
237
- function getImages(): SleekSiteContent["images"] {
238
- return data.images ?? {};
239
- }
240
-
241
- function getImage(name: string): unknown | undefined {
242
- if (!name) return undefined;
243
- return data.images ? data.images[name] : undefined;
244
- }
245
-
246
- function getList<T = unknown>(name: string): T[] | undefined {
247
- if (!name) return undefined;
248
- const lists = data.lists ?? {};
249
- const list = lists[name];
250
- return Array.isArray(list) ? (list as T[]) : undefined;
251
- }
252
-
253
- return {
254
- getContent,
255
- findPages,
256
- getImages,
257
- getImage,
258
- getList
259
- };
260
- }
package/src/types.ts DELETED
@@ -1,20 +0,0 @@
1
-
2
- type Entry = Record<string, unknown>;
3
- type Page = { _path: string; [key: string]: unknown };
4
- type Image = { url: string; [key: string]: unknown };
5
- type List = Array<{ label: string; value: string }>;
6
-
7
- export interface SleekSiteContent {
8
- entries?: Record<string, Entry> | Record<string, Entry[]>;
9
- pages?: Array<Page>;
10
- images?: Record<string, Image>;
11
- lists?: Record<string, List>;
12
- config?: { title?: string; };
13
- }
14
-
15
- export interface ClientOptions {
16
- siteToken: string;
17
- env?: string;
18
- cache?: boolean;
19
- mock?: boolean;
20
- }
package/vitest.config.ts DELETED
@@ -1,8 +0,0 @@
1
- import { defineConfig } from "vitest/config";
2
-
3
- export default defineConfig({
4
- test: {
5
- globals: true,
6
- environment: "node"
7
- }
8
- });
File without changes