@zenith-open/zenithcms-sdk 1.0.0-beta.8 → 1.0.0-beta.9

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zenith-open/zenithcms-sdk",
3
- "version": "1.0.0-beta.8",
3
+ "version": "1.0.0-beta.9",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -19,7 +19,7 @@
19
19
  "license": "MIT",
20
20
  "devDependencies": {
21
21
  "vitest": "^1.6.0",
22
- "@zenith-open/zenithcms-types": "1.0.0-beta.8"
22
+ "@zenith-open/zenithcms-types": "1.0.0-beta.9"
23
23
  },
24
24
  "files": [
25
25
  "dist",
package/dist/cache.d.ts DELETED
@@ -1,21 +0,0 @@
1
- /**
2
- * Zenith Advanced Cache Service
3
- * ─────────────────────────────
4
- * Features:
5
- * 1. Tag-based invalidation (e.g., invalidate all 'posts')
6
- * 2. TTL support
7
- * 3. Atomic clears
8
- */
9
- export declare class CacheService {
10
- private static cache;
11
- private static tags;
12
- static initialize(): void;
13
- static get<T>(key: string): T | undefined;
14
- static set(key: string, value: unknown, ttl?: number, tags?: string[]): void;
15
- static del(keys: string | string[]): void;
16
- /**
17
- * Invalidate all keys associated with a tag
18
- */
19
- static invalidateTag(tag: string): void;
20
- static flush(): void;
21
- }
package/dist/cache.js DELETED
@@ -1,55 +0,0 @@
1
- import NodeCache from 'node-cache';
2
- import { logger } from './logger';
3
- /**
4
- * Zenith Advanced Cache Service
5
- * ─────────────────────────────
6
- * Features:
7
- * 1. Tag-based invalidation (e.g., invalidate all 'posts')
8
- * 2. TTL support
9
- * 3. Atomic clears
10
- */
11
- export class CacheService {
12
- static cache = new NodeCache({ stdTTL: 600 }); // 10 min default
13
- static tags = {};
14
- // Handle key expiration to prevent memory leaks in the tags array
15
- static initialize() {
16
- this.cache.on('expired', (key) => {
17
- Object.keys(this.tags).forEach((tag) => {
18
- this.tags[tag] = this.tags[tag].filter((k) => k !== key);
19
- });
20
- });
21
- }
22
- static get(key) {
23
- return this.cache.get(key);
24
- }
25
- static set(key, value, ttl, tags = []) {
26
- this.cache.set(key, value, ttl || 600);
27
- // Track tags
28
- tags.forEach((tag) => {
29
- if (!this.tags[tag])
30
- this.tags[tag] = [];
31
- if (!this.tags[tag].includes(key)) {
32
- this.tags[tag].push(key);
33
- }
34
- });
35
- }
36
- static del(keys) {
37
- this.cache.del(keys);
38
- }
39
- /**
40
- * Invalidate all keys associated with a tag
41
- */
42
- static invalidateTag(tag) {
43
- const keys = this.tags[tag];
44
- if (keys && keys.length > 0) {
45
- logger.info({ tag, count: keys.length }, 'Invalidating cache tag');
46
- this.cache.del(keys);
47
- this.tags[tag] = [];
48
- }
49
- }
50
- static flush() {
51
- this.cache.flushAll();
52
- this.tags = {};
53
- }
54
- }
55
- CacheService.initialize();
package/dist/client.d.ts DELETED
@@ -1,65 +0,0 @@
1
- export interface ZenithClientOptions {
2
- url: string;
3
- apiKey?: string;
4
- siteId?: string;
5
- }
6
- export interface FetchOptions extends RequestInit {
7
- locale?: string;
8
- depth?: number;
9
- drafts?: boolean;
10
- }
11
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
12
- export interface FindOptions extends FetchOptions {
13
- where?: Record<string, any>; // eslint-disable-line @typescript-eslint/no-explicit-any
14
- sort?: string;
15
- limit?: number;
16
- page?: number;
17
- }
18
- /**
19
- * Lightweight JavaScript client for Zenith CMS, optimized for Edge environments.
20
- */
21
- export declare class ZenithClient {
22
- private url;
23
- private apiKey?;
24
- private siteId?;
25
- constructor(options: ZenithClientOptions);
26
- private buildQueryString;
27
- private flattenWhereParams;
28
- private fetchAPI;
29
- /**
30
- * Find multiple documents in a collection.
31
- */
32
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
33
- find<T = any>(collection: string, options?: FindOptions): Promise<{ // eslint-disable-line @typescript-eslint/no-explicit-any
34
- docs: T[];
35
- totalDocs: number;
36
- totalPages: number;
37
- page: number;
38
- }>;
39
- /**
40
- * Find a single document by its ID.
41
- */
42
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
43
- findById<T = any>(collection: string, id: string, options?: FetchOptions): Promise<T>; // eslint-disable-line @typescript-eslint/no-explicit-any
44
- /**
45
- * Fetch a singleton configuration.
46
- */
47
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
48
- findGlobal<T = any>(slug: string, options?: FetchOptions): Promise<T>; // eslint-disable-line @typescript-eslint/no-explicit-any
49
- /**
50
- * Create a new document in a collection.
51
- */
52
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
53
- create<T = any>(collection: string, payload: any, options?: FetchOptions): Promise<T>; // eslint-disable-line @typescript-eslint/no-explicit-any
54
- /**
55
- * Update an existing document.
56
- */
57
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
58
- update<T = any>(collection: string, id: string, payload: any, options?: FetchOptions): Promise<T>; // eslint-disable-line @typescript-eslint/no-explicit-any
59
- /**
60
- * Delete a document.
61
- */
62
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
63
- delete<T = any>(collection: string, id: string, options?: FetchOptions): Promise<T>; // eslint-disable-line @typescript-eslint/no-explicit-any
64
- }
65
- export declare function createClient(options: ZenithClientOptions): ZenithClient;
package/dist/client.js DELETED
@@ -1,129 +0,0 @@
1
- /**
2
- * Lightweight JavaScript client for Zenith CMS, optimized for Edge environments.
3
- */
4
- export class ZenithClient {
5
- url;
6
- apiKey;
7
- siteId;
8
- constructor(options) {
9
- this.url = options.url.replace(/\/$/, '');
10
- this.apiKey = options.apiKey;
11
- this.siteId = options.siteId;
12
- }
13
- buildQueryString(options) {
14
- const params = new URLSearchParams();
15
- if (options.locale)
16
- params.append('locale', options.locale);
17
- if (options.depth !== undefined)
18
- params.append('depth', String(options.depth));
19
- if (options.drafts)
20
- params.append('drafts', 'true');
21
- if (options.sort)
22
- params.append('sort', options.sort);
23
- if (options.limit !== undefined)
24
- params.append('limit', String(options.limit));
25
- if (options.page !== undefined)
26
- params.append('page', String(options.page));
27
- if (options.where) {
28
- this.flattenWhereParams(options.where, 'where').forEach((value, key) => {
29
- params.append(key, value);
30
- });
31
- }
32
- const str = params.toString();
33
- return str ? `?${str}` : '';
34
- }
35
- flattenWhereParams(obj, prefix) {
36
- const map = new Map();
37
- for (const [key, value] of Object.entries(obj)) {
38
- if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
39
- const nested = this.flattenWhereParams(value, `${prefix}[${key}]`);
40
- nested.forEach((v, k) => map.set(k, v));
41
- }
42
- else {
43
- map.set(`${prefix}[${key}]`, String(value));
44
- }
45
- }
46
- return map;
47
- }
48
- async fetchAPI(path, options = {}) {
49
- const headers = new Headers(options.headers);
50
- headers.set('Content-Type', 'application/json');
51
- if (this.apiKey) {
52
- headers.set('Authorization', `Bearer ${this.apiKey}`);
53
- }
54
- if (this.siteId) {
55
- headers.set('X-Zenith-Site-Id', this.siteId);
56
- }
57
- const response = await fetch(`${this.url}${path}`, {
58
- ...options,
59
- headers,
60
- });
61
- const data = await response.json().catch(() => null);
62
- if (!response.ok) {
63
- throw new Error(data?.message || `Zenith API error: ${response.status} ${response.statusText}`);
64
- }
65
- return data;
66
- }
67
- /**
68
- * Find multiple documents in a collection.
69
- */
70
- async find(collection, options = {}) {
71
- const qs = this.buildQueryString(options);
72
- const data = await this.fetchAPI(`/api/v1/${collection}${qs}`, { method: 'GET', ...options });
73
- return data.data || data; // Handles different API response envelopes
74
- }
75
- /**
76
- * Find a single document by its ID.
77
- */
78
- async findById(collection, id, options = {}) {
79
- const qs = this.buildQueryString(options);
80
- const data = await this.fetchAPI(`/api/v1/${collection}/${id}${qs}`, { method: 'GET', ...options });
81
- return data.data?.document || data.data || data;
82
- }
83
- /**
84
- * Fetch a singleton configuration.
85
- */
86
- async findGlobal(slug, options = {}) {
87
- const qs = this.buildQueryString(options);
88
- const data = await this.fetchAPI(`/api/v1/globals/${slug}${qs}`, { method: 'GET', ...options });
89
- return data.data?.document || data.data || data;
90
- }
91
- /**
92
- * Create a new document in a collection.
93
- */
94
- async create(collection, payload, options = {}) {
95
- const qs = this.buildQueryString(options);
96
- const data = await this.fetchAPI(`/api/v1/${collection}${qs}`, {
97
- method: 'POST',
98
- body: JSON.stringify(payload),
99
- ...options,
100
- });
101
- return data.data || data;
102
- }
103
- /**
104
- * Update an existing document.
105
- */
106
- async update(collection, id, payload, options = {}) {
107
- const qs = this.buildQueryString(options);
108
- const data = await this.fetchAPI(`/api/v1/${collection}/${id}${qs}`, {
109
- method: 'PATCH',
110
- body: JSON.stringify(payload),
111
- ...options,
112
- });
113
- return data.data?.document || data.data || data;
114
- }
115
- /**
116
- * Delete a document.
117
- */
118
- async delete(collection, id, options = {}) {
119
- const qs = this.buildQueryString(options);
120
- const data = await this.fetchAPI(`/api/v1/${collection}/${id}${qs}`, {
121
- method: 'DELETE',
122
- ...options,
123
- });
124
- return data.data?.document || data.data || data;
125
- }
126
- }
127
- export function createClient(options) {
128
- return new ZenithClient(options);
129
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1,95 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach } from 'vitest';
2
- import { ZenithClient } from './index';
3
- // Minimal fetch mock — intercepts calls and returns JSON
4
- function makeClient() {
5
- return new ZenithClient({ url: 'http://localhost:3000' });
6
- }
7
- beforeEach(() => {
8
- global.fetch = vi.fn();
9
- });
10
- describe('ZenithClient — SWR cache', () => {
11
- it('serves stale data immediately and revalidates in background', async () => {
12
- const client = makeClient();
13
- const mockData = { data: { docs: [{ _id: '1', title: 'Cached Post' }] } };
14
- fetch.mockResolvedValueOnce({
15
- ok: true,
16
- json: () => Promise.resolve(mockData),
17
- });
18
- const result1 = await client.find('posts', { limit: 5, cacheTtl: 30_000 });
19
- expect(result1.docs[0].title).toBe('Cached Post');
20
- // Second request should return cached data immediately without waiting
21
- const start = Date.now();
22
- const result2 = await client.find('posts', { limit: 5 });
23
- const elapsed = Date.now() - start;
24
- // With cache hit and SWR revalidation, this should be near-instant
25
- expect(elapsed).toBeLessThan(50);
26
- expect(result2.docs[0].title).toBe('Cached Post');
27
- });
28
- it('bypasses cache when cacheTtl is 0', async () => {
29
- const client = makeClient();
30
- fetch.mockResolvedValue({
31
- ok: true,
32
- json: () => Promise.resolve({ data: { docs: [] } }),
33
- });
34
- // Should call fetch both times
35
- await client.find('posts', { cacheTtl: 0 });
36
- await client.find('posts', { cacheTtl: 0 });
37
- expect(fetch.mock.calls.length).toBeGreaterThanOrEqual(2);
38
- });
39
- });
40
- describe('ZenithClient — batch', () => {
41
- it('executes multiple requests in parallel', async () => {
42
- const client = makeClient();
43
- fetch.mockResolvedValue({
44
- ok: true,
45
- json: () => Promise.resolve({ data: { posts: [] } }),
46
- });
47
- await client.batch([
48
- { method: 'GET', path: '/api/beta/posts' },
49
- { method: 'GET', path: '/api/beta/authors' },
50
- ]);
51
- expect(fetch.mock.calls.length).toBe(2);
52
- });
53
- });
54
- describe('ZenithClient — upload', () => {
55
- it('sends FormData for file uploads and omits Content-Type header', async () => {
56
- const client = makeClient();
57
- fetch.mockResolvedValue({
58
- ok: true,
59
- json: () => Promise.resolve({ data: { _id: '123', url: 'http://cdn/img.jpg' } }),
60
- });
61
- const file = new File(['hello'], 'test.jpg', { type: 'image/jpeg' });
62
- await client.upload(file, { alt: 'Test alt', focalPoint: { x: 50, y: 50 } });
63
- const [url, options] = fetch.mock.calls[0];
64
- expect(url).toContain('/api/beta/upload');
65
- expect(options.headers.get('Content-Type')).toBeNull(); // fetch auto-sets multipart
66
- expect(options.method).toBe('POST');
67
- });
68
- });
69
- describe('ZenithClient — site switching', () => {
70
- it('updates siteId and flushes cache when setSiteId is called', async () => {
71
- const client = makeClient();
72
- const mockData1 = { data: { docs: [{ _id: '1', title: 'Post Site A' }] } };
73
- const mockData2 = { data: { docs: [{ _id: '2', title: 'Post Site B' }] } };
74
- fetch
75
- .mockResolvedValueOnce({
76
- ok: true,
77
- json: () => Promise.resolve(mockData1),
78
- })
79
- .mockResolvedValueOnce({
80
- ok: true,
81
- json: () => Promise.resolve(mockData2),
82
- });
83
- // Fetch on default site ID (empty)
84
- const res1 = await client.find('posts', { limit: 5 });
85
- expect(res1.docs[0].title).toBe('Post Site A');
86
- // Change site ID using setSiteId
87
- client.setSiteId('site-b');
88
- // Fetch again — cache should be flushed, performing a new fetch with headers
89
- const res2 = await client.find('posts', { limit: 5 });
90
- expect(res2.docs[0].title).toBe('Post Site B');
91
- // Verify correct header was sent in the second request
92
- const lastCall = fetch.mock.calls[1];
93
- expect(lastCall[1].headers.get('X-Zenith-Site-Id')).toBe('site-b');
94
- });
95
- });
@@ -1,43 +0,0 @@
1
- interface ZenithConfig {
2
- baseURL: string;
3
- token?: string;
4
- cacheTTL?: number;
5
- }
6
- /**
7
- * Zenith SDK — Universal Client
8
- * ─────────────────────────────
9
- * A high-performance, type-safe client for consuming Zenith CMS content.
10
- * Features built-in caching and automated error handling.
11
- */
12
- export declare class ZenithClient {
13
- private api;
14
- private cache;
15
- private ttl;
16
- constructor(config: ZenithConfig);
17
- /**
18
- * Fetch a list of entries from a collection
19
- */
20
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
21
- find<T = any>(slug: string, params?: Record<string, any>): Promise<{
22
  // eslint-disable-line @typescript-eslint/no-explicit-any
23
- data: T[];
24
- meta: any;
25
1
  // eslint-disable-line @typescript-eslint/no-explicit-any
26
- }>;
27
- /**
28
- * Fetch a single entry by ID
29
- */
30
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
31
- findOne<T = any>(slug: string, id: string): Promise<{
32
2
  // eslint-disable-line @typescript-eslint/no-explicit-any
33
- data: T;
34
- }>;
35
- /**
36
- * Fetch a singleton collection
37
- */
38
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
39
- getSingleton<T = any>(slug: string): Promise<{
40
3
  // eslint-disable-line @typescript-eslint/no-explicit-any
41
- data: T;
42
- }>;
43
- private getCache;
44
- private setCache;
45
- private handleError;
46
- }
47
- export {};
package/dist/src/index.js DELETED
@@ -1,86 +0,0 @@
1
- import axios from 'axios';
2
- /**
3
- * Zenith SDK — Universal Client
4
- * ─────────────────────────────
5
- * A high-performance, type-safe client for consuming Zenith CMS content.
6
- * Features built-in caching and automated error handling.
7
- */
8
- export class ZenithClient {
9
- api;
10
- cache = new Map();
11
- ttl;
12
- constructor(config) {
13
- this.ttl = config.cacheTTL || 60000; // Default 1 min
14
- this.api = axios.create({
15
- baseURL: config.baseURL.endsWith('/api/beta') ? config.baseURL : `${config.baseURL}/api/beta`,
16
- headers: config.token ? { Authorization: `Bearer ${config.token}` } : {},
17
- });
18
- }
19
- /**
20
- * Fetch a list of entries from a collection
21
- */
22
- async find(slug, params = {}) {
23
- const cacheKey = `${slug}:list:${JSON.stringify(params)}`;
24
- const cached = this.getCache(cacheKey);
25
- if (cached)
26
- return cached;
27
- try {
28
- const res = await this.api.get(`/${slug}`, { params });
29
- this.setCache(cacheKey, res.data);
30
- return res.data;
31
- }
32
- catch (err) {
33
- this.handleError(err);
34
- }
35
- }
36
- /**
37
- * Fetch a single entry by ID
38
- */
39
- async findOne(slug, id) {
40
- const cacheKey = `${slug}:${id}`;
41
- const cached = this.getCache(cacheKey);
42
- if (cached)
43
- return cached;
44
- try {
45
- const res = await this.api.get(`/${slug}/${id}`);
46
- this.setCache(cacheKey, res.data);
47
- return res.data;
48
- }
49
- catch (err) {
50
- this.handleError(err);
51
- }
52
- }
53
- /**
54
- * Fetch a singleton collection
55
- */
56
- async getSingleton(slug) {
57
- const cacheKey = `singleton:${slug}`;
58
- const cached = this.getCache(cacheKey);
59
- if (cached)
60
- return cached;
61
- try {
62
- const res = await this.api.get(`/${slug}`);
63
- this.setCache(cacheKey, res.data);
64
- return res.data;
65
- }
66
- catch (err) {
67
- this.handleError(err);
68
- }
69
- }
70
- // --- Cache Helpers ---
71
- getCache(key) {
72
- const cached = this.cache.get(key);
73
- if (cached && cached.expiry > Date.now())
74
- return cached.data;
75
- if (cached)
76
- this.cache.delete(key);
77
- return null;
78
- }
79
- setCache(key, data) {
80
- this.cache.set(key, { data, expiry: Date.now() + this.ttl });
81
- }
82
- handleError(err) {
83
- const message = err.response?.data?.error?.message || err.message || 'Unknown Zenith SDK Error';
84
- throw new Error(`[Zenith SDK] ${message}`);
85
- }
86
- }
package/dist/types.d.ts DELETED
@@ -1,54 +0,0 @@
1
- import type { paths } from './schema';
2
- /**
3
- * Resolve a collection name string to its document type via OpenAPI schema paths.
4
- * Falls back to unknown for unknown collections.
5
- */
6
- type GetPath<C extends string> = `/${C}`;
7
- type ExtractArrayItem<T> = T extends (infer U)[] ? U : unknown;
8
- type OpenAPIResponse<P extends string> = P extends keyof paths ? 'get' extends keyof paths[P] ? 'responses' extends keyof paths[P]['get'] ? 200 extends keyof paths[P]['get']['responses'] ? 'content' extends keyof paths[P]['get']['responses'][200] ? 'application/json' extends keyof paths[P]['get']['responses'][200]['content'] ? 'schema' extends keyof paths[P]['get']['responses'][200]['content']['application/json'] ? paths[P]['get']['responses'][200]['content']['application/json']['schema'] : unknown : unknown : unknown : unknown : unknown : unknown : unknown;
9
- export type DocType<C extends string> = OpenAPIResponse<GetPath<C>> extends never ? unknown : ExtractArrayItem<OpenAPIResponse<GetPath<C>>>;
10
- export interface ZenithClientOptions {
11
- url: string;
12
- apiKey?: string;
13
- siteId?: string;
14
- /** SWR cache TTL in ms. default 30_000 (30s). set 0 to disable. */
15
- cacheTtl?: number;
16
- }
17
- export interface FetchOptions extends RequestInit {
18
- locale?: string;
19
- depth?: number;
20
- drafts?: boolean;
21
- populate?: string[] | string;
22
- select?: string[] | string;
23
- /** Override the global cache TTL for this request. 0 = bypass cache. */
24
- cacheTtl?: number;
25
- /** Tag-based cache invalidation key for this request */
26
- cacheTag?: string;
27
- }
28
- export interface FindOptions extends FetchOptions {
29
- where?: Record<string, unknown>;
30
- sort?: string;
31
- limit?: number;
32
- page?: number;
33
- }
34
- export interface BatchRequest {
35
- method?: 'GET' | 'POST' | 'PATCH' | 'DELETE';
36
- path: string;
37
- body?: unknown;
38
- headers?: Record<string, string>;
39
- }
40
- /** Structured error thrown by all ZenithClient methods. */
41
- export declare class ZenithAPIError extends Error {
42
- readonly status: number;
43
- readonly code?: string;
44
- readonly isNetworkError: boolean;
45
- readonly isParseError: boolean;
46
- constructor(opts: {
47
- message: string;
48
- status: number;
49
- code?: string;
50
- isNetworkError?: boolean;
51
- isParseError?: boolean;
52
- });
53
- }
54
- export {};
package/dist/types.js DELETED
@@ -1,16 +0,0 @@
1
- // ── Error Handling ────────────────────────────────────────────────────────────
2
- /** Structured error thrown by all ZenithClient methods. */
3
- export class ZenithAPIError extends Error {
4
- status;
5
- code;
6
- isNetworkError;
7
- isParseError;
8
- constructor(opts) {
9
- super(opts.message);
10
- this.name = 'ZenithAPIError';
11
- this.status = opts.status;
12
- this.code = opts.code;
13
- this.isNetworkError = opts.isNetworkError ?? false;
14
- this.isParseError = opts.isParseError ?? false;
15
- }
16
- }