@sprintup-cms/sdk 1.0.0 → 1.2.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/CHANGELOG.md +33 -0
- package/README.md +362 -72
- package/dist/client.cjs +30 -15
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.cts +36 -17
- package/dist/client.d.ts +36 -17
- package/dist/client.js +30 -15
- package/dist/client.js.map +1 -1
- package/dist/index.cjs +179 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +176 -0
- package/dist/index.js.map +1 -0
- package/dist/next/index.cjs +46 -23
- package/dist/next/index.cjs.map +1 -1
- package/dist/next/index.d.cts +8 -2
- package/dist/next/index.d.ts +8 -2
- package/dist/next/index.js +47 -24
- package/dist/next/index.js.map +1 -1
- package/package.json +9 -4
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/client.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * @sprintup-cms/sdk — Core Client\n *\n * Zero-dependency, framework-agnostic typed API client for SprintUp Forge CMS.\n *\n * @example\n * import { cmsClient } from '@sprintup-cms/sdk'\n * const page = await cmsClient.getPage('about')\n *\n * @example Custom instance\n * import { createCMSClient } from '@sprintup-cms/sdk'\n * const cms = createCMSClient({ baseUrl: '...', apiKey: '...', appId: '...' })\n */\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\nexport interface CMSBlock {\n id: string\n type: string\n label?: string\n locked?: boolean\n data?: Record<string, any>\n /** Legacy field — blocks created before v1.1 used `content` instead of `data` */\n content?: Record<string, any>\n order?: number\n}\n\nexport interface CMSPage {\n _id?: string\n slug: string\n title: string\n description?: string\n pageType?: string\n pageTypeId?: string\n variant?: string\n status: 'draft' | 'published' | 'archived'\n visibility?: 'public' | 'private' | 'password'\n blocks: CMSBlock[]\n publishedAt?: string\n updatedAt?: string\n seo?: {\n title?: string\n description?: string\n keywords?: string[]\n ogImage?: string\n noIndex?: boolean\n }\n}\n\nexport interface CMSPageTypeField {\n id: string\n name: string\n label: string\n fieldType:\n | 'text' | 'textarea' | 'richtext' | 'image' | 'url'\n | 'number' | 'boolean' | 'date' | 'select' | 'relation'\n | 'email' | 'phone' | 'slug' | 'color' | 'embed'\n | 'multi-select' | 'repeater' | 'file' | 'code'\n required?: boolean\n options?: string[]\n description?: string\n maxLength?: number\n multiple?: boolean\n targetPageTypeKey?: string\n}\n\nexport interface CMSPageTypeSection {\n id: string\n name: string\n label: string\n order: number\n locked?: boolean\n fields: CMSPageTypeField[]\n}\n\nexport interface CMSPageTypeVariant {\n key: string\n label: string\n description?: string\n visibleSections?: string[]\n}\n\nexport interface CMSPageType {\n _id: string\n name: string\n key: string\n description?: string\n icon?: string\n /** Used by page-type-renderers to pick a specialised React component */\n rendererKey?: string\n /** Active variant key (set per-page) */\n variant?: string\n category: 'singleton' | 'collection' | 'global'\n contentCategory: 'singleton' | 'collection' | 'global'\n allowedRoles?: string[]\n variants: CMSPageTypeVariant[]\n sections: CMSPageTypeSection[]\n}\n\n// ── Sitemap ────────────────────────────────────────────────────────────────────\n\nexport interface CMSSitemapUrl {\n loc: string\n lastmod?: string\n changefreq?: string\n priority?: number\n title?: string\n}\n\nexport interface CMSSitemapResponse {\n enabled: boolean\n urls: CMSSitemapUrl[]\n config?: {\n defaultChangeFreq?: string\n defaultPriority?: number\n }\n}\n\n// ── Status ─────────────────────────────────────────────────────────────────────\n\nexport interface CMSStatusResponse {\n appId: string\n totalPages: number\n publishedPages: number\n connected: boolean\n pages: Array<{\n slug: string\n title: string\n pageType?: string\n group?: string\n }>\n}\n\n// ── Site Structure ─────────────────────────────────────────────────────────────\n\nexport type CMSMenuItemType = 'page' | 'url' | 'dynamic'\n\nexport interface CMSMenuItem {\n id: string\n type: CMSMenuItemType\n label: string\n contentId?: string\n url?: string\n /** Resolved href — ready to pass to <a href> or <Link href>. Never null, falls back to \"#\". */\n href: string\n locked: boolean\n openInNewTab: boolean\n children: CMSMenuItem[]\n page?: {\n title?: string\n slug?: string\n seoTitle?: string\n seoDescription?: string\n }\n}\n\nexport interface CMSPageTreeNode {\n id: string\n contentId: string\n parentId: string | null\n order: number\n locked: boolean\n visible?: boolean\n page?: {\n title?: string\n slug?: string\n seoTitle?: string\n seoDescription?: string\n }\n}\n\nexport interface CMSFooterGroup {\n id: string\n heading: string\n headingUrl?: string\n locked: boolean\n links: CMSMenuItem[]\n}\n\nexport interface CMSSiteStructure {\n appId: string\n pageTree: CMSPageTreeNode[]\n menus: {\n header: CMSMenuItem[]\n footer: CMSFooterGroup[]\n footerBottom: CMSMenuItem[]\n sidebar: CMSMenuItem[]\n [slot: string]: CMSMenuItem[] | CMSFooterGroup[]\n }\n updatedAt?: string\n}\n\n// ── Pagination & Responses ─────────────────────────────────────────────────────\n\n// v1 route returns { data, count } — flat, no nested meta object\ninterface CMSListResponse {\n data: CMSPage[]\n count: number\n appId?: string\n}\n\ninterface CMSSingleResponse {\n data: CMSPage\n}\n\n// ── Client Options ─────────────────────────────────────────────────────────────\n\nexport interface CMSClientOptions {\n /** Base URL of your Forge CMS instance, e.g. https://cms.yourschool.io */\n baseUrl?: string\n /** API key generated in CMS Admin → API Keys. Server-side only. */\n apiKey?: string\n /** App ID from CMS Admin → Apps, e.g. \"school-website\" */\n appId?: string\n}\n\nexport interface CMSGetPagesOptions {\n type?: string\n group?: string\n page?: number\n perPage?: number\n status?: 'published' | 'draft' | 'archived'\n}\n\n// ── Factory ───────────────────────────────────────────────────────────────────\n\nexport function createCMSClient(options?: CMSClientOptions) {\n /**\n * Resolve config lazily at request time — NOT at module/build time.\n * This prevents static prerendering crashes when env vars are absent during build.\n */\n function cfg() {\n return {\n baseUrl: (options?.baseUrl ?? process.env.NEXT_PUBLIC_CMS_URL ?? process.env.CMS_BASE_URL ?? '').replace(/\\/$/, ''),\n apiKey: options?.apiKey ?? process.env.CMS_API_KEY ?? '',\n appId: options?.appId ?? process.env.CMS_APP_ID ?? '',\n }\n }\n\n function headers() {\n return { 'X-CMS-API-Key': cfg().apiKey, 'Content-Type': 'application/json' }\n }\n\n // ── Pages ──────────────────────────────────────────────────────────────────\n\n async function getPages(params?: CMSGetPagesOptions): Promise<CMSPage[]> {\n const { baseUrl, apiKey, appId } = cfg()\n if (!baseUrl || !apiKey || !appId) {\n console.warn('[sprintup-cms] Missing CMS_BASE_URL / CMS_API_KEY / CMS_APP_ID — returning []')\n return []\n }\n try {\n const qs = new URLSearchParams()\n if (params?.type) qs.set('type', params.type)\n if (params?.group) qs.set('group', params.group)\n if (params?.page) qs.set('page', String(params.page))\n if (params?.perPage) qs.set('perPage', String(params.perPage))\n const url = `${baseUrl}/api/v1/${appId}/pages${qs.size ? `?${qs}` : ''}`\n const res = await fetch(url, {\n headers: headers(),\n next: { revalidate: 60, tags: [`cms-pages-${appId}`] },\n } as RequestInit)\n if (!res.ok) { console.error(`[sprintup-cms] getPages (${res.status})`); return [] }\n const json: CMSListResponse = await res.json()\n return json.data ?? []\n } catch (err) {\n console.error('[sprintup-cms] getPages error:', err)\n return []\n }\n }\n\n async function getPage(slug: string): Promise<CMSPage | null> {\n const { baseUrl, apiKey, appId } = cfg()\n if (!baseUrl || !apiKey || !appId) {\n console.warn('[sprintup-cms] Missing config — returning null')\n return null\n }\n try {\n const res = await fetch(`${baseUrl}/api/v1/${appId}/pages/${slug}`, {\n headers: headers(),\n next: { revalidate: 60, tags: [`cms-page-${slug}`, `cms-pages-${appId}`] },\n } as RequestInit)\n if (res.status === 404) return null\n if (!res.ok) { console.error(`[sprintup-cms] getPage \"${slug}\" (${res.status})`); return null }\n const json: CMSSingleResponse = await res.json()\n return json.data ?? null\n } catch (err) {\n console.error(`[sprintup-cms] getPage \"${slug}\" error:`, err)\n return null\n }\n }\n\n async function getBlogPosts(): Promise<CMSPage[]> {\n return getPages({ type: 'blog-post' })\n }\n\n async function getEvents(): Promise<CMSPage[]> {\n return getPages({ type: 'event-page' })\n }\n\n async function getAnnouncements(): Promise<CMSPage[]> {\n return getPages({ type: 'announcement-page' })\n }\n\n // ── Preview ────────────────────────────────────────────────────────────────\n\n async function getPreviewPage(token: string): Promise<CMSPage | null> {\n const { baseUrl, appId } = cfg()\n if (!baseUrl || !appId) return null\n try {\n const res = await fetch(`${baseUrl}/api/v1/${appId}/preview?token=${encodeURIComponent(token)}`, {\n cache: 'no-store',\n })\n if (!res.ok) return null\n const json = await res.json()\n return json.data ?? null\n } catch {\n return null\n }\n }\n\n async function getPageWithPreview(slug: string, previewToken?: string | null): Promise<CMSPage | null> {\n if (previewToken) {\n const preview = await getPreviewPage(previewToken)\n if (preview?.slug === slug) return preview\n }\n return getPage(slug)\n }\n\n // ── Page Types ─────────────────────────────────────────────────────────────\n\n async function getPageType(pageTypeId: string): Promise<CMSPageType | null> {\n const { baseUrl, apiKey, appId } = cfg()\n if (!baseUrl || !apiKey || !appId || !pageTypeId) return null\n try {\n const res = await fetch(`${baseUrl}/api/v1/${appId}/page-types/${pageTypeId}`, {\n headers: headers(),\n next: { revalidate: 3600, tags: [`cms-page-type-${pageTypeId}`] },\n } as RequestInit)\n if (!res.ok) return null\n const json = await res.json()\n return json.data ?? null\n } catch {\n return null\n }\n }\n\n // ── Site Structure ─────────────────────────────────────────────────────────\n\n async function getSiteStructure(): Promise<CMSSiteStructure | null> {\n const { baseUrl, apiKey, appId } = cfg()\n if (!baseUrl || !apiKey || !appId) return null\n try {\n const res = await fetch(`${baseUrl}/api/v1/${appId}/site-structure`, {\n headers: headers(),\n next: {\n revalidate: 300,\n tags: [`site-structure-${appId}`],\n },\n } as RequestInit)\n if (!res.ok) return null\n const json = await res.json()\n return json.data ?? null\n } catch {\n return null\n }\n }\n\n /**\n * GET /api/v1/{appId}/sitemap\n * Fetch sitemap data for all published pages (priority, changefreq, lastmod).\n *\n * @example app/sitemap.ts\n * import type { MetadataRoute } from 'next'\n * import { cmsClient } from '@sprintup-cms/sdk'\n *\n * export default async function sitemap(): Promise<MetadataRoute.Sitemap> {\n * const data = await cmsClient.getSitemap()\n * if (!data?.enabled) return []\n * return data.urls.map(url => ({\n * url: `${process.env.NEXT_PUBLIC_SITE_URL}${url.loc}`,\n * lastModified: url.lastmod,\n * changeFrequency: url.changefreq as MetadataRoute.Sitemap[0]['changeFrequency'],\n * priority: url.priority,\n * }))\n * }\n */\n async function getSitemap(): Promise<CMSSitemapResponse | null> {\n const { baseUrl, apiKey, appId } = cfg()\n if (!baseUrl || !apiKey || !appId) return null\n try {\n const res = await fetch(`${baseUrl}/api/v1/${appId}/sitemap`, {\n headers: headers(),\n next: { revalidate: 3600 },\n } as RequestInit)\n if (!res.ok) return null\n const json = await res.json()\n return json.data ?? null\n } catch { return null }\n }\n\n /**\n * GET /api/v1/{appId}/status\n * Verify connectivity and retrieve page counts. Never cached — always fresh.\n * Useful for debugging API key setup.\n */\n async function getStatus(): Promise<CMSStatusResponse | null> {\n const { baseUrl, apiKey, appId } = cfg()\n if (!baseUrl || !apiKey || !appId) return null\n try {\n const res = await fetch(`${baseUrl}/api/v1/${appId}/status`, {\n headers: headers(),\n cache: 'no-store',\n } as RequestInit)\n if (!res.ok) return null\n return await res.json()\n } catch { return null }\n }\n\n return {\n getPages,\n getPage,\n getBlogPosts,\n getEvents,\n getAnnouncements,\n getPreviewPage,\n getPageWithPreview,\n getPageType,\n getSiteStructure,\n getSitemap,\n getStatus,\n }\n}\n\n// ── Default singleton ─────────────────────────────────────────────────────────\n\n/** Pre-configured singleton. Reads env vars lazily at request time. */\nexport const cmsClient = createCMSClient()\n"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { CMSBlock, CMSClientOptions, CMSFooterGroup, CMSGetPagesOptions, CMSMenuItem, CMSMenuItemType, CMSPage, CMSPageTreeNode, CMSPageType, CMSPageTypeField, CMSPageTypeSection, CMSPageTypeVariant, CMSSiteStructure, CMSSitemapResponse, CMSSitemapUrl, CMSStatusResponse, cmsClient, createCMSClient } from './client.cjs';
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { CMSBlock, CMSClientOptions, CMSFooterGroup, CMSGetPagesOptions, CMSMenuItem, CMSMenuItemType, CMSPage, CMSPageTreeNode, CMSPageType, CMSPageTypeField, CMSPageTypeSection, CMSPageTypeVariant, CMSSiteStructure, CMSSitemapResponse, CMSSitemapUrl, CMSStatusResponse, cmsClient, createCMSClient } from './client.js';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/* @sprintup-cms/sdk — https://forgecms.io */
|
|
2
|
+
|
|
3
|
+
// src/client.ts
|
|
4
|
+
function createCMSClient(options) {
|
|
5
|
+
function cfg() {
|
|
6
|
+
return {
|
|
7
|
+
baseUrl: (options?.baseUrl ?? process.env.NEXT_PUBLIC_CMS_URL ?? process.env.CMS_BASE_URL ?? "").replace(/\/$/, ""),
|
|
8
|
+
apiKey: options?.apiKey ?? process.env.CMS_API_KEY ?? "",
|
|
9
|
+
appId: options?.appId ?? process.env.CMS_APP_ID ?? ""
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
function headers() {
|
|
13
|
+
return { "X-CMS-API-Key": cfg().apiKey, "Content-Type": "application/json" };
|
|
14
|
+
}
|
|
15
|
+
async function getPages(params) {
|
|
16
|
+
const { baseUrl, apiKey, appId } = cfg();
|
|
17
|
+
if (!baseUrl || !apiKey || !appId) {
|
|
18
|
+
console.warn("[sprintup-cms] Missing CMS_BASE_URL / CMS_API_KEY / CMS_APP_ID \u2014 returning []");
|
|
19
|
+
return [];
|
|
20
|
+
}
|
|
21
|
+
try {
|
|
22
|
+
const qs = new URLSearchParams();
|
|
23
|
+
if (params?.type) qs.set("type", params.type);
|
|
24
|
+
if (params?.group) qs.set("group", params.group);
|
|
25
|
+
if (params?.page) qs.set("page", String(params.page));
|
|
26
|
+
if (params?.perPage) qs.set("perPage", String(params.perPage));
|
|
27
|
+
const url = `${baseUrl}/api/v1/${appId}/pages${qs.size ? `?${qs}` : ""}`;
|
|
28
|
+
const res = await fetch(url, {
|
|
29
|
+
headers: headers(),
|
|
30
|
+
next: { revalidate: 60, tags: [`cms-pages-${appId}`] }
|
|
31
|
+
});
|
|
32
|
+
if (!res.ok) {
|
|
33
|
+
console.error(`[sprintup-cms] getPages (${res.status})`);
|
|
34
|
+
return [];
|
|
35
|
+
}
|
|
36
|
+
const json = await res.json();
|
|
37
|
+
return json.data ?? [];
|
|
38
|
+
} catch (err) {
|
|
39
|
+
console.error("[sprintup-cms] getPages error:", err);
|
|
40
|
+
return [];
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
async function getPage(slug) {
|
|
44
|
+
const { baseUrl, apiKey, appId } = cfg();
|
|
45
|
+
if (!baseUrl || !apiKey || !appId) {
|
|
46
|
+
console.warn("[sprintup-cms] Missing config \u2014 returning null");
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
try {
|
|
50
|
+
const res = await fetch(`${baseUrl}/api/v1/${appId}/pages/${slug}`, {
|
|
51
|
+
headers: headers(),
|
|
52
|
+
next: { revalidate: 60, tags: [`cms-page-${slug}`, `cms-pages-${appId}`] }
|
|
53
|
+
});
|
|
54
|
+
if (res.status === 404) return null;
|
|
55
|
+
if (!res.ok) {
|
|
56
|
+
console.error(`[sprintup-cms] getPage "${slug}" (${res.status})`);
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
const json = await res.json();
|
|
60
|
+
return json.data ?? null;
|
|
61
|
+
} catch (err) {
|
|
62
|
+
console.error(`[sprintup-cms] getPage "${slug}" error:`, err);
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
async function getBlogPosts() {
|
|
67
|
+
return getPages({ type: "blog-post" });
|
|
68
|
+
}
|
|
69
|
+
async function getEvents() {
|
|
70
|
+
return getPages({ type: "event-page" });
|
|
71
|
+
}
|
|
72
|
+
async function getAnnouncements() {
|
|
73
|
+
return getPages({ type: "announcement-page" });
|
|
74
|
+
}
|
|
75
|
+
async function getPreviewPage(token) {
|
|
76
|
+
const { baseUrl, appId } = cfg();
|
|
77
|
+
if (!baseUrl || !appId) return null;
|
|
78
|
+
try {
|
|
79
|
+
const res = await fetch(`${baseUrl}/api/v1/${appId}/preview?token=${encodeURIComponent(token)}`, {
|
|
80
|
+
cache: "no-store"
|
|
81
|
+
});
|
|
82
|
+
if (!res.ok) return null;
|
|
83
|
+
const json = await res.json();
|
|
84
|
+
return json.data ?? null;
|
|
85
|
+
} catch {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
async function getPageWithPreview(slug, previewToken) {
|
|
90
|
+
if (previewToken) {
|
|
91
|
+
const preview = await getPreviewPage(previewToken);
|
|
92
|
+
if (preview?.slug === slug) return preview;
|
|
93
|
+
}
|
|
94
|
+
return getPage(slug);
|
|
95
|
+
}
|
|
96
|
+
async function getPageType(pageTypeId) {
|
|
97
|
+
const { baseUrl, apiKey, appId } = cfg();
|
|
98
|
+
if (!baseUrl || !apiKey || !appId || !pageTypeId) return null;
|
|
99
|
+
try {
|
|
100
|
+
const res = await fetch(`${baseUrl}/api/v1/${appId}/page-types/${pageTypeId}`, {
|
|
101
|
+
headers: headers(),
|
|
102
|
+
next: { revalidate: 3600, tags: [`cms-page-type-${pageTypeId}`] }
|
|
103
|
+
});
|
|
104
|
+
if (!res.ok) return null;
|
|
105
|
+
const json = await res.json();
|
|
106
|
+
return json.data ?? null;
|
|
107
|
+
} catch {
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
async function getSiteStructure() {
|
|
112
|
+
const { baseUrl, apiKey, appId } = cfg();
|
|
113
|
+
if (!baseUrl || !apiKey || !appId) return null;
|
|
114
|
+
try {
|
|
115
|
+
const res = await fetch(`${baseUrl}/api/v1/${appId}/site-structure`, {
|
|
116
|
+
headers: headers(),
|
|
117
|
+
next: {
|
|
118
|
+
revalidate: 300,
|
|
119
|
+
tags: [`site-structure-${appId}`]
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
if (!res.ok) return null;
|
|
123
|
+
const json = await res.json();
|
|
124
|
+
return json.data ?? null;
|
|
125
|
+
} catch {
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
async function getSitemap() {
|
|
130
|
+
const { baseUrl, apiKey, appId } = cfg();
|
|
131
|
+
if (!baseUrl || !apiKey || !appId) return null;
|
|
132
|
+
try {
|
|
133
|
+
const res = await fetch(`${baseUrl}/api/v1/${appId}/sitemap`, {
|
|
134
|
+
headers: headers(),
|
|
135
|
+
next: { revalidate: 3600 }
|
|
136
|
+
});
|
|
137
|
+
if (!res.ok) return null;
|
|
138
|
+
const json = await res.json();
|
|
139
|
+
return json.data ?? null;
|
|
140
|
+
} catch {
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
async function getStatus() {
|
|
145
|
+
const { baseUrl, apiKey, appId } = cfg();
|
|
146
|
+
if (!baseUrl || !apiKey || !appId) return null;
|
|
147
|
+
try {
|
|
148
|
+
const res = await fetch(`${baseUrl}/api/v1/${appId}/status`, {
|
|
149
|
+
headers: headers(),
|
|
150
|
+
cache: "no-store"
|
|
151
|
+
});
|
|
152
|
+
if (!res.ok) return null;
|
|
153
|
+
return await res.json();
|
|
154
|
+
} catch {
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return {
|
|
159
|
+
getPages,
|
|
160
|
+
getPage,
|
|
161
|
+
getBlogPosts,
|
|
162
|
+
getEvents,
|
|
163
|
+
getAnnouncements,
|
|
164
|
+
getPreviewPage,
|
|
165
|
+
getPageWithPreview,
|
|
166
|
+
getPageType,
|
|
167
|
+
getSiteStructure,
|
|
168
|
+
getSitemap,
|
|
169
|
+
getStatus
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
var cmsClient = createCMSClient();
|
|
173
|
+
|
|
174
|
+
export { cmsClient, createCMSClient };
|
|
175
|
+
//# sourceMappingURL=index.js.map
|
|
176
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/client.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @sprintup-cms/sdk — Core Client\n *\n * Zero-dependency, framework-agnostic typed API client for SprintUp Forge CMS.\n *\n * @example\n * import { cmsClient } from '@sprintup-cms/sdk'\n * const page = await cmsClient.getPage('about')\n *\n * @example Custom instance\n * import { createCMSClient } from '@sprintup-cms/sdk'\n * const cms = createCMSClient({ baseUrl: '...', apiKey: '...', appId: '...' })\n */\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\nexport interface CMSBlock {\n id: string\n type: string\n label?: string\n locked?: boolean\n data?: Record<string, any>\n /** Legacy field — blocks created before v1.1 used `content` instead of `data` */\n content?: Record<string, any>\n order?: number\n}\n\nexport interface CMSPage {\n _id?: string\n slug: string\n title: string\n description?: string\n pageType?: string\n pageTypeId?: string\n variant?: string\n status: 'draft' | 'published' | 'archived'\n visibility?: 'public' | 'private' | 'password'\n blocks: CMSBlock[]\n publishedAt?: string\n updatedAt?: string\n seo?: {\n title?: string\n description?: string\n keywords?: string[]\n ogImage?: string\n noIndex?: boolean\n }\n}\n\nexport interface CMSPageTypeField {\n id: string\n name: string\n label: string\n fieldType:\n | 'text' | 'textarea' | 'richtext' | 'image' | 'url'\n | 'number' | 'boolean' | 'date' | 'select' | 'relation'\n | 'email' | 'phone' | 'slug' | 'color' | 'embed'\n | 'multi-select' | 'repeater' | 'file' | 'code'\n required?: boolean\n options?: string[]\n description?: string\n maxLength?: number\n multiple?: boolean\n targetPageTypeKey?: string\n}\n\nexport interface CMSPageTypeSection {\n id: string\n name: string\n label: string\n order: number\n locked?: boolean\n fields: CMSPageTypeField[]\n}\n\nexport interface CMSPageTypeVariant {\n key: string\n label: string\n description?: string\n visibleSections?: string[]\n}\n\nexport interface CMSPageType {\n _id: string\n name: string\n key: string\n description?: string\n icon?: string\n /** Used by page-type-renderers to pick a specialised React component */\n rendererKey?: string\n /** Active variant key (set per-page) */\n variant?: string\n category: 'singleton' | 'collection' | 'global'\n contentCategory: 'singleton' | 'collection' | 'global'\n allowedRoles?: string[]\n variants: CMSPageTypeVariant[]\n sections: CMSPageTypeSection[]\n}\n\n// ── Sitemap ────────────────────────────────────────────────────────────────────\n\nexport interface CMSSitemapUrl {\n loc: string\n lastmod?: string\n changefreq?: string\n priority?: number\n title?: string\n}\n\nexport interface CMSSitemapResponse {\n enabled: boolean\n urls: CMSSitemapUrl[]\n config?: {\n defaultChangeFreq?: string\n defaultPriority?: number\n }\n}\n\n// ── Status ─────────────────────────────────────────────────────────────────────\n\nexport interface CMSStatusResponse {\n appId: string\n totalPages: number\n publishedPages: number\n connected: boolean\n pages: Array<{\n slug: string\n title: string\n pageType?: string\n group?: string\n }>\n}\n\n// ── Site Structure ─────────────────────────────────────────────────────────────\n\nexport type CMSMenuItemType = 'page' | 'url' | 'dynamic'\n\nexport interface CMSMenuItem {\n id: string\n type: CMSMenuItemType\n label: string\n contentId?: string\n url?: string\n /** Resolved href — ready to pass to <a href> or <Link href>. Never null, falls back to \"#\". */\n href: string\n locked: boolean\n openInNewTab: boolean\n children: CMSMenuItem[]\n page?: {\n title?: string\n slug?: string\n seoTitle?: string\n seoDescription?: string\n }\n}\n\nexport interface CMSPageTreeNode {\n id: string\n contentId: string\n parentId: string | null\n order: number\n locked: boolean\n visible?: boolean\n page?: {\n title?: string\n slug?: string\n seoTitle?: string\n seoDescription?: string\n }\n}\n\nexport interface CMSFooterGroup {\n id: string\n heading: string\n headingUrl?: string\n locked: boolean\n links: CMSMenuItem[]\n}\n\nexport interface CMSSiteStructure {\n appId: string\n pageTree: CMSPageTreeNode[]\n menus: {\n header: CMSMenuItem[]\n footer: CMSFooterGroup[]\n footerBottom: CMSMenuItem[]\n sidebar: CMSMenuItem[]\n [slot: string]: CMSMenuItem[] | CMSFooterGroup[]\n }\n updatedAt?: string\n}\n\n// ── Pagination & Responses ─────────────────────────────────────────────────────\n\n// v1 route returns { data, count } — flat, no nested meta object\ninterface CMSListResponse {\n data: CMSPage[]\n count: number\n appId?: string\n}\n\ninterface CMSSingleResponse {\n data: CMSPage\n}\n\n// ── Client Options ─────────────────────────────────────────────────────────────\n\nexport interface CMSClientOptions {\n /** Base URL of your Forge CMS instance, e.g. https://cms.yourschool.io */\n baseUrl?: string\n /** API key generated in CMS Admin → API Keys. Server-side only. */\n apiKey?: string\n /** App ID from CMS Admin → Apps, e.g. \"school-website\" */\n appId?: string\n}\n\nexport interface CMSGetPagesOptions {\n type?: string\n group?: string\n page?: number\n perPage?: number\n status?: 'published' | 'draft' | 'archived'\n}\n\n// ── Factory ───────────────────────────────────────────────────────────────────\n\nexport function createCMSClient(options?: CMSClientOptions) {\n /**\n * Resolve config lazily at request time — NOT at module/build time.\n * This prevents static prerendering crashes when env vars are absent during build.\n */\n function cfg() {\n return {\n baseUrl: (options?.baseUrl ?? process.env.NEXT_PUBLIC_CMS_URL ?? process.env.CMS_BASE_URL ?? '').replace(/\\/$/, ''),\n apiKey: options?.apiKey ?? process.env.CMS_API_KEY ?? '',\n appId: options?.appId ?? process.env.CMS_APP_ID ?? '',\n }\n }\n\n function headers() {\n return { 'X-CMS-API-Key': cfg().apiKey, 'Content-Type': 'application/json' }\n }\n\n // ── Pages ──────────────────────────────────────────────────────────────────\n\n async function getPages(params?: CMSGetPagesOptions): Promise<CMSPage[]> {\n const { baseUrl, apiKey, appId } = cfg()\n if (!baseUrl || !apiKey || !appId) {\n console.warn('[sprintup-cms] Missing CMS_BASE_URL / CMS_API_KEY / CMS_APP_ID — returning []')\n return []\n }\n try {\n const qs = new URLSearchParams()\n if (params?.type) qs.set('type', params.type)\n if (params?.group) qs.set('group', params.group)\n if (params?.page) qs.set('page', String(params.page))\n if (params?.perPage) qs.set('perPage', String(params.perPage))\n const url = `${baseUrl}/api/v1/${appId}/pages${qs.size ? `?${qs}` : ''}`\n const res = await fetch(url, {\n headers: headers(),\n next: { revalidate: 60, tags: [`cms-pages-${appId}`] },\n } as RequestInit)\n if (!res.ok) { console.error(`[sprintup-cms] getPages (${res.status})`); return [] }\n const json: CMSListResponse = await res.json()\n return json.data ?? []\n } catch (err) {\n console.error('[sprintup-cms] getPages error:', err)\n return []\n }\n }\n\n async function getPage(slug: string): Promise<CMSPage | null> {\n const { baseUrl, apiKey, appId } = cfg()\n if (!baseUrl || !apiKey || !appId) {\n console.warn('[sprintup-cms] Missing config — returning null')\n return null\n }\n try {\n const res = await fetch(`${baseUrl}/api/v1/${appId}/pages/${slug}`, {\n headers: headers(),\n next: { revalidate: 60, tags: [`cms-page-${slug}`, `cms-pages-${appId}`] },\n } as RequestInit)\n if (res.status === 404) return null\n if (!res.ok) { console.error(`[sprintup-cms] getPage \"${slug}\" (${res.status})`); return null }\n const json: CMSSingleResponse = await res.json()\n return json.data ?? null\n } catch (err) {\n console.error(`[sprintup-cms] getPage \"${slug}\" error:`, err)\n return null\n }\n }\n\n async function getBlogPosts(): Promise<CMSPage[]> {\n return getPages({ type: 'blog-post' })\n }\n\n async function getEvents(): Promise<CMSPage[]> {\n return getPages({ type: 'event-page' })\n }\n\n async function getAnnouncements(): Promise<CMSPage[]> {\n return getPages({ type: 'announcement-page' })\n }\n\n // ── Preview ────────────────────────────────────────────────────────────────\n\n async function getPreviewPage(token: string): Promise<CMSPage | null> {\n const { baseUrl, appId } = cfg()\n if (!baseUrl || !appId) return null\n try {\n const res = await fetch(`${baseUrl}/api/v1/${appId}/preview?token=${encodeURIComponent(token)}`, {\n cache: 'no-store',\n })\n if (!res.ok) return null\n const json = await res.json()\n return json.data ?? null\n } catch {\n return null\n }\n }\n\n async function getPageWithPreview(slug: string, previewToken?: string | null): Promise<CMSPage | null> {\n if (previewToken) {\n const preview = await getPreviewPage(previewToken)\n if (preview?.slug === slug) return preview\n }\n return getPage(slug)\n }\n\n // ── Page Types ─────────────────────────────────────────────────────────────\n\n async function getPageType(pageTypeId: string): Promise<CMSPageType | null> {\n const { baseUrl, apiKey, appId } = cfg()\n if (!baseUrl || !apiKey || !appId || !pageTypeId) return null\n try {\n const res = await fetch(`${baseUrl}/api/v1/${appId}/page-types/${pageTypeId}`, {\n headers: headers(),\n next: { revalidate: 3600, tags: [`cms-page-type-${pageTypeId}`] },\n } as RequestInit)\n if (!res.ok) return null\n const json = await res.json()\n return json.data ?? null\n } catch {\n return null\n }\n }\n\n // ── Site Structure ─────────────────────────────────────────────────────────\n\n async function getSiteStructure(): Promise<CMSSiteStructure | null> {\n const { baseUrl, apiKey, appId } = cfg()\n if (!baseUrl || !apiKey || !appId) return null\n try {\n const res = await fetch(`${baseUrl}/api/v1/${appId}/site-structure`, {\n headers: headers(),\n next: {\n revalidate: 300,\n tags: [`site-structure-${appId}`],\n },\n } as RequestInit)\n if (!res.ok) return null\n const json = await res.json()\n return json.data ?? null\n } catch {\n return null\n }\n }\n\n /**\n * GET /api/v1/{appId}/sitemap\n * Fetch sitemap data for all published pages (priority, changefreq, lastmod).\n *\n * @example app/sitemap.ts\n * import type { MetadataRoute } from 'next'\n * import { cmsClient } from '@sprintup-cms/sdk'\n *\n * export default async function sitemap(): Promise<MetadataRoute.Sitemap> {\n * const data = await cmsClient.getSitemap()\n * if (!data?.enabled) return []\n * return data.urls.map(url => ({\n * url: `${process.env.NEXT_PUBLIC_SITE_URL}${url.loc}`,\n * lastModified: url.lastmod,\n * changeFrequency: url.changefreq as MetadataRoute.Sitemap[0]['changeFrequency'],\n * priority: url.priority,\n * }))\n * }\n */\n async function getSitemap(): Promise<CMSSitemapResponse | null> {\n const { baseUrl, apiKey, appId } = cfg()\n if (!baseUrl || !apiKey || !appId) return null\n try {\n const res = await fetch(`${baseUrl}/api/v1/${appId}/sitemap`, {\n headers: headers(),\n next: { revalidate: 3600 },\n } as RequestInit)\n if (!res.ok) return null\n const json = await res.json()\n return json.data ?? null\n } catch { return null }\n }\n\n /**\n * GET /api/v1/{appId}/status\n * Verify connectivity and retrieve page counts. Never cached — always fresh.\n * Useful for debugging API key setup.\n */\n async function getStatus(): Promise<CMSStatusResponse | null> {\n const { baseUrl, apiKey, appId } = cfg()\n if (!baseUrl || !apiKey || !appId) return null\n try {\n const res = await fetch(`${baseUrl}/api/v1/${appId}/status`, {\n headers: headers(),\n cache: 'no-store',\n } as RequestInit)\n if (!res.ok) return null\n return await res.json()\n } catch { return null }\n }\n\n return {\n getPages,\n getPage,\n getBlogPosts,\n getEvents,\n getAnnouncements,\n getPreviewPage,\n getPageWithPreview,\n getPageType,\n getSiteStructure,\n getSitemap,\n getStatus,\n }\n}\n\n// ── Default singleton ─────────────────────────────────────────────────────────\n\n/** Pre-configured singleton. Reads env vars lazily at request time. */\nexport const cmsClient = createCMSClient()\n"]}
|
package/dist/next/index.cjs
CHANGED
|
@@ -22,21 +22,29 @@ function createRevalidateHandler(options) {
|
|
|
22
22
|
return server.NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
|
23
23
|
}
|
|
24
24
|
const body = await request.json();
|
|
25
|
-
const { slug, tag } = body;
|
|
26
|
-
if (!slug) {
|
|
27
|
-
return server.NextResponse.json({ error: "Missing slug in payload" }, { status: 400 });
|
|
25
|
+
const { slug, tag, appId, data } = body;
|
|
26
|
+
if (!tag && !slug) {
|
|
27
|
+
return server.NextResponse.json({ error: "Missing tag or slug in payload" }, { status: 400 });
|
|
28
|
+
}
|
|
29
|
+
if (tag) {
|
|
30
|
+
cache.revalidateTag(tag);
|
|
31
|
+
}
|
|
32
|
+
const resolvedSlug = slug ?? data?.slug ?? tag?.replace(/^content-/, "");
|
|
33
|
+
if (resolvedSlug) {
|
|
34
|
+
cache.revalidatePath(`/${resolvedSlug}`);
|
|
35
|
+
cache.revalidateTag(`cms-page-${resolvedSlug}`);
|
|
28
36
|
}
|
|
29
|
-
cache.revalidatePath(`/${slug}`);
|
|
30
|
-
cache.revalidateTag(`cms-page-${slug}`);
|
|
31
37
|
cache.revalidateTag("cms-pages");
|
|
32
|
-
|
|
33
|
-
|
|
38
|
+
const isStructure = tag?.startsWith("site-structure") || body.event === "structure.update";
|
|
39
|
+
const isPublish = body.event === "published" || body.event === "content.publish";
|
|
40
|
+
if (isStructure || isPublish) {
|
|
34
41
|
cache.revalidatePath("/", "layout");
|
|
35
42
|
}
|
|
36
43
|
await options?.onRevalidate?.(body);
|
|
37
44
|
return server.NextResponse.json({
|
|
38
45
|
revalidated: true,
|
|
39
|
-
|
|
46
|
+
tag,
|
|
47
|
+
slug: resolvedSlug,
|
|
40
48
|
event: body.event,
|
|
41
49
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
42
50
|
});
|
|
@@ -169,31 +177,31 @@ function createCMSClient(options) {
|
|
|
169
177
|
return null;
|
|
170
178
|
}
|
|
171
179
|
}
|
|
172
|
-
async function
|
|
180
|
+
async function getSiteStructure() {
|
|
173
181
|
const { baseUrl, apiKey, appId } = cfg();
|
|
174
|
-
if (!baseUrl || !apiKey || !appId) return
|
|
182
|
+
if (!baseUrl || !apiKey || !appId) return null;
|
|
175
183
|
try {
|
|
176
|
-
const res = await fetch(`${baseUrl}/api/v1/${appId}/
|
|
184
|
+
const res = await fetch(`${baseUrl}/api/v1/${appId}/site-structure`, {
|
|
177
185
|
headers: headers(),
|
|
178
|
-
next: {
|
|
186
|
+
next: {
|
|
187
|
+
revalidate: 300,
|
|
188
|
+
tags: [`site-structure-${appId}`]
|
|
189
|
+
}
|
|
179
190
|
});
|
|
180
|
-
if (!res.ok) return
|
|
191
|
+
if (!res.ok) return null;
|
|
181
192
|
const json = await res.json();
|
|
182
|
-
return json.data ??
|
|
193
|
+
return json.data ?? null;
|
|
183
194
|
} catch {
|
|
184
|
-
return
|
|
195
|
+
return null;
|
|
185
196
|
}
|
|
186
197
|
}
|
|
187
|
-
async function
|
|
198
|
+
async function getSitemap() {
|
|
188
199
|
const { baseUrl, apiKey, appId } = cfg();
|
|
189
200
|
if (!baseUrl || !apiKey || !appId) return null;
|
|
190
201
|
try {
|
|
191
|
-
const res = await fetch(`${baseUrl}/api/v1/${appId}/
|
|
202
|
+
const res = await fetch(`${baseUrl}/api/v1/${appId}/sitemap`, {
|
|
192
203
|
headers: headers(),
|
|
193
|
-
next: {
|
|
194
|
-
revalidate: 300,
|
|
195
|
-
tags: [`site-structure-${appId}`]
|
|
196
|
-
}
|
|
204
|
+
next: { revalidate: 3600 }
|
|
197
205
|
});
|
|
198
206
|
if (!res.ok) return null;
|
|
199
207
|
const json = await res.json();
|
|
@@ -202,6 +210,20 @@ function createCMSClient(options) {
|
|
|
202
210
|
return null;
|
|
203
211
|
}
|
|
204
212
|
}
|
|
213
|
+
async function getStatus() {
|
|
214
|
+
const { baseUrl, apiKey, appId } = cfg();
|
|
215
|
+
if (!baseUrl || !apiKey || !appId) return null;
|
|
216
|
+
try {
|
|
217
|
+
const res = await fetch(`${baseUrl}/api/v1/${appId}/status`, {
|
|
218
|
+
headers: headers(),
|
|
219
|
+
cache: "no-store"
|
|
220
|
+
});
|
|
221
|
+
if (!res.ok) return null;
|
|
222
|
+
return await res.json();
|
|
223
|
+
} catch {
|
|
224
|
+
return null;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
205
227
|
return {
|
|
206
228
|
getPages,
|
|
207
229
|
getPage,
|
|
@@ -211,8 +233,9 @@ function createCMSClient(options) {
|
|
|
211
233
|
getPreviewPage,
|
|
212
234
|
getPageWithPreview,
|
|
213
235
|
getPageType,
|
|
214
|
-
|
|
215
|
-
|
|
236
|
+
getSiteStructure,
|
|
237
|
+
getSitemap,
|
|
238
|
+
getStatus
|
|
216
239
|
};
|
|
217
240
|
}
|
|
218
241
|
function getData(block) {
|