@inneropen/marvin-sdk 1.0.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/QUICKSTART.md +408 -0
- package/README.md +721 -0
- package/dist/index.cjs +859 -0
- package/dist/index.d.cts +430 -0
- package/dist/index.d.ts +430 -0
- package/dist/index.js +846 -0
- package/dist/types/index.cjs +18 -0
- package/dist/types/index.d.cts +120 -0
- package/dist/types/index.d.ts +120 -0
- package/dist/types/index.js +0 -0
- package/examples.md +961 -0
- package/package.json +73 -0
package/QUICKSTART.md
ADDED
|
@@ -0,0 +1,408 @@
|
|
|
1
|
+
# Marvin SDK Quick Start
|
|
2
|
+
|
|
3
|
+
Get up and running with the Marvin TypeScript SDK in 5 minutes.
|
|
4
|
+
|
|
5
|
+
The Marvin SDK is framework-agnostic and works with Astro, Next.js, Express, CLI tools, automation workflows, and any trusted runtime.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Step 1: Copy the SDK
|
|
10
|
+
|
|
11
|
+
Copy the SDK into your project:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
# Navigate to your project
|
|
15
|
+
cd /path/to/your-project
|
|
16
|
+
|
|
17
|
+
# Create lib directory
|
|
18
|
+
mkdir -p src/lib
|
|
19
|
+
|
|
20
|
+
# Copy the SDK
|
|
21
|
+
cp -r /path/to/marvin/packages/marvin-sdk src/lib/marvin
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Step 2: Configure Environment Variables
|
|
25
|
+
|
|
26
|
+
Create a `.env` file in your project root:
|
|
27
|
+
|
|
28
|
+
```env
|
|
29
|
+
MARVIN_API_URL=https://marvin.example.com
|
|
30
|
+
MARVIN_SITE_CLIENT_TOKEN=your-token-here
|
|
31
|
+
MARVIN_WORKSPACE_SLUG=your-workspace
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
**Get your Site Client Token:**
|
|
35
|
+
|
|
36
|
+
1. Log into Marvin
|
|
37
|
+
2. Go to **Settings → Publishing → Site Clients**
|
|
38
|
+
3. Create a new client for your application
|
|
39
|
+
4. Copy the token
|
|
40
|
+
|
|
41
|
+
**🔒 Security Note:** Site client tokens must only be used in **server-side, build-time, or trusted backend code**. Never expose them in browser-side bundles.
|
|
42
|
+
|
|
43
|
+
## Step 3: Create SDK Instance
|
|
44
|
+
|
|
45
|
+
Create a file to initialize the SDK:
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
// src/lib/marvin.ts
|
|
49
|
+
import { createMarvinClient } from './marvin';
|
|
50
|
+
|
|
51
|
+
export const marvin = createMarvinClient();
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Optional: Preload Workspace Data
|
|
55
|
+
|
|
56
|
+
If you want to preload site configuration at startup:
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
// src/lib/marvin.ts
|
|
60
|
+
import { createMarvinClient } from './marvin';
|
|
61
|
+
|
|
62
|
+
export const marvin = createMarvinClient({
|
|
63
|
+
autoInitialize: true, // Preload site config on creation
|
|
64
|
+
});
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Or initialize manually when needed:
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
import { marvin } from '@/lib/marvin';
|
|
71
|
+
|
|
72
|
+
// Initialize when needed
|
|
73
|
+
await marvin.initialize();
|
|
74
|
+
|
|
75
|
+
// Now site config is cached
|
|
76
|
+
console.log(marvin.site?.title);
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Step 4: Fetch Content
|
|
80
|
+
|
|
81
|
+
### Workspace-First API (Recommended)
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
import { marvin } from '@/lib/marvin';
|
|
85
|
+
|
|
86
|
+
// Get workspace
|
|
87
|
+
const workspace = await marvin.getWorkspace();
|
|
88
|
+
console.log(workspace.site?.title);
|
|
89
|
+
|
|
90
|
+
// Get entries
|
|
91
|
+
const entries = await workspace.entries.list();
|
|
92
|
+
|
|
93
|
+
// Get a collection
|
|
94
|
+
const collection = await workspace.collections.get('projects');
|
|
95
|
+
const projectEntries = await collection.entries();
|
|
96
|
+
|
|
97
|
+
// Get resources
|
|
98
|
+
const resources = await workspace.resources.list();
|
|
99
|
+
const fabric = await workspace.resources.get('kuroki-s022');
|
|
100
|
+
const entriesUsingFabric = await fabric.entries();
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Convenience API
|
|
104
|
+
|
|
105
|
+
```ts
|
|
106
|
+
import { marvin } from '@/lib/marvin';
|
|
107
|
+
|
|
108
|
+
// Quick access to entries
|
|
109
|
+
const entry = await marvin.entry('about-us');
|
|
110
|
+
const projects = await marvin.projects();
|
|
111
|
+
|
|
112
|
+
// Get entries by type
|
|
113
|
+
const articles = await marvin.getEntries({ entryType: 'article' });
|
|
114
|
+
|
|
115
|
+
// Get a collection
|
|
116
|
+
const featured = await marvin.collection('featured');
|
|
117
|
+
|
|
118
|
+
// Get resources
|
|
119
|
+
const fabric = await marvin.resource('kuroki-s022');
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Backwards-Compatible API
|
|
123
|
+
|
|
124
|
+
```ts
|
|
125
|
+
import { marvin } from '@/lib/marvin';
|
|
126
|
+
|
|
127
|
+
// Still works! (Deprecated but supported)
|
|
128
|
+
const site = await marvin.getSite();
|
|
129
|
+
const entries = await marvin.getEntries();
|
|
130
|
+
const entry = await marvin.getEntry('about');
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## Framework Examples
|
|
136
|
+
|
|
137
|
+
### Astro (Static Site)
|
|
138
|
+
|
|
139
|
+
```astro
|
|
140
|
+
---
|
|
141
|
+
// src/pages/[slug].astro
|
|
142
|
+
import { marked } from 'marked';
|
|
143
|
+
import { marvin } from '@/lib/marvin';
|
|
144
|
+
|
|
145
|
+
export async function getStaticPaths() {
|
|
146
|
+
const entries = await marvin.getEntries({ entryType: 'page' });
|
|
147
|
+
|
|
148
|
+
return entries.map((entry) => ({
|
|
149
|
+
params: { slug: entry.slug },
|
|
150
|
+
props: { entry },
|
|
151
|
+
}));
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const { entry } = Astro.props;
|
|
155
|
+
|
|
156
|
+
// Parse Markdown to HTML
|
|
157
|
+
const contentHtml = marked.parse(entry.contentMarkdown ?? '');
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
<html>
|
|
161
|
+
<head>
|
|
162
|
+
<title>{entry.title}</title>
|
|
163
|
+
</head>
|
|
164
|
+
<body>
|
|
165
|
+
<h1>{entry.title}</h1>
|
|
166
|
+
<div class="content" set:html={contentHtml} />
|
|
167
|
+
</body>
|
|
168
|
+
</html>
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### Next.js (App Router)
|
|
172
|
+
|
|
173
|
+
```tsx
|
|
174
|
+
// app/projects/page.tsx
|
|
175
|
+
import { marvin } from '@/lib/marvin';
|
|
176
|
+
|
|
177
|
+
export default async function ProjectsPage() {
|
|
178
|
+
const projects = await marvin.projects();
|
|
179
|
+
|
|
180
|
+
return (
|
|
181
|
+
<div>
|
|
182
|
+
<h1>Projects</h1>
|
|
183
|
+
{projects.map((project) => (
|
|
184
|
+
<article key={project.id}>
|
|
185
|
+
<h2>{project.title}</h2>
|
|
186
|
+
<p>{project.summary}</p>
|
|
187
|
+
<a href={`/projects/${project.slug}`}>View project →</a>
|
|
188
|
+
</article>
|
|
189
|
+
))}
|
|
190
|
+
</div>
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### Express (Server)
|
|
196
|
+
|
|
197
|
+
```ts
|
|
198
|
+
import express from 'express';
|
|
199
|
+
import { marvin } from './lib/marvin';
|
|
200
|
+
|
|
201
|
+
const app = express();
|
|
202
|
+
|
|
203
|
+
app.get('/api/entries', async (req, res) => {
|
|
204
|
+
const entries = await marvin.getEntries({
|
|
205
|
+
entryType: req.query.type as string,
|
|
206
|
+
});
|
|
207
|
+
res.json(entries);
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
app.get('/api/entries/:slug', async (req, res) => {
|
|
211
|
+
const entry = await marvin.entry(req.params.slug);
|
|
212
|
+
res.json(entry.toJSON());
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
app.listen(3000, () => {
|
|
216
|
+
console.log('Server running on http://localhost:3000');
|
|
217
|
+
});
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### CLI Tool
|
|
221
|
+
|
|
222
|
+
```ts
|
|
223
|
+
#!/usr/bin/env node
|
|
224
|
+
import { marvin } from './lib/marvin';
|
|
225
|
+
|
|
226
|
+
async function main() {
|
|
227
|
+
// Initialize SDK
|
|
228
|
+
await marvin.initialize();
|
|
229
|
+
|
|
230
|
+
console.log('Site:', marvin.site?.title);
|
|
231
|
+
|
|
232
|
+
// List all entries
|
|
233
|
+
const entries = await marvin.getEntries();
|
|
234
|
+
console.log(`\nFound ${entries.length} entries:`);
|
|
235
|
+
|
|
236
|
+
for (const entry of entries) {
|
|
237
|
+
console.log(`- ${entry.title} (${entry.status})`);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// List collections
|
|
241
|
+
const collections = await marvin.getCollections();
|
|
242
|
+
console.log(`\nFound ${collections.length} collections:`);
|
|
243
|
+
|
|
244
|
+
for (const collection of collections) {
|
|
245
|
+
console.log(`- ${collection.name}`);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
main().catch(console.error);
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
---
|
|
253
|
+
|
|
254
|
+
## Rendering Markdown Content
|
|
255
|
+
|
|
256
|
+
**Important:** `entry.contentMarkdown` is raw Markdown, not HTML. You must parse it before rendering.
|
|
257
|
+
|
|
258
|
+
### Install Markdown Parser
|
|
259
|
+
|
|
260
|
+
```bash
|
|
261
|
+
npm install marked
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
### Parse Before Rendering
|
|
265
|
+
|
|
266
|
+
```ts
|
|
267
|
+
import { marked } from 'marked';
|
|
268
|
+
|
|
269
|
+
const entry = await marvin.entry('about');
|
|
270
|
+
|
|
271
|
+
// Parse Markdown to HTML
|
|
272
|
+
const contentHtml = marked.parse(entry.contentMarkdown ?? '');
|
|
273
|
+
|
|
274
|
+
// Then render in your template
|
|
275
|
+
// Astro: <div set:html={contentHtml} />
|
|
276
|
+
// React: <div dangerouslySetInnerHTML={{ __html: contentHtml }} />
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
---
|
|
280
|
+
|
|
281
|
+
## Build Your Application
|
|
282
|
+
|
|
283
|
+
```bash
|
|
284
|
+
npm run build
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
Your application will fetch all content from Marvin at build time!
|
|
288
|
+
|
|
289
|
+
---
|
|
290
|
+
|
|
291
|
+
## API Styles
|
|
292
|
+
|
|
293
|
+
The SDK supports three API styles:
|
|
294
|
+
|
|
295
|
+
### 1. Workspace API (Recommended)
|
|
296
|
+
|
|
297
|
+
Work with workspace as a first-class object:
|
|
298
|
+
|
|
299
|
+
```ts
|
|
300
|
+
const workspace = await marvin.getWorkspace();
|
|
301
|
+
const entries = await workspace.entries.list();
|
|
302
|
+
const collection = await workspace.collections.get('featured');
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
### 2. Convenience API
|
|
306
|
+
|
|
307
|
+
Quick access to common patterns:
|
|
308
|
+
|
|
309
|
+
```ts
|
|
310
|
+
const entry = await marvin.entry('about');
|
|
311
|
+
const projects = await marvin.projects();
|
|
312
|
+
const featured = await marvin.collection('featured');
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
### 3. Backwards-Compatible API
|
|
316
|
+
|
|
317
|
+
Original publishing client methods still work:
|
|
318
|
+
|
|
319
|
+
```ts
|
|
320
|
+
const site = await marvin.getSite();
|
|
321
|
+
const entries = await marvin.getEntries();
|
|
322
|
+
const entry = await marvin.getEntry('about');
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
---
|
|
326
|
+
|
|
327
|
+
## Security Best Practices
|
|
328
|
+
|
|
329
|
+
### ✅ DO
|
|
330
|
+
|
|
331
|
+
- Use site client tokens in **server-side** code only
|
|
332
|
+
- Use tokens at **build time** in static site generators
|
|
333
|
+
- Use tokens in **trusted backend** services
|
|
334
|
+
- Store tokens in `.env` files (never commit to git)
|
|
335
|
+
- Add `.env` to `.gitignore`
|
|
336
|
+
|
|
337
|
+
### ❌ DON'T
|
|
338
|
+
|
|
339
|
+
- Expose tokens in browser-side JavaScript
|
|
340
|
+
- Commit tokens to version control
|
|
341
|
+
- Use tokens in client-side bundles
|
|
342
|
+
- Share tokens publicly
|
|
343
|
+
- Use user tokens instead of site client tokens
|
|
344
|
+
|
|
345
|
+
---
|
|
346
|
+
|
|
347
|
+
## Next Steps
|
|
348
|
+
|
|
349
|
+
- 📖 Read the [full README](./README.md) for all API methods
|
|
350
|
+
- 💡 Check out [examples](./examples.md) for real-world usage patterns
|
|
351
|
+
- 🎯 Explore object-oriented APIs (Entry, Collection objects)
|
|
352
|
+
- 🚀 Build with your framework of choice
|
|
353
|
+
- 🔧 Use convenience methods for common content types
|
|
354
|
+
|
|
355
|
+
---
|
|
356
|
+
|
|
357
|
+
## Troubleshooting
|
|
358
|
+
|
|
359
|
+
### Cannot find module '@/lib/marvin'
|
|
360
|
+
|
|
361
|
+
Add path alias to your `tsconfig.json`:
|
|
362
|
+
|
|
363
|
+
```json
|
|
364
|
+
{
|
|
365
|
+
"compilerOptions": {
|
|
366
|
+
"baseUrl": ".",
|
|
367
|
+
"paths": {
|
|
368
|
+
"@/*": ["src/*"]
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
### "MARVIN_API_URL is required"
|
|
375
|
+
|
|
376
|
+
Make sure:
|
|
377
|
+
1. `.env` file exists in project root
|
|
378
|
+
2. All three variables are set
|
|
379
|
+
3. Restart your dev server after adding `.env`
|
|
380
|
+
|
|
381
|
+
### 401 Unauthorized
|
|
382
|
+
|
|
383
|
+
- Verify your site client token is correct
|
|
384
|
+
- Check that the token hasn't expired
|
|
385
|
+
- Make sure you're using a **site client token**, not a user token
|
|
386
|
+
- Ensure the token is only used server-side
|
|
387
|
+
|
|
388
|
+
### TypeScript Errors
|
|
389
|
+
|
|
390
|
+
Make sure your `tsconfig.json` includes:
|
|
391
|
+
|
|
392
|
+
```json
|
|
393
|
+
{
|
|
394
|
+
"compilerOptions": {
|
|
395
|
+
"moduleResolution": "bundler",
|
|
396
|
+
"types": ["node"]
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
---
|
|
402
|
+
|
|
403
|
+
## Need Help?
|
|
404
|
+
|
|
405
|
+
- 📖 Read the [full documentation](./README.md)
|
|
406
|
+
- 💡 Check the [examples](./examples.md)
|
|
407
|
+
- 🐛 Report issues on GitHub
|
|
408
|
+
- 🚀 Check TODO comments for upcoming features
|