@usegraft/sdk-next 0.1.0 → 0.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/README.md +59 -0
- package/dist/index.d.ts +22 -1
- package/dist/index.js +7 -1
- package/package.json +23 -7
package/README.md
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# @usegraft/sdk-next
|
|
2
|
+
|
|
3
|
+
> Next.js App Router adapter: typed content reads in Server Components, cache-tag invalidation, and real MDX rendering.
|
|
4
|
+
|
|
5
|
+
Part of [Graft](https://github.com/AndersonDesign1/graft), a CMS built so an AI agent is the primary operator.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm i @usegraft/sdk-next
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Read content
|
|
14
|
+
|
|
15
|
+
`createGraft` wraps the read client with `React.cache`, so repeated reads of the same document within one render are deduped. Server-only: it holds a database handle.
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import { createGraft } from "@usegraft/sdk-next";
|
|
19
|
+
import { collections } from "@/graft.config";
|
|
20
|
+
|
|
21
|
+
export const graft = createGraft({ db, collections });
|
|
22
|
+
|
|
23
|
+
// in a Server Component
|
|
24
|
+
const page = await graft.getContent("pages", "home");
|
|
25
|
+
const posts = await graft.listContent("posts", { limit: 10 });
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Return types come from your `defineCollection` schemas, so a renamed field is a build error rather than a runtime `undefined`.
|
|
29
|
+
|
|
30
|
+
## Render MDX bodies
|
|
31
|
+
|
|
32
|
+
```tsx
|
|
33
|
+
import { MdxBody } from "@usegraft/sdk-next";
|
|
34
|
+
import { mdxComponents } from "@/components/mdx-components";
|
|
35
|
+
|
|
36
|
+
<MdxBody source={page.body} components={mdxComponents} />;
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
`MdxBody` defaults to `trust: "restricted"`, which refuses `{…}` expressions, `import`, `export` and spread attributes. Rendering evaluates MDX as JavaScript on the server, so on a Studio hosted for writers, "can author a page" would otherwise mean "can execute code on the render host".
|
|
40
|
+
|
|
41
|
+
Pass `trust="full"` only when every author of the repository has commit access, and set `export const mdxTrust = "full"` in `graft.config.ts` to match. The two have to agree.
|
|
42
|
+
|
|
43
|
+
## Invalidate only what changed
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
import { revalidateContent, updateContent } from "@usegraft/sdk-next";
|
|
47
|
+
|
|
48
|
+
// in a route handler, after a compile webhook
|
|
49
|
+
revalidateContent(branch, changes);
|
|
50
|
+
|
|
51
|
+
// in a Server Action, for read-your-own-writes
|
|
52
|
+
updateContent(branch, changes);
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Both turn a compile's `ChangeSet` into the exact `revalidateTag` / `updateTag` calls that refresh the changed pages, and no others. A no-op unless your reads were cached with `'use cache'` and `cacheTag`, but always safe to call.
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
MIT. [Repository](https://github.com/AndersonDesign1/graft) · [Changelog](https://github.com/AndersonDesign1/graft/blob/feat/core/packages/sdk-next/CHANGELOG.md)
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { AnyCollection, ReadOptions, Document, ListOptions, SearchOptions, SearchHit, GraftClient, ClientOptions, ChangeSet } from '@usegraft/sdk-core';
|
|
2
2
|
export { AnyCollection, ChangeSet, ClientOptions, Document, GraftClient, ListOptions, ReadOptions, SearchHit, SearchOptions, TAG_NAMESPACE, collectionTag, createClient, documentTag, tagsFor, tagsForChanges, toDocument } from '@usegraft/sdk-core';
|
|
3
|
+
import { MdxTrust } from '@usegraft/mdx-safety';
|
|
4
|
+
export { MdxTrust } from '@usegraft/mdx-safety';
|
|
3
5
|
import { MDXComponents } from 'mdx/types';
|
|
4
6
|
import { ReactElement } from 'react';
|
|
5
7
|
|
|
@@ -40,16 +42,35 @@ declare function revalidateContent(branch: string, changes: ChangeSet, profile?:
|
|
|
40
42
|
declare function updateContent(branch: string, changes: ChangeSet): string[];
|
|
41
43
|
|
|
42
44
|
type MdxComponents = MDXComponents;
|
|
45
|
+
|
|
43
46
|
interface MdxBodyProps {
|
|
44
47
|
/** Raw MDX body (the string stored on the document / content_index row). */
|
|
45
48
|
source: string;
|
|
46
49
|
/** Component map — typically the generated mdx-components export. */
|
|
47
50
|
components?: MdxComponents;
|
|
51
|
+
/**
|
|
52
|
+
* How much of MDX this body is allowed to be. Defaults to `"restricted"`.
|
|
53
|
+
*
|
|
54
|
+
* `run()` evaluates the compiled body with `new Function` in this process, so
|
|
55
|
+
* `{expr}` and `import` are arbitrary server-side JavaScript. That is fine
|
|
56
|
+
* for content the operator wrote and reviewed in git, and not fine for
|
|
57
|
+
* content written through the API — where "can author a page" would
|
|
58
|
+
* otherwise mean "can execute code on the render host".
|
|
59
|
+
*
|
|
60
|
+
* `"restricted"` refuses executable constructs before compiling. Pass
|
|
61
|
+
* `"full"` only for bodies you know came from your own repository.
|
|
62
|
+
*
|
|
63
|
+
* `graft compile` applies the same rule to the authored tree, from
|
|
64
|
+
* `mdxTrust` in graft.config.ts. The two have to agree: a project that sets
|
|
65
|
+
* `mdxTrust = "full"` and leaves this at the default indexes bodies that then
|
|
66
|
+
* fail to render.
|
|
67
|
+
*/
|
|
68
|
+
trust?: MdxTrust;
|
|
48
69
|
}
|
|
49
70
|
/**
|
|
50
71
|
* Compile + run an MDX source string and render it with the given components.
|
|
51
72
|
* Empty source renders nothing (valid for body-less docs).
|
|
52
73
|
*/
|
|
53
|
-
declare function MdxBody({ source, components }: MdxBodyProps): Promise<ReactElement | null>;
|
|
74
|
+
declare function MdxBody({ source, components, trust, }: MdxBodyProps): Promise<ReactElement | null>;
|
|
54
75
|
|
|
55
76
|
export { type Graft, MdxBody, type MdxBodyProps, type MdxComponents, type RevalidateProfile, createGraft, revalidateContent, updateContent };
|
package/dist/index.js
CHANGED
|
@@ -40,12 +40,18 @@ function updateContent(branch, changes) {
|
|
|
40
40
|
|
|
41
41
|
// src/mdx.tsx
|
|
42
42
|
import { compile, run } from "@mdx-js/mdx";
|
|
43
|
+
import { assertSafeMdx } from "@usegraft/mdx-safety";
|
|
43
44
|
import * as runtime from "react/jsx-runtime";
|
|
44
45
|
import remarkGfm from "remark-gfm";
|
|
45
46
|
import { jsx } from "react/jsx-runtime";
|
|
46
|
-
async function MdxBody({
|
|
47
|
+
async function MdxBody({
|
|
48
|
+
source,
|
|
49
|
+
components,
|
|
50
|
+
trust = "restricted"
|
|
51
|
+
}) {
|
|
47
52
|
const trimmed = source.trim();
|
|
48
53
|
if (trimmed.length === 0) return null;
|
|
54
|
+
if (trust !== "full") assertSafeMdx(trimmed);
|
|
49
55
|
const compiled = String(
|
|
50
56
|
await compile(trimmed, {
|
|
51
57
|
outputFormat: "function-body",
|
package/package.json
CHANGED
|
@@ -1,6 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@usegraft/sdk-next",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Next.js App Router adapter: typed content reads in Server Components, cache-tag invalidation, and real MDX rendering.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"agent",
|
|
7
|
+
"ai",
|
|
8
|
+
"app-router",
|
|
9
|
+
"cms",
|
|
10
|
+
"graft",
|
|
11
|
+
"headless-cms",
|
|
12
|
+
"mcp",
|
|
13
|
+
"mdx",
|
|
14
|
+
"nextjs",
|
|
15
|
+
"react",
|
|
16
|
+
"rsc",
|
|
17
|
+
"typescript"
|
|
18
|
+
],
|
|
19
|
+
"homepage": "https://github.com/AndersonDesign1/graft#readme",
|
|
4
20
|
"license": "MIT",
|
|
5
21
|
"repository": {
|
|
6
22
|
"type": "git",
|
|
@@ -30,15 +46,16 @@
|
|
|
30
46
|
},
|
|
31
47
|
"dependencies": {
|
|
32
48
|
"@mdx-js/mdx": "^3.1.1",
|
|
33
|
-
"
|
|
34
|
-
"@usegraft/sdk-core": "0.
|
|
49
|
+
"@usegraft/mdx-safety": "0.2.0",
|
|
50
|
+
"@usegraft/sdk-core": "0.2.0",
|
|
51
|
+
"remark-gfm": "^4.0.1"
|
|
35
52
|
},
|
|
36
53
|
"devDependencies": {
|
|
37
54
|
"@types/mdx": "^2.0.14",
|
|
38
55
|
"@types/react": "^19.0.0",
|
|
56
|
+
"@usegraft/core": "0.2.0",
|
|
39
57
|
"next": "^16.0.0",
|
|
40
|
-
"react": "^19.0.0"
|
|
41
|
-
"@usegraft/core": "0.1.0"
|
|
58
|
+
"react": "^19.0.0"
|
|
42
59
|
},
|
|
43
60
|
"peerDependencies": {
|
|
44
61
|
"next": ">=15.0.0",
|
|
@@ -51,7 +68,6 @@
|
|
|
51
68
|
"build": "tsup src/index.ts src/next-config.ts --format esm --dts --clean --external react --external react/jsx-runtime --external next",
|
|
52
69
|
"dev": "tsup src/index.ts src/next-config.ts --format esm --watch --external react --external react/jsx-runtime --external next",
|
|
53
70
|
"typecheck": "tsc --noEmit",
|
|
54
|
-
"test": "vitest run --passWithNoTests"
|
|
55
|
-
"lint": "oxlint ."
|
|
71
|
+
"test": "vitest run --passWithNoTests"
|
|
56
72
|
}
|
|
57
73
|
}
|