chaingrow-blog 0.1.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 +161 -0
- package/dist/chunk-TDHCPX4T.js +52 -0
- package/dist/chunk-TDHCPX4T.js.map +1 -0
- package/dist/index.cjs +78 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +20 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/next/index.cjs +124 -0
- package/dist/next/index.cjs.map +1 -0
- package/dist/next/index.d.cts +14 -0
- package/dist/next/index.d.ts +14 -0
- package/dist/next/index.js +52 -0
- package/dist/next/index.js.map +1 -0
- package/dist/next/webhook.cjs +80 -0
- package/dist/next/webhook.cjs.map +1 -0
- package/dist/next/webhook.d.cts +12 -0
- package/dist/next/webhook.d.ts +12 -0
- package/dist/next/webhook.js +54 -0
- package/dist/next/webhook.js.map +1 -0
- package/dist/react/index.cjs +113 -0
- package/dist/react/index.cjs.map +1 -0
- package/dist/react/index.d.cts +62 -0
- package/dist/react/index.d.ts +62 -0
- package/dist/react/index.js +79 -0
- package/dist/react/index.js.map +1 -0
- package/dist/types-CoPD0ejB.d.cts +53 -0
- package/dist/types-CoPD0ejB.d.ts +53 -0
- package/package.json +63 -0
package/README.md
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
# chaingrow-blog
|
|
2
|
+
|
|
3
|
+
Drop-in Next.js SDK for rendering blogs published from [ChainGrow](https://chaingrow.ai).
|
|
4
|
+
|
|
5
|
+
- **Zero database.** Your site stores nothing — ChainGrow is the source of truth.
|
|
6
|
+
- **Instant publishing.** When you press Publish in ChainGrow, the SDK's webhook handler invalidates the relevant page via `revalidateTag`. Next request serves fresh HTML.
|
|
7
|
+
- **Headless-first.** The core SDK is unstyled semantic HTML. Supply your own components for any block type you want to customize.
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pnpm add chaingrow-blog
|
|
13
|
+
# or
|
|
14
|
+
npm install chaingrow-blog
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Requires Next.js ≥ 14 (App Router) and React ≥ 18.
|
|
18
|
+
|
|
19
|
+
## Setup (3 files)
|
|
20
|
+
|
|
21
|
+
### 1. Environment variables
|
|
22
|
+
|
|
23
|
+
```env
|
|
24
|
+
CHAINGROW_API_URL=https://api.chaingrow.ai
|
|
25
|
+
CHAINGROW_API_KEY=ck_live_xxx
|
|
26
|
+
CHAINGROW_WEBHOOK_SECRET=whsec_xxx
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Generate the API key + webhook secret in your ChainGrow dashboard under **Settings → API Keys**. Set the webhook URL on the key to `https://your-site.com/api/chaingrow/webhook`.
|
|
30
|
+
|
|
31
|
+
### 2. Webhook receiver
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
// app/api/chaingrow/webhook/route.ts
|
|
35
|
+
export { POST } from 'chaingrow-blog/next/webhook';
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### 3. Blog page
|
|
39
|
+
|
|
40
|
+
**Option A — full design control:**
|
|
41
|
+
|
|
42
|
+
```tsx
|
|
43
|
+
// app/blog/[slug]/page.tsx
|
|
44
|
+
import { notFound } from 'next/navigation';
|
|
45
|
+
import { getBlog } from 'chaingrow-blog/next';
|
|
46
|
+
|
|
47
|
+
export default async function BlogPage({
|
|
48
|
+
params,
|
|
49
|
+
}: {
|
|
50
|
+
params: { slug: string };
|
|
51
|
+
}) {
|
|
52
|
+
const blog = await getBlog(params.slug);
|
|
53
|
+
if (!blog) notFound();
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
<article>
|
|
57
|
+
<h1>{blog.title}</h1>
|
|
58
|
+
{blog.blocks.map((b, i) => (
|
|
59
|
+
/* render however you want */
|
|
60
|
+
<YourCustomBlock key={i} block={b} />
|
|
61
|
+
))}
|
|
62
|
+
</article>
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
**Option B — default renderer with per-block overrides:**
|
|
68
|
+
|
|
69
|
+
```tsx
|
|
70
|
+
import { notFound } from 'next/navigation';
|
|
71
|
+
import { getBlog } from 'chaingrow-blog/next';
|
|
72
|
+
import { BlogRenderer } from 'chaingrow-blog/react';
|
|
73
|
+
|
|
74
|
+
export default async function BlogPage({
|
|
75
|
+
params,
|
|
76
|
+
}: {
|
|
77
|
+
params: { slug: string };
|
|
78
|
+
}) {
|
|
79
|
+
const blog = await getBlog(params.slug);
|
|
80
|
+
if (!blog) notFound();
|
|
81
|
+
|
|
82
|
+
return (
|
|
83
|
+
<article className="prose">
|
|
84
|
+
<h1>{blog.title}</h1>
|
|
85
|
+
<BlogRenderer
|
|
86
|
+
blog={blog}
|
|
87
|
+
components={{
|
|
88
|
+
heading: ({ level, children }) =>
|
|
89
|
+
level === 2 ? (
|
|
90
|
+
<h2 className="text-3xl font-bold">{children}</h2>
|
|
91
|
+
) : (
|
|
92
|
+
<h3 className="text-2xl font-semibold">{children}</h3>
|
|
93
|
+
),
|
|
94
|
+
quote: ({ children }) => (
|
|
95
|
+
<blockquote className="border-l-4 border-blue-500 pl-4 italic">
|
|
96
|
+
{children}
|
|
97
|
+
</blockquote>
|
|
98
|
+
),
|
|
99
|
+
}}
|
|
100
|
+
/>
|
|
101
|
+
</article>
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## API
|
|
107
|
+
|
|
108
|
+
### `getBlog(slug, language?, opts?)`
|
|
109
|
+
|
|
110
|
+
Fetches a blog by slug (server-side, ISR-cached under tag `chaingrow:blog:<slug>`). Returns `null` if not found.
|
|
111
|
+
|
|
112
|
+
### `listBlogs(opts?, clientOpts?)`
|
|
113
|
+
|
|
114
|
+
Lists blogs (optionally filtered by `status` or `language`).
|
|
115
|
+
|
|
116
|
+
### `createClient({ apiUrl, apiKey })`
|
|
117
|
+
|
|
118
|
+
Framework-agnostic client. Use from edge functions, scripts, or non-Next.js environments.
|
|
119
|
+
|
|
120
|
+
### `<BlogRenderer blog={blog} components={...} />`
|
|
121
|
+
|
|
122
|
+
Renders all `blog.blocks` using unstyled default components. Every block type is overridable via the `components` prop — supply only the ones you want to customize.
|
|
123
|
+
|
|
124
|
+
Block types: `heading`, `paragraph`, `list`, `quote`, `code`, `image_placeholder`.
|
|
125
|
+
|
|
126
|
+
### `createWebhookHandler({ secret?, onEvent? })`
|
|
127
|
+
|
|
128
|
+
Returns a `{ POST }` route handler. Verifies the `X-ChainGrow-Signature` HMAC and calls `revalidateTag`. Pass `onEvent` to run custom logic after revalidation (e.g. cache warming, analytics).
|
|
129
|
+
|
|
130
|
+
## How publishing works
|
|
131
|
+
|
|
132
|
+
```
|
|
133
|
+
ChainGrow dashboard Your Next.js site
|
|
134
|
+
─────────────────── ─────────────────
|
|
135
|
+
Press Publish
|
|
136
|
+
│
|
|
137
|
+
▼
|
|
138
|
+
blog.status = PUBLISHED
|
|
139
|
+
│
|
|
140
|
+
▼
|
|
141
|
+
HMAC-signed webhook ──────────────► /api/chaingrow/webhook
|
|
142
|
+
│
|
|
143
|
+
▼
|
|
144
|
+
verify signature
|
|
145
|
+
│
|
|
146
|
+
▼
|
|
147
|
+
revalidateTag('chaingrow:blog:<slug>')
|
|
148
|
+
│
|
|
149
|
+
▼
|
|
150
|
+
Next user request /blog/<slug> cache miss
|
|
151
|
+
│ │
|
|
152
|
+
▼ ▼
|
|
153
|
+
Fresh HTML getBlog() → GET /api/v1/blogs/by-slug/<slug>
|
|
154
|
+
│
|
|
155
|
+
▼
|
|
156
|
+
cached for next visitors
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## License
|
|
160
|
+
|
|
161
|
+
MIT
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// src/client.ts
|
|
2
|
+
function createClient(opts) {
|
|
3
|
+
const baseUrl = opts.apiUrl.replace(/\/$/, "");
|
|
4
|
+
const doFetch = opts.fetch ?? fetch;
|
|
5
|
+
const request = async (path, init) => {
|
|
6
|
+
const res = await doFetch(`${baseUrl}${path}`, {
|
|
7
|
+
...init,
|
|
8
|
+
headers: {
|
|
9
|
+
"X-API-Key": opts.apiKey,
|
|
10
|
+
Accept: "application/json",
|
|
11
|
+
...init?.headers ?? {}
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
if (res.status === 404) return null;
|
|
15
|
+
if (!res.ok) {
|
|
16
|
+
throw new Error(
|
|
17
|
+
`chaingrow-blog: ${init?.method ?? "GET"} ${path} failed (${res.status})`
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
return await res.json();
|
|
21
|
+
};
|
|
22
|
+
return {
|
|
23
|
+
async getBlog(slug, language) {
|
|
24
|
+
const qs = language ? `?language=${encodeURIComponent(language)}` : "";
|
|
25
|
+
return request(
|
|
26
|
+
`/api/v1/blogs/by-slug/${encodeURIComponent(slug)}${qs}`
|
|
27
|
+
);
|
|
28
|
+
},
|
|
29
|
+
async getBlogById(id) {
|
|
30
|
+
return request(`/api/v1/blogs/${encodeURIComponent(id)}`);
|
|
31
|
+
},
|
|
32
|
+
async listBlogs(listOpts = {}) {
|
|
33
|
+
const params = new URLSearchParams();
|
|
34
|
+
if (listOpts.status) params.set("status", listOpts.status);
|
|
35
|
+
const qs = params.toString();
|
|
36
|
+
const response = await request(
|
|
37
|
+
`/api/v1/blogs${qs ? `?${qs}` : ""}`
|
|
38
|
+
);
|
|
39
|
+
if (!response) return [];
|
|
40
|
+
const blogs = response.data ?? [];
|
|
41
|
+
if (listOpts.language) {
|
|
42
|
+
return blogs.filter((b) => b.language === listOpts.language);
|
|
43
|
+
}
|
|
44
|
+
return blogs;
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export {
|
|
50
|
+
createClient
|
|
51
|
+
};
|
|
52
|
+
//# sourceMappingURL=chunk-TDHCPX4T.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/client.ts"],"sourcesContent":["import type { Blog } from './types';\n\nexport type ClientOptions = {\n apiUrl: string;\n apiKey: string;\n fetch?: typeof fetch;\n};\n\nexport type ListBlogsOptions = {\n status?: 'DRAFT' | 'PUBLISHED';\n language?: string;\n};\n\nexport type ChaingrowClient = {\n getBlog(slug: string, language?: string): Promise<Blog | null>;\n getBlogById(id: string): Promise<Blog | null>;\n listBlogs(opts?: ListBlogsOptions): Promise<Blog[]>;\n};\n\nexport function createClient(opts: ClientOptions): ChaingrowClient {\n const baseUrl = opts.apiUrl.replace(/\\/$/, '');\n const doFetch = opts.fetch ?? fetch;\n\n const request = async <T>(\n path: string,\n init?: RequestInit,\n ): Promise<T | null> => {\n const res = await doFetch(`${baseUrl}${path}`, {\n ...init,\n headers: {\n 'X-API-Key': opts.apiKey,\n Accept: 'application/json',\n ...(init?.headers ?? {}),\n },\n });\n if (res.status === 404) return null;\n if (!res.ok) {\n throw new Error(\n `chaingrow-blog: ${init?.method ?? 'GET'} ${path} failed (${res.status})`,\n );\n }\n return (await res.json()) as T;\n };\n\n return {\n async getBlog(slug, language) {\n const qs = language ? `?language=${encodeURIComponent(language)}` : '';\n return request<Blog>(\n `/api/v1/blogs/by-slug/${encodeURIComponent(slug)}${qs}`,\n );\n },\n\n async getBlogById(id) {\n return request<Blog>(`/api/v1/blogs/${encodeURIComponent(id)}`);\n },\n\n async listBlogs(listOpts = {}) {\n const params = new URLSearchParams();\n if (listOpts.status) params.set('status', listOpts.status);\n const qs = params.toString();\n const response = await request<{ data: Blog[]; total: number }>(\n `/api/v1/blogs${qs ? `?${qs}` : ''}`,\n );\n if (!response) return [];\n const blogs = response.data ?? [];\n if (listOpts.language) {\n return blogs.filter((b) => b.language === listOpts.language);\n }\n return blogs;\n },\n };\n}\n"],"mappings":";AAmBO,SAAS,aAAa,MAAsC;AACjE,QAAM,UAAU,KAAK,OAAO,QAAQ,OAAO,EAAE;AAC7C,QAAM,UAAU,KAAK,SAAS;AAE9B,QAAM,UAAU,OACd,MACA,SACsB;AACtB,UAAM,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,IAAI,IAAI;AAAA,MAC7C,GAAG;AAAA,MACH,SAAS;AAAA,QACP,aAAa,KAAK;AAAA,QAClB,QAAQ;AAAA,QACR,GAAI,MAAM,WAAW,CAAC;AAAA,MACxB;AAAA,IACF,CAAC;AACD,QAAI,IAAI,WAAW,IAAK,QAAO;AAC/B,QAAI,CAAC,IAAI,IAAI;AACX,YAAM,IAAI;AAAA,QACR,mBAAmB,MAAM,UAAU,KAAK,IAAI,IAAI,YAAY,IAAI,MAAM;AAAA,MACxE;AAAA,IACF;AACA,WAAQ,MAAM,IAAI,KAAK;AAAA,EACzB;AAEA,SAAO;AAAA,IACL,MAAM,QAAQ,MAAM,UAAU;AAC5B,YAAM,KAAK,WAAW,aAAa,mBAAmB,QAAQ,CAAC,KAAK;AACpE,aAAO;AAAA,QACL,yBAAyB,mBAAmB,IAAI,CAAC,GAAG,EAAE;AAAA,MACxD;AAAA,IACF;AAAA,IAEA,MAAM,YAAY,IAAI;AACpB,aAAO,QAAc,iBAAiB,mBAAmB,EAAE,CAAC,EAAE;AAAA,IAChE;AAAA,IAEA,MAAM,UAAU,WAAW,CAAC,GAAG;AAC7B,YAAM,SAAS,IAAI,gBAAgB;AACnC,UAAI,SAAS,OAAQ,QAAO,IAAI,UAAU,SAAS,MAAM;AACzD,YAAM,KAAK,OAAO,SAAS;AAC3B,YAAM,WAAW,MAAM;AAAA,QACrB,gBAAgB,KAAK,IAAI,EAAE,KAAK,EAAE;AAAA,MACpC;AACA,UAAI,CAAC,SAAU,QAAO,CAAC;AACvB,YAAM,QAAQ,SAAS,QAAQ,CAAC;AAChC,UAAI,SAAS,UAAU;AACrB,eAAO,MAAM,OAAO,CAAC,MAAM,EAAE,aAAa,SAAS,QAAQ;AAAA,MAC7D;AACA,aAAO;AAAA,IACT;AAAA,EACF;AACF;","names":[]}
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var src_exports = {};
|
|
22
|
+
__export(src_exports, {
|
|
23
|
+
createClient: () => createClient
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(src_exports);
|
|
26
|
+
|
|
27
|
+
// src/client.ts
|
|
28
|
+
function createClient(opts) {
|
|
29
|
+
const baseUrl = opts.apiUrl.replace(/\/$/, "");
|
|
30
|
+
const doFetch = opts.fetch ?? fetch;
|
|
31
|
+
const request = async (path, init) => {
|
|
32
|
+
const res = await doFetch(`${baseUrl}${path}`, {
|
|
33
|
+
...init,
|
|
34
|
+
headers: {
|
|
35
|
+
"X-API-Key": opts.apiKey,
|
|
36
|
+
Accept: "application/json",
|
|
37
|
+
...init?.headers ?? {}
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
if (res.status === 404) return null;
|
|
41
|
+
if (!res.ok) {
|
|
42
|
+
throw new Error(
|
|
43
|
+
`chaingrow-blog: ${init?.method ?? "GET"} ${path} failed (${res.status})`
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
return await res.json();
|
|
47
|
+
};
|
|
48
|
+
return {
|
|
49
|
+
async getBlog(slug, language) {
|
|
50
|
+
const qs = language ? `?language=${encodeURIComponent(language)}` : "";
|
|
51
|
+
return request(
|
|
52
|
+
`/api/v1/blogs/by-slug/${encodeURIComponent(slug)}${qs}`
|
|
53
|
+
);
|
|
54
|
+
},
|
|
55
|
+
async getBlogById(id) {
|
|
56
|
+
return request(`/api/v1/blogs/${encodeURIComponent(id)}`);
|
|
57
|
+
},
|
|
58
|
+
async listBlogs(listOpts = {}) {
|
|
59
|
+
const params = new URLSearchParams();
|
|
60
|
+
if (listOpts.status) params.set("status", listOpts.status);
|
|
61
|
+
const qs = params.toString();
|
|
62
|
+
const response = await request(
|
|
63
|
+
`/api/v1/blogs${qs ? `?${qs}` : ""}`
|
|
64
|
+
);
|
|
65
|
+
if (!response) return [];
|
|
66
|
+
const blogs = response.data ?? [];
|
|
67
|
+
if (listOpts.language) {
|
|
68
|
+
return blogs.filter((b) => b.language === listOpts.language);
|
|
69
|
+
}
|
|
70
|
+
return blogs;
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
75
|
+
0 && (module.exports = {
|
|
76
|
+
createClient
|
|
77
|
+
});
|
|
78
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/client.ts"],"sourcesContent":["export * from './types';\nexport { createClient, type ChaingrowClient, type ClientOptions } from './client';\n","import type { Blog } from './types';\n\nexport type ClientOptions = {\n apiUrl: string;\n apiKey: string;\n fetch?: typeof fetch;\n};\n\nexport type ListBlogsOptions = {\n status?: 'DRAFT' | 'PUBLISHED';\n language?: string;\n};\n\nexport type ChaingrowClient = {\n getBlog(slug: string, language?: string): Promise<Blog | null>;\n getBlogById(id: string): Promise<Blog | null>;\n listBlogs(opts?: ListBlogsOptions): Promise<Blog[]>;\n};\n\nexport function createClient(opts: ClientOptions): ChaingrowClient {\n const baseUrl = opts.apiUrl.replace(/\\/$/, '');\n const doFetch = opts.fetch ?? fetch;\n\n const request = async <T>(\n path: string,\n init?: RequestInit,\n ): Promise<T | null> => {\n const res = await doFetch(`${baseUrl}${path}`, {\n ...init,\n headers: {\n 'X-API-Key': opts.apiKey,\n Accept: 'application/json',\n ...(init?.headers ?? {}),\n },\n });\n if (res.status === 404) return null;\n if (!res.ok) {\n throw new Error(\n `chaingrow-blog: ${init?.method ?? 'GET'} ${path} failed (${res.status})`,\n );\n }\n return (await res.json()) as T;\n };\n\n return {\n async getBlog(slug, language) {\n const qs = language ? `?language=${encodeURIComponent(language)}` : '';\n return request<Blog>(\n `/api/v1/blogs/by-slug/${encodeURIComponent(slug)}${qs}`,\n );\n },\n\n async getBlogById(id) {\n return request<Blog>(`/api/v1/blogs/${encodeURIComponent(id)}`);\n },\n\n async listBlogs(listOpts = {}) {\n const params = new URLSearchParams();\n if (listOpts.status) params.set('status', listOpts.status);\n const qs = params.toString();\n const response = await request<{ data: Blog[]; total: number }>(\n `/api/v1/blogs${qs ? `?${qs}` : ''}`,\n );\n if (!response) return [];\n const blogs = response.data ?? [];\n if (listOpts.language) {\n return blogs.filter((b) => b.language === listOpts.language);\n }\n return blogs;\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACmBO,SAAS,aAAa,MAAsC;AACjE,QAAM,UAAU,KAAK,OAAO,QAAQ,OAAO,EAAE;AAC7C,QAAM,UAAU,KAAK,SAAS;AAE9B,QAAM,UAAU,OACd,MACA,SACsB;AACtB,UAAM,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,IAAI,IAAI;AAAA,MAC7C,GAAG;AAAA,MACH,SAAS;AAAA,QACP,aAAa,KAAK;AAAA,QAClB,QAAQ;AAAA,QACR,GAAI,MAAM,WAAW,CAAC;AAAA,MACxB;AAAA,IACF,CAAC;AACD,QAAI,IAAI,WAAW,IAAK,QAAO;AAC/B,QAAI,CAAC,IAAI,IAAI;AACX,YAAM,IAAI;AAAA,QACR,mBAAmB,MAAM,UAAU,KAAK,IAAI,IAAI,YAAY,IAAI,MAAM;AAAA,MACxE;AAAA,IACF;AACA,WAAQ,MAAM,IAAI,KAAK;AAAA,EACzB;AAEA,SAAO;AAAA,IACL,MAAM,QAAQ,MAAM,UAAU;AAC5B,YAAM,KAAK,WAAW,aAAa,mBAAmB,QAAQ,CAAC,KAAK;AACpE,aAAO;AAAA,QACL,yBAAyB,mBAAmB,IAAI,CAAC,GAAG,EAAE;AAAA,MACxD;AAAA,IACF;AAAA,IAEA,MAAM,YAAY,IAAI;AACpB,aAAO,QAAc,iBAAiB,mBAAmB,EAAE,CAAC,EAAE;AAAA,IAChE;AAAA,IAEA,MAAM,UAAU,WAAW,CAAC,GAAG;AAC7B,YAAM,SAAS,IAAI,gBAAgB;AACnC,UAAI,SAAS,OAAQ,QAAO,IAAI,UAAU,SAAS,MAAM;AACzD,YAAM,KAAK,OAAO,SAAS;AAC3B,YAAM,WAAW,MAAM;AAAA,QACrB,gBAAgB,KAAK,IAAI,EAAE,KAAK,EAAE;AAAA,MACpC;AACA,UAAI,CAAC,SAAU,QAAO,CAAC;AACvB,YAAM,QAAQ,SAAS,QAAQ,CAAC;AAChC,UAAI,SAAS,UAAU;AACrB,eAAO,MAAM,OAAO,CAAC,MAAM,EAAE,aAAa,SAAS,QAAQ;AAAA,MAC7D;AACA,aAAO;AAAA,IACT;AAAA,EACF;AACF;","names":[]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { B as Blog } from './types-CoPD0ejB.cjs';
|
|
2
|
+
export { a as Block, F as FaqItem, S as SocialPost, W as WebhookEvent, b as WebhookPayload } from './types-CoPD0ejB.cjs';
|
|
3
|
+
|
|
4
|
+
type ClientOptions = {
|
|
5
|
+
apiUrl: string;
|
|
6
|
+
apiKey: string;
|
|
7
|
+
fetch?: typeof fetch;
|
|
8
|
+
};
|
|
9
|
+
type ListBlogsOptions = {
|
|
10
|
+
status?: 'DRAFT' | 'PUBLISHED';
|
|
11
|
+
language?: string;
|
|
12
|
+
};
|
|
13
|
+
type ChaingrowClient = {
|
|
14
|
+
getBlog(slug: string, language?: string): Promise<Blog | null>;
|
|
15
|
+
getBlogById(id: string): Promise<Blog | null>;
|
|
16
|
+
listBlogs(opts?: ListBlogsOptions): Promise<Blog[]>;
|
|
17
|
+
};
|
|
18
|
+
declare function createClient(opts: ClientOptions): ChaingrowClient;
|
|
19
|
+
|
|
20
|
+
export { Blog, type ChaingrowClient, type ClientOptions, createClient };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { B as Blog } from './types-CoPD0ejB.js';
|
|
2
|
+
export { a as Block, F as FaqItem, S as SocialPost, W as WebhookEvent, b as WebhookPayload } from './types-CoPD0ejB.js';
|
|
3
|
+
|
|
4
|
+
type ClientOptions = {
|
|
5
|
+
apiUrl: string;
|
|
6
|
+
apiKey: string;
|
|
7
|
+
fetch?: typeof fetch;
|
|
8
|
+
};
|
|
9
|
+
type ListBlogsOptions = {
|
|
10
|
+
status?: 'DRAFT' | 'PUBLISHED';
|
|
11
|
+
language?: string;
|
|
12
|
+
};
|
|
13
|
+
type ChaingrowClient = {
|
|
14
|
+
getBlog(slug: string, language?: string): Promise<Blog | null>;
|
|
15
|
+
getBlogById(id: string): Promise<Blog | null>;
|
|
16
|
+
listBlogs(opts?: ListBlogsOptions): Promise<Blog[]>;
|
|
17
|
+
};
|
|
18
|
+
declare function createClient(opts: ClientOptions): ChaingrowClient;
|
|
19
|
+
|
|
20
|
+
export { Blog, type ChaingrowClient, type ClientOptions, createClient };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/next/index.ts
|
|
21
|
+
var next_exports = {};
|
|
22
|
+
__export(next_exports, {
|
|
23
|
+
getBlog: () => getBlog,
|
|
24
|
+
listBlogs: () => listBlogs
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(next_exports);
|
|
27
|
+
|
|
28
|
+
// src/client.ts
|
|
29
|
+
function createClient(opts) {
|
|
30
|
+
const baseUrl = opts.apiUrl.replace(/\/$/, "");
|
|
31
|
+
const doFetch = opts.fetch ?? fetch;
|
|
32
|
+
const request = async (path, init) => {
|
|
33
|
+
const res = await doFetch(`${baseUrl}${path}`, {
|
|
34
|
+
...init,
|
|
35
|
+
headers: {
|
|
36
|
+
"X-API-Key": opts.apiKey,
|
|
37
|
+
Accept: "application/json",
|
|
38
|
+
...init?.headers ?? {}
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
if (res.status === 404) return null;
|
|
42
|
+
if (!res.ok) {
|
|
43
|
+
throw new Error(
|
|
44
|
+
`chaingrow-blog: ${init?.method ?? "GET"} ${path} failed (${res.status})`
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
return await res.json();
|
|
48
|
+
};
|
|
49
|
+
return {
|
|
50
|
+
async getBlog(slug, language) {
|
|
51
|
+
const qs = language ? `?language=${encodeURIComponent(language)}` : "";
|
|
52
|
+
return request(
|
|
53
|
+
`/api/v1/blogs/by-slug/${encodeURIComponent(slug)}${qs}`
|
|
54
|
+
);
|
|
55
|
+
},
|
|
56
|
+
async getBlogById(id) {
|
|
57
|
+
return request(`/api/v1/blogs/${encodeURIComponent(id)}`);
|
|
58
|
+
},
|
|
59
|
+
async listBlogs(listOpts = {}) {
|
|
60
|
+
const params = new URLSearchParams();
|
|
61
|
+
if (listOpts.status) params.set("status", listOpts.status);
|
|
62
|
+
const qs = params.toString();
|
|
63
|
+
const response = await request(
|
|
64
|
+
`/api/v1/blogs${qs ? `?${qs}` : ""}`
|
|
65
|
+
);
|
|
66
|
+
if (!response) return [];
|
|
67
|
+
const blogs = response.data ?? [];
|
|
68
|
+
if (listOpts.language) {
|
|
69
|
+
return blogs.filter((b) => b.language === listOpts.language);
|
|
70
|
+
}
|
|
71
|
+
return blogs;
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// src/next/index.ts
|
|
77
|
+
var resolveEnv = (opts = {}) => {
|
|
78
|
+
const apiUrl = opts.apiUrl ?? process.env.CHAINGROW_API_URL;
|
|
79
|
+
const apiKey = opts.apiKey ?? process.env.CHAINGROW_API_KEY;
|
|
80
|
+
if (!apiUrl) {
|
|
81
|
+
throw new Error(
|
|
82
|
+
"chaingrow-blog: CHAINGROW_API_URL is not set (env var or apiUrl option required)"
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
if (!apiKey) {
|
|
86
|
+
throw new Error(
|
|
87
|
+
"chaingrow-blog: CHAINGROW_API_KEY is not set (env var or apiKey option required)"
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
return { apiUrl, apiKey };
|
|
91
|
+
};
|
|
92
|
+
var buildFetch = (tag, revalidate) => {
|
|
93
|
+
return ((input, init) => fetch(input, {
|
|
94
|
+
...init,
|
|
95
|
+
next: {
|
|
96
|
+
tags: ["chaingrow:blog", tag],
|
|
97
|
+
...revalidate === false ? { revalidate: false } : revalidate !== void 0 ? { revalidate } : {}
|
|
98
|
+
}
|
|
99
|
+
}));
|
|
100
|
+
};
|
|
101
|
+
async function getBlog(slug, language, opts = {}) {
|
|
102
|
+
const { apiUrl, apiKey } = resolveEnv(opts);
|
|
103
|
+
const client = createClient({
|
|
104
|
+
apiUrl,
|
|
105
|
+
apiKey,
|
|
106
|
+
fetch: buildFetch(`chaingrow:blog:${slug}`, opts.revalidate)
|
|
107
|
+
});
|
|
108
|
+
return client.getBlog(slug, language);
|
|
109
|
+
}
|
|
110
|
+
async function listBlogs(listOpts = {}, opts = {}) {
|
|
111
|
+
const { apiUrl, apiKey } = resolveEnv(opts);
|
|
112
|
+
const client = createClient({
|
|
113
|
+
apiUrl,
|
|
114
|
+
apiKey,
|
|
115
|
+
fetch: buildFetch("chaingrow:blog:list", opts.revalidate)
|
|
116
|
+
});
|
|
117
|
+
return client.listBlogs(listOpts);
|
|
118
|
+
}
|
|
119
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
120
|
+
0 && (module.exports = {
|
|
121
|
+
getBlog,
|
|
122
|
+
listBlogs
|
|
123
|
+
});
|
|
124
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/next/index.ts","../../src/client.ts"],"sourcesContent":["import type { Blog } from '../types';\nimport { createClient } from '../client';\n\nexport type NextClientOptions = {\n apiUrl?: string;\n apiKey?: string;\n revalidate?: number | false;\n};\n\nconst resolveEnv = (opts: NextClientOptions = {}) => {\n const apiUrl = opts.apiUrl ?? process.env.CHAINGROW_API_URL;\n const apiKey = opts.apiKey ?? process.env.CHAINGROW_API_KEY;\n if (!apiUrl) {\n throw new Error(\n 'chaingrow-blog: CHAINGROW_API_URL is not set (env var or apiUrl option required)',\n );\n }\n if (!apiKey) {\n throw new Error(\n 'chaingrow-blog: CHAINGROW_API_KEY is not set (env var or apiKey option required)',\n );\n }\n return { apiUrl, apiKey };\n};\n\nconst buildFetch = (tag: string, revalidate: number | false | undefined) => {\n return ((input: RequestInfo | URL, init?: RequestInit) =>\n fetch(input, {\n ...init,\n next: {\n tags: ['chaingrow:blog', tag],\n ...(revalidate === false\n ? { revalidate: false as const }\n : revalidate !== undefined\n ? { revalidate }\n : {}),\n },\n } as RequestInit)) as typeof fetch;\n};\n\nexport async function getBlog(\n slug: string,\n language?: string,\n opts: NextClientOptions = {},\n): Promise<Blog | null> {\n const { apiUrl, apiKey } = resolveEnv(opts);\n const client = createClient({\n apiUrl,\n apiKey,\n fetch: buildFetch(`chaingrow:blog:${slug}`, opts.revalidate),\n });\n return client.getBlog(slug, language);\n}\n\nexport async function listBlogs(\n listOpts: { status?: 'DRAFT' | 'PUBLISHED'; language?: string } = {},\n opts: NextClientOptions = {},\n): Promise<Blog[]> {\n const { apiUrl, apiKey } = resolveEnv(opts);\n const client = createClient({\n apiUrl,\n apiKey,\n fetch: buildFetch('chaingrow:blog:list', opts.revalidate),\n });\n return client.listBlogs(listOpts);\n}\n\nexport type { Blog } from '../types';\n","import type { Blog } from './types';\n\nexport type ClientOptions = {\n apiUrl: string;\n apiKey: string;\n fetch?: typeof fetch;\n};\n\nexport type ListBlogsOptions = {\n status?: 'DRAFT' | 'PUBLISHED';\n language?: string;\n};\n\nexport type ChaingrowClient = {\n getBlog(slug: string, language?: string): Promise<Blog | null>;\n getBlogById(id: string): Promise<Blog | null>;\n listBlogs(opts?: ListBlogsOptions): Promise<Blog[]>;\n};\n\nexport function createClient(opts: ClientOptions): ChaingrowClient {\n const baseUrl = opts.apiUrl.replace(/\\/$/, '');\n const doFetch = opts.fetch ?? fetch;\n\n const request = async <T>(\n path: string,\n init?: RequestInit,\n ): Promise<T | null> => {\n const res = await doFetch(`${baseUrl}${path}`, {\n ...init,\n headers: {\n 'X-API-Key': opts.apiKey,\n Accept: 'application/json',\n ...(init?.headers ?? {}),\n },\n });\n if (res.status === 404) return null;\n if (!res.ok) {\n throw new Error(\n `chaingrow-blog: ${init?.method ?? 'GET'} ${path} failed (${res.status})`,\n );\n }\n return (await res.json()) as T;\n };\n\n return {\n async getBlog(slug, language) {\n const qs = language ? `?language=${encodeURIComponent(language)}` : '';\n return request<Blog>(\n `/api/v1/blogs/by-slug/${encodeURIComponent(slug)}${qs}`,\n );\n },\n\n async getBlogById(id) {\n return request<Blog>(`/api/v1/blogs/${encodeURIComponent(id)}`);\n },\n\n async listBlogs(listOpts = {}) {\n const params = new URLSearchParams();\n if (listOpts.status) params.set('status', listOpts.status);\n const qs = params.toString();\n const response = await request<{ data: Blog[]; total: number }>(\n `/api/v1/blogs${qs ? `?${qs}` : ''}`,\n );\n if (!response) return [];\n const blogs = response.data ?? [];\n if (listOpts.language) {\n return blogs.filter((b) => b.language === listOpts.language);\n }\n return blogs;\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACmBO,SAAS,aAAa,MAAsC;AACjE,QAAM,UAAU,KAAK,OAAO,QAAQ,OAAO,EAAE;AAC7C,QAAM,UAAU,KAAK,SAAS;AAE9B,QAAM,UAAU,OACd,MACA,SACsB;AACtB,UAAM,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,IAAI,IAAI;AAAA,MAC7C,GAAG;AAAA,MACH,SAAS;AAAA,QACP,aAAa,KAAK;AAAA,QAClB,QAAQ;AAAA,QACR,GAAI,MAAM,WAAW,CAAC;AAAA,MACxB;AAAA,IACF,CAAC;AACD,QAAI,IAAI,WAAW,IAAK,QAAO;AAC/B,QAAI,CAAC,IAAI,IAAI;AACX,YAAM,IAAI;AAAA,QACR,mBAAmB,MAAM,UAAU,KAAK,IAAI,IAAI,YAAY,IAAI,MAAM;AAAA,MACxE;AAAA,IACF;AACA,WAAQ,MAAM,IAAI,KAAK;AAAA,EACzB;AAEA,SAAO;AAAA,IACL,MAAM,QAAQ,MAAM,UAAU;AAC5B,YAAM,KAAK,WAAW,aAAa,mBAAmB,QAAQ,CAAC,KAAK;AACpE,aAAO;AAAA,QACL,yBAAyB,mBAAmB,IAAI,CAAC,GAAG,EAAE;AAAA,MACxD;AAAA,IACF;AAAA,IAEA,MAAM,YAAY,IAAI;AACpB,aAAO,QAAc,iBAAiB,mBAAmB,EAAE,CAAC,EAAE;AAAA,IAChE;AAAA,IAEA,MAAM,UAAU,WAAW,CAAC,GAAG;AAC7B,YAAM,SAAS,IAAI,gBAAgB;AACnC,UAAI,SAAS,OAAQ,QAAO,IAAI,UAAU,SAAS,MAAM;AACzD,YAAM,KAAK,OAAO,SAAS;AAC3B,YAAM,WAAW,MAAM;AAAA,QACrB,gBAAgB,KAAK,IAAI,EAAE,KAAK,EAAE;AAAA,MACpC;AACA,UAAI,CAAC,SAAU,QAAO,CAAC;AACvB,YAAM,QAAQ,SAAS,QAAQ,CAAC;AAChC,UAAI,SAAS,UAAU;AACrB,eAAO,MAAM,OAAO,CAAC,MAAM,EAAE,aAAa,SAAS,QAAQ;AAAA,MAC7D;AACA,aAAO;AAAA,IACT;AAAA,EACF;AACF;;;AD9DA,IAAM,aAAa,CAAC,OAA0B,CAAC,MAAM;AACnD,QAAM,SAAS,KAAK,UAAU,QAAQ,IAAI;AAC1C,QAAM,SAAS,KAAK,UAAU,QAAQ,IAAI;AAC1C,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,SAAO,EAAE,QAAQ,OAAO;AAC1B;AAEA,IAAM,aAAa,CAAC,KAAa,eAA2C;AAC1E,UAAQ,CAAC,OAA0B,SACjC,MAAM,OAAO;AAAA,IACX,GAAG;AAAA,IACH,MAAM;AAAA,MACJ,MAAM,CAAC,kBAAkB,GAAG;AAAA,MAC5B,GAAI,eAAe,QACf,EAAE,YAAY,MAAe,IAC7B,eAAe,SACb,EAAE,WAAW,IACb,CAAC;AAAA,IACT;AAAA,EACF,CAAgB;AACpB;AAEA,eAAsB,QACpB,MACA,UACA,OAA0B,CAAC,GACL;AACtB,QAAM,EAAE,QAAQ,OAAO,IAAI,WAAW,IAAI;AAC1C,QAAM,SAAS,aAAa;AAAA,IAC1B;AAAA,IACA;AAAA,IACA,OAAO,WAAW,kBAAkB,IAAI,IAAI,KAAK,UAAU;AAAA,EAC7D,CAAC;AACD,SAAO,OAAO,QAAQ,MAAM,QAAQ;AACtC;AAEA,eAAsB,UACpB,WAAkE,CAAC,GACnE,OAA0B,CAAC,GACV;AACjB,QAAM,EAAE,QAAQ,OAAO,IAAI,WAAW,IAAI;AAC1C,QAAM,SAAS,aAAa;AAAA,IAC1B;AAAA,IACA;AAAA,IACA,OAAO,WAAW,uBAAuB,KAAK,UAAU;AAAA,EAC1D,CAAC;AACD,SAAO,OAAO,UAAU,QAAQ;AAClC;","names":[]}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { B as Blog } from '../types-CoPD0ejB.cjs';
|
|
2
|
+
|
|
3
|
+
type NextClientOptions = {
|
|
4
|
+
apiUrl?: string;
|
|
5
|
+
apiKey?: string;
|
|
6
|
+
revalidate?: number | false;
|
|
7
|
+
};
|
|
8
|
+
declare function getBlog(slug: string, language?: string, opts?: NextClientOptions): Promise<Blog | null>;
|
|
9
|
+
declare function listBlogs(listOpts?: {
|
|
10
|
+
status?: 'DRAFT' | 'PUBLISHED';
|
|
11
|
+
language?: string;
|
|
12
|
+
}, opts?: NextClientOptions): Promise<Blog[]>;
|
|
13
|
+
|
|
14
|
+
export { Blog, type NextClientOptions, getBlog, listBlogs };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { B as Blog } from '../types-CoPD0ejB.js';
|
|
2
|
+
|
|
3
|
+
type NextClientOptions = {
|
|
4
|
+
apiUrl?: string;
|
|
5
|
+
apiKey?: string;
|
|
6
|
+
revalidate?: number | false;
|
|
7
|
+
};
|
|
8
|
+
declare function getBlog(slug: string, language?: string, opts?: NextClientOptions): Promise<Blog | null>;
|
|
9
|
+
declare function listBlogs(listOpts?: {
|
|
10
|
+
status?: 'DRAFT' | 'PUBLISHED';
|
|
11
|
+
language?: string;
|
|
12
|
+
}, opts?: NextClientOptions): Promise<Blog[]>;
|
|
13
|
+
|
|
14
|
+
export { Blog, type NextClientOptions, getBlog, listBlogs };
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createClient
|
|
3
|
+
} from "../chunk-TDHCPX4T.js";
|
|
4
|
+
|
|
5
|
+
// src/next/index.ts
|
|
6
|
+
var resolveEnv = (opts = {}) => {
|
|
7
|
+
const apiUrl = opts.apiUrl ?? process.env.CHAINGROW_API_URL;
|
|
8
|
+
const apiKey = opts.apiKey ?? process.env.CHAINGROW_API_KEY;
|
|
9
|
+
if (!apiUrl) {
|
|
10
|
+
throw new Error(
|
|
11
|
+
"chaingrow-blog: CHAINGROW_API_URL is not set (env var or apiUrl option required)"
|
|
12
|
+
);
|
|
13
|
+
}
|
|
14
|
+
if (!apiKey) {
|
|
15
|
+
throw new Error(
|
|
16
|
+
"chaingrow-blog: CHAINGROW_API_KEY is not set (env var or apiKey option required)"
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
return { apiUrl, apiKey };
|
|
20
|
+
};
|
|
21
|
+
var buildFetch = (tag, revalidate) => {
|
|
22
|
+
return ((input, init) => fetch(input, {
|
|
23
|
+
...init,
|
|
24
|
+
next: {
|
|
25
|
+
tags: ["chaingrow:blog", tag],
|
|
26
|
+
...revalidate === false ? { revalidate: false } : revalidate !== void 0 ? { revalidate } : {}
|
|
27
|
+
}
|
|
28
|
+
}));
|
|
29
|
+
};
|
|
30
|
+
async function getBlog(slug, language, opts = {}) {
|
|
31
|
+
const { apiUrl, apiKey } = resolveEnv(opts);
|
|
32
|
+
const client = createClient({
|
|
33
|
+
apiUrl,
|
|
34
|
+
apiKey,
|
|
35
|
+
fetch: buildFetch(`chaingrow:blog:${slug}`, opts.revalidate)
|
|
36
|
+
});
|
|
37
|
+
return client.getBlog(slug, language);
|
|
38
|
+
}
|
|
39
|
+
async function listBlogs(listOpts = {}, opts = {}) {
|
|
40
|
+
const { apiUrl, apiKey } = resolveEnv(opts);
|
|
41
|
+
const client = createClient({
|
|
42
|
+
apiUrl,
|
|
43
|
+
apiKey,
|
|
44
|
+
fetch: buildFetch("chaingrow:blog:list", opts.revalidate)
|
|
45
|
+
});
|
|
46
|
+
return client.listBlogs(listOpts);
|
|
47
|
+
}
|
|
48
|
+
export {
|
|
49
|
+
getBlog,
|
|
50
|
+
listBlogs
|
|
51
|
+
};
|
|
52
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/next/index.ts"],"sourcesContent":["import type { Blog } from '../types';\nimport { createClient } from '../client';\n\nexport type NextClientOptions = {\n apiUrl?: string;\n apiKey?: string;\n revalidate?: number | false;\n};\n\nconst resolveEnv = (opts: NextClientOptions = {}) => {\n const apiUrl = opts.apiUrl ?? process.env.CHAINGROW_API_URL;\n const apiKey = opts.apiKey ?? process.env.CHAINGROW_API_KEY;\n if (!apiUrl) {\n throw new Error(\n 'chaingrow-blog: CHAINGROW_API_URL is not set (env var or apiUrl option required)',\n );\n }\n if (!apiKey) {\n throw new Error(\n 'chaingrow-blog: CHAINGROW_API_KEY is not set (env var or apiKey option required)',\n );\n }\n return { apiUrl, apiKey };\n};\n\nconst buildFetch = (tag: string, revalidate: number | false | undefined) => {\n return ((input: RequestInfo | URL, init?: RequestInit) =>\n fetch(input, {\n ...init,\n next: {\n tags: ['chaingrow:blog', tag],\n ...(revalidate === false\n ? { revalidate: false as const }\n : revalidate !== undefined\n ? { revalidate }\n : {}),\n },\n } as RequestInit)) as typeof fetch;\n};\n\nexport async function getBlog(\n slug: string,\n language?: string,\n opts: NextClientOptions = {},\n): Promise<Blog | null> {\n const { apiUrl, apiKey } = resolveEnv(opts);\n const client = createClient({\n apiUrl,\n apiKey,\n fetch: buildFetch(`chaingrow:blog:${slug}`, opts.revalidate),\n });\n return client.getBlog(slug, language);\n}\n\nexport async function listBlogs(\n listOpts: { status?: 'DRAFT' | 'PUBLISHED'; language?: string } = {},\n opts: NextClientOptions = {},\n): Promise<Blog[]> {\n const { apiUrl, apiKey } = resolveEnv(opts);\n const client = createClient({\n apiUrl,\n apiKey,\n fetch: buildFetch('chaingrow:blog:list', opts.revalidate),\n });\n return client.listBlogs(listOpts);\n}\n\nexport type { Blog } from '../types';\n"],"mappings":";;;;;AASA,IAAM,aAAa,CAAC,OAA0B,CAAC,MAAM;AACnD,QAAM,SAAS,KAAK,UAAU,QAAQ,IAAI;AAC1C,QAAM,SAAS,KAAK,UAAU,QAAQ,IAAI;AAC1C,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,SAAO,EAAE,QAAQ,OAAO;AAC1B;AAEA,IAAM,aAAa,CAAC,KAAa,eAA2C;AAC1E,UAAQ,CAAC,OAA0B,SACjC,MAAM,OAAO;AAAA,IACX,GAAG;AAAA,IACH,MAAM;AAAA,MACJ,MAAM,CAAC,kBAAkB,GAAG;AAAA,MAC5B,GAAI,eAAe,QACf,EAAE,YAAY,MAAe,IAC7B,eAAe,SACb,EAAE,WAAW,IACb,CAAC;AAAA,IACT;AAAA,EACF,CAAgB;AACpB;AAEA,eAAsB,QACpB,MACA,UACA,OAA0B,CAAC,GACL;AACtB,QAAM,EAAE,QAAQ,OAAO,IAAI,WAAW,IAAI;AAC1C,QAAM,SAAS,aAAa;AAAA,IAC1B;AAAA,IACA;AAAA,IACA,OAAO,WAAW,kBAAkB,IAAI,IAAI,KAAK,UAAU;AAAA,EAC7D,CAAC;AACD,SAAO,OAAO,QAAQ,MAAM,QAAQ;AACtC;AAEA,eAAsB,UACpB,WAAkE,CAAC,GACnE,OAA0B,CAAC,GACV;AACjB,QAAM,EAAE,QAAQ,OAAO,IAAI,WAAW,IAAI;AAC1C,QAAM,SAAS,aAAa;AAAA,IAC1B;AAAA,IACA;AAAA,IACA,OAAO,WAAW,uBAAuB,KAAK,UAAU;AAAA,EAC1D,CAAC;AACD,SAAO,OAAO,UAAU,QAAQ;AAClC;","names":[]}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/next/webhook.ts
|
|
21
|
+
var webhook_exports = {};
|
|
22
|
+
__export(webhook_exports, {
|
|
23
|
+
POST: () => POST,
|
|
24
|
+
createWebhookHandler: () => createWebhookHandler
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(webhook_exports);
|
|
27
|
+
var import_cache = require("next/cache");
|
|
28
|
+
var import_node_crypto = require("crypto");
|
|
29
|
+
var verifySignature = (body, signature, secret) => {
|
|
30
|
+
const expected = (0, import_node_crypto.createHmac)("sha256", secret).update(body).digest("hex");
|
|
31
|
+
const provided = signature.trim();
|
|
32
|
+
if (expected.length !== provided.length) return false;
|
|
33
|
+
try {
|
|
34
|
+
return (0, import_node_crypto.timingSafeEqual)(Buffer.from(expected), Buffer.from(provided));
|
|
35
|
+
} catch {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
var json = (status, body) => new Response(JSON.stringify(body), {
|
|
40
|
+
status,
|
|
41
|
+
headers: { "Content-Type": "application/json" }
|
|
42
|
+
});
|
|
43
|
+
function createWebhookHandler(opts = {}) {
|
|
44
|
+
const POST2 = async (req) => {
|
|
45
|
+
const secret = opts.secret ?? process.env.CHAINGROW_WEBHOOK_SECRET;
|
|
46
|
+
const body = await req.text();
|
|
47
|
+
if (secret) {
|
|
48
|
+
const signature = req.headers.get("x-chaingrow-signature") ?? req.headers.get("X-ChainGrow-Signature") ?? "";
|
|
49
|
+
if (!signature || !verifySignature(body, signature, secret)) {
|
|
50
|
+
return json(401, { error: "Invalid signature" });
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
let payload;
|
|
54
|
+
try {
|
|
55
|
+
payload = JSON.parse(body);
|
|
56
|
+
} catch {
|
|
57
|
+
return json(400, { error: "Invalid JSON body" });
|
|
58
|
+
}
|
|
59
|
+
const slug = typeof payload?.blog?.slug === "string" ? payload.blog.slug : null;
|
|
60
|
+
(0, import_cache.revalidateTag)("chaingrow:blog");
|
|
61
|
+
(0, import_cache.revalidateTag)("chaingrow:blog:list");
|
|
62
|
+
if (slug) (0, import_cache.revalidateTag)(`chaingrow:blog:${slug}`);
|
|
63
|
+
if (opts.onEvent) {
|
|
64
|
+
try {
|
|
65
|
+
await opts.onEvent(payload);
|
|
66
|
+
} catch (err) {
|
|
67
|
+
console.error("chaingrow-blog onEvent handler failed:", err);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return json(200, { ok: true, revalidated: slug ?? null });
|
|
71
|
+
};
|
|
72
|
+
return { POST: POST2 };
|
|
73
|
+
}
|
|
74
|
+
var { POST } = createWebhookHandler();
|
|
75
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
76
|
+
0 && (module.exports = {
|
|
77
|
+
POST,
|
|
78
|
+
createWebhookHandler
|
|
79
|
+
});
|
|
80
|
+
//# sourceMappingURL=webhook.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/next/webhook.ts"],"sourcesContent":["import { revalidateTag } from 'next/cache';\nimport { createHmac, timingSafeEqual } from 'node:crypto';\nimport type { WebhookPayload } from '../types';\n\nexport type WebhookHandlerOptions = {\n secret?: string;\n onEvent?: (payload: WebhookPayload) => void | Promise<void>;\n};\n\nconst verifySignature = (body: string, signature: string, secret: string) => {\n const expected = createHmac('sha256', secret).update(body).digest('hex');\n const provided = signature.trim();\n if (expected.length !== provided.length) return false;\n try {\n return timingSafeEqual(Buffer.from(expected), Buffer.from(provided));\n } catch {\n return false;\n }\n};\n\nconst json = (status: number, body: Record<string, unknown>) =>\n new Response(JSON.stringify(body), {\n status,\n headers: { 'Content-Type': 'application/json' },\n });\n\nexport function createWebhookHandler(opts: WebhookHandlerOptions = {}) {\n const POST = async (req: Request): Promise<Response> => {\n const secret = opts.secret ?? process.env.CHAINGROW_WEBHOOK_SECRET;\n const body = await req.text();\n\n if (secret) {\n const signature =\n req.headers.get('x-chaingrow-signature') ??\n req.headers.get('X-ChainGrow-Signature') ??\n '';\n if (!signature || !verifySignature(body, signature, secret)) {\n return json(401, { error: 'Invalid signature' });\n }\n }\n\n let payload: WebhookPayload;\n try {\n payload = JSON.parse(body) as WebhookPayload;\n } catch {\n return json(400, { error: 'Invalid JSON body' });\n }\n\n const slug =\n typeof payload?.blog?.slug === 'string' ? payload.blog.slug : null;\n\n revalidateTag('chaingrow:blog');\n revalidateTag('chaingrow:blog:list');\n if (slug) revalidateTag(`chaingrow:blog:${slug}`);\n\n if (opts.onEvent) {\n try {\n await opts.onEvent(payload);\n } catch (err) {\n console.error('chaingrow-blog onEvent handler failed:', err);\n }\n }\n\n return json(200, { ok: true, revalidated: slug ?? null });\n };\n\n return { POST };\n}\n\nexport const { POST } = createWebhookHandler();\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAA8B;AAC9B,yBAA4C;AAQ5C,IAAM,kBAAkB,CAAC,MAAc,WAAmB,WAAmB;AAC3E,QAAM,eAAW,+BAAW,UAAU,MAAM,EAAE,OAAO,IAAI,EAAE,OAAO,KAAK;AACvE,QAAM,WAAW,UAAU,KAAK;AAChC,MAAI,SAAS,WAAW,SAAS,OAAQ,QAAO;AAChD,MAAI;AACF,eAAO,oCAAgB,OAAO,KAAK,QAAQ,GAAG,OAAO,KAAK,QAAQ,CAAC;AAAA,EACrE,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,IAAM,OAAO,CAAC,QAAgB,SAC5B,IAAI,SAAS,KAAK,UAAU,IAAI,GAAG;AAAA,EACjC;AAAA,EACA,SAAS,EAAE,gBAAgB,mBAAmB;AAChD,CAAC;AAEI,SAAS,qBAAqB,OAA8B,CAAC,GAAG;AACrE,QAAMA,QAAO,OAAO,QAAoC;AACtD,UAAM,SAAS,KAAK,UAAU,QAAQ,IAAI;AAC1C,UAAM,OAAO,MAAM,IAAI,KAAK;AAE5B,QAAI,QAAQ;AACV,YAAM,YACJ,IAAI,QAAQ,IAAI,uBAAuB,KACvC,IAAI,QAAQ,IAAI,uBAAuB,KACvC;AACF,UAAI,CAAC,aAAa,CAAC,gBAAgB,MAAM,WAAW,MAAM,GAAG;AAC3D,eAAO,KAAK,KAAK,EAAE,OAAO,oBAAoB,CAAC;AAAA,MACjD;AAAA,IACF;AAEA,QAAI;AACJ,QAAI;AACF,gBAAU,KAAK,MAAM,IAAI;AAAA,IAC3B,QAAQ;AACN,aAAO,KAAK,KAAK,EAAE,OAAO,oBAAoB,CAAC;AAAA,IACjD;AAEA,UAAM,OACJ,OAAO,SAAS,MAAM,SAAS,WAAW,QAAQ,KAAK,OAAO;AAEhE,oCAAc,gBAAgB;AAC9B,oCAAc,qBAAqB;AACnC,QAAI,KAAM,iCAAc,kBAAkB,IAAI,EAAE;AAEhD,QAAI,KAAK,SAAS;AAChB,UAAI;AACF,cAAM,KAAK,QAAQ,OAAO;AAAA,MAC5B,SAAS,KAAK;AACZ,gBAAQ,MAAM,0CAA0C,GAAG;AAAA,MAC7D;AAAA,IACF;AAEA,WAAO,KAAK,KAAK,EAAE,IAAI,MAAM,aAAa,QAAQ,KAAK,CAAC;AAAA,EAC1D;AAEA,SAAO,EAAE,MAAAA,MAAK;AAChB;AAEO,IAAM,EAAE,KAAK,IAAI,qBAAqB;","names":["POST"]}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { b as WebhookPayload } from '../types-CoPD0ejB.cjs';
|
|
2
|
+
|
|
3
|
+
type WebhookHandlerOptions = {
|
|
4
|
+
secret?: string;
|
|
5
|
+
onEvent?: (payload: WebhookPayload) => void | Promise<void>;
|
|
6
|
+
};
|
|
7
|
+
declare function createWebhookHandler(opts?: WebhookHandlerOptions): {
|
|
8
|
+
POST: (req: Request) => Promise<Response>;
|
|
9
|
+
};
|
|
10
|
+
declare const POST: (req: Request) => Promise<Response>;
|
|
11
|
+
|
|
12
|
+
export { POST, type WebhookHandlerOptions, createWebhookHandler };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { b as WebhookPayload } from '../types-CoPD0ejB.js';
|
|
2
|
+
|
|
3
|
+
type WebhookHandlerOptions = {
|
|
4
|
+
secret?: string;
|
|
5
|
+
onEvent?: (payload: WebhookPayload) => void | Promise<void>;
|
|
6
|
+
};
|
|
7
|
+
declare function createWebhookHandler(opts?: WebhookHandlerOptions): {
|
|
8
|
+
POST: (req: Request) => Promise<Response>;
|
|
9
|
+
};
|
|
10
|
+
declare const POST: (req: Request) => Promise<Response>;
|
|
11
|
+
|
|
12
|
+
export { POST, type WebhookHandlerOptions, createWebhookHandler };
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// src/next/webhook.ts
|
|
2
|
+
import { revalidateTag } from "next/cache";
|
|
3
|
+
import { createHmac, timingSafeEqual } from "crypto";
|
|
4
|
+
var verifySignature = (body, signature, secret) => {
|
|
5
|
+
const expected = createHmac("sha256", secret).update(body).digest("hex");
|
|
6
|
+
const provided = signature.trim();
|
|
7
|
+
if (expected.length !== provided.length) return false;
|
|
8
|
+
try {
|
|
9
|
+
return timingSafeEqual(Buffer.from(expected), Buffer.from(provided));
|
|
10
|
+
} catch {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
var json = (status, body) => new Response(JSON.stringify(body), {
|
|
15
|
+
status,
|
|
16
|
+
headers: { "Content-Type": "application/json" }
|
|
17
|
+
});
|
|
18
|
+
function createWebhookHandler(opts = {}) {
|
|
19
|
+
const POST2 = async (req) => {
|
|
20
|
+
const secret = opts.secret ?? process.env.CHAINGROW_WEBHOOK_SECRET;
|
|
21
|
+
const body = await req.text();
|
|
22
|
+
if (secret) {
|
|
23
|
+
const signature = req.headers.get("x-chaingrow-signature") ?? req.headers.get("X-ChainGrow-Signature") ?? "";
|
|
24
|
+
if (!signature || !verifySignature(body, signature, secret)) {
|
|
25
|
+
return json(401, { error: "Invalid signature" });
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
let payload;
|
|
29
|
+
try {
|
|
30
|
+
payload = JSON.parse(body);
|
|
31
|
+
} catch {
|
|
32
|
+
return json(400, { error: "Invalid JSON body" });
|
|
33
|
+
}
|
|
34
|
+
const slug = typeof payload?.blog?.slug === "string" ? payload.blog.slug : null;
|
|
35
|
+
revalidateTag("chaingrow:blog");
|
|
36
|
+
revalidateTag("chaingrow:blog:list");
|
|
37
|
+
if (slug) revalidateTag(`chaingrow:blog:${slug}`);
|
|
38
|
+
if (opts.onEvent) {
|
|
39
|
+
try {
|
|
40
|
+
await opts.onEvent(payload);
|
|
41
|
+
} catch (err) {
|
|
42
|
+
console.error("chaingrow-blog onEvent handler failed:", err);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return json(200, { ok: true, revalidated: slug ?? null });
|
|
46
|
+
};
|
|
47
|
+
return { POST: POST2 };
|
|
48
|
+
}
|
|
49
|
+
var { POST } = createWebhookHandler();
|
|
50
|
+
export {
|
|
51
|
+
POST,
|
|
52
|
+
createWebhookHandler
|
|
53
|
+
};
|
|
54
|
+
//# sourceMappingURL=webhook.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/next/webhook.ts"],"sourcesContent":["import { revalidateTag } from 'next/cache';\nimport { createHmac, timingSafeEqual } from 'node:crypto';\nimport type { WebhookPayload } from '../types';\n\nexport type WebhookHandlerOptions = {\n secret?: string;\n onEvent?: (payload: WebhookPayload) => void | Promise<void>;\n};\n\nconst verifySignature = (body: string, signature: string, secret: string) => {\n const expected = createHmac('sha256', secret).update(body).digest('hex');\n const provided = signature.trim();\n if (expected.length !== provided.length) return false;\n try {\n return timingSafeEqual(Buffer.from(expected), Buffer.from(provided));\n } catch {\n return false;\n }\n};\n\nconst json = (status: number, body: Record<string, unknown>) =>\n new Response(JSON.stringify(body), {\n status,\n headers: { 'Content-Type': 'application/json' },\n });\n\nexport function createWebhookHandler(opts: WebhookHandlerOptions = {}) {\n const POST = async (req: Request): Promise<Response> => {\n const secret = opts.secret ?? process.env.CHAINGROW_WEBHOOK_SECRET;\n const body = await req.text();\n\n if (secret) {\n const signature =\n req.headers.get('x-chaingrow-signature') ??\n req.headers.get('X-ChainGrow-Signature') ??\n '';\n if (!signature || !verifySignature(body, signature, secret)) {\n return json(401, { error: 'Invalid signature' });\n }\n }\n\n let payload: WebhookPayload;\n try {\n payload = JSON.parse(body) as WebhookPayload;\n } catch {\n return json(400, { error: 'Invalid JSON body' });\n }\n\n const slug =\n typeof payload?.blog?.slug === 'string' ? payload.blog.slug : null;\n\n revalidateTag('chaingrow:blog');\n revalidateTag('chaingrow:blog:list');\n if (slug) revalidateTag(`chaingrow:blog:${slug}`);\n\n if (opts.onEvent) {\n try {\n await opts.onEvent(payload);\n } catch (err) {\n console.error('chaingrow-blog onEvent handler failed:', err);\n }\n }\n\n return json(200, { ok: true, revalidated: slug ?? null });\n };\n\n return { POST };\n}\n\nexport const { POST } = createWebhookHandler();\n"],"mappings":";AAAA,SAAS,qBAAqB;AAC9B,SAAS,YAAY,uBAAuB;AAQ5C,IAAM,kBAAkB,CAAC,MAAc,WAAmB,WAAmB;AAC3E,QAAM,WAAW,WAAW,UAAU,MAAM,EAAE,OAAO,IAAI,EAAE,OAAO,KAAK;AACvE,QAAM,WAAW,UAAU,KAAK;AAChC,MAAI,SAAS,WAAW,SAAS,OAAQ,QAAO;AAChD,MAAI;AACF,WAAO,gBAAgB,OAAO,KAAK,QAAQ,GAAG,OAAO,KAAK,QAAQ,CAAC;AAAA,EACrE,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,IAAM,OAAO,CAAC,QAAgB,SAC5B,IAAI,SAAS,KAAK,UAAU,IAAI,GAAG;AAAA,EACjC;AAAA,EACA,SAAS,EAAE,gBAAgB,mBAAmB;AAChD,CAAC;AAEI,SAAS,qBAAqB,OAA8B,CAAC,GAAG;AACrE,QAAMA,QAAO,OAAO,QAAoC;AACtD,UAAM,SAAS,KAAK,UAAU,QAAQ,IAAI;AAC1C,UAAM,OAAO,MAAM,IAAI,KAAK;AAE5B,QAAI,QAAQ;AACV,YAAM,YACJ,IAAI,QAAQ,IAAI,uBAAuB,KACvC,IAAI,QAAQ,IAAI,uBAAuB,KACvC;AACF,UAAI,CAAC,aAAa,CAAC,gBAAgB,MAAM,WAAW,MAAM,GAAG;AAC3D,eAAO,KAAK,KAAK,EAAE,OAAO,oBAAoB,CAAC;AAAA,MACjD;AAAA,IACF;AAEA,QAAI;AACJ,QAAI;AACF,gBAAU,KAAK,MAAM,IAAI;AAAA,IAC3B,QAAQ;AACN,aAAO,KAAK,KAAK,EAAE,OAAO,oBAAoB,CAAC;AAAA,IACjD;AAEA,UAAM,OACJ,OAAO,SAAS,MAAM,SAAS,WAAW,QAAQ,KAAK,OAAO;AAEhE,kBAAc,gBAAgB;AAC9B,kBAAc,qBAAqB;AACnC,QAAI,KAAM,eAAc,kBAAkB,IAAI,EAAE;AAEhD,QAAI,KAAK,SAAS;AAChB,UAAI;AACF,cAAM,KAAK,QAAQ,OAAO;AAAA,MAC5B,SAAS,KAAK;AACZ,gBAAQ,MAAM,0CAA0C,GAAG;AAAA,MAC7D;AAAA,IACF;AAEA,WAAO,KAAK,KAAK,EAAE,IAAI,MAAM,aAAa,QAAQ,KAAK,CAAC;AAAA,EAC1D;AAEA,SAAO,EAAE,MAAAA,MAAK;AAChB;AAEO,IAAM,EAAE,KAAK,IAAI,qBAAqB;","names":["POST"]}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/react/index.ts
|
|
21
|
+
var react_exports = {};
|
|
22
|
+
__export(react_exports, {
|
|
23
|
+
BlockRenderer: () => BlockRenderer,
|
|
24
|
+
BlogRenderer: () => BlogRenderer,
|
|
25
|
+
DefaultCode: () => DefaultCode,
|
|
26
|
+
DefaultHeading: () => DefaultHeading,
|
|
27
|
+
DefaultImage: () => DefaultImage,
|
|
28
|
+
DefaultList: () => DefaultList,
|
|
29
|
+
DefaultParagraph: () => DefaultParagraph,
|
|
30
|
+
DefaultQuote: () => DefaultQuote
|
|
31
|
+
});
|
|
32
|
+
module.exports = __toCommonJS(react_exports);
|
|
33
|
+
|
|
34
|
+
// src/react/defaults.tsx
|
|
35
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
36
|
+
var DefaultHeading = ({
|
|
37
|
+
level,
|
|
38
|
+
children
|
|
39
|
+
}) => {
|
|
40
|
+
const Tag = level === 2 ? "h2" : level === 4 ? "h4" : "h3";
|
|
41
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Tag, { children });
|
|
42
|
+
};
|
|
43
|
+
var DefaultParagraph = ({
|
|
44
|
+
children
|
|
45
|
+
}) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children });
|
|
46
|
+
var DefaultList = ({ items }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { children: items.map((item, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", { children: item }, i)) });
|
|
47
|
+
var DefaultQuote = ({ children }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("blockquote", { children });
|
|
48
|
+
var DefaultCode = ({ children }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("pre", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", { children }) });
|
|
49
|
+
var DefaultImage = ({
|
|
50
|
+
src,
|
|
51
|
+
alt
|
|
52
|
+
}) => {
|
|
53
|
+
if (!src) return null;
|
|
54
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("img", { src, alt });
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
// src/react/BlogRenderer.tsx
|
|
58
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
59
|
+
var parseListItems = (content) => content.split("\n").map((line) => line.replace(/^\s*[-•*]\s*/, "").trim()).filter(Boolean);
|
|
60
|
+
function BlockRenderer({
|
|
61
|
+
block,
|
|
62
|
+
components = {}
|
|
63
|
+
}) {
|
|
64
|
+
switch (block.type) {
|
|
65
|
+
case "heading": {
|
|
66
|
+
const Heading = components.heading ?? DefaultHeading;
|
|
67
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Heading, { level: block.level ?? 2, children: block.content });
|
|
68
|
+
}
|
|
69
|
+
case "paragraph": {
|
|
70
|
+
const Paragraph = components.paragraph ?? DefaultParagraph;
|
|
71
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Paragraph, { children: block.content });
|
|
72
|
+
}
|
|
73
|
+
case "list": {
|
|
74
|
+
const List = components.list ?? DefaultList;
|
|
75
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(List, { items: parseListItems(block.content) });
|
|
76
|
+
}
|
|
77
|
+
case "quote": {
|
|
78
|
+
const Quote = components.quote ?? DefaultQuote;
|
|
79
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Quote, { children: block.content });
|
|
80
|
+
}
|
|
81
|
+
case "code": {
|
|
82
|
+
const Code = components.code ?? DefaultCode;
|
|
83
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Code, { children: block.content });
|
|
84
|
+
}
|
|
85
|
+
case "image_placeholder": {
|
|
86
|
+
const Image = components.image ?? DefaultImage;
|
|
87
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Image, { alt: block.content });
|
|
88
|
+
}
|
|
89
|
+
default:
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
function BlogRenderer({
|
|
94
|
+
blog,
|
|
95
|
+
components = {}
|
|
96
|
+
}) {
|
|
97
|
+
const Wrapper = components.wrapper;
|
|
98
|
+
const content = /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: blog.blocks.map((block, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(BlockRenderer, { block, components }, i)) });
|
|
99
|
+
if (Wrapper) return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Wrapper, { blog, children: content });
|
|
100
|
+
return content;
|
|
101
|
+
}
|
|
102
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
103
|
+
0 && (module.exports = {
|
|
104
|
+
BlockRenderer,
|
|
105
|
+
BlogRenderer,
|
|
106
|
+
DefaultCode,
|
|
107
|
+
DefaultHeading,
|
|
108
|
+
DefaultImage,
|
|
109
|
+
DefaultList,
|
|
110
|
+
DefaultParagraph,
|
|
111
|
+
DefaultQuote
|
|
112
|
+
});
|
|
113
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/react/index.ts","../../src/react/defaults.tsx","../../src/react/BlogRenderer.tsx"],"sourcesContent":["export {\n BlogRenderer,\n BlockRenderer,\n type BlockComponents,\n} from './BlogRenderer';\nexport * from './defaults';\nexport type { Blog, Block, FaqItem, SocialPost } from '../types';\n","import * as React from 'react';\n\nexport const DefaultHeading = ({\n level,\n children,\n}: {\n level: number;\n children: React.ReactNode;\n}) => {\n const Tag = (level === 2 ? 'h2' : level === 4 ? 'h4' : 'h3') as\n | 'h2'\n | 'h3'\n | 'h4';\n return <Tag>{children}</Tag>;\n};\n\nexport const DefaultParagraph = ({\n children,\n}: {\n children: React.ReactNode;\n}) => <p>{children}</p>;\n\nexport const DefaultList = ({ items }: { items: string[] }) => (\n <ul>\n {items.map((item, i) => (\n <li key={i}>{item}</li>\n ))}\n </ul>\n);\n\nexport const DefaultQuote = ({ children }: { children: React.ReactNode }) => (\n <blockquote>{children}</blockquote>\n);\n\nexport const DefaultCode = ({ children }: { children: React.ReactNode }) => (\n <pre>\n <code>{children}</code>\n </pre>\n);\n\nexport const DefaultImage = ({\n src,\n alt,\n}: {\n src?: string;\n alt: string;\n}) => {\n if (!src) return null;\n // eslint-disable-next-line @next/next/no-img-element\n return <img src={src} alt={alt} />;\n};\n","import * as React from 'react';\nimport type { Blog, Block } from '../types';\nimport {\n DefaultHeading,\n DefaultParagraph,\n DefaultList,\n DefaultQuote,\n DefaultCode,\n DefaultImage,\n} from './defaults';\n\nexport type BlockComponents = Partial<{\n heading: React.ComponentType<{ level: number; children: React.ReactNode }>;\n paragraph: React.ComponentType<{ children: React.ReactNode }>;\n list: React.ComponentType<{ items: string[] }>;\n quote: React.ComponentType<{ children: React.ReactNode }>;\n code: React.ComponentType<{ children: React.ReactNode }>;\n image: React.ComponentType<{ src?: string; alt: string }>;\n wrapper: React.ComponentType<{ children: React.ReactNode; blog: Blog }>;\n}>;\n\nconst parseListItems = (content: string): string[] =>\n content\n .split('\\n')\n .map((line) => line.replace(/^\\s*[-•*]\\s*/, '').trim())\n .filter(Boolean);\n\nexport function BlockRenderer({\n block,\n components = {},\n}: {\n block: Block;\n components?: BlockComponents;\n}) {\n switch (block.type) {\n case 'heading': {\n const Heading = components.heading ?? DefaultHeading;\n return <Heading level={block.level ?? 2}>{block.content}</Heading>;\n }\n case 'paragraph': {\n const Paragraph = components.paragraph ?? DefaultParagraph;\n return <Paragraph>{block.content}</Paragraph>;\n }\n case 'list': {\n const List = components.list ?? DefaultList;\n return <List items={parseListItems(block.content)} />;\n }\n case 'quote': {\n const Quote = components.quote ?? DefaultQuote;\n return <Quote>{block.content}</Quote>;\n }\n case 'code': {\n const Code = components.code ?? DefaultCode;\n return <Code>{block.content}</Code>;\n }\n case 'image_placeholder': {\n const Image = components.image ?? DefaultImage;\n return <Image alt={block.content} />;\n }\n default:\n return null;\n }\n}\n\nexport function BlogRenderer({\n blog,\n components = {},\n}: {\n blog: Blog;\n components?: BlockComponents;\n}) {\n const Wrapper = components.wrapper;\n const content = (\n <>\n {blog.blocks.map((block, i) => (\n <BlockRenderer key={i} block={block} components={components} />\n ))}\n </>\n );\n if (Wrapper) return <Wrapper blog={blog}>{content}</Wrapper>;\n return content;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACaS;AAXF,IAAM,iBAAiB,CAAC;AAAA,EAC7B;AAAA,EACA;AACF,MAGM;AACJ,QAAM,MAAO,UAAU,IAAI,OAAO,UAAU,IAAI,OAAO;AAIvD,SAAO,4CAAC,OAAK,UAAS;AACxB;AAEO,IAAM,mBAAmB,CAAC;AAAA,EAC/B;AACF,MAEM,4CAAC,OAAG,UAAS;AAEZ,IAAM,cAAc,CAAC,EAAE,MAAM,MAClC,4CAAC,QACE,gBAAM,IAAI,CAAC,MAAM,MAChB,4CAAC,QAAY,kBAAJ,CAAS,CACnB,GACH;AAGK,IAAM,eAAe,CAAC,EAAE,SAAS,MACtC,4CAAC,gBAAY,UAAS;AAGjB,IAAM,cAAc,CAAC,EAAE,SAAS,MACrC,4CAAC,SACC,sDAAC,UAAM,UAAS,GAClB;AAGK,IAAM,eAAe,CAAC;AAAA,EAC3B;AAAA,EACA;AACF,MAGM;AACJ,MAAI,CAAC,IAAK,QAAO;AAEjB,SAAO,4CAAC,SAAI,KAAU,KAAU;AAClC;;;ACba,IAAAA,sBAAA;AAhBb,IAAM,iBAAiB,CAAC,YACtB,QACG,MAAM,IAAI,EACV,IAAI,CAAC,SAAS,KAAK,QAAQ,gBAAgB,EAAE,EAAE,KAAK,CAAC,EACrD,OAAO,OAAO;AAEZ,SAAS,cAAc;AAAA,EAC5B;AAAA,EACA,aAAa,CAAC;AAChB,GAGG;AACD,UAAQ,MAAM,MAAM;AAAA,IAClB,KAAK,WAAW;AACd,YAAM,UAAU,WAAW,WAAW;AACtC,aAAO,6CAAC,WAAQ,OAAO,MAAM,SAAS,GAAI,gBAAM,SAAQ;AAAA,IAC1D;AAAA,IACA,KAAK,aAAa;AAChB,YAAM,YAAY,WAAW,aAAa;AAC1C,aAAO,6CAAC,aAAW,gBAAM,SAAQ;AAAA,IACnC;AAAA,IACA,KAAK,QAAQ;AACX,YAAM,OAAO,WAAW,QAAQ;AAChC,aAAO,6CAAC,QAAK,OAAO,eAAe,MAAM,OAAO,GAAG;AAAA,IACrD;AAAA,IACA,KAAK,SAAS;AACZ,YAAM,QAAQ,WAAW,SAAS;AAClC,aAAO,6CAAC,SAAO,gBAAM,SAAQ;AAAA,IAC/B;AAAA,IACA,KAAK,QAAQ;AACX,YAAM,OAAO,WAAW,QAAQ;AAChC,aAAO,6CAAC,QAAM,gBAAM,SAAQ;AAAA,IAC9B;AAAA,IACA,KAAK,qBAAqB;AACxB,YAAM,QAAQ,WAAW,SAAS;AAClC,aAAO,6CAAC,SAAM,KAAK,MAAM,SAAS;AAAA,IACpC;AAAA,IACA;AACE,aAAO;AAAA,EACX;AACF;AAEO,SAAS,aAAa;AAAA,EAC3B;AAAA,EACA,aAAa,CAAC;AAChB,GAGG;AACD,QAAM,UAAU,WAAW;AAC3B,QAAM,UACJ,6EACG,eAAK,OAAO,IAAI,CAAC,OAAO,MACvB,6CAAC,iBAAsB,OAAc,cAAjB,CAAyC,CAC9D,GACH;AAEF,MAAI,QAAS,QAAO,6CAAC,WAAQ,MAAa,mBAAQ;AAClD,SAAO;AACT;","names":["import_jsx_runtime"]}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import { B as Blog, a as Block } from '../types-CoPD0ejB.cjs';
|
|
4
|
+
export { F as FaqItem, S as SocialPost } from '../types-CoPD0ejB.cjs';
|
|
5
|
+
|
|
6
|
+
type BlockComponents = Partial<{
|
|
7
|
+
heading: React.ComponentType<{
|
|
8
|
+
level: number;
|
|
9
|
+
children: React.ReactNode;
|
|
10
|
+
}>;
|
|
11
|
+
paragraph: React.ComponentType<{
|
|
12
|
+
children: React.ReactNode;
|
|
13
|
+
}>;
|
|
14
|
+
list: React.ComponentType<{
|
|
15
|
+
items: string[];
|
|
16
|
+
}>;
|
|
17
|
+
quote: React.ComponentType<{
|
|
18
|
+
children: React.ReactNode;
|
|
19
|
+
}>;
|
|
20
|
+
code: React.ComponentType<{
|
|
21
|
+
children: React.ReactNode;
|
|
22
|
+
}>;
|
|
23
|
+
image: React.ComponentType<{
|
|
24
|
+
src?: string;
|
|
25
|
+
alt: string;
|
|
26
|
+
}>;
|
|
27
|
+
wrapper: React.ComponentType<{
|
|
28
|
+
children: React.ReactNode;
|
|
29
|
+
blog: Blog;
|
|
30
|
+
}>;
|
|
31
|
+
}>;
|
|
32
|
+
declare function BlockRenderer({ block, components, }: {
|
|
33
|
+
block: Block;
|
|
34
|
+
components?: BlockComponents;
|
|
35
|
+
}): react_jsx_runtime.JSX.Element | null;
|
|
36
|
+
declare function BlogRenderer({ blog, components, }: {
|
|
37
|
+
blog: Blog;
|
|
38
|
+
components?: BlockComponents;
|
|
39
|
+
}): react_jsx_runtime.JSX.Element;
|
|
40
|
+
|
|
41
|
+
declare const DefaultHeading: ({ level, children, }: {
|
|
42
|
+
level: number;
|
|
43
|
+
children: React.ReactNode;
|
|
44
|
+
}) => react_jsx_runtime.JSX.Element;
|
|
45
|
+
declare const DefaultParagraph: ({ children, }: {
|
|
46
|
+
children: React.ReactNode;
|
|
47
|
+
}) => react_jsx_runtime.JSX.Element;
|
|
48
|
+
declare const DefaultList: ({ items }: {
|
|
49
|
+
items: string[];
|
|
50
|
+
}) => react_jsx_runtime.JSX.Element;
|
|
51
|
+
declare const DefaultQuote: ({ children }: {
|
|
52
|
+
children: React.ReactNode;
|
|
53
|
+
}) => react_jsx_runtime.JSX.Element;
|
|
54
|
+
declare const DefaultCode: ({ children }: {
|
|
55
|
+
children: React.ReactNode;
|
|
56
|
+
}) => react_jsx_runtime.JSX.Element;
|
|
57
|
+
declare const DefaultImage: ({ src, alt, }: {
|
|
58
|
+
src?: string;
|
|
59
|
+
alt: string;
|
|
60
|
+
}) => react_jsx_runtime.JSX.Element | null;
|
|
61
|
+
|
|
62
|
+
export { Block, type BlockComponents, BlockRenderer, Blog, BlogRenderer, DefaultCode, DefaultHeading, DefaultImage, DefaultList, DefaultParagraph, DefaultQuote };
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import { B as Blog, a as Block } from '../types-CoPD0ejB.js';
|
|
4
|
+
export { F as FaqItem, S as SocialPost } from '../types-CoPD0ejB.js';
|
|
5
|
+
|
|
6
|
+
type BlockComponents = Partial<{
|
|
7
|
+
heading: React.ComponentType<{
|
|
8
|
+
level: number;
|
|
9
|
+
children: React.ReactNode;
|
|
10
|
+
}>;
|
|
11
|
+
paragraph: React.ComponentType<{
|
|
12
|
+
children: React.ReactNode;
|
|
13
|
+
}>;
|
|
14
|
+
list: React.ComponentType<{
|
|
15
|
+
items: string[];
|
|
16
|
+
}>;
|
|
17
|
+
quote: React.ComponentType<{
|
|
18
|
+
children: React.ReactNode;
|
|
19
|
+
}>;
|
|
20
|
+
code: React.ComponentType<{
|
|
21
|
+
children: React.ReactNode;
|
|
22
|
+
}>;
|
|
23
|
+
image: React.ComponentType<{
|
|
24
|
+
src?: string;
|
|
25
|
+
alt: string;
|
|
26
|
+
}>;
|
|
27
|
+
wrapper: React.ComponentType<{
|
|
28
|
+
children: React.ReactNode;
|
|
29
|
+
blog: Blog;
|
|
30
|
+
}>;
|
|
31
|
+
}>;
|
|
32
|
+
declare function BlockRenderer({ block, components, }: {
|
|
33
|
+
block: Block;
|
|
34
|
+
components?: BlockComponents;
|
|
35
|
+
}): react_jsx_runtime.JSX.Element | null;
|
|
36
|
+
declare function BlogRenderer({ blog, components, }: {
|
|
37
|
+
blog: Blog;
|
|
38
|
+
components?: BlockComponents;
|
|
39
|
+
}): react_jsx_runtime.JSX.Element;
|
|
40
|
+
|
|
41
|
+
declare const DefaultHeading: ({ level, children, }: {
|
|
42
|
+
level: number;
|
|
43
|
+
children: React.ReactNode;
|
|
44
|
+
}) => react_jsx_runtime.JSX.Element;
|
|
45
|
+
declare const DefaultParagraph: ({ children, }: {
|
|
46
|
+
children: React.ReactNode;
|
|
47
|
+
}) => react_jsx_runtime.JSX.Element;
|
|
48
|
+
declare const DefaultList: ({ items }: {
|
|
49
|
+
items: string[];
|
|
50
|
+
}) => react_jsx_runtime.JSX.Element;
|
|
51
|
+
declare const DefaultQuote: ({ children }: {
|
|
52
|
+
children: React.ReactNode;
|
|
53
|
+
}) => react_jsx_runtime.JSX.Element;
|
|
54
|
+
declare const DefaultCode: ({ children }: {
|
|
55
|
+
children: React.ReactNode;
|
|
56
|
+
}) => react_jsx_runtime.JSX.Element;
|
|
57
|
+
declare const DefaultImage: ({ src, alt, }: {
|
|
58
|
+
src?: string;
|
|
59
|
+
alt: string;
|
|
60
|
+
}) => react_jsx_runtime.JSX.Element | null;
|
|
61
|
+
|
|
62
|
+
export { Block, type BlockComponents, BlockRenderer, Blog, BlogRenderer, DefaultCode, DefaultHeading, DefaultImage, DefaultList, DefaultParagraph, DefaultQuote };
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
// src/react/defaults.tsx
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
var DefaultHeading = ({
|
|
4
|
+
level,
|
|
5
|
+
children
|
|
6
|
+
}) => {
|
|
7
|
+
const Tag = level === 2 ? "h2" : level === 4 ? "h4" : "h3";
|
|
8
|
+
return /* @__PURE__ */ jsx(Tag, { children });
|
|
9
|
+
};
|
|
10
|
+
var DefaultParagraph = ({
|
|
11
|
+
children
|
|
12
|
+
}) => /* @__PURE__ */ jsx("p", { children });
|
|
13
|
+
var DefaultList = ({ items }) => /* @__PURE__ */ jsx("ul", { children: items.map((item, i) => /* @__PURE__ */ jsx("li", { children: item }, i)) });
|
|
14
|
+
var DefaultQuote = ({ children }) => /* @__PURE__ */ jsx("blockquote", { children });
|
|
15
|
+
var DefaultCode = ({ children }) => /* @__PURE__ */ jsx("pre", { children: /* @__PURE__ */ jsx("code", { children }) });
|
|
16
|
+
var DefaultImage = ({
|
|
17
|
+
src,
|
|
18
|
+
alt
|
|
19
|
+
}) => {
|
|
20
|
+
if (!src) return null;
|
|
21
|
+
return /* @__PURE__ */ jsx("img", { src, alt });
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
// src/react/BlogRenderer.tsx
|
|
25
|
+
import { Fragment, jsx as jsx2 } from "react/jsx-runtime";
|
|
26
|
+
var parseListItems = (content) => content.split("\n").map((line) => line.replace(/^\s*[-•*]\s*/, "").trim()).filter(Boolean);
|
|
27
|
+
function BlockRenderer({
|
|
28
|
+
block,
|
|
29
|
+
components = {}
|
|
30
|
+
}) {
|
|
31
|
+
switch (block.type) {
|
|
32
|
+
case "heading": {
|
|
33
|
+
const Heading = components.heading ?? DefaultHeading;
|
|
34
|
+
return /* @__PURE__ */ jsx2(Heading, { level: block.level ?? 2, children: block.content });
|
|
35
|
+
}
|
|
36
|
+
case "paragraph": {
|
|
37
|
+
const Paragraph = components.paragraph ?? DefaultParagraph;
|
|
38
|
+
return /* @__PURE__ */ jsx2(Paragraph, { children: block.content });
|
|
39
|
+
}
|
|
40
|
+
case "list": {
|
|
41
|
+
const List = components.list ?? DefaultList;
|
|
42
|
+
return /* @__PURE__ */ jsx2(List, { items: parseListItems(block.content) });
|
|
43
|
+
}
|
|
44
|
+
case "quote": {
|
|
45
|
+
const Quote = components.quote ?? DefaultQuote;
|
|
46
|
+
return /* @__PURE__ */ jsx2(Quote, { children: block.content });
|
|
47
|
+
}
|
|
48
|
+
case "code": {
|
|
49
|
+
const Code = components.code ?? DefaultCode;
|
|
50
|
+
return /* @__PURE__ */ jsx2(Code, { children: block.content });
|
|
51
|
+
}
|
|
52
|
+
case "image_placeholder": {
|
|
53
|
+
const Image = components.image ?? DefaultImage;
|
|
54
|
+
return /* @__PURE__ */ jsx2(Image, { alt: block.content });
|
|
55
|
+
}
|
|
56
|
+
default:
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function BlogRenderer({
|
|
61
|
+
blog,
|
|
62
|
+
components = {}
|
|
63
|
+
}) {
|
|
64
|
+
const Wrapper = components.wrapper;
|
|
65
|
+
const content = /* @__PURE__ */ jsx2(Fragment, { children: blog.blocks.map((block, i) => /* @__PURE__ */ jsx2(BlockRenderer, { block, components }, i)) });
|
|
66
|
+
if (Wrapper) return /* @__PURE__ */ jsx2(Wrapper, { blog, children: content });
|
|
67
|
+
return content;
|
|
68
|
+
}
|
|
69
|
+
export {
|
|
70
|
+
BlockRenderer,
|
|
71
|
+
BlogRenderer,
|
|
72
|
+
DefaultCode,
|
|
73
|
+
DefaultHeading,
|
|
74
|
+
DefaultImage,
|
|
75
|
+
DefaultList,
|
|
76
|
+
DefaultParagraph,
|
|
77
|
+
DefaultQuote
|
|
78
|
+
};
|
|
79
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/react/defaults.tsx","../../src/react/BlogRenderer.tsx"],"sourcesContent":["import * as React from 'react';\n\nexport const DefaultHeading = ({\n level,\n children,\n}: {\n level: number;\n children: React.ReactNode;\n}) => {\n const Tag = (level === 2 ? 'h2' : level === 4 ? 'h4' : 'h3') as\n | 'h2'\n | 'h3'\n | 'h4';\n return <Tag>{children}</Tag>;\n};\n\nexport const DefaultParagraph = ({\n children,\n}: {\n children: React.ReactNode;\n}) => <p>{children}</p>;\n\nexport const DefaultList = ({ items }: { items: string[] }) => (\n <ul>\n {items.map((item, i) => (\n <li key={i}>{item}</li>\n ))}\n </ul>\n);\n\nexport const DefaultQuote = ({ children }: { children: React.ReactNode }) => (\n <blockquote>{children}</blockquote>\n);\n\nexport const DefaultCode = ({ children }: { children: React.ReactNode }) => (\n <pre>\n <code>{children}</code>\n </pre>\n);\n\nexport const DefaultImage = ({\n src,\n alt,\n}: {\n src?: string;\n alt: string;\n}) => {\n if (!src) return null;\n // eslint-disable-next-line @next/next/no-img-element\n return <img src={src} alt={alt} />;\n};\n","import * as React from 'react';\nimport type { Blog, Block } from '../types';\nimport {\n DefaultHeading,\n DefaultParagraph,\n DefaultList,\n DefaultQuote,\n DefaultCode,\n DefaultImage,\n} from './defaults';\n\nexport type BlockComponents = Partial<{\n heading: React.ComponentType<{ level: number; children: React.ReactNode }>;\n paragraph: React.ComponentType<{ children: React.ReactNode }>;\n list: React.ComponentType<{ items: string[] }>;\n quote: React.ComponentType<{ children: React.ReactNode }>;\n code: React.ComponentType<{ children: React.ReactNode }>;\n image: React.ComponentType<{ src?: string; alt: string }>;\n wrapper: React.ComponentType<{ children: React.ReactNode; blog: Blog }>;\n}>;\n\nconst parseListItems = (content: string): string[] =>\n content\n .split('\\n')\n .map((line) => line.replace(/^\\s*[-•*]\\s*/, '').trim())\n .filter(Boolean);\n\nexport function BlockRenderer({\n block,\n components = {},\n}: {\n block: Block;\n components?: BlockComponents;\n}) {\n switch (block.type) {\n case 'heading': {\n const Heading = components.heading ?? DefaultHeading;\n return <Heading level={block.level ?? 2}>{block.content}</Heading>;\n }\n case 'paragraph': {\n const Paragraph = components.paragraph ?? DefaultParagraph;\n return <Paragraph>{block.content}</Paragraph>;\n }\n case 'list': {\n const List = components.list ?? DefaultList;\n return <List items={parseListItems(block.content)} />;\n }\n case 'quote': {\n const Quote = components.quote ?? DefaultQuote;\n return <Quote>{block.content}</Quote>;\n }\n case 'code': {\n const Code = components.code ?? DefaultCode;\n return <Code>{block.content}</Code>;\n }\n case 'image_placeholder': {\n const Image = components.image ?? DefaultImage;\n return <Image alt={block.content} />;\n }\n default:\n return null;\n }\n}\n\nexport function BlogRenderer({\n blog,\n components = {},\n}: {\n blog: Blog;\n components?: BlockComponents;\n}) {\n const Wrapper = components.wrapper;\n const content = (\n <>\n {blog.blocks.map((block, i) => (\n <BlockRenderer key={i} block={block} components={components} />\n ))}\n </>\n );\n if (Wrapper) return <Wrapper blog={blog}>{content}</Wrapper>;\n return content;\n}\n"],"mappings":";AAaS;AAXF,IAAM,iBAAiB,CAAC;AAAA,EAC7B;AAAA,EACA;AACF,MAGM;AACJ,QAAM,MAAO,UAAU,IAAI,OAAO,UAAU,IAAI,OAAO;AAIvD,SAAO,oBAAC,OAAK,UAAS;AACxB;AAEO,IAAM,mBAAmB,CAAC;AAAA,EAC/B;AACF,MAEM,oBAAC,OAAG,UAAS;AAEZ,IAAM,cAAc,CAAC,EAAE,MAAM,MAClC,oBAAC,QACE,gBAAM,IAAI,CAAC,MAAM,MAChB,oBAAC,QAAY,kBAAJ,CAAS,CACnB,GACH;AAGK,IAAM,eAAe,CAAC,EAAE,SAAS,MACtC,oBAAC,gBAAY,UAAS;AAGjB,IAAM,cAAc,CAAC,EAAE,SAAS,MACrC,oBAAC,SACC,8BAAC,UAAM,UAAS,GAClB;AAGK,IAAM,eAAe,CAAC;AAAA,EAC3B;AAAA,EACA;AACF,MAGM;AACJ,MAAI,CAAC,IAAK,QAAO;AAEjB,SAAO,oBAAC,SAAI,KAAU,KAAU;AAClC;;;ACba,SAoCT,UApCS,OAAAA,YAAA;AAhBb,IAAM,iBAAiB,CAAC,YACtB,QACG,MAAM,IAAI,EACV,IAAI,CAAC,SAAS,KAAK,QAAQ,gBAAgB,EAAE,EAAE,KAAK,CAAC,EACrD,OAAO,OAAO;AAEZ,SAAS,cAAc;AAAA,EAC5B;AAAA,EACA,aAAa,CAAC;AAChB,GAGG;AACD,UAAQ,MAAM,MAAM;AAAA,IAClB,KAAK,WAAW;AACd,YAAM,UAAU,WAAW,WAAW;AACtC,aAAO,gBAAAA,KAAC,WAAQ,OAAO,MAAM,SAAS,GAAI,gBAAM,SAAQ;AAAA,IAC1D;AAAA,IACA,KAAK,aAAa;AAChB,YAAM,YAAY,WAAW,aAAa;AAC1C,aAAO,gBAAAA,KAAC,aAAW,gBAAM,SAAQ;AAAA,IACnC;AAAA,IACA,KAAK,QAAQ;AACX,YAAM,OAAO,WAAW,QAAQ;AAChC,aAAO,gBAAAA,KAAC,QAAK,OAAO,eAAe,MAAM,OAAO,GAAG;AAAA,IACrD;AAAA,IACA,KAAK,SAAS;AACZ,YAAM,QAAQ,WAAW,SAAS;AAClC,aAAO,gBAAAA,KAAC,SAAO,gBAAM,SAAQ;AAAA,IAC/B;AAAA,IACA,KAAK,QAAQ;AACX,YAAM,OAAO,WAAW,QAAQ;AAChC,aAAO,gBAAAA,KAAC,QAAM,gBAAM,SAAQ;AAAA,IAC9B;AAAA,IACA,KAAK,qBAAqB;AACxB,YAAM,QAAQ,WAAW,SAAS;AAClC,aAAO,gBAAAA,KAAC,SAAM,KAAK,MAAM,SAAS;AAAA,IACpC;AAAA,IACA;AACE,aAAO;AAAA,EACX;AACF;AAEO,SAAS,aAAa;AAAA,EAC3B;AAAA,EACA,aAAa,CAAC;AAChB,GAGG;AACD,QAAM,UAAU,WAAW;AAC3B,QAAM,UACJ,gBAAAA,KAAA,YACG,eAAK,OAAO,IAAI,CAAC,OAAO,MACvB,gBAAAA,KAAC,iBAAsB,OAAc,cAAjB,CAAyC,CAC9D,GACH;AAEF,MAAI,QAAS,QAAO,gBAAAA,KAAC,WAAQ,MAAa,mBAAQ;AAClD,SAAO;AACT;","names":["jsx"]}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
type Block = {
|
|
2
|
+
type: 'heading';
|
|
3
|
+
level?: number;
|
|
4
|
+
content: string;
|
|
5
|
+
} | {
|
|
6
|
+
type: 'paragraph';
|
|
7
|
+
content: string;
|
|
8
|
+
} | {
|
|
9
|
+
type: 'list';
|
|
10
|
+
content: string;
|
|
11
|
+
} | {
|
|
12
|
+
type: 'quote';
|
|
13
|
+
content: string;
|
|
14
|
+
} | {
|
|
15
|
+
type: 'code';
|
|
16
|
+
content: string;
|
|
17
|
+
} | {
|
|
18
|
+
type: 'image_placeholder';
|
|
19
|
+
content: string;
|
|
20
|
+
};
|
|
21
|
+
type FaqItem = {
|
|
22
|
+
question: string;
|
|
23
|
+
answer: string;
|
|
24
|
+
};
|
|
25
|
+
type SocialPost = {
|
|
26
|
+
provider: string;
|
|
27
|
+
content: string;
|
|
28
|
+
social_title: string | null;
|
|
29
|
+
};
|
|
30
|
+
type Blog = {
|
|
31
|
+
id: string;
|
|
32
|
+
slug: string;
|
|
33
|
+
language: string;
|
|
34
|
+
title: string;
|
|
35
|
+
description: string | null;
|
|
36
|
+
blocks: Block[];
|
|
37
|
+
faq: FaqItem[];
|
|
38
|
+
og_image: string | null;
|
|
39
|
+
image_url: string | null;
|
|
40
|
+
video_url: string | null;
|
|
41
|
+
video_prompt: string | null;
|
|
42
|
+
structured_data: Record<string, unknown> | null;
|
|
43
|
+
published_at: string;
|
|
44
|
+
social_posts: SocialPost[];
|
|
45
|
+
};
|
|
46
|
+
type WebhookEvent = 'blog.created' | 'blog.updated' | 'blog.published';
|
|
47
|
+
type WebhookPayload = {
|
|
48
|
+
event: WebhookEvent;
|
|
49
|
+
timestamp: string;
|
|
50
|
+
blog: Blog & Record<string, unknown>;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export type { Blog as B, FaqItem as F, SocialPost as S, WebhookEvent as W, Block as a, WebhookPayload as b };
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
type Block = {
|
|
2
|
+
type: 'heading';
|
|
3
|
+
level?: number;
|
|
4
|
+
content: string;
|
|
5
|
+
} | {
|
|
6
|
+
type: 'paragraph';
|
|
7
|
+
content: string;
|
|
8
|
+
} | {
|
|
9
|
+
type: 'list';
|
|
10
|
+
content: string;
|
|
11
|
+
} | {
|
|
12
|
+
type: 'quote';
|
|
13
|
+
content: string;
|
|
14
|
+
} | {
|
|
15
|
+
type: 'code';
|
|
16
|
+
content: string;
|
|
17
|
+
} | {
|
|
18
|
+
type: 'image_placeholder';
|
|
19
|
+
content: string;
|
|
20
|
+
};
|
|
21
|
+
type FaqItem = {
|
|
22
|
+
question: string;
|
|
23
|
+
answer: string;
|
|
24
|
+
};
|
|
25
|
+
type SocialPost = {
|
|
26
|
+
provider: string;
|
|
27
|
+
content: string;
|
|
28
|
+
social_title: string | null;
|
|
29
|
+
};
|
|
30
|
+
type Blog = {
|
|
31
|
+
id: string;
|
|
32
|
+
slug: string;
|
|
33
|
+
language: string;
|
|
34
|
+
title: string;
|
|
35
|
+
description: string | null;
|
|
36
|
+
blocks: Block[];
|
|
37
|
+
faq: FaqItem[];
|
|
38
|
+
og_image: string | null;
|
|
39
|
+
image_url: string | null;
|
|
40
|
+
video_url: string | null;
|
|
41
|
+
video_prompt: string | null;
|
|
42
|
+
structured_data: Record<string, unknown> | null;
|
|
43
|
+
published_at: string;
|
|
44
|
+
social_posts: SocialPost[];
|
|
45
|
+
};
|
|
46
|
+
type WebhookEvent = 'blog.created' | 'blog.updated' | 'blog.published';
|
|
47
|
+
type WebhookPayload = {
|
|
48
|
+
event: WebhookEvent;
|
|
49
|
+
timestamp: string;
|
|
50
|
+
blog: Blog & Record<string, unknown>;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export type { Blog as B, FaqItem as F, SocialPost as S, WebhookEvent as W, Block as a, WebhookPayload as b };
|
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "chaingrow-blog",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Drop-in Next.js SDK for rendering blogs published from ChainGrow.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "ChainGrow",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/chainmatics/chaingrow.git",
|
|
10
|
+
"directory": "packages/blog-sdk"
|
|
11
|
+
},
|
|
12
|
+
"keywords": ["chaingrow", "blog", "nextjs", "cms", "sdk"],
|
|
13
|
+
"sideEffects": false,
|
|
14
|
+
"type": "module",
|
|
15
|
+
"main": "./dist/index.cjs",
|
|
16
|
+
"module": "./dist/index.js",
|
|
17
|
+
"types": "./dist/index.d.ts",
|
|
18
|
+
"exports": {
|
|
19
|
+
".": {
|
|
20
|
+
"types": "./dist/index.d.ts",
|
|
21
|
+
"import": "./dist/index.js",
|
|
22
|
+
"require": "./dist/index.cjs"
|
|
23
|
+
},
|
|
24
|
+
"./next": {
|
|
25
|
+
"types": "./dist/next/index.d.ts",
|
|
26
|
+
"import": "./dist/next/index.js",
|
|
27
|
+
"require": "./dist/next/index.cjs"
|
|
28
|
+
},
|
|
29
|
+
"./next/webhook": {
|
|
30
|
+
"types": "./dist/next/webhook.d.ts",
|
|
31
|
+
"import": "./dist/next/webhook.js",
|
|
32
|
+
"require": "./dist/next/webhook.cjs"
|
|
33
|
+
},
|
|
34
|
+
"./react": {
|
|
35
|
+
"types": "./dist/react/index.d.ts",
|
|
36
|
+
"import": "./dist/react/index.js",
|
|
37
|
+
"require": "./dist/react/index.cjs"
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
"files": ["dist", "README.md"],
|
|
41
|
+
"scripts": {
|
|
42
|
+
"build": "tsup",
|
|
43
|
+
"dev": "tsup --watch",
|
|
44
|
+
"typecheck": "tsc --noEmit",
|
|
45
|
+
"prepublishOnly": "yarn build"
|
|
46
|
+
},
|
|
47
|
+
"peerDependencies": {
|
|
48
|
+
"next": ">=14.0.0",
|
|
49
|
+
"react": ">=18.0.0"
|
|
50
|
+
},
|
|
51
|
+
"peerDependenciesMeta": {
|
|
52
|
+
"next": { "optional": true },
|
|
53
|
+
"react": { "optional": true }
|
|
54
|
+
},
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"@types/node": "^20.0.0",
|
|
57
|
+
"@types/react": "^18.2.0",
|
|
58
|
+
"next": "^14.2.0",
|
|
59
|
+
"react": "^18.3.0",
|
|
60
|
+
"tsup": "^8.0.0",
|
|
61
|
+
"typescript": "^5.4.0"
|
|
62
|
+
}
|
|
63
|
+
}
|