cmx-sdk 0.1.10 → 0.1.13
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 +351 -53
- package/dist/cli.js +968 -83
- package/dist/index.d.ts +1968 -74
- package/dist/index.js +1040 -69
- package/dist/index.js.map +1 -0
- package/package.json +47 -17
- package/dist/cli.d.ts +0 -2
- package/dist/commands/create-collection.d.ts +0 -9
- package/dist/commands/create-collection.js +0 -32
- package/dist/commands/create-data-type.d.ts +0 -8
- package/dist/commands/create-data-type.js +0 -32
- package/dist/commands/create-form.d.ts +0 -8
- package/dist/commands/create-form.js +0 -32
- package/dist/commands/generate.d.ts +0 -3
- package/dist/commands/generate.js +0 -164
- package/dist/commands/import-schema.d.ts +0 -5
- package/dist/commands/import-schema.js +0 -96
- package/dist/commands/list-collections.d.ts +0 -1
- package/dist/commands/list-collections.js +0 -27
- package/dist/commands/list-components.d.ts +0 -1
- package/dist/commands/list-components.js +0 -26
- package/dist/commands/list-data-types.d.ts +0 -1
- package/dist/commands/list-data-types.js +0 -27
- package/dist/commands/list-forms.d.ts +0 -1
- package/dist/commands/list-forms.js +0 -27
- package/dist/commands/sync-components.d.ts +0 -6
- package/dist/commands/sync-components.js +0 -42
- package/dist/lib/api-client.d.ts +0 -63
- package/dist/lib/api-client.js +0 -74
- package/src/cli.ts +0 -103
- package/src/commands/create-collection.ts +0 -47
- package/src/commands/create-data-type.ts +0 -53
- package/src/commands/create-form.ts +0 -53
- package/src/commands/generate.ts +0 -219
- package/src/commands/import-schema.ts +0 -151
- package/src/commands/list-collections.ts +0 -30
- package/src/commands/list-components.ts +0 -29
- package/src/commands/list-data-types.ts +0 -30
- package/src/commands/list-forms.ts +0 -30
- package/src/commands/sync-components.ts +0 -57
- package/src/index.ts +0 -219
- package/src/lib/api-client.ts +0 -138
- package/tsconfig.json +0 -17
package/README.md
CHANGED
|
@@ -1,95 +1,393 @@
|
|
|
1
|
-
#
|
|
1
|
+
# cmx-sdk
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Official SDK for building content-driven websites with CMX (Content Management Experience).
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
8
|
npm install cmx-sdk
|
|
9
|
+
# or
|
|
10
|
+
pnpm add cmx-sdk
|
|
11
|
+
# or
|
|
12
|
+
yarn add cmx-sdk
|
|
9
13
|
```
|
|
10
14
|
|
|
11
|
-
##
|
|
15
|
+
## Features
|
|
12
16
|
|
|
13
|
-
|
|
14
|
-
|
|
17
|
+
- Type-Safe API Client - Fetch content from CMX Admin with full TypeScript support
|
|
18
|
+
- MDX Rendering - Server-side MDX compilation with reference resolution
|
|
19
|
+
- MDX Components - Pre-built React components (Callout, Embed, BlogCard, Image, etc.)
|
|
20
|
+
- Next.js Optimized - Built-in support for ISR, caching, and revalidation
|
|
21
|
+
- Customizable - Inject your own custom components into MDX rendering
|
|
22
|
+
|
|
23
|
+
## Quick Start
|
|
24
|
+
|
|
25
|
+
### 1. Set up environment variables
|
|
26
|
+
|
|
27
|
+
Create a `.env.local` file:
|
|
28
|
+
|
|
29
|
+
```env
|
|
30
|
+
CMX_API_URL=https://your-cmx-admin.example.com
|
|
15
31
|
CMX_API_KEY=your_api_key_here
|
|
16
32
|
```
|
|
17
33
|
|
|
18
|
-
|
|
34
|
+
### 2. Fetch content
|
|
19
35
|
|
|
20
|
-
|
|
36
|
+
```tsx
|
|
37
|
+
import { getCollectionContents, getCollectionContentDetail } from 'cmx-sdk';
|
|
21
38
|
|
|
22
|
-
|
|
39
|
+
// List contents in a collection
|
|
40
|
+
const { collection, contents } = await getCollectionContents('blog');
|
|
23
41
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
42
|
+
// Get a specific content with references
|
|
43
|
+
const { collection, content, references } = await getCollectionContentDetail('blog', 'my-post');
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### 3. Render MDX content
|
|
27
47
|
|
|
28
|
-
|
|
29
|
-
|
|
48
|
+
```tsx
|
|
49
|
+
import { renderMdx } from 'cmx-sdk';
|
|
50
|
+
|
|
51
|
+
// Render MDX with reference resolution (BlogCard, Image, etc.)
|
|
52
|
+
const { rendered } = await renderMdx(content.mdx, references);
|
|
53
|
+
return <article>{rendered}</article>;
|
|
54
|
+
|
|
55
|
+
// With custom components
|
|
56
|
+
import { FeatureCard } from './components/custom';
|
|
57
|
+
const { rendered } = await renderMdx(content.mdx, references, {
|
|
58
|
+
additionalComponents: { FeatureCard },
|
|
59
|
+
});
|
|
30
60
|
```
|
|
31
61
|
|
|
32
|
-
|
|
62
|
+
## API Reference
|
|
33
63
|
|
|
34
|
-
|
|
64
|
+
### Content Fetching
|
|
35
65
|
|
|
36
|
-
```
|
|
37
|
-
|
|
66
|
+
```tsx
|
|
67
|
+
import {
|
|
68
|
+
getCollectionContents,
|
|
69
|
+
getCollectionContentDetail,
|
|
70
|
+
getDataEntries,
|
|
71
|
+
getDataEntry,
|
|
72
|
+
getPreviewByToken,
|
|
73
|
+
} from 'cmx-sdk';
|
|
74
|
+
|
|
75
|
+
// Get contents from a collection
|
|
76
|
+
const { contents } = await getCollectionContents('blog');
|
|
77
|
+
|
|
78
|
+
// Get a specific content with resolved references
|
|
79
|
+
const { content, references } = await getCollectionContentDetail('blog', 'my-post');
|
|
80
|
+
|
|
81
|
+
// Get data entries (custom data types)
|
|
82
|
+
const { items } = await getDataEntries('team-members');
|
|
83
|
+
|
|
84
|
+
// Get data entries with options
|
|
85
|
+
const { items } = await getDataEntries('team-members', {
|
|
86
|
+
sortBy: 'createdAt',
|
|
87
|
+
sortOrder: 'desc',
|
|
88
|
+
limit: 10,
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
// Get a specific data entry
|
|
92
|
+
const entry = await getDataEntry('team-members', 'entry-id');
|
|
93
|
+
|
|
94
|
+
// Get preview content by token (no auth required)
|
|
95
|
+
const preview = await getPreviewByToken(token);
|
|
96
|
+
if (preview) {
|
|
97
|
+
const { content } = await renderMdx(preview.content.mdx, preview.references);
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### MDX Rendering
|
|
102
|
+
|
|
103
|
+
```tsx
|
|
104
|
+
import { renderMdx, renderMdxPreview } from 'cmx-sdk';
|
|
105
|
+
|
|
106
|
+
// Full rendering with reference resolution
|
|
107
|
+
const { content, references } = await renderMdx(item.mdx, item.references);
|
|
108
|
+
|
|
109
|
+
// With custom components injected
|
|
110
|
+
const { content } = await renderMdx(item.mdx, item.references, {
|
|
111
|
+
additionalComponents: { FeatureCard, PricingTable },
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
// Preview rendering (no reference resolution)
|
|
115
|
+
const element = await renderMdxPreview(mdxString);
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Cache Tags
|
|
119
|
+
|
|
120
|
+
Use Next.js cache tags for on-demand revalidation:
|
|
121
|
+
|
|
122
|
+
```tsx
|
|
123
|
+
import { CACHE_TAGS, sdkFetchWithTags } from 'cmx-sdk';
|
|
124
|
+
|
|
125
|
+
// Built-in cache tags used by convenience functions:
|
|
126
|
+
// CACHE_TAGS.collections - all collections
|
|
127
|
+
// CACHE_TAGS.collection(slug) - specific collection
|
|
128
|
+
// CACHE_TAGS.content(col, content) - specific content
|
|
129
|
+
// CACHE_TAGS.data - all data types
|
|
130
|
+
// CACHE_TAGS.dataType(slug) - specific data type
|
|
131
|
+
|
|
132
|
+
// Revalidate by tag
|
|
133
|
+
import { revalidateTag } from 'next/cache';
|
|
134
|
+
revalidateTag(CACHE_TAGS.collection('blog'));
|
|
135
|
+
|
|
136
|
+
// Low-level: fetch with custom cache tags
|
|
137
|
+
const data = await sdkFetchWithTags<T>(
|
|
138
|
+
'/collections/blog/contents',
|
|
139
|
+
[CACHE_TAGS.collections, CACHE_TAGS.collection('blog')]
|
|
140
|
+
);
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### Preview Mode
|
|
144
|
+
|
|
145
|
+
```tsx
|
|
146
|
+
import { getPreviewByToken, renderMdx } from 'cmx-sdk';
|
|
147
|
+
|
|
148
|
+
const preview = await getPreviewByToken(token);
|
|
149
|
+
if (preview) {
|
|
150
|
+
const { content } = await renderMdx(preview.content.mdx, preview.references);
|
|
151
|
+
return <article>{content}</article>;
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Components
|
|
156
|
+
|
|
157
|
+
The SDK includes MDX components that are automatically available in `renderMdx`:
|
|
158
|
+
|
|
159
|
+
### Standard Components
|
|
160
|
+
|
|
161
|
+
- `Callout` - Highlighted information boxes (info, warning, error, success)
|
|
162
|
+
- `Embed` - External content embedding (YouTube, Twitter, etc.)
|
|
163
|
+
- `Button` - Interactive buttons
|
|
164
|
+
|
|
165
|
+
### Reference-Resolving Components
|
|
166
|
+
|
|
167
|
+
These components automatically resolve references from the API response:
|
|
168
|
+
|
|
169
|
+
- `BlogCard` - Linked content cards (resolves content title, slug, description)
|
|
170
|
+
- `Image` - Asset images with variants (resolves URL, alt text, dimensions)
|
|
171
|
+
|
|
172
|
+
### Component Catalog
|
|
173
|
+
|
|
174
|
+
Access component metadata and validation:
|
|
175
|
+
|
|
176
|
+
```tsx
|
|
177
|
+
import {
|
|
178
|
+
componentCatalog,
|
|
179
|
+
isValidComponent,
|
|
180
|
+
validateComponentProps,
|
|
181
|
+
validateMdx,
|
|
182
|
+
} from 'cmx-sdk';
|
|
183
|
+
|
|
184
|
+
// Check if a component is valid
|
|
185
|
+
isValidComponent('Callout'); // true
|
|
186
|
+
|
|
187
|
+
// Validate component props
|
|
188
|
+
const result = validateComponentProps('Callout', {
|
|
189
|
+
type: 'info',
|
|
190
|
+
title: 'Note',
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
// Validate entire MDX content
|
|
194
|
+
const validation = validateMdx(mdxString);
|
|
38
195
|
```
|
|
39
196
|
|
|
40
|
-
|
|
197
|
+
## Code Generation (CLI)
|
|
198
|
+
|
|
199
|
+
Generate typed functions and interfaces from your CMX data types and collections.
|
|
41
200
|
|
|
42
|
-
|
|
201
|
+
### Usage
|
|
43
202
|
|
|
44
203
|
```bash
|
|
45
|
-
cmx-sdk
|
|
204
|
+
npx cmx-sdk generate
|
|
46
205
|
```
|
|
47
206
|
|
|
48
|
-
|
|
207
|
+
This fetches your workspace's schema from the CMX API and generates TypeScript files in `cmx/generated/`.
|
|
49
208
|
|
|
50
|
-
|
|
209
|
+
### Options
|
|
51
210
|
|
|
52
211
|
```bash
|
|
53
|
-
#
|
|
54
|
-
|
|
212
|
+
npx cmx-sdk generate --output src/cmx/generated # Custom output directory
|
|
213
|
+
```
|
|
55
214
|
|
|
56
|
-
|
|
57
|
-
|
|
215
|
+
### Requirements
|
|
216
|
+
|
|
217
|
+
Set `CMX_API_URL` and `CMX_API_KEY` in your `.env` or `.env.local`:
|
|
218
|
+
|
|
219
|
+
```env
|
|
220
|
+
CMX_API_URL=https://your-cmx-admin.example.com
|
|
221
|
+
CMX_API_KEY=your_api_key_here
|
|
58
222
|
```
|
|
59
223
|
|
|
60
|
-
###
|
|
224
|
+
### Generated Output
|
|
61
225
|
|
|
62
|
-
|
|
226
|
+
```
|
|
227
|
+
cmx/generated/
|
|
228
|
+
├── index.ts
|
|
229
|
+
├── collections/
|
|
230
|
+
│ ├── index.ts
|
|
231
|
+
│ ├── blog.ts # getBlogContents(), getBlogContentDetail()
|
|
232
|
+
│ └── docs.ts # getDocsContents(), getDocsContentDetail()
|
|
233
|
+
└── data-types/
|
|
234
|
+
├── index.ts
|
|
235
|
+
├── team-members.ts # TeamMember type, getTeamMembers(), getTeamMember()
|
|
236
|
+
└── faq.ts # Faq type, getFaqs(), getFaq()
|
|
237
|
+
```
|
|
63
238
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
239
|
+
### Using Generated Code
|
|
240
|
+
|
|
241
|
+
```tsx
|
|
242
|
+
// Before (generic, no type safety)
|
|
243
|
+
import { getDataEntries } from "cmx-sdk"
|
|
244
|
+
const { items } = await getDataEntries("team-members")
|
|
245
|
+
const name = items[0].name // ❌ type is unknown
|
|
246
|
+
|
|
247
|
+
// After (typed, auto-generated)
|
|
248
|
+
import { getTeamMembers } from "./cmx/generated"
|
|
249
|
+
const { items } = await getTeamMembers()
|
|
250
|
+
const name = items[0].name // ✅ type is string
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
Data type fields are mapped to TypeScript types:
|
|
254
|
+
|
|
255
|
+
| Field Type | TypeScript Type |
|
|
256
|
+
|---|---|
|
|
257
|
+
| text, textarea, richtext, url, email | `string` |
|
|
258
|
+
| number | `number` |
|
|
259
|
+
| boolean | `boolean` |
|
|
260
|
+
| date, datetime | `string` |
|
|
261
|
+
| select (with choices) | `"value1" \| "value2"` |
|
|
262
|
+
| multiselect | `string[]` or union array |
|
|
263
|
+
| image, file | `string` |
|
|
264
|
+
| relation | `string` (or `string[]` if multiple) |
|
|
265
|
+
| json | `unknown` |
|
|
266
|
+
|
|
267
|
+
Non-required fields are typed as `T | null`.
|
|
268
|
+
|
|
269
|
+
## TypeScript
|
|
270
|
+
|
|
271
|
+
The SDK is fully typed. Import types as needed:
|
|
272
|
+
|
|
273
|
+
```tsx
|
|
274
|
+
import type {
|
|
275
|
+
// API response types
|
|
276
|
+
CollectionContentsResponse,
|
|
277
|
+
CollectionContentDetailResponse,
|
|
278
|
+
DataListResponse,
|
|
279
|
+
DataEntryItem,
|
|
280
|
+
PreviewResponse,
|
|
281
|
+
References,
|
|
282
|
+
ContentReference,
|
|
283
|
+
AssetReference,
|
|
284
|
+
// Rendering types
|
|
285
|
+
RenderResult,
|
|
286
|
+
RenderOptions,
|
|
287
|
+
ResolvedReferences,
|
|
288
|
+
// Component types
|
|
289
|
+
ComponentDefinition,
|
|
290
|
+
ValidationResult,
|
|
291
|
+
} from 'cmx-sdk';
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
## Advanced Usage
|
|
295
|
+
|
|
296
|
+
### Low-Level Generated Functions
|
|
297
|
+
|
|
298
|
+
For advanced use cases, the SDK also exports the raw Orval-generated endpoint functions:
|
|
299
|
+
|
|
300
|
+
```tsx
|
|
301
|
+
import {
|
|
302
|
+
getCollectionsSlugContents,
|
|
303
|
+
getCollectionsSlugContentsContentSlug,
|
|
304
|
+
getDataTypeSlug,
|
|
305
|
+
getPreviewToken,
|
|
306
|
+
} from 'cmx-sdk';
|
|
307
|
+
|
|
308
|
+
// These return { data, status, headers } instead of just the data
|
|
309
|
+
const response = await getCollectionsSlugContents('blog');
|
|
310
|
+
if (response.status === 200) {
|
|
311
|
+
const contents = response.data;
|
|
81
312
|
}
|
|
82
313
|
```
|
|
83
314
|
|
|
84
|
-
|
|
315
|
+
### Direct Fetcher Access
|
|
85
316
|
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
|
|
317
|
+
```tsx
|
|
318
|
+
import {
|
|
319
|
+
sdkFetcher,
|
|
320
|
+
sdkFetchWithTags,
|
|
321
|
+
getSdkApiBaseUrl,
|
|
322
|
+
getSdkApiToken,
|
|
323
|
+
} from 'cmx-sdk';
|
|
324
|
+
|
|
325
|
+
// Use the fetcher directly for custom endpoints
|
|
326
|
+
const data = await sdkFetchWithTags<MyType>('/custom/endpoint', ['my-tag']);
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
## Environment Variables
|
|
330
|
+
|
|
331
|
+
| Variable | Description | Required |
|
|
332
|
+
|----------|-------------|----------|
|
|
333
|
+
| `CMX_API_URL` | CMX Admin API base URL | Yes |
|
|
334
|
+
| `CMX_API_KEY` | API key for authentication | Yes |
|
|
335
|
+
|
|
336
|
+
## Next.js Integration
|
|
337
|
+
|
|
338
|
+
The SDK is optimized for Next.js App Router (Server Components):
|
|
339
|
+
|
|
340
|
+
```tsx
|
|
341
|
+
// app/blog/[slug]/page.tsx
|
|
342
|
+
import { getCollectionContents, getCollectionContentDetail, renderMdx } from 'cmx-sdk';
|
|
89
343
|
|
|
90
|
-
|
|
91
|
-
|
|
344
|
+
export default async function BlogContent({
|
|
345
|
+
params,
|
|
346
|
+
}: {
|
|
347
|
+
params: Promise<{ slug: string }>;
|
|
348
|
+
}) {
|
|
349
|
+
const { slug } = await params;
|
|
350
|
+
const { content, references } = await getCollectionContentDetail('blog', slug);
|
|
351
|
+
const { content: rendered } = await renderMdx(content.mdx, references);
|
|
92
352
|
|
|
93
|
-
|
|
94
|
-
|
|
353
|
+
return (
|
|
354
|
+
<article>
|
|
355
|
+
<h1>{content.title}</h1>
|
|
356
|
+
<p>{content.description}</p>
|
|
357
|
+
<div className="prose">{rendered}</div>
|
|
358
|
+
</article>
|
|
359
|
+
);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
export async function generateStaticParams() {
|
|
363
|
+
const { contents } = await getCollectionContents('blog');
|
|
364
|
+
return contents.map((item) => ({ slug: item.slug }));
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
export async function generateMetadata({
|
|
368
|
+
params,
|
|
369
|
+
}: {
|
|
370
|
+
params: Promise<{ slug: string }>;
|
|
371
|
+
}) {
|
|
372
|
+
const { slug } = await params;
|
|
373
|
+
const { content } = await getCollectionContentDetail('blog', slug);
|
|
374
|
+
return {
|
|
375
|
+
title: content.title,
|
|
376
|
+
description: content.description,
|
|
377
|
+
};
|
|
378
|
+
}
|
|
95
379
|
```
|
|
380
|
+
|
|
381
|
+
## Examples
|
|
382
|
+
|
|
383
|
+
Check out the [CMX Starter Kit](https://github.com/kzkm-lab/cmx-starter-kit) for a complete example of using the SDK.
|
|
384
|
+
|
|
385
|
+
## License
|
|
386
|
+
|
|
387
|
+
MIT
|
|
388
|
+
|
|
389
|
+
## Links
|
|
390
|
+
|
|
391
|
+
- [Documentation](https://github.com/kzkm-lab/cmx)
|
|
392
|
+
- [CMX Starter Kit](https://github.com/kzkm-lab/cmx-starter-kit)
|
|
393
|
+
- [Issues](https://github.com/kzkm-lab/cmx/issues)
|